diff --git a/CouchPotato.py b/CouchPotato.py
old mode 100755
new mode 100644
index f4083f6013479ee497e8be9e495a703f7a3a27d6..a07f235c862f1fea815f98896fd7160889696141
--- a/CouchPotato.py
+++ b/CouchPotato.py
@@ -1,63 +1,167 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-'''Wrapper for the command line interface.'''
-
-from os.path import dirname
+from threading import Thread
+from wx.lib.softwareupdate import SoftwareUpdate
 import os
 import sys
-import subprocess
-import time
+import webbrowser
+import wx
 
 
-# Root path
-base_path = dirname(os.path.abspath(__file__))
+# Include proper dirs
+if hasattr(sys, 'frozen'):
+    import libs
+    base_path = os.path.dirname(os.path.dirname(os.path.abspath(libs.__file__)))
+    print base_path
+else:
+    base_path = os.path.dirname(os.path.abspath(__file__))
 
-# Insert local directories into path
-sys.path.insert(0, os.path.join(base_path, 'libs'))
+lib_dir = os.path.join(base_path, 'libs')
 
-from couchpotato.core.logger import CPLog
-log = CPLog(__name__)
+sys.path.insert(0, base_path)
+sys.path.insert(0, lib_dir)
 
 # Get options via arg
 from couchpotato.runner import getOptions
-from couchpotato.core.helpers.variable import getDataDir
-options = getOptions(base_path, sys.argv[1:])
-data_dir = getDataDir()
-
-def start():
-    try:
-        args = [sys.executable] + sys.argv
-        new_environ = os.environ.copy()
-        new_environ['cp_main'] = 'true'
-
-        if os.name == 'nt':
-            for key, value in new_environ.iteritems():
-                if isinstance(value, unicode):
-                    new_environ[key] = value.encode('iso-8859-1')
-
-        subprocess.call(args, env = new_environ)
-        return os.path.isfile(os.path.join(data_dir, 'restart'))
-    except KeyboardInterrupt, e:
-        pass
-    except Exception, e:
-        log.critical(e)
-        return 0
-
 from couchpotato.runner import runCouchPotato
-if __name__ == '__main__':
 
-    if os.environ.get('cp_main', 'false') == 'true':
+
+class TaskBarIcon(wx.TaskBarIcon):
+
+    TBMENU_OPEN = wx.NewId()
+    TBMENU_SETTINGS = wx.NewId()
+    TBMENU_ABOUT = wx.ID_ABOUT
+    TBMENU_EXIT = wx.ID_EXIT
+
+    def __init__(self, frame):
+        wx.TaskBarIcon.__init__(self)
+        self.frame = frame
+
+        icon = wx.Icon('icon.ico', wx.BITMAP_TYPE_ANY)
+        self.SetIcon(icon)
+
+        self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.onTaskBarActivate)
+
+        self.Bind(wx.EVT_MENU, self.onOpen, id = self.TBMENU_OPEN)
+        self.Bind(wx.EVT_MENU, self.onSettings, id = self.TBMENU_SETTINGS)
+        self.Bind(wx.EVT_MENU, self.onAbout, id = self.TBMENU_ABOUT)
+        self.Bind(wx.EVT_MENU, self.onTaskBarClose, id = self.TBMENU_EXIT)
+
+
+    def CreatePopupMenu(self):
+        menu = wx.Menu()
+        menu.Append(self.TBMENU_OPEN, "Open")
+        menu.Append(self.TBMENU_SETTINGS, "Settings")
+        menu.Append(self.TBMENU_ABOUT, "About")
+        menu.Append(self.TBMENU_EXIT, "Close")
+        return menu
+
+    def onOpen(self, event):
+        url = self.frame.parent.getSetting('base_url')
+        webbrowser.open(url)
+
+    def onSettings(self, event):
+        url = self.frame.parent.getSetting('base_url') + '/settings/'
+        webbrowser.open(url)
+
+    def onAbout(self, event):
+        print 'onAbout'
+
+    def onTaskBarActivate(self, evt):
+        if not self.frame.IsShown():
+            self.frame.Show(True)
+        self.frame.Raise()
+
+    def onTaskBarClose(self, evt):
+        wx.CallAfter(self.frame.Close)
+
+    def makeIcon(self, img):
+        if "wxMSW" in wx.PlatformInfo:
+            img = img.Scale(16, 16)
+        elif "wxGTK" in wx.PlatformInfo:
+            img = img.Scale(22, 22)
+
+        icon = wx.IconFromBitmap(img.CopyFromBitmap())
+        return icon
+
+
+class MainFrame(wx.Frame):
+
+    def __init__(self, parent):
+        wx.Frame.__init__(self, None)
+
+        self.parent = parent
+        self.tbicon = TaskBarIcon(self)
+
+
+class WorkerThread(Thread):
+
+    def __init__(self, desktop):
+        Thread.__init__(self)
+        self._desktop = desktop
+
+        self.start()
+
+    def run(self):
+
+        args = ['--nogit', '--console_log']#, '--quiet']
+        options = getOptions(base_path, args)
+
         try:
