-
Notifications
You must be signed in to change notification settings - Fork 0
/
check.py
227 lines (193 loc) · 8.83 KB
/
check.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# -*- coding: utf-8 -*-
# Pitivi video editor
#
# pitivi/check.py
#
# Copyright (c) 2005, Edward Hervey <[email protected]>
# Copyright (c) 2012, Jean-François Fortin Tam <[email protected]>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser 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.
"""
This file is run by bin/pitivi on startup. Its purpose is to ensure that all
the important dependencies for running the pitivi UI can be imported and satisfy
our version number requirements.
The checks here are supposed to take a negligible amount of time (< 0.2 seconds)
and not impact startup. Module imports have no impact (they get imported later
by the app anyway). For more complex checks, you can measure (with time.time()),
when called from application.py instead of bin/pitivi, if it has an impact.
"""
from sys import modules
from gettext import gettext as _
# This list is meant to be a complete list for packagers.
# Unless otherwise noted, modules are accessed through gobject introspection
HARD_DEPS = {
"youtube_dl": None,
"Gst": "1.0.0",
"Gtk": "3.4.0",
"gdata": None,
# The following are not checked, but needed for the rest to work:
"gobject-introspection": "1.34.0",
"gst-python": "1.1.90",
"pygobject": "3.4.0",
}
# For the list of soft dependencies, see the "check_soft_dependencies" method,
# near the end of this file.
global missing_soft_deps
missing_soft_deps = {}
def _initiate_videosinks(Gst):
# TODO: eventually switch to a clutter sink
sink = Gst.ElementFactory.make("autovideosink", None)
if not sink:
return False
return True
def _initiate_audiosinks(Gst):
# Yes, this can still fail, if PulseAudio is non-responsive for example.
sink = Gst.ElementFactory.make("autoaudiosink", None)
if not sink:
return False
return True
def _try_import_from_gi(modulename):
try:
__import__("gi.repository." + modulename)
return True
except:
return False
def _try_import(modulename):
try:
__import__(modulename)
return True
except:
return False
def _version_to_string(version):
return ".".join([str(x) for x in version])
def _string_to_list(version):
return [int(x) for x in version.split(".")]
def _check_dependency(modulename, from_gobject_introspection):
"""
Checks if the given module can be imported and is recent enough.
"modulename" is case-sensitive
"from_gobject_introspection" is a mandatory boolean variable.
Returns: [satisfied, version_required, version_installed]
"""
VERSION_REQ = HARD_DEPS[modulename]
# What happens here is that we try to import the module. If it works,
# assign it to a "module" variable and check the version reqs with it.
module = None
if from_gobject_introspection is True:
if _try_import_from_gi(modulename):
module = modules["gi.repository." + modulename]
else:
if _try_import(modulename):
module = modules[modulename]
if module is None:
# Import failed, the dependency can't be satisfied, don't check versions
return [False, VERSION_REQ, None]
elif not VERSION_REQ:
# Import succeeded but there is no requirement, skip further checks
return [True, None, False]
# The import succeeded and there is a version requirement, so check it out:
if modulename == "Gst" or modulename == "GES":
if list(module.version()) < _string_to_list(VERSION_REQ):
return [False, VERSION_REQ, _version_to_string(module.version())]
else:
return [True, None, _version_to_string(module.version())]
if modulename == "Gtk" or modulename == "Clutter":
gtk_version_tuple = (module.MAJOR_VERSION, module.MINOR_VERSION, module.MICRO_VERSION)
if list(gtk_version_tuple) < _string_to_list(VERSION_REQ):
return [False, VERSION_REQ, _version_to_string(gtk_version_tuple)]
else:
return [True, None, _version_to_string(gtk_version_tuple)]
if modulename == "cairo":
if _string_to_list(module.cairo_version_string()) < _string_to_list(VERSION_REQ):
return [False, VERSION_REQ, module.cairo_version_string()]
else:
return [True, None, module.cairo_version_string()]
oops = 'Module "%s" is installed, but version checking is not defined in check_dependency' % modulename
raise NotImplementedError(oops)
def check_hard_dependencies():
missing_hard_deps = {}
satisfied, req, inst = _check_dependency("youtube_dl", False)
if not satisfied:
missing_hard_deps["youtube_dl"] = (req, inst)
satisfied, req, inst = _check_dependency("gdata", False)
if not satisfied:
missing_hard_deps["gdata"] = (req, inst)
satisfied, req, inst = _check_dependency("Gst", True)
if not satisfied:
missing_hard_deps["GStreamer"] = (req, inst)
satisfied, req, inst = _check_dependency("Gtk", True)
if not satisfied:
missing_hard_deps["GTK+"] = (req, inst)
# Since we had to check Gst beforehand, we only do the import now:
from gi.repository import Gst
Gst.init(None)
reg = Gst.Registry.get()
# Prepare the list of hard deps errors to warn about later:
for dependency in missing_hard_deps:
req = missing_hard_deps[dependency][0]
inst = missing_hard_deps[dependency][1]
if req and not inst:
message = "%s or newer is required, but was not found on your system." % req
elif req and inst:
message = "%s or newer is required, but only version %s was found." % (req, inst)
else:
message = "not found on your system."
missing_hard_deps[dependency] = message
# And finally, do a few last checks for basic sanity.
# Yes, a broken/dead autoaudiosink is still possible in 2012 with PulseAudio
if not _initiate_videosinks(Gst):
missing_hard_deps["autovideosink"] = \
"Could not initiate video output sink. "\
"Make sure you have a valid one (xvimagesink or ximagesink)."
if not _initiate_audiosinks(Gst):
missing_hard_deps["autoaudiosink"] = \
"Could not initiate audio output sink. "\
"Make sure you have a valid one (pulsesink, alsasink or osssink)."
return missing_hard_deps
def check_soft_dependencies():
"""
Verify for the presence of optional modules that enhance the user experience
If those are missing from the system, the user will be notified of their
existence by the presence of a "Missing dependencies..." button at startup.
"""
# Importing Gst again (even if we did it in hard deps checks), anyway it
# seems to have no measurable performance impact the 2nd time:
from gi.repository import Gst
Gst.init(None)
registry = Gst.Registry.get()
# Description strings are translatable as they may be shown in the pitivi UI
if not _try_import("pycanberra"):
missing_soft_deps["PyCanberra"] = \
_("enables sound notifications when rendering is complete")
if not _try_import_from_gi("Notify"):
missing_soft_deps["libnotify"] = \
_("enables visual notifications when rendering is complete")
if not registry.find_plugin("libav"):
missing_soft_deps["GStreamer Libav plugin"] = \
_('additional multimedia codecs through the Libav library')
# Apparently, doing a registry.find_plugin("frei0r") is not enough.
# Sometimes it still returns something even when frei0r is uninstalled,
# and anyway we're looking specifically for the scale0tilt filter.
# Don't use Gst.ElementFactory.make for this check, it's very I/O intensive.
# Instead, ask the registry with .lookup_feature or .check_feature_version:
if not registry.lookup_feature("frei0r-filter-scale0tilt"):
missing_soft_deps["Frei0r"] = \
_("additional video effects, clip transformation feature")
# TODO: we're not actually checking for gst bad and ugly... by definition,
# gst bad is a set of plugins that can move to gst good or ugly, and anyway
# distro packagers may split/combine the gstreamer plugins into any way they
# see fit. We can do a registry.find_plugin for specific encoders, but we
# don't really have something generic to rely on; ideas/patches welcome.