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

« back to all changes in this revision

Viewing changes to portable/autoupdate.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
 
"""Checks the AUTOUPDATE_URL to see if there's a more recent version
30
 
of the application.
31
 
 
32
 
Call ``check_for_updates``.
33
 
"""
34
 
 
35
 
import logging
36
 
 
37
 
from miro import prefs
38
 
from miro import config
39
 
from miro import eventloop
40
 
from miro import feedparser
41
 
from miro import signals
42
 
 
43
 
from miro.httpclient import grabURL
44
 
 
45
 
check_in_progress = False
46
 
 
47
 
def check_for_updates(up_to_date_callback=None):
48
 
    """Checks the AUTOUPDATE_URL for the recent version.
49
 
 
50
 
    The ``up_to_date_callback`` is a function that should take no
51
 
    arguments and return nothing.
52
 
    """
53
 
    import miro.plat
54
 
    if miro.plat.AUTOUPDATE == False:
55
 
        logging.info("this platform has autoupdate disabled.  skipping.")
56
 
        return
57
 
 
58
 
    global check_in_progress
59
 
    if not check_in_progress:
60
 
        check_in_progress = True
61
 
        logging.info("Checking for updates...")
62
 
        url = config.get(prefs.AUTOUPDATE_URL)
63
 
        update_handler = lambda data: _handle_app_cast(data,
64
 
                                                       up_to_date_callback)
65
 
        error_handler = _handle_error
66
 
        grabURL(url, update_handler, error_handler)
67
 
 
68
 
def _handle_error(error):
69
 
    """Error handler"""
70
 
    global check_in_progress
71
 
    check_in_progress = False
72
 
    logging.warn("HTTP error while checking for updates %s", error)
73
 
    eventloop.addTimeout(86400, check_for_updates, "Check for updates")
74
 
 
75
 
def _handle_app_cast(data, up_to_date_callback):
76
 
    """Handle appcast data when it's correctly fetched
77
 
    """
78
 
    # python 2.4 requires that except and finally clauses be in different
79
 
    # try blocks.
80
 
    try:
81
 
        try:
82
 
            appcast = feedparser.parse(data['body'])
83
 
            if appcast['bozo'] == '1':
84
 
                return
85
 
 
86
 
            up_to_date = True
87
 
            latest = _get_item_for_latest(appcast)
88
 
            if latest is None:
89
 
                logging.info('No updates for this platform.')
90
 
                # this will go through the finally clause below
91
 
                return
92
 
 
93
 
            serial = int(config.get(prefs.APP_SERIAL))
94
 
            up_to_date = (serial >= _get_item_serial(latest))
95
 
 
96
 
            if not up_to_date:
97
 
                logging.info('New update available.')
98
 
                signals.system.update_available(latest)
99
 
            elif up_to_date_callback:
100
 
                logging.info('Up to date.  Notifying callback.')
101
 
                up_to_date_callback()
102
 
            else:
103
 
                logging.info('Up to date.')
104
 
 
105
 
        except (SystemExit, KeyboardInterrupt):
106
 
            raise
107
 
        except:
108
 
            logging.exception("Error while handling appcast data.")
109
 
 
110
 
    finally:
111
 
        global check_in_progress
112
 
        check_in_progress = False
113
 
        eventloop.addTimeout(86400, check_for_updates, "Check for updates")
114
 
 
115
 
def _get_item_for_latest(appcast):
116
 
    """Filter out non platform items, sort remaining from latest to
117
 
    oldest and return the item corresponding to the latest known
118
 
    version.
119
 
 
120
 
    If there are no entries for this platform (this happens with
121
 
    Linux), then this returns None.
122
 
    """
123
 
    platform = config.get(prefs.APP_PLATFORM)
124
 
    rejected_items = []
125
 
 
126
 
    for item in appcast['entries']:
127
 
        rejected_enclosures = []
128
 
        for enclosure in item['enclosures']:
129
 
            if enclosure['dtv:platform'] != platform:
130
 
                rejected_enclosures.append(enclosure)
131
 
            if enclosure['type'] != 'application/octet-stream':
132
 
                rejected_enclosures.append(enclosure)
133
 
        for enclosure in rejected_enclosures:
134
 
            item['enclosures'].remove(enclosure)
135
 
        if len(item['enclosures']) == 0:
136
 
            rejected_items.append(item)
137
 
 
138
 
    for item in rejected_items:
139
 
        appcast['entries'].remove(item)
140
 
 
141
 
    # we've removed all entries that aren't relevant to this platform.
142
 
    # if there aren't any left, we return None and the caller can deal
143
 
    # with things.
144
 
    if not appcast['entries']:
145
 
        return None
146
 
 
147
 
    appcast['entries'].sort(key=_get_item_serial, reverse=True)
148
 
    return appcast['entries'][0]
149
 
 
150
 
def _get_item_serial(item):
151
 
    """Returns the serial of the first enclosure of the passed item
152
 
    """
153
 
    return int(item['enclosures'][0]['dtv:serial'])