5
# Copyright (C) 2012 Thomas Mashos
8
# This program is free software: you can redistribute it and/or modify
9
# it under the terms of the GNU General Public License as published by
10
# the Free Software Foundation, either version 3 of the License, or
11
# (at your option) any later version.
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# GNU General Public License for more details.
18
# You should have received a copy of the GNU General Public License
19
# along with this program. If not, see <http://www.gnu.org/licenses/>.
24
from gi.repository import (
36
from urlparse import urlparse
42
class XDGError(Exception): pass
43
class FileNotFoundError(Exception): pass
45
socket.setdefaulttimeout(5)
46
BUS_NAME = "net.launchpad.scope.video.mythtv"
50
self.mt = mythtvapi.MythTVService()
51
self.scope = Unity.Scope.new ("/net/launchpad/scope/video/mythtv")
52
self.scope.search_in_global = False
53
self.scope.connect ("search-changed", self.on_search_changed)
54
self.scope.connect ("filters-changed", self.on_search_changed)
55
self.scope.connect ("activate-uri", self.on_activate_uri)
57
self.scope.connect("notify::active", self.on_lens_active)
58
self.scope.connect("filters-changed", self.on_filtering_changed)
59
self.scope.props.sources.connect("notify::filtering",
60
self.on_filtering_changed)
61
self.scope.search_in_global = False
64
if os.path.isfile("/usr/bin/mythavtest"):
65
self.player = "/usr/bin/mythavtest"
66
self.scope.props.sources.add_option('MythTV', 'MythTV', None)
67
self.sources_list = []
69
def on_filtering_changed(self, *_):
70
"""Run another search when a filter change is notified."""
71
self.scope.queue_search_changed(Unity.SearchType.DEFAULT)
73
def on_lens_active(self, *_):
74
""" Run a search when the lens is opened """
75
if self.scope.props.active:
76
self.scope.queue_search_changed(Unity.SearchType.DEFAULT)
78
def on_activate_uri (self, scope, uri):
79
if not self.player == "/usr/bin/mythavtest":
80
p, VAR = self.get_defaults('video/mpeg')['exec'].split(' ', 1)
81
vidplayer = subprocess.Popen(['which', p],
82
stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
83
self.player = vidplayer[0].strip()
85
GLib.spawn_async([str(self.player), uri])
86
return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri='')
89
paths = os.environ.get('XDG_DATA_HOME',
90
os.path.expanduser('~/.local/share/')).split(os.path.pathsep)
91
paths.extend(os.environ.get('XDG_DATA_DIRS',
92
'/usr/local/share/:/usr/share/').split(os.path.pathsep))
95
def xdg_query(self, command, parameter):
96
p = subprocess.Popen(['xdg-mime', 'query', command, parameter],
97
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
98
output, errors = p.communicate()
99
if p.returncode or errors:
100
raise XDGError('xdg-mime returned error code %d: %s' %
101
(p.returncode, errors.strip()))
102
return output.strip()
104
def locate_desktop_file(self, filename, mode='r', encoding='utf-8', _paths=_get_app_paths()):
106
for thispath, dirs, files in os.walk(os.path.join(path, 'applications')):
107
if filename not in files:
109
fullname = os.path.join(thispath, filename)
111
return codecs.open(fullname, mode, encoding)
115
raise FileNotFoundError(filename)
117
def get_defaults(self, MIMETYPE):
118
desktop_filename = self.xdg_query('default', MIMETYPE)
119
with self.locate_desktop_file(desktop_filename) as desktop_file:
120
parser = ConfigParser.ConfigParser()
121
parser.readfp(desktop_file, desktop_filename)
122
return dict(parser.items(parser.sections()[0]))
124
def get_search_string (self):
125
search = self.scope.props.active_search
126
return search.props.search_string if search else None
128
def on_search_changed(self, scope, search, search_type, _):
129
"""Get the search string, prepare a list of activated sources for
130
the server, update the model and notify the lens when we are done."""
131
search_string = search.props.search_string.strip()
132
# note search_string is a utf-8 encoded string, now:
133
print "Search changed to %r" % search_string
134
model = search.props.results_model
136
# Create a list of activated sources
137
sources = [source for n, source in enumerate(self.sources_list)
138
if self.source_activated(str(n))]
139
#Ensure MythTV is selected
140
if self.source_activated('MythTV'):
141
# Ensure that we are not in Global search
142
if search_type is Unity.SearchType.DEFAULT:
143
if len(search_string) > 0:
144
self.update_results_model(search_string, model, sources)
145
model.flush_revision_queue()
149
def source_activated(self, source):
150
"""Return the state of a sources filter option."""
151
active = self.scope.props.sources.get_option(source).props.active
152
filtering = self.scope.props.sources.props.filtering
153
if (active and filtering) or (not active and not filtering):
158
def update_results_model(self, search, model, sources):
160
Autos,Comedy,Education,Entertainment,Film,Games,Howto,Music,News,
161
Nonprofit,People,Animals,Tech,Sports,Travel
165
for option in self.scope.get_filter('categories').options:
166
if option.props.active:
167
categories.append(option.props.id)
170
for result in self.mt.search(SEARCHTERM = search, CATEGORY = categories):
171
mimetype = mimetypes.guess_type(result[8])[0]
172
if self.player == "/usr/bin/mythavtest":
173
BACKEND = urlparse(result[13]).netloc
174
URI = 'myth://'+result[7]+'@'+BACKEND+'/'+result[8]
176
URI = result[13]+"/Content/GetFile?StorageGroup="+result[7]+"&FileName="+result[8]
178
icon = Gio.ThemedIcon.new ("video").to_string()
179
NAME = result[0]+' - S'+result[11]+'E'+result[12]+' - '+result[1]
190
model.flush_revision_queue ()
191
#if self.scope.props.active_search:
192
# self.scope.props.active_search.emit('finished')
194
if __name__ == "__main__":
195
session_bus_connection = Gio.bus_get_sync (Gio.BusType.SESSION, None)
196
session_bus = Gio.DBusProxy.new_sync (session_bus_connection, 0, None,
197
'org.freedesktop.DBus',
198
'/org/freedesktop/DBus',
199
'org.freedesktop.DBus', None)
200
result = session_bus.call_sync('RequestName',
201
GLib.Variant ("(su)", (BUS_NAME, 0x4)),
204
# Unpack variant response with signature "(u)". 1 means we got it.
205
result = result.unpack()[0]
208
print >> sys.stderr, "Failed to own name %s. Unity-Scope-MythTV is working." % BUS_NAME
212
GObject.MainLoop().run()