Compare commits

...

16 Commits

Author SHA1 Message Date
Ruud e9593f60e5 Question button 10 years ago
Ruud 9a8e86b9db One up! 10 years ago
Ruud 350999eaa7 Merge branch 'master' into desktop 10 years ago
Ruud 9b21ca9ad1 Merge branch 'master' into desktop 10 years ago
Ruud 08800e4596 Merge branch 'master' into desktop 10 years ago
Ruud bc6727c948 One up! 10 years ago
Ruud 8bb0e40cfb Flexbox IE bug 10 years ago
Ruud 915ef2ecc9 Nice userscript message on empty list 10 years ago
Ruud fc12a3e325 Merge branch 'master' into desktop 10 years ago
Ruud 965b8089f1 Don't error out on empty date 10 years ago
Ruud 0bd953409e One up! 10 years ago
Ruud 32cf40c121 Merge branch 'master' into desktop 10 years ago
Ruud be1065641c Merge branch 'master' into desktop 10 years ago
Ruud Burger d5627c47f9 Dummy commit 10 years ago
Ruud b20a590aab One up! 10 years ago
Ruud f08f2b0339 Merge branch 'develop' 10 years ago
  1. 241
      Desktop.py
  2. 4
      couchpotato/core/_base/updater/main.py
  3. 2
      couchpotato/static/scripts/couchpotato.js
  4. 10
      couchpotato/static/style/combined.min.css
  5. 4
      couchpotato/static/style/main.scss
  6. BIN
      icon.icns
  7. BIN
      icon.ico
  8. BIN
      icon_mac.png
  9. BIN
      icon_windows.png
  10. 52
      installer.iss
  11. BIN
      installer_banner.bmp
  12. BIN
      installer_icon.bmp
  13. 134
      setup.py
  14. 4
      version.py

241
Desktop.py

@ -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:])

4
couchpotato/core/_base/updater/main.py

@ -83,6 +83,8 @@ class Updater(Plugin):
try: try:
if self.conf('notification'): if self.conf('notification'):
info = self.updater.info() info = self.updater.info()
version_date = 'the future!'
if info['update_version']['date']:
version_date = datetime.fromtimestamp(info['update_version']['date']) version_date = datetime.fromtimestamp(info['update_version']['date'])
fireEvent('updater.updated', 'Updated to a new version with hash "%s", this version is from %s' % (info['update_version']['hash'], version_date), data = info) fireEvent('updater.updated', 'Updated to a new version with hash "%s", this version is from %s' % (info['update_version']['hash'], version_date), data = info)
except: except:
@ -101,6 +103,8 @@ class Updater(Plugin):
if self.updater.check(): if self.updater.check():
if not self.available_notified and self.conf('notification') and not self.conf('automatic'): if not self.available_notified and self.conf('notification') and not self.conf('automatic'):
info = self.updater.info() info = self.updater.info()
version_date = 'the future!'
if info['update_version']['date']:
version_date = datetime.fromtimestamp(info['update_version']['date']) version_date = datetime.fromtimestamp(info['update_version']['date'])
fireEvent('updater.available', message = 'A new update with hash "%s" is available, this version is from %s' % (info['update_version']['hash'], version_date), data = info) fireEvent('updater.available', message = 'A new update with hash "%s" is available, this version is from %s' % (info['update_version']['hash'], version_date), data = info)
self.available_notified = True self.available_notified = True

2
couchpotato/static/scripts/couchpotato.js