-            runCouchPotato(options, base_path, sys.argv[1:])
+            runCouchPotato(options, base_path, args, desktop = self._desktop)
+        except KeyboardInterrupt, e:
+            raise
         except Exception, e:
-            log.critical(e)
-    else:
-        while 1:
-            restart = start()
-            if not restart:
-                break
-
-    from couchpotato.core.event import fireEvent
-    fireEvent('app.crappy_shutdown', single = True)
-    time.sleep(1)
-    sys.exit()
+            raise
+        finally:
+            pass
+
+
+class CouchPotatoApp(wx.App, SoftwareUpdate):
+
+    settings = {}
+    events = {}
+    restart = False
+
+    def OnInit(self):
+
+        # Updater
+        base_url = 'http://couchpotatoapp.com/updates/'
+        self.InitUpdates(base_url, base_url + 'changelog.txt',
+                         icon = wx.Icon('icon.ico'))
+
+        self.frame = MainFrame(self)
+        self.frame.Bind(wx.EVT_CLOSE, self.onClose)
+
+        # CouchPotato thread
+        self.worker = WorkerThread(self)
+
+        return True
+
+    def setSettings(self, settings = {}):
+        self.settings = settings
+
+    def getSetting(self, name):
+        return self.settings.get(name)
+
+    def addEvents(self, events = {}):
+        for name in events.iterkeys():
+            self.events[name] = events[name]
+
+    def onClose(self, event):
+        onClose = self.events.get('onClose')
+        if self.events.get('onClose'):
+            onClose(event)
+        else:
+            self.afterShutdown()
+
+    def afterShutdown(self, restart = False):
+        self.frame.Destroy()
+        self.restart = restart
+
+
+if __name__ == '__main__':
+    app = CouchPotatoApp(redirect = False)
+    app.MainLoop()
+
+    #path = os.path.join(sys.path[0].decode(sys.getfilesystemencoding()), sys.argv[0])
+    #if app.restart:
+    #    wx.Process.Open(sys.executable + ' ' + path)
diff --git a/README.md b/README.md
index 4f70d0912de5de6f0b544a9a076d9cb3849183e2..8226558949da1981cefda24f7934fd3502e6d051 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1 @@
-CouchPotato Server
-=====
-
-CouchPotato (CP) is an automatic NZB and torrent downloader. You can keep a "movies I want"-list and it will search for NZBs/torrents of these movies every X hours.
-Once a movie is found, it will send it to SABnzbd or download the torrent to a specified directory.
\ No newline at end of file
+CouchPotato Desktop
\ No newline at end of file
diff --git a/couchpotato/core/_base/_core/main.py b/couchpotato/core/_base/_core/main.py
index e9b4bdf5e3d6a1918d95edf5d0303c455e3d3055..4c7e75b56ef27e6c9c6a741264f2f438f8a1ded9 100644
--- a/couchpotato/core/_base/_core/main.py
+++ b/couchpotato/core/_base/_core/main.py
@@ -1,4 +1,3 @@
-from couchpotato import app
 from couchpotato.api import addApiView
 from couchpotato.core.event import fireEvent, addEvent
 from couchpotato.core.helpers.request import jsonified
