Skip to content

Instantly share code, notes, and snippets.

@ajordens
Created September 5, 2010 21:36
Show Gist options
  • Save ajordens/566344 to your computer and use it in GitHub Desktop.
Save ajordens/566344 to your computer and use it in GitHub Desktop.
Groovy script that exports your iTunes Mobile Application downloads to a csv.
import groovy.io.FileType
import java.util.zip.ZipEntry
import au.com.bytecode.opencsv.CSVWriter
import java.util.zip.ZipFile
@Grapes([
@Grab(group = 'net.sf.opencsv', module = 'opencsv', version = '2.1')])
/**
* Exports your 'Mobile Application' contents to a .csv
*
* usage:
*
* Exporter exporter = new Exporter("/Users/username/Music/iTunes/")
* exporter.parse()
* expoter.export("/Users/username/iTunes_export.csv")
*
* @author Adam Jordens (adam@jordens.org)
*/
class Exporter {
public static final String META_DATA_FILENAME = 'iTunesMetadata.plist'
public static final String PLIST_DICT_KEY = 'key'
public static final String APP_EXTENSION = 'ipa'
private final String iTunesDirectory
private final Map infoByApplication = new HashMap<String, Map<String, String>>()
/**
* Construct an Exporter.
*
* @param iTunesDirectory Full path to your iTunes directory
*/
def Exporter(String iTunesDirectory) {
this.iTunesDirectory = iTunesDirectory
}
/**
* Parse the application meta-data contained within 'iTunesDirectory'
*
* @return A collection of parsed application names
*/
public Collection<String> parse() {
def applications = [:] as Map
File directory = new File(iTunesDirectory)
directory.eachFileRecurse(FileType.ANY, { File file ->
if (file.getName().endsWith(APP_EXTENSION)) {
applications[file.getName()] = file.getAbsolutePath()
}
})
String tempDir = System.getProperty('java.io.tmpdir')
def temporaryFile = new File(tempDir, META_DATA_FILENAME)
applications.each { def applicationName, String zipArchivePath ->
def dict = [:] as TreeMap
def zipFile = new ZipFile(new File(zipArchivePath))
ZipEntry metaDataEntry = (ZipEntry) zipFile.entries().find {it.name == META_DATA_FILENAME}
def inputStream = zipFile.getInputStream(metaDataEntry)
def outputStream = new FileOutputStream(temporaryFile)
outputStream << inputStream
def xml = convertBinaryToXml(temporaryFile)
def plist = new XmlSlurper().parseText(xml)
Iterator it = plist.dict.children().iterator()
while (it.hasNext()) {
def node = it.next()
if (node.name() == PLIST_DICT_KEY) {
dict[node.text()] = it.next().text()
}
}
infoByApplication[applicationName] = dict
}
return infoByApplication.keySet()
}
/**
* Exports all previously parsed applications as csv
*
* @param outputCsv Destination CSV
* @return true if export was successful, false otherwise
*/
public boolean export(String outputCsv) {
CSVWriter writer = new CSVWriter(new FileWriter(outputCsv));
try
{
// not all meta-data contain the same attributes, header should be the (unique) union of everything
def header = [] as SortedSet
infoByApplication.values().each { Map info ->
header.addAll(info.keySet())
}
writer.writeNext(header as String[])
// write out each application, if an application does not have meta-data for a specific attribute, output null
infoByApplication.each {def applicationName, Map applicationInfo ->
def line = []
header.each { String column ->
line << applicationInfo[column]
}
writer.writeNext(line as String[])
}
}
finally
{
writer.close();
}
return true
}
/**
* Uses 'plutil' on OS X to convert a binary plist to xml
*
* @param file Binary-encoded plist file (if file is already XML, that's fine)
* @return
*/
private String convertBinaryToXml(File file) {
Process process = ["sh", "-c", "plutil -convert xml1 ${file.getAbsolutePath()}"].execute()
process.waitFor()
if (process.exitValue() != 0) {
println("stderr: ${process.err.text}")
println("stdout: ${process.in.text}")
}
InputStream inputStream = new FileInputStream(file)
return inputStream.text
}
}
Exporter exporter = new Exporter("/Users/ajordens/Mobile Applications/")
def applications = exporter.parse()
println 'Applications'
println '------------'
println applications.join('\n')
def outputCsv = '/Users/ajordens/iTunes_export.csv'
boolean success = exporter.export(outputCsv)
println ''
println "export to '${outputCsv}' was ${success ? '' : 'not'}successful"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment