~ubuntu-branches/ubuntu/natty/miro/natty

« back to all changes in this revision

Viewing changes to lib/dl_daemon/command.py

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2011-01-22 02:46:33 UTC
  • mfrom: (1.4.10 upstream) (1.7.5 experimental)
  • Revision ID: james.westby@ubuntu.com-20110122024633-kjme8u93y2il5nmf
Tags: 3.5.1-1ubuntu1
* Merge from debian.  Remaining ubuntu changes:
  - Use python 2.7 instead of python 2.6
  - Relax dependency on python-dbus to >= 0.83.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Miro - an RSS based video player application
 
2
# Copyright (C) 2005-2010 Participatory Culture Foundation
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
17
#
 
18
# In addition, as a special exception, the copyright holders give
 
19
# permission to link the code of portions of this program with the OpenSSL
 
20
# library.
 
21
#
 
22
# You must obey the GNU General Public License in all respects for all of
 
23
# the code used other than OpenSSL. If you modify file(s) with this
 
24
# exception, you may extend this exception to your version of the file(s),
 
25
# but you are not obligated to do so. If you do not wish to do so, delete
 
26
# this exception statement from your version. If you delete this exception
 
27
# statement from all source files in the program, then also delete it here.
 
28
 
 
29
import time
 
30
import random
 
31
import threading
 
32
import logging
 
33
 
 
34
from miro import eventloop
 
35
 
 
36
# Amount of time to wait for daemonic threads to quit.  Right now, the
 
37
# only thing we use Daemonic threads for is to send HTTP requests to
 
38
# BitTorrent trackers.
 
39
DAEMONIC_THREAD_TIMEOUT = 2
 
40
 
 
41
class Command(object):
 
42
    spammy = False
 
43
    def __init__(self, daemon, *args, **kws):
 
44
        self.command_id = "cmd%08d" % random.randint(0, 99999999)
 
45
        self.orig = True
 
46
        self.args = args
 
47
        self.kws = kws
 
48
        self.daemon = daemon
 
49
 
 
50
    def set_daemon(self, daemon):
 
51
        self.daemon = daemon
 
52
 
 
53
    def send(self, callback=None):
 
54
        if self.daemon.shutdown:
 
55
            return
 
56
        eventloop.add_idle(lambda : self.daemon.send(self, callback),
 
57
                           "sending command %r" % self)
 
58
 
 
59
    def action(self):
 
60
        # for overriding
 
61
        logging.warning("no action defined for command %s", self.command_id)
 
62
 
 
63
    def __getstate__(self):
 
64
        out = {"id": self.command_id,
 
65
               "args": self.args,
 
66
               "kws": self.kws,
 
67
               "orig": self.orig}
 
68
        try:
 
69
            out["ret"] = self.ret
 
70
        except AttributeError:
 
71
            pass
 
72
        return out
 
73
 
 
74
    def __setstate__(self, data):
 
75
        self.command_id = data["id"]
 
76
        self.kws = data["kws"]
 
77
        self.args = data["args"]
 
78
        self.orig = data["orig"]
 
79
        try:
 
80
            self.ret = data["ret"]
 
81
        except KeyError:
 
82
            pass
 
83
 
 
84
#############################################################################
 
85
#  Downloader to App commands                                               #
 
86
#############################################################################
 
87
class FindHTTPAuthCommand(Command):
 
88
    def action(self):
 
89
        from miro import httpauth
 
90
        id_, args = self.args[0], self.args[1:]
 
91
        def callback(auth_header):
 
92
            c = GotHTTPAuthCommand(self.daemon, id_, auth_header)
 
93
            c.send()
 
94
        httpauth.find_http_auth(callback, *args)
 
95
 
 
96
class AskForHTTPAuthCommand(Command):
 
97
    def action(self):
 
98
        from miro import httpauth
 
99
        id_, args = self.args[0], self.args[1:]
 
100
        def callback(auth_header):
 
101
            c = GotHTTPAuthCommand(self.daemon, id_, auth_header)
 
102
            c.send()
 
103
        httpauth.ask_for_http_auth(callback, *args)
 
104
 
 
105
class RemoveHTTPAuthCommand(Command):
 
106
    def action(self):
 
107
        from miro import httpauth
 
108
        httpauth.remove_by_url_and_realm(*self.args)
 
109
 
 
110
class UpdateDownloadStatus(Command):
 
111
    def action(self):
 
112
        from miro.downloader import RemoteDownloader
 
113
        return RemoteDownloader.update_status(*self.args, **self.kws)
 
114
 
 
115
class BatchUpdateDownloadStatus(Command):
 
116
    spammy = True
 
117
    def action(self):
 
118
        from miro.downloader import RemoteDownloader
 
119
        for status in self.args[0]:
 
120
            RemoteDownloader.update_status(status)
 
121
 
 
122
class DownloaderErrorCommand(Command):
 
123
    def action(self):
 
124
        from miro import signals
 
125
        signals.system.failed("In Downloader process", details=self.args[0])
 
126
 
 
127
class DuplicateTorrent(Command):
 