@@ -7,7 +6,6 @@ from couchpotato.core.logger import CPLog
 from couchpotato.core.plugins.base import Plugin
 from couchpotato.environment import Env
 from flask import request
-from flask.helpers import url_for
 import os
 import time
 import traceback
@@ -29,6 +27,7 @@ class Core(Plugin):
         addEvent('app.crappy_restart', self.crappyRestart)
         addEvent('app.load', self.launchBrowser, priority = 1)
         addEvent('app.base_url', self.createBaseUrl)
+        addEvent('app.api_url', self.createApiUrl)
 
         addEvent('setting.save.core.password', self.md5Password)
 
@@ -43,16 +42,10 @@ class Core(Plugin):
         })
 
     def crappyShutdown(self):
-        ctx = app.test_request_context()
-        ctx.push()
-        self.urlopen('%s%sapp.shutdown' % (fireEvent('app.base_url', single = True), url_for('api.index')))
-        ctx.pop()
+        self.urlopen('%sapp.shutdown' % self.createApiUrl())
 
     def crappyRestart(self):
-        ctx = app.test_request_context()
-        ctx.push()
-        self.urlopen('%s%sapp.restart' % (fireEvent('app.base_url', single = True), url_for('api.index')))
-        ctx.pop()
+        self.urlopen('%sapp.restart' % self.createApiUrl())
 
     def shutdown(self):
         self.initShutdown()
@@ -63,6 +56,7 @@ class Core(Plugin):
         return 'restarting'
 
     def initShutdown(self, restart = False):
+        log.info('Shutting down' if not restart else 'Restarting')
 
         fireEvent('app.shutdown')
 
@@ -122,3 +116,7 @@ class Core(Plugin):
         port = Env.setting('port')
 
         return '%s:%d' % (cleanHost(host).rstrip('/'), int(port))
+
+    def createApiUrl(self):
+
+        return '%s/%s/' % (self.createBaseUrl(), Env.setting('api_key'))
diff --git a/couchpotato/core/_base/desktop/main.py b/couchpotato/core/_base/desktop/main.py
index bd52dcd5f2e0e1b465fb71cfa6f964dc4a27919b..ce1ff282413d40ce5fdf3609e69bfebb7ff40719 100644
--- a/couchpotato/core/_base/desktop/main.py
+++ b/couchpotato/core/_base/desktop/main.py
@@ -7,25 +7,28 @@ log = CPLog(__name__)
 
 if Env.get('desktop'):
 
-    #import os
-    #import sys
-    import wx
-
     class Desktop(Plugin):
 
         def __init__(self):
 
             desktop = Env.get('desktop')
             desktop.setSettings({
-                'url': fireEvent('app.base_url', single = True)
+                'base_url': fireEvent('app.base_url', single = True),
+                'api_url': fireEvent('app.api_url', single = True),
+                'api': Env.setting('api'),
             })
 
-            def onClose(event):
-                return fireEvent('app.crappy_shutdown')
-            desktop.close_handler = onClose
+            # Events from desktop
+            desktop.addEvents({
+                'onClose': self.onClose,
+            })
 
+            # Events to desktop
             addEvent('app.after_shutdown', desktop.afterShutdown)
 
+        def onClose(self, event):
+            return fireEvent('app.crappy_shutdown', single = True)
+
 else:
 
     class Desktop(Plugin):
diff --git a/couchpotato/environment.py b/couchpotato/environment.py
index c6b427e5b481c7ac84eb06ff2fb79e2648dbb944..39adb3dfa8d93f76b8821bd9bf9e09a9a7ec7dd0 100644
--- a/couchpotato/environment.py
+++ b/couchpotato/environment.py
@@ -14,7 +14,7 @@ class Env(object):
     _args = None
     _quiet = False
     _deamonize = False