@ -1,4 +1,4 @@
var CouchPotato = new Class({ var CouchPotato = new Class({
Implements: [Events, Options], Implements: [Events, Options],

10
couchpotato/static/style/combined.min.css

@ -775,7 +775,7 @@ input[type=text],textarea{-webkit-appearance:none}
.more_menu .wrapper ul li:first-child{border-top:0} .more_menu .wrapper ul li:first-child{border-top:0}
.more_menu .wrapper ul li a{display:block;color:#000;padding:5px 10px;font-size:1em;line-height:22px} .more_menu .wrapper ul li a{display:block;color:#000;padding:5px 10px;font-size:1em;line-height:22px}
.question,.table .item{display:-webkit-flex;display:-ms-flexbox} .question,.table .item{display:-webkit-flex;display:-ms-flexbox}
.dark .more_menu .wrapper ul li a,.question,.question a{color:#FFF} .dark .more_menu .wrapper ul li a{color:#FFF}
.more_menu .wrapper ul li:first-child a{padding-top:10px} .more_menu .wrapper ul li:first-child a{padding-top:10px}
.more_menu .wrapper ul li:last-child a{padding-bottom:10px} .more_menu .wrapper ul li:last-child a{padding-bottom:10px}
.messages{position:fixed;right:0;bottom:0;width:320px;z-index:2000;overflow:hidden;font-size:14px;font-weight:700;padding:5px} .messages{position:fixed;right:0;bottom:0;width:320px;z-index:2000;overflow:hidden;font-size:14px;font-weight:700;padding:5px}
@ -789,13 +789,13 @@ input[type=text],textarea{-webkit-appearance:none}
.messages .message.show{max-height:100px;-webkit-transform:scale(1);transform:scale(1)} .messages .message.show{max-height:100px;-webkit-transform:scale(1);transform:scale(1)}
.messages .message.hide{max-height:0;padding:0 20px;margin:0;-webkit-transform:scale(0);transform:scale(0)} .messages .message.hide{max-height:0;padding:0 20px;margin:0;-webkit-transform:scale(0);transform:scale(0)}
.messages .close{padding:10px 8px;color:#FFF} .messages .close{padding:10px 8px;color:#FFF}
.question{position:fixed;z-index:20000;padding:20px;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center} .question{position:fixed;z-index:20000;color:#FFF;padding:20px;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}
.question .inner{width:100%;max-width:500px} .question .inner{width:100%;max-width:500px}
.question h3{display:block;margin-bottom:20px;font-size:1.4em;font-weight:lighter} .question h3{display:block;margin-bottom:20px;font-size:1.4em;font-weight:lighter}
.question .hint{margin:-20px 0 20px} .question .hint{margin:-20px 0 20px}
.question a{border-color:#FFF;transition:none} .question a{border-color:#FFF;color:#FFF;transition:none}
.question a:hover{background:#FFF;color:#ac0000} .question a:hover{background:#ac0000;color:#FFF}
.dark .question a:hover{color:#0C9DF8} .dark .question a:hover{background:#0C9DF8}
.mask{background:rgba(0,0,0,.8);z-index:1000;text-align:center;bottom:0;left:0;opacity:0;transition:opacity 500ms} .mask{background:rgba(0,0,0,.8);z-index:1000;text-align:center;bottom:0;left:0;opacity:0;transition:opacity 500ms}
.mask .message,.mask .spinner{position:absolute;top:50%;left:50%} .mask .message,.mask .spinner{position:absolute;top:50%;left:50%}
.mask .message{color:#FFF;text-align:center;width:320px;margin:-49px 0 0 -160px;font-size:16px} .mask .message{color:#FFF;text-align:center;width:320px;margin:-49px 0 0 -160px;font-size:16px}

4
couchpotato/static/style/main.scss

@ -729,8 +729,8 @@ input[type=text], textarea {
transition: none; transition: none;
&:hover { &:hover {
background: #FFF; @include theme(background, primary);
@include theme(color, primary); color: #FFF;
} }
} }
} }

BIN
icon.icns

Binary file not shown.

BIN
icon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 KiB

BIN
icon_mac.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

BIN
icon_windows.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

52
installer.iss

@ -0,0 +1,52 @@
#define MyAppName "CouchPotato"
#define MyAppVer "3.0.1"
#define MyAppBit "win32"
//#define MyAppBit "win-amd64"
[Setup]
AppName={#MyAppName}
AppVersion=3
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"

BIN
installer_banner.bmp

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

BIN
installer_icon.bmp

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

134
setup.py

@ -0,0 +1,134 @@
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',
'OpenSSL', 'cffi',
]
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 = 0,
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)

4
version.py

@ -1,2 +1,2 @@
VERSION = None VERSION = '3.0.1'
BRANCH = 'master' BRANCH = 'desktop'

Loading…
Cancel
Save