diff --git a/Darwin/Applications/Open Media Library.app/Contents/Info.plist b/Darwin/Applications/Open Media Library.app/Contents/Info.plist
new file mode 100644
index 0000000..98023b4
--- /dev/null
+++ b/Darwin/Applications/Open Media Library.app/Contents/Info.plist
@@ -0,0 +1,59 @@
+
+
+
+
+ BuildMachineOSBuild
+ 15C40a
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ Open Media Library
+ CFBundleIconFile
+ AppIcon
+ CFBundleIdentifier
+ com.openmedialibrary.oml
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ Open Media Library
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 0.2
+ CFBundleSignature
+ ????
+ CFBundleSupportedPlatforms
+
+ MacOSX
+
+ CFBundleVersion
+ 1
+ DTCompiler
+ com.apple.compilers.llvm.clang.1_0
+ DTPlatformBuild
+ 7B1005
+ DTPlatformVersion
+ GM
+ DTSDKBuild
+ 15A278
+ DTSDKName
+ macosx10.11
+ DTXcode
+ 0711
+ DTXcodeBuild
+ 7B1005
+ LSApplicationCategoryType
+ public.app-category.education
+ LSMinimumSystemVersion
+ 10.6
+ NSAppTransportSecurity
+
+ NSAllowsArbitraryLoads
+
+
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/Darwin/Applications/Open Media Library.app/Contents/MacOS/Open Media Library b/Darwin/Applications/Open Media Library.app/Contents/MacOS/Open Media Library
new file mode 100755
index 0000000..4e01166
Binary files /dev/null and b/Darwin/Applications/Open Media Library.app/Contents/MacOS/Open Media Library differ
diff --git a/Darwin/Applications/Open Media Library.app/Contents/PkgInfo b/Darwin/Applications/Open Media Library.app/Contents/PkgInfo
new file mode 100644
index 0000000..bd04210
--- /dev/null
+++ b/Darwin/Applications/Open Media Library.app/Contents/PkgInfo
@@ -0,0 +1 @@
+APPL????
\ No newline at end of file
diff --git a/Darwin/Applications/Open Media Library.app/Contents/Resources/AppIcon.icns b/Darwin/Applications/Open Media Library.app/Contents/Resources/AppIcon.icns
new file mode 100644
index 0000000..4271786
Binary files /dev/null and b/Darwin/Applications/Open Media Library.app/Contents/Resources/AppIcon.icns differ
diff --git a/Darwin/Applications/Open Media Library.app/Contents/Resources/English.lproj/InfoPlist.strings b/Darwin/Applications/Open Media Library.app/Contents/Resources/English.lproj/InfoPlist.strings
new file mode 100644
index 0000000..5e45963
Binary files /dev/null and b/Darwin/Applications/Open Media Library.app/Contents/Resources/English.lproj/InfoPlist.strings differ
diff --git a/Darwin/Applications/Open Media Library.app/Contents/Resources/English.lproj/MainMenu.nib b/Darwin/Applications/Open Media Library.app/Contents/Resources/English.lproj/MainMenu.nib
new file mode 100644
index 0000000..e955860
Binary files /dev/null and b/Darwin/Applications/Open Media Library.app/Contents/Resources/English.lproj/MainMenu.nib differ
diff --git a/Darwin/Applications/Open Media Library.app/Contents/Resources/index.html b/Darwin/Applications/Open Media Library.app/Contents/Resources/index.html
new file mode 100644
index 0000000..cb02249
--- /dev/null
+++ b/Darwin/Applications/Open Media Library.app/Contents/Resources/index.html
@@ -0,0 +1,10 @@
+
+
+ Open Media Library
+
+
+ Installing Open Media Library...
+
+
+
+
diff --git a/Darwin/Applications/Open Media Library.app/Contents/Resources/install.html b/Darwin/Applications/Open Media Library.app/Contents/Resources/install.html
new file mode 100644
index 0000000..12604c6
--- /dev/null
+++ b/Darwin/Applications/Open Media Library.app/Contents/Resources/install.html
@@ -0,0 +1,24 @@
+
+
+
+
+ Open Media Library
+
+
+
+
+
diff --git a/Darwin/Applications/Open Media Library.app/Contents/Resources/install.py b/Darwin/Applications/Open Media Library.app/Contents/Resources/install.py
new file mode 100755
index 0000000..0dc329b
--- /dev/null
+++ b/Darwin/Applications/Open Media Library.app/Contents/Resources/install.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+from __future__ import division, with_statement
+
+from contextlib import closing
+import json
+import os
+import sys
+import time
+import tarfile
+import urllib2
+import SimpleHTTPServer
+import SocketServer
+from threading import Thread
+
+
+PORT = 9841
+static_dir = os.path.normpath(os.path.abspath(os.path.dirname(__file__)))
+
+
+class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+ def do_OPTIONS(self):
+ self.send_response(200, 'OK')
+ self.send_header('Allow', 'GET, POST, OPTIONS')
+ self.send_header('Access-Control-Allow-Origin', '*')
+ self.send_header('Access-Control-Allow-Headers', 'X-Requested-With')
+ self.send_header('Content-Length', '0')
+ self.end_headers()
+
+ def do_GET(self):
+
+ if self.path == '/status':
+ content = json.dumps(self.server.install.status)
+ self.send_response(200, 'OK')
+ else:
+ path = os.path.join(static_dir, 'index.html' if self.path == '/' else self.path[1:])
+ if os.path.exists(path):
+ with open(path) as fd:
+ content = fd.read()
+ self.send_response(200, 'OK')
+ else:
+ self.send_response(404, 'not found')
+ content = '404 not found'
+ self.send_header('Access-Control-Allow-Origin', '*')
+ self.send_header('Content-Length', str(len(content)))
+ self.end_headers()
+ self.wfile.write(content)
+
+
+class Install(Thread):
+
+ release_url = "http://downloads.openmedialibrary.com/release.json"
+ status = {}
+
+ def __init__(self, target, httpd):
+ target = os.path.normpath(os.path.join(os.path.abspath(target)))
+ self.target = target
+ self.httpd = httpd
+ Thread.__init__(self)
+ self.daemon = True
+ self.start()
+
+ def run(self):
+ target = self.target
+ if not os.path.exists(target):
+ os.makedirs(target)
+ os.chdir(target)
+ release = self.get_release()
+ self.status["release"] = release
+ for module in release['modules']:
+ self.status["step"] = 'downloading %s' % module
+ self.status["progress"] = 0
+ self.status["size"] = 0
+ package_tar = release['modules'][module]['name']
+ url = self.release_url.replace('release.json', package_tar)
+ self.download(url, package_tar)
+ self.status["step"] = 'extracting %s' % module
+ self.status["progress"] = 0
+ tar = tarfile.open(package_tar)
+ tar.extractall()
+ tar.close()
+ os.unlink(package_tar)
+ os.symlink('openmedialibrary/ctl', 'ctl')
+ self.status["progress"] = 0
+ self.status["step"] = "setup"
+ os.system("./ctl setup")
+ self.status["progress"] = 1
+ with open('config/release.json', 'w') as fd:
+ json.dump(release, fd, indent=2)
+ self.status = {"installation finished. starting...": True}
+ os.system("./ctl start &")
+ time.sleep(1)
+ self.httpd.shutdown()
+
+
+ def download(self, url, filename):
+ dirname = os.path.dirname(filename)
+ if dirname and not os.path.exists(dirname):
+ os.makedirs(dirname)
+ with open(filename, 'w') as f:
+ with closing(urllib2.urlopen(url)) as u:
+ size = int(u.headers.get('content-length', 0))
+ self.status["size"] = size
+ available = 0
+ data = u.read(4096)
+ while data:
+ if size:
+ available += len(data)
+ self.status["progress"] = available/size
+ f.write(data)
+ data = u.read(4096)
+
+ def get_release(self):
+ with closing(urllib2.urlopen(self.release_url)) as u:
+ data = json.load(u)
+ return data
+
+
+
+if __name__ == '__main__':
+ if len(sys.argv) == 1:
+ target = os.path.expanduser("~/Library/Application Support/Open Media Library")
+ elif len(sys.argv) != 2:
+ print "usage: %s [target]" % sys.argv[0]
+ sys.exit(1)
+ else:
+ target = sys.argv[1]
+ SocketServer.TCPServer.allow_reuse_address = True
+ httpd = SocketServer.TCPServer(("", PORT), Handler)
+ install = Install(target, httpd)
+ httpd.install = install
+ httpd.serve_forever()
diff --git a/Darwin/Applications/Open Media Library.app/Contents/Resources/js/install.js b/Darwin/Applications/Open Media Library.app/Contents/Resources/js/install.js
new file mode 100644
index 0000000..2bc80e2
--- /dev/null
+++ b/Darwin/Applications/Open Media Library.app/Contents/Resources/js/install.js
@@ -0,0 +1,44 @@
+function load() {
+ var base = '//127.0.0.1:9842',
+ ws = new WebSocket('ws:' + base + '/ws');
+ ws.onopen = function(event) {
+ document.location.href = 'http:' + base;
+ };
+ ws.onerror = function(event) {
+ ws.close();
+ setTimeout(load, 500);
+ };
+ ws.onclose = function(event) {
+ setTimeout(load, 500);
+ };
+};
+
+function update() {
+ var xhr = new XMLHttpRequest();
+ xhr.onload = function() {
+ var response = JSON.parse(this.responseText);
+ if (response.step) {
+ var status = response.step;
+ if (response.progress) {
+ status = parseInt(response.progress * 100) + '% ' + status;
+ }
+ document.getElementById('status').innerHTML = status;
+ setTimeout(update, 1000);
+ } else {
+ document.getElementById('status').innerHTML = "done";
+ setTimeout(load, 500);
+ }
+ };
+ xhr.onerror = function() {
+ var status = document.getElementById('status').innerHTML;
+ if (['done', 'setup'].indexOf(status) == -1) {
+ document.getElementById('status').innerHTML = "error";
+ }
+ load();
+ }
+ xhr.open('get', '/status');
+ xhr.send();
+
+}
+
+update();