A few months ago I wrote a blog post about working with RAR files in ColdFusion. The short story is that RAR, a file archive format, is not supported by cfzip. Surprisingly there doesn't seem to be in Java packages out there that provide full integration with the format. I ended up writing a wrapper around the free 7-Zip command line program. I mentioned this in passing but the real reason I wanted to build this was to do something with the CBR files (Comic Book files) I had on my hard drive. I thought it would be interesting to write code to extract the cover image from the files. You could imagine many uses for this. Perhaps simply creating a nice HTML page of covers would be neat. Whatever. To be honest, it's kind of pointless - but that hasn't stopped me before so why now. Here is what I built.
I began by creating a list of comics from my hard drive. I assumed I was going to have multiple screw ups as I worked on my code so I also added basic caching in.
<!--- make a copy since arraydelat will change copy --->
<cfset comics = duplicate(comics)>
<cfset dir = "H:\comics">
<cfset comics = cacheGet("comics")>
<cfif isNull(comics)>
<cfoutput>
<p>Getting initial list...</p>
</cfoutput>
<cfset comics = directoryList(dir,true,"list","*.cbr")>
<cfset cachePut("comics", comics,createTimeSpan(0,0,10,0))>
</cfif>
That last bit of code there just handles breaking the reference to the cache since I end up manipulating the results a bit. Ok - now a quick output:
<cfoutput>
<p>There are #arrayLen(comics)# total comics.</p>
</cfoutput>
I then decided to grab 100 comics from the set:
<!--- Given our list, pick a set --->
<cfset chosen = []>
<cfset total = min(100, arrayLen(comics))>
<cfloop condition="arrayLen(chosen) lt total">
<cfset target = randRange(1, arrayLen(comics))>
<!--- I had a few ._ files --->
<cfif not find("\._",comics[target])>
<cfset arrayAppend(chosen, comics[target])>
<cfset arrayDeleteAt(comics, target)>
</cfif>
</cfloop>
And just to be sure - output that size as well:
<cfoutput>
<p>Ok, we have #arrayLen(chosen)# comics.</p>
</cfoutput>
Next - I set up a temporary directory. Remember that I'm going to be shelling out to an executable. This means I won't be able to use RAM drives:
<!--- make a temp dir --->
<cfset tmpDir = expandPath("./comics")>
<cfif not directoryExists(tmpDir)>
<cfdirectory action="create" directory="#tmpDir#">
</cfif>
Ok - now let's make our CFC:
<!--- initialize our CFC --->
<cfset sevenZipexe = "C:\Program Files\7-Zip\7z.exe">
<cfset sevenzipcfc = new sevenzip(sevenzipexe)>
Now we are going to loop through our comics and try to get the cover. In general, the first item in a list of files from a CBR RAR file is either an image, or a directory. So my code handles that and looks in the second item if the first is a directory:
<!--- now get our covers --->
<cfloop index="c" array="#chosen#">
<cftry>
<cfset files = sevenzipcfc.list(c)>
<!--- first item may be a directory - for now we see if size is 0, and if 0, skip to the next --->
<cfif files.size[1] neq 0 and listLast(files.name[1],".") is "jpg">
<cfset sevenzipcfc.extract(c,files.name[1],tmpdir)>
<cfoutput>Just extracted #files.name[1]# from #c#<br/></cfoutput>
<cfelseif files.recordCount gte 2 and files.size[2] neq 0 and listLast(files.name[2],".") is "jpg">
<cfset sevenzipcfc.extract(c,files.name[2],tmpdir)>
<cfoutput>Just extracted #files.name[2]# from #c#<br/></cfoutput>
</cfif>
<cfcatch>
<cfoutput><b>Error: #cfcatch.message#</b><br/></cfoutput>
</cfcatch>
</cftry>
</cfloop>
At this point I had a bunch of images! It worked. Then I thought - let's do something neat with the images...
<cfset images = directoryList(tmpdir)>
<cfset canvas = imageNew("", 1500, 1145)>
<cfset processed = 0>
<cfset i = 1>
<cfset x = 0>
<cfset y = 0>
<cfloop condition="processed lt 50">
<cfif isImageFile(images[i])>
<cfset myimg = imageRead(images[i])>
<cfset imageResize(myimg,150,229)>
<!--- Position is based on the index --->
<cfset imagePaste(canvas, myimg, x, y)>
<cfset processed++>
<cfset x+= 150>
<cfif x is 1500>
<cfset x = 0>
<cfset y+= 229>
</cfif>
</cfif>
<cfset i++>
</cfloop>
Basically - read in a list of images, process line by line and resize each one. I then paste it into a large canvas moving from left to right. And finally...
<cfset imageWrite(canvas, "c:\Users\Raymond\Desktop\finalcover.jpg")>
So the result? Click to make it bigger...
Not perfect - but kind of cool I think. Here is the entire template. Please note I wrote this rather quickly. It's not meant to be production ready.
<!--- make a copy since arraydelat will change copy --->
<cfset comics = duplicate(comics)> <cfoutput>
<p>There are #arrayLen(comics)# total comics.</p>
</cfoutput> <!--- Given our list, pick a set --->
<cfset chosen = []>
<cfset total = min(100, arrayLen(comics))>
<cfloop condition="arrayLen(chosen) lt total">
<cfset target = randRange(1, arrayLen(comics))>
<!--- I had a few ._ files --->
<cfif not find("._",comics[target])>
<cfset arrayAppend(chosen, comics[target])>
<cfset arrayDeleteAt(comics, target)>
</cfif>
</cfloop> <cfoutput>
<p>Ok, we have #arrayLen(chosen)# comics.</p>
</cfoutput> <!--- make a temp dir --->
<cfset tmpDir = expandPath("./comics")>
<cfif not directoryExists(tmpDir)>
<cfdirectory action="create" directory="#tmpDir#">
</cfif> <!--- initialize our CFC --->
<cfset sevenZipexe = "C:\Program Files\7-Zip\7z.exe">
<cfset sevenzipcfc = new sevenzip(sevenzipexe)> <!--- now get our covers --->
<cfloop index="c" array="#chosen#">
<cftry>
<cfset files = sevenzipcfc.list(c)>
<!--- first item may be a directory - for now we see if size is 0, and if 0, skip to the next --->
<cfif files.size[1] neq 0 and listLast(files.name[1],".") is "jpg">
<cfset sevenzipcfc.extract(c,files.name[1],tmpdir)>
<cfoutput>Just extracted #files.name[1]# from #c#<br/></cfoutput>
<cfelseif files.recordCount gte 2 and files.size[2] neq 0 and listLast(files.name[2],".") is "jpg">
<cfset sevenzipcfc.extract(c,files.name[2],tmpdir)>
<cfoutput>Just extracted #files.name[2]# from #c#<br/></cfoutput>
</cfif>
<cfcatch>
<cfoutput><b>Error: #cfcatch.message#</b><br/></cfoutput>
</cfcatch>
</cftry>
</cfloop> <cfset images = directoryList(tmpdir)>
<cfset canvas = imageNew("", 1500, 1145)>
<cfset processed = 0>
<cfset i = 1>
<cfset x = 0>
<cfset y = 0>
<cfloop condition="processed lt 50">
<cfif isImageFile(images[i])>
<cfset myimg = imageRead(images[i])>
<cfset imageResize(myimg,150,229)>
<!--- Position is based on the index --->
<cfset imagePaste(canvas, myimg, x, y)>
<cfset processed++>
<cfset x+= 150>
<cfif x is 1500>
<cfset x = 0>
<cfset y+= 229>
</cfif>
</cfif>
<cfset i++>
</cfloop> <cfset imageWrite(canvas, "c:\Users\Raymond\Desktop\finalcover.jpg")>
<cfset dir = "H:\comics">
<cfset comics = cacheGet("comics")>
<cfif isNull(comics) or 0>
<cfoutput>
<p>Getting initial list...</p>
</cfoutput>
<cfset comics = directoryList(dir,true,"list","*.cbr")>
<cfset cachePut("comics", comics,createTimeSpan(0,0,10,0))>
</cfif>