128
    """The downloader daemon detected that one download was for the same
 
129
    torrent as another one.
 
130
    """
 
131
    def action(self):
 
132
        original_id, duplicate_id = self.args[0], self.args[1]
 
133
        from miro import downloader
 
134
        duplicate = downloader.get_downloader_by_dlid(duplicate_id)
 
135
        original = downloader.get_downloader_by_dlid(original_id)
 
136
        if duplicate is None:
 
137
            logging.warn("duplicate torrent doesn't exist anymore, "
 
138
                    "ignoring (dlid %s)", duplicate_id)
 
139
            return
 
140
        if original is None:
 
141
            logging.warn("original torrent doesn't exist anymore, "
 
142
                    "restarting (dlid %s)", original_id)
 
143
            duplicate.restart()
 
144
            return
 
145
        for item in duplicate.item_list:
 
146
            item.set_downloader(original)
 
147
 
 
148
class ShutDownResponseCommand(Command):
 
149
    def action(self):
 
150
        self.daemon.shutdown_response()
 
151
 
 
152
#############################################################################
 
153
#  App to Downloader commands                                               #
 
154
#############################################################################
 
155
class InitialConfigCommand(Command):
 
156
    def action(self):
 
157
        from miro import config
 
158
        from miro.dl_daemon import download
 
159
        config.set_dictionary(*self.args, **self.kws)
 
160
        download.config_received()
 
161
 
 
162
class UpdateConfigCommand(Command):
 
163
    def action(self):
 
164
        from miro import config
 
165
        config.update_dictionary(*self.args, **self.kws)
 
166
 
 
167
class UpdateHTTPPasswordsCommand(Command):
 
168
    def action(self):
 
169
        from miro.dl_daemon.private import httpauth
 
170
        httpauth.update_passwords(*self.args)
 
171
 
 
172
class StartNewDownloadCommand(Command):
 
173
    def action(self):
 
174
        from miro.dl_daemon import download
 
175
        return download.start_new_download(*self.args, **self.kws)
 
176
 
 
177
class StartDownloadCommand(Command):
 
178
    def action(self):
 
179
        from miro.dl_daemon import download
 
180
        return download.start_download(*self.args, **self.kws)
 
181
 
 
182
class PauseDownloadCommand(Command):
 
183
    def action(self):
 
184
        from miro.dl_daemon import download
 
185
        return download.pause_download(*self.args, **self.kws)
 
186
 
 
187
class StopDownloadCommand(Command):
 
188
    def action(self):
 
189
        from miro.dl_daemon import download
 
190
        return download.stop_download(*self.args, **self.kws)
 
191
 
 
192
class StopUploadCommand(Command):
 
193
    def action(self):
 
194
        from miro.dl_daemon import download
 
195
        return download.stop_upload(*self.args, **self.kws)
 
196
 
 
197
class PauseUploadCommand(Command):
 
198
    def action(self):
 
199
        from miro.dl_daemon import download
 
200
        return download.pause_upload(*self.args, **self.kws)
 
201
 
 
202
class GetDownloadStatusCommand(Command):
 
203
    def action(self):
 
204
        from miro.dl_daemon import download
 
205
        return download.get_download_status(*self.args, **self.kws)
 
206
 
 
207
class RestoreDownloaderCommand(Command):
 
208
    def action(self):
 
209
        from miro.dl_daemon import download
 
210
        return download.restore_downloader(*self.args, **self.kws)
 
211
 
 
212
class MigrateDownloadCommand(Command):
 
213
    def action(self):
 
214
        from miro.dl_daemon import download
 
215
        return download.migrate_download(*self.args, **self.kws)
 
216
 
 
217
class GotHTTPAuthCommand(Command):
 
218
    def action(self):
 
219
        id_, auth_header = self.args
 
220
        from miro.dl_daemon.private import httpauth
 
221
        httpauth.handle_http_auth_response(id_, auth_header)
 
222
 
 
223
class ShutDownCommand(Command):
 
224
    def response_sent(self):
 
225
        eventloop.shutdown()
 
226
        logging.info("Shutdown complete")
 
227
 
 
228
    def action(self):
 
229
        starttime = time.time()
 
230
        from miro import httpclient
 
231
        from miro.dl_daemon import download
 
232
        download.shutdown()
 
233
        httpclient.stop_thread()
 
234
        eventloop.thread_pool_quit()
 
235
        for thread in threading.enumerate():
 
236
            if (thread != threading.currentThread()
 
237
                 and thread.getName() != "MainThread"
 
238
                 and not thread.isDaemon()):
 
239
                thread.join()
 
240
        endtime = starttime + DAEMONIC_THREAD_TIMEOUT
 
241
        for thread in threading.enumerate():
 
242
            if (thread != threading.currentThread() 
 
243
                and thread.getName() != "MainThread"):
 
244
                timeout = endtime - time.time()
 
245
                if timeout <= 0:
 
246
                    break
 
247
                thread.join(timeout)
 
248
        c = ShutDownResponseCommand(self.daemon)
 
249
        c.send(callback=self.response_sent)
 
250
        self.daemon.shutdown = True