diff options
Diffstat (limited to 'lvc/windows')
-rw-r--r-- | lvc/windows/__init__.py | 0 | ||||
-rw-r--r-- | lvc/windows/autoupdate.py | 101 | ||||
-rwxr-xr-x | lvc/windows/exe_main.py | 22 | ||||
-rw-r--r-- | lvc/windows/exelogging.py | 91 | ||||
-rw-r--r-- | lvc/windows/specialfolders.py | 94 |
5 files changed, 308 insertions, 0 deletions
diff --git a/lvc/windows/__init__.py b/lvc/windows/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lvc/windows/__init__.py diff --git a/lvc/windows/autoupdate.py b/lvc/windows/autoupdate.py new file mode 100644 index 0000000..f6d47c8 --- /dev/null +++ b/lvc/windows/autoupdate.py @@ -0,0 +1,101 @@ +# @Base: Miro - an RSS based video player application +# Copyright (C) 2017 +# Jesus E. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +"""Autoupdate functionality """ + +import ctypes +import _winreg as winreg +import logging + +winsparkle = ctypes.cdll.WinSparkle + +APPCAST_URL = 'http://miro-updates.participatoryculture.org/lvc-appcast.xml' + +def startup(): + enable_automatic_checks() + winsparkle.win_sparkle_set_appcast_url(APPCAST_URL) + winsparkle.win_sparkle_init() + +def shutdown(): + winsparkle.win_sparkle_cleanup() + +def enable_automatic_checks(): + # We should be able to use win_sparkle_set_automatic_check_for_updates, + # but that's only available after version 0.4 and the current release + # version is 0.4 + with open_winsparkle_key() as winsparkle_key: + if not check_for_updates_set(winsparkle_key): + set_default_check_for_updates(winsparkle_key) + +def open_winsparkle_key(): + """Open the MVC WinSparkle registry key + + If any components are not created yet, we will try to create them + """ + with open_or_create_key(winreg.HKEY_CURRENT_USER, "Software") as software: + with open_or_create_key(software, + "Participatory Culture Foundation") as pcf: + with open_or_create_key(pcf, "Libre Video Converter") as lvc: + return open_or_create_key(lvc, "WinSparkle", + write_access=True) + +def open_or_create_key(key, subkey, write_access=False): + if write_access: + sam = winreg.KEY_READ | winreg.KEY_WRITE + else: + sam = winreg.KEY_READ + try: + return winreg.OpenKey(key, subkey, 0, sam) + except OSError, e: + if e.errno == 2: + # Not Found error. We should create the key + return winreg.CreateKey(key, subkey) + else: + raise + +def check_for_updates_set(winsparkle_key): + try: + winreg.QueryValueEx(winsparkle_key, "CheckForUpdates") + except OSError, e: + if e.errno == 2: + # not found error. + return False + else: + raise + else: + return True + + +def set_default_check_for_updates(winsparkle_key): + """Initialize the WinSparkle regstry values with our defaults. + + :param lvc_key winreg.HKey object for to the MVC registry + """ + logging.info("Writing WinSparkle keys") + winreg.SetValueEx(winsparkle_key, "CheckForUpdates", 0, winreg.REG_SZ, "1") diff --git a/lvc/windows/exe_main.py b/lvc/windows/exe_main.py new file mode 100755 index 0000000..66b97a5 --- /dev/null +++ b/lvc/windows/exe_main.py @@ -0,0 +1,22 @@ +# before anything else, settup logging +from lvc.windows import exelogging +exelogging.setup_logging() + +import os +import sys + +from lvc import settings +from lvc.windows import autoupdate +from lvc.widgets import app +from lvc.widgets import initialize +from lvc.ui.widgets import Application + +# add the directories for ffmpeg and avconv to our search path +exe_dir = os.path.dirname(sys.executable) +settings.add_to_search_path(os.path.join(exe_dir, 'ffmpeg')) +settings.add_to_search_path(os.path.join(exe_dir, 'avconv')) +# run the app +app.widgetapp = Application() +app.widgetapp.connect("window-shown", lambda w: autoupdate.startup()) +initialize(app.widgetapp) +autoupdate.shutdown() diff --git a/lvc/windows/exelogging.py b/lvc/windows/exelogging.py new file mode 100644 index 0000000..a63c836 --- /dev/null +++ b/lvc/windows/exelogging.py @@ -0,0 +1,91 @@ +"""lvc.windows.exelogging -- handle logging inside an exe file + +Most of this is copied from the Miro code. +""" + +import logging +import os +import sys +import tempfile +from StringIO import StringIO +from logging.handlers import RotatingFileHandler + +class ApatheticRotatingFileHandler(RotatingFileHandler): + """The whole purpose of this class is to prevent rotation errors + from percolating up into stdout/stderr and popping up a dialog + that's not particularly useful to users or us. + """ + def doRollover(self): + # If you shut down LibreVideoConverter then start it up again immediately + # afterwards, then we get in this squirrely situation where + # the log is opened by another process. We ignore the + # exception, but make sure we have an open file. (bug #11228) + try: + RotatingFileHandler.doRollover(self) + except WindowsError: + if not self.stream or self.stream.closed: + self.stream = open(self.baseFilename, "a") + try: + RotatingFileHandler.doRollover(self) + except WindowsError: + pass + + def shouldRollover(self, record): + # if doRollover doesn't work, then we don't want to find + # ourselves in a situation where we're trying to do things on + # a closed stream. + if self.stream.closed: + self.stream = open(self.baseFilename, "a") + return RotatingFileHandler.shouldRollover(self, record) + + def handleError(self, record): + # ignore logging errors that occur rather than printing them to + # stdout/stderr which isn't helpful to us + + pass +class AutoLoggingStream(StringIO): + """Create a stream that intercepts write calls and sends them to + the log. + """ + def __init__(self, logging_callback, prefix): + StringIO.__init__(self) + # We init from StringIO to give us a bunch of stream-related + # methods, like closed() and read() automatically. + self.logging_callback = logging_callback + self.prefix = prefix + + def write(self, data): + if isinstance(data, unicode): + data = data.encode('ascii', 'backslashreplace') + if data.endswith("\n"): + data = data[:-1] + if data: + self.logging_callback(self.prefix + data) + +FORMAT = "%(asctime)s %(levelname)-8s %(name)s: %(message)s" +def setup_logging(): + """Setup logging for when we're running inside a windows exe. + + The object here is to avoid logging anything to stderr since + windows will consider that an error. + + We also catch things written to sys.stdout and forward that to the logging + system. + + Finally we also copy the log output to stdout so that when MVC is run in + console mode we see the logs + """ + + log_path = os.path.join(tempfile.gettempdir(), "MVC.log") + rotater = ApatheticRotatingFileHandler( + log_path, mode="a", maxBytes=100000, backupCount=5) + + formatter = logging.Formatter(FORMAT) + rotater.setFormatter(formatter) + logger = logging.getLogger('') + logger.addHandler(rotater) + logger.addHandler(logging.StreamHandler(sys.stdout)) + logger.setLevel(logging.INFO) + rotater.doRollover() + sys.stdout = AutoLoggingStream(logging.warn, '(from stdout) ') + sys.stderr = AutoLoggingStream(logging.error, '(from stderr) ') diff --git a/lvc/windows/specialfolders.py b/lvc/windows/specialfolders.py new file mode 100644 index 0000000..2e1e7c6 --- /dev/null +++ b/lvc/windows/specialfolders.py @@ -0,0 +1,94 @@ +# @Base: Miro - an RSS based video player application +# Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 +# Participatory Culture Foundation +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +"""Contains the locations of special windows folders like "My +Documents". +""" + +import ctypes +import os +# from miro import u3info + +GetShortPathName = ctypes.windll.kernel32.GetShortPathNameW + +_special_folder_CSIDLs = { + "Fonts": 0x0014, + "AppData": 0x001a, + "My Music": 0x000d, + "My Pictures": 0x0027, + "My Videos": 0x000e, + "My Documents": 0x0005, + "Desktop": 0x0000, + "Common AppData": 0x0023, + "System": 0x0025 +} + +def get_short_path_name(name): + """Given a path, returns the shortened path name. + """ + buf = ctypes.c_wchar_p(name) + buf2 = ctypes.create_unicode_buffer(1024) + + if GetShortPathName(buf, buf2, 1024): + return buf2.value + else: + return buf.value + +def get_special_folder(name): + """Get the location of a special folder. name should be one of + the following: 'AppData', 'My Music', 'My Pictures', 'My Videos', + 'My Documents', 'Desktop'. + + The path to the folder will be returned, or None if the lookup + fails + """ + try: + csidl = _special_folder_CSIDLs[name] + except KeyError: + # FIXME - this will silently fail if the dev did a typo + # for the path name. e.g. My Musc + return None + + buf = ctypes.create_unicode_buffer(260) + SHGetSpecialFolderPath = ctypes.windll.shell32.SHGetSpecialFolderPathW + if SHGetSpecialFolderPath(None, buf, csidl, False): + return buf.value + else: + return None + +common_app_data_directory = get_special_folder("Common AppData") +app_data_directory = get_special_folder("AppData") + +base_movies_directory = get_special_folder('My Videos') +non_video_directory = get_special_folder('Desktop') +# The "My Videos" folder isn't guaranteed to be listed. If it isn't +# there, we do this hack. +if base_movies_directory is None: + base_movies_directory = os.path.join( + get_special_folder('My Documents'), 'My Videos') |