aboutsummaryrefslogtreecommitdiffstats
path: root/lvc/windows
diff options
context:
space:
mode:
Diffstat (limited to 'lvc/windows')
-rw-r--r--lvc/windows/__init__.py0
-rw-r--r--lvc/windows/autoupdate.py101
-rwxr-xr-xlvc/windows/exe_main.py22
-rw-r--r--lvc/windows/exelogging.py91
-rw-r--r--lvc/windows/specialfolders.py94
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')