Created
September 5, 2010 21:36
-
-
Save ajordens/566344 to your computer and use it in GitHub Desktop.
Groovy script that exports your iTunes Mobile Application downloads to a csv.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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