11 changed files with 429 additions and 3 deletions
@ -0,0 +1,241 @@ |
|||||
|
from esky.util import appdir_from_executable #@UnresolvedImport |
||||
|
from threading import Thread |
||||
|
from version import VERSION |
||||
|
from wx.lib.softwareupdate import SoftwareUpdate |
||||
|
import os |
||||
|
import sys |
||||
|
import time |
||||
|
import webbrowser |
||||
|
import wx |
||||
|
|
||||
|
# Include proper dirs |
||||
|
if hasattr(sys, 'frozen'): |
||||
|
import libs |
||||
|
base_path = os.path.dirname(os.path.dirname(os.path.abspath(libs.__file__))) |
||||
|
else: |
||||
|
base_path = os.path.dirname(os.path.abspath(__file__)) |
||||
|
|
||||
|
def icon(): |
||||
|
icon = 'icon_windows.png' |
||||
|
if os.path.isfile('icon_mac.png'): |
||||
|
icon = 'icon_mac.png' |
||||
|
|
||||
|
return wx.Icon(icon, wx.BITMAP_TYPE_PNG) |
||||
|
|
||||
|
lib_dir = os.path.join(base_path, 'libs') |
||||
|
|
||||
|
sys.path.insert(0, base_path) |
||||
|
sys.path.insert(0, lib_dir) |
||||
|
|
||||
|
from couchpotato.environment import Env |
||||
|
|
||||
|
class TaskBarIcon(wx.TaskBarIcon): |
||||
|
|
||||
|
TBMENU_OPEN = wx.NewId() |
||||
|
TBMENU_SETTINGS = wx.NewId() |
||||
|
TBMENU_EXIT = wx.ID_EXIT |
||||
|
|
||||
|
closed = False |
||||
|
menu = False |
||||
|
enabled = False |
||||
|
|
||||
|
def __init__(self, frame): |
||||
|
wx.TaskBarIcon.__init__(self) |
||||
|
self.frame = frame |
||||
|
|
||||
|
self.SetIcon(icon()) |
||||
|
|
||||
|
self.Bind(wx.EVT_TASKBAR_LEFT_UP, self.OnTaskBarClick) |
||||
|
self.Bind(wx.EVT_TASKBAR_RIGHT_UP, self.OnTaskBarClick) |
||||
|
|
||||
|
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.onTaskBarClose, id = self.TBMENU_EXIT) |
||||
|
|
||||
|
def OnTaskBarClick(self, evt): |
||||
|
menu = self.CreatePopupMenu() |
||||
|
self.PopupMenu(menu) |
||||
|
menu.Destroy() |
||||
|
|
||||
|
def enable(self): |
||||
|
self.enabled = True |
||||
|
|
||||
|
if self.menu: |
||||
|
self.open_menu.Enable(True) |
||||
|
self.setting_menu.Enable(True) |
||||
|
|
||||
|
self.open_menu.SetText('Open') |
||||
|
|
||||
|
def CreatePopupMenu(self): |
||||
|
|
||||
|
if not self.menu: |
||||
|
self.menu = wx.Menu() |
||||
|
self.open_menu = self.menu.Append(self.TBMENU_OPEN, 'Open') |
||||
|
self.setting_menu = self.menu.Append(self.TBMENU_SETTINGS, 'About') |
||||
|
self.exit_menu = self.menu.Append(self.TBMENU_EXIT, 'Quit') |
||||
|
|
||||
|
if not self.enabled: |
||||
|
self.open_menu.Enable(False) |
||||
|
self.setting_menu.Enable(False) |
||||
|
|
||||
|
self.open_menu.SetText('Loading...') |
||||
|
|
||||
|
return self.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/about/' |
||||
|
webbrowser.open(url) |
||||
|
|
||||
|
def onTaskBarClose(self, evt): |
||||
|
if self.closed: |
||||
|
return |
||||
|
|
||||
|
self.closed = True |
||||
|
|
||||
|
self.RemoveIcon() |
||||
|
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, style = wx.FRAME_NO_TASKBAR) |
||||
|
|
||||
|
self.parent = parent |
||||
|
self.tbicon = TaskBarIcon(self) |
||||
|
|
||||
|
|
||||
|
class WorkerThread(Thread): |
||||
|
|
||||
|
def __init__(self, desktop): |
||||
|
Thread.__init__(self) |
||||
|
self.daemon = True |
||||
|
self._desktop = desktop |
||||
|
|
||||
|
self.start() |
||||
|
|
||||
|
def run(self): |
||||
|
|
||||
|
# Get options via arg |
||||
|
from couchpotato.runner import getOptions |
||||
|
args = ['--quiet'] |
||||
|
self.options = getOptions(args) |
||||
|
|
||||
|
# Load settings |
||||
|
settings = Env.get('settings') |
||||
|
settings.setFile(self.options.config_file) |
||||
|
|
||||
|
# Create data dir if needed |
||||
|
self.data_dir = os.path.expanduser(Env.setting('data_dir')) |
||||
|
if self.data_dir == '': |
||||
|
from couchpotato.core.helpers.variable import getDataDir |
||||
|
self.data_dir = getDataDir() |
||||
|
|
||||
|
if not os.path.isdir(self.data_dir): |
||||
|
os.makedirs(self.data_dir) |
||||
|
|
||||
|
# Create logging dir |
||||
|
self.log_dir = os.path.join(self.data_dir, 'logs'); |
||||
|
if not os.path.isdir(self.log_dir): |
||||
|
os.mkdir(self.log_dir) |
||||
|
|
||||
|
try: |
||||
|
from couchpotato.runner import runCouchPotato |
||||
|
runCouchPotato(self.options, base_path, args, data_dir = self.data_dir, log_dir = self.log_dir, Env = Env, desktop = self._desktop) |
||||
|
except: |
||||
|
pass |
||||
|
|
||||
|
self._desktop.frame.Close() |
||||
|
self._desktop.ExitMainLoop() |
||||
|
|
||||
|
|
||||
|
class CouchPotatoApp(wx.App, SoftwareUpdate): |
||||
|
|
||||
|
settings = {} |
||||
|
events = {} |
||||
|
restart = False |
||||
|
closing = False |
||||
|
triggered_onClose = False |
||||
|
|
||||
|
def OnInit(self): |
||||
|
|
||||
|
# Updater |
||||
|
base_url = 'https://api.couchpota.to/updates/%s' |
||||
|
self.InitUpdates(base_url % VERSION + '/', 'https://couchpota.to/updates/%s' % 'changelog.html', |
||||
|
icon = icon()) |
||||
|
|
||||
|
self.frame = MainFrame(self) |
||||
|
self.frame.Bind(wx.EVT_CLOSE, self.onClose) |
||||
|
|
||||
|
# CouchPotato thread |
||||
|
self.worker = WorkerThread(self) |
||||
|
|
||||
|
return True |
||||
|
|
||||
|
def onAppLoad(self): |
||||
|
self.frame.tbicon.enable() |
||||
|
|
||||
|
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): |
||||
|
|
||||
|
if not self.closing: |
||||
|
self.closing = True |
||||
|
self.frame.tbicon.onTaskBarClose(event) |
||||
|
|
||||
|
onClose = self.events.get('onClose') |
||||
|
if onClose and not self.triggered_onClose: |
||||
|
self.triggered_onClose = True |
||||
|
onClose(event) |
||||
|
|
||||
|
def afterShutdown(self, restart = False): |
||||
|
self.frame.Destroy() |
||||
|
self.restart = restart |
||||
|
self.ExitMainLoop() |
||||
|
|
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
|
||||
|
app = CouchPotatoApp(redirect = False) |
||||
|
app.MainLoop() |
||||
|
|
||||
|
time.sleep(1) |
||||
|
|
||||
|
if app.restart: |
||||
|
|
||||
|
def appexe_from_executable(exepath): |
||||
|
appdir = appdir_from_executable(exepath) |
||||
|
exename = os.path.basename(exepath) |
||||
|
|
||||
|
if sys.platform == "darwin": |
||||
|
if os.path.isdir(os.path.join(appdir, "Contents", "MacOS")): |
||||
|
return os.path.join(appdir, "Contents", "MacOS", exename) |
||||
|
|
||||
|
return os.path.join(appdir, exename) |
||||
|
|
||||
|
exe = appexe_from_executable(sys.executable) |
||||
|
os.chdir(os.path.dirname(exe)) |
||||
|
|
||||
|
os.execv(exe, [exe] + sys.argv[1:]) |
Binary file not shown.
After Width: | Height: | Size: 345 KiB |
After Width: | Height: | Size: 435 B |
After Width: | Height: | Size: 367 B |
@ -0,0 +1,52 @@ |
|||||
|
#define MyAppName "CouchPotato" |
||||
|
#define MyAppVer "2.6.1" |
||||
|
#define MyAppBit "win32" |
||||
|
//#define MyAppBit "win-amd64" |
||||
|
|
||||
|
[Setup] |
||||
|
AppName={#MyAppName} |
||||
|
AppVersion=2 |
||||
|
AppVerName={#MyAppName} |
||||
|
DefaultDirName={userappdata}\{#MyAppName}\application |
||||
|
DisableProgramGroupPage=yes |
||||
|
DisableDirPage=yes |
||||
|
UninstallDisplayIcon=./icon.ico |
||||
|
SetupIconFile=./icon.ico |
||||
|
OutputDir=./dist |
||||
|
OutputBaseFilename={#MyAppName}-{#MyAppVer}.{#MyAppBit}.installer |
||||
|
AppPublisher=Your Mom |
||||
|
AppPublisherURL=http://couchpota.to |
||||
|
PrivilegesRequired=none |
||||
|
WizardSmallImageFile=installer_icon.bmp |
||||
|
WizardImageFile=installer_banner.bmp |
||||
|
UsePreviousAppDir=no |
||||
|
|
||||
|
[Messages] |
||||
|
WelcomeLabel1=Installing [name]! |
||||
|
WelcomeLabel2=This wizard will install [name] to your AppData folder. It does this so it can use the build in updater without needing admin rights. |
||||
|
|
||||
|
[CustomMessages] |
||||
|
LaunchProgram=Launch {#MyAppName} right now. |
||||
|
|
||||
|
[Files] |
||||
|
Source: "./dist/{#MyAppName}-{#MyAppVer}.{#MyAppBit}/*"; Flags: recursesubdirs; DestDir: "{app}" |
||||
|
|
||||
|
[Icons] |
||||
|
Name: "{commonprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppName}.exe" |
||||
|
Name: "{userstartup}\{#MyAppName}"; Filename: "{app}\{#MyAppName}.exe"; Tasks: startup |
||||
|
|
||||
|
[Tasks] |
||||
|
Name: "startup"; Description: "Run {#MyAppName} at startup"; Flags: unchecked |
||||
|
|
||||
|
[Run] |
||||
|
Filename: {app}\{#MyAppName}.exe; Description: {cm:LaunchProgram,{#MyAppName}}; Flags: nowait postinstall skipifsilent |
||||
|
|
||||
|
|
||||
|
[UninstallDelete] |
||||
|
Type: filesandordirs; Name: "{app}\appdata" |
||||
|
Type: filesandordirs; Name: "{app}\Microsoft.VC90.CRT" |
||||
|
Type: filesandordirs; Name: "{app}\updates" |
||||
|
Type: filesandordirs; Name: "{app}\CouchPotato*" |
||||
|
Type: filesandordirs; Name: "{app}\python27.dll" |
||||
|
Type: filesandordirs; Name: "{app}\unins000.dat" |
||||
|
Type: filesandordirs; Name: "{app}\unins000.exe" |
After Width: | Height: | Size: 151 KiB |
After Width: | Height: | Size: 9.6 KiB |
@ -0,0 +1,133 @@ |
|||||
|
from esky import bdist_esky |
||||
|
from setuptools import setup |
||||
|
import os |
||||
|
import sys |
||||
|
import version |
||||
|
|
||||
|
|
||||
|
# 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) |
||||
|
|
||||
|
def getDataFiles(dirs): |
||||
|
data_files = [] |
||||
|
for directory in dirs: |
||||
|
for root, dirs, files in os.walk(directory): |
||||
|
if files: |
||||
|
for filename in files: |
||||
|
if filename[:-4] is not '.pyc': |
||||
|
data_files.append((root, [os.path.join(root, filename)])) |
||||
|
|
||||
|
return data_files |
||||
|
|
||||
|
includes = [ |
||||
|
'telnetlib', |
||||
|
'xml.etree.ElementTree', |
||||
|
'xml.etree.cElementTree', |
||||
|
'xml.dom', |
||||
|
'xml.dom.minidom', |
||||
|
'netrc', |
||||
|
'csv', |
||||
|
'HTMLParser', |
||||
|
'version', |
||||
|
'distutils', |
||||
|
'lxml', 'lxml.etree', 'lxml._elementpath', 'gzip', |
||||
|
] |
||||
|
|
||||
|
excludes = [ |
||||
|
'doctest', |
||||
|
'pdb', |
||||
|
'unittest', |
||||
|
'difflib', |
||||
|
'bsddb', |
||||
|
'pywin.debugger', 'pywin.debugger.dbgcon', 'pywin.dialogs', |
||||
|
'Tkconstants', 'Tkinter', |
||||
|
'curses', |
||||
|
'_gtkagg', '_tkagg', |
||||
|
] |
||||
|
|
||||
|
# Windows |
||||
|
if sys.platform == "win32": |
||||
|
import py2exe |
||||
|
|
||||
|
sys.path.append('C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.21022.8_none_bcb86ed6ac711f91') |
||||
|
|
||||
|
FREEZER = 'py2exe' |
||||
|
FREEZER_OPTIONS = dict( |
||||
|
compressed = 0, |
||||
|
bundle_files = 3, |
||||
|
dll_excludes = [ |
||||
|
'msvcp90.dll', |
||||
|
'msvcr90.dll', |
||||
|
'msvcr71.dll', |
||||
|
'mswsock.dll', |
||||
|
'powrprof.dll', |
||||
|
'USP10.dll', |
||||
|
'libgdk-win32-2.0-0.dll', |
||||
|
'libgobject-2.0-0.dll', |
||||
|
'tcl84.dll', |
||||
|
'tk84.dll' |
||||
|
], |
||||
|
packages = ['couchpotato', 'libs'], |
||||
|
includes = includes, |
||||
|
excludes = excludes, |
||||
|
skip_archive = 1, |
||||
|
) |
||||
|
exeICON = os.path.join(base_path, 'icon.ico') |
||||
|
DATA_FILES = getDataFiles([r'.\\couchpotato', r'.\\libs']) |
||||
|
DATA_FILES.append('icon_windows.png') |
||||
|
file_ext = 'win32.zip' |
||||
|
|
||||
|
|
||||
|
# OSX |
||||
|
elif sys.platform == "darwin": |
||||
|
import py2app |
||||
|
|
||||
|
FREEZER = 'py2app' |
||||
|
FREEZER_OPTIONS = dict( |
||||
|
arch = 'intel', |
||||
|
optimize = 2, |
||||
|
strip = True, |
||||
|
argv_emulation = False, |
||||
|
site_packages = False, |
||||
|
iconfile = 'icon.icns', |
||||
|
plist = dict( |
||||
|
LSUIElement = True, |
||||
|
), |
||||
|
packages = ['couchpotato', 'libs'], |
||||
|
includes = includes, |
||||
|
excludes = excludes, |
||||
|
) |
||||
|
exeICON = None |
||||
|
DATA_FILES = ['icon_mac.png'] |
||||
|
|
||||
|
file_ext = 'macosx-10_6-intel.zip' |
||||
|
|
||||
|
# Common |
||||
|
NAME = "CouchPotato" |
||||
|
APP = [bdist_esky.Executable("Desktop.py", name = NAME, icon = exeICON, gui_only = True,)] |
||||
|
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, |
||||
|
author = "Ruud", |
||||
|
author_email = "info@couchpota.to", |
||||
|
description = 'CouchPotato %s' % version.VERSION, |
||||
|
data_files = DATA_FILES, |
||||
|
options = dict(bdist_esky = ESKY_OPTIONS), |
||||
|
) |
||||
|
|
||||
|
#distpath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dist') |
||||
|
#zipfilename = os.path.join(distpath, '%s-%s.%s' % (NAME, version.VERSION, file_ext)) |
||||
|
#zfile = zipfile.ZipFile(zipfilename, "r") |
||||
|
#zfile.extractall(distpath) |
@ -1,2 +1,2 @@ |
|||||
VERSION = None |
VERSION = '2.6.1' |
||||
BRANCH = 'master' |
BRANCH = 'desktop' |
||||
|
Loading…
Reference in new issue