-    _version = 0.5
+    _desktop = None
 
     ''' Data paths and directories '''
     _app_dir = ""
diff --git a/icon.icns b/icon.icns
new file mode 100644
index 0000000000000000000000000000000000000000..04cbc86716a3ac1d1ed54800755fbbd9c52872ae
Binary files /dev/null and b/icon.icns differ
diff --git a/icon.ico b/icon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..f93f9fbe77b7025a5dd2ed49855fcdd36ee60fbe
Binary files /dev/null and b/icon.ico differ
diff --git a/libs/guessit/fileutils.py b/libs/guessit/fileutils.py
index 7c07af7132141d1631493aae4b47ce04901c6fbd..1c263b811c4a7f3b0c1fa961ac6fe5e0f7450fe9 100644
--- a/libs/guessit/fileutils.py
+++ b/libs/guessit/fileutils.py
@@ -18,7 +18,6 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-import ntpath
 import os.path
 
 
@@ -45,7 +44,7 @@ def split_path(path):
     """
     result = []
     while True:
-        head, tail = ntpath.split(path)
+        head, tail = os.path.split(path)
 
         # on Unix systems, the root folder is '/'
         if head == '/' and tail == '':
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..c505c6b1d79dd9dfbea899772c19af93650faae0
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,86 @@
+from esky import bdist_esky
+from setuptools import setup
+import sys
+import version
+import os
+
+
+# Include proper dirs
+base_path = os.path.dirname(os.path.abspath(__file__))
+lib_dir = os.path.join(base_path, 'libs')
+
+sys.path.insert(0, base_path)
+sys.path.insert(0, lib_dir)
+
+
+
+# Windows
+if sys.platform == "win32":
+    import py2exe
+
+    FREEZER = 'py2exe'
+    FREEZER_OPTIONS = dict(
+        compressed = 0,
+        optimize = 0,
+        bundle_files = 3,
+        dll_excludes = [
+            'MSVCP90.dll',
+            'mswsock.dll',
+            'powrprof.dll',
+            'USP10.dll',
+        ],
+        packages = ['couchpotato', 'libs'],
+        includes = [
+            'telnetlib',
+            'xml.etree.ElementTree',
+            'xml.etree.cElementTree',
+            'xml.dom',
+            'xml.dom.minidom',
+        ],
+    )
+    exeICON = 'icon.ico'
+
+
+# OSX
+elif sys.platform == "darwin":
+    import py2app
+
+    FREEZER = 'py2app'
+    FREEZER_OPTIONS = dict(
+        argv_emulation = False,
+        iconfile = 'icon.icns',
+        plist = dict(
+            LSUIElement = True,
+        ),
+        packages = ['couchpotato', 'libs'],
+        includes = [
+            'telnetlib',
+            'xml.etree.ElementTree',
+            'xml.etree.cElementTree',
+            'xml.dom',
+            'xml.dom.minidom',
+        ],
+    )
+    exeICON = None
+
+# Common
+NAME = "CouchPotato"
+APP = [bdist_esky.Executable("CouchPotato.py", gui_only = True, icon = exeICON,)]
+DATA_FILES = ['icon.ico']
+ESKY_OPTIONS = dict(
+    freezer_module = FREEZER,
+    freezer_options = FREEZER_OPTIONS,
+    bundle_msvcrt = True,
+)
+
+
+# Build the app and the esky bundle
+setup(
+    name = NAME,
+    scripts = APP,
+    version = version.VERSION,
+    data_files = DATA_FILES,
+    options = dict(bdist_esky = ESKY_OPTIONS),
+)
+
+
diff --git a/version.py b/version.py
new file mode 100644
index 0000000000000000000000000000000000000000..f42418682769f8b6abea9d921dd84e956e639b4c
--- /dev/null
+++ b/version.py
@@ -0,0 +1 @@
+VERSION = '0.5'