~ubuntu-branches/ubuntu/karmic/sonata/karmic

« back to all changes in this revision

Viewing changes to sonata/scrobbler.py

  • Committer: Bazaar Package Importer
  • Author(s): Michal Čihař
  • Date: 2009-05-11 09:10:00 UTC
  • mfrom: (4.2.3 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090511091000-jzspxudws5ngxb5e
Tags: 1.6.2-1
New upstream version (Closes: #528045).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
"""
 
3
This module makes Sonata submit the songs played to a Last.fm account.
 
4
 
 
5
Example usage:
 
6
import scrobbler
 
7
self.scrobbler = scrobbler.Scrobbler(self.config)
 
8
self.scrobbler.import_module()
 
9
self.scrobbler.init()
 
10
...
 
11
self.scrobbler.iterate()
 
12
...
 
13
self.scrobbler.handle_change_status(False, self.prevsonginfo)
 
14
"""
 
15
 
 
16
import os, time, sys
 
17
import threading # init, np, post start threads init_thread, do_np, do_post
 
18
 
 
19
audioscrobbler = None # imported when first needed
 
20
 
 
21
import mpdhelper as mpdh
 
22
 
 
23
class Scrobbler(object):
 
24
        def __init__(self, config):
 
25
                self.config = config
 
26
 
 
27
                self.scrob = None
 
28
                self.scrob_post = None
 
29
                self.scrob_start_time = ""
 
30
                self.scrob_playing_duration = 0
 
31
                self.scrob_last_prepared = ""
 
32
                self.scrob_time_now = None
 
33
 
 
34
                self.elapsed_now = None
 
35
        
 
36
        def import_module(self, _show_error=False):
 
37
                """Import the audioscrobbler module"""
 
38
                # We need to try to import audioscrobbler either when the app starts (if
 
39
                # as_enabled=True) or if the user enables it in prefs.
 
40
                global audioscrobbler
 
41
                if audioscrobbler is None:
 
42
                        import audioscrobbler
 
43
 
 
44
        def imported(self):
 
45
                """Return True if the audioscrobbler module has been imported"""
 
46
                return audioscrobbler is not None
 
47
 
 
48
        def init(self):
 
49
                """Initialize the Audioscrobbler support if enabled and configured"""
 
50
                if audioscrobbler is not None and self.config.as_enabled and len(self.config.as_username) > 0 and len(self.config.as_password_md5) > 0:
 
51
                        thread = threading.Thread(target=self.init_thread)
 
52
                        thread.setDaemon(True)
 
53
                        thread.start()
 
54
 
 
55
        def init_thread(self):
 
56
                if self.scrob is None:
 
57
                        self.scrob = audioscrobbler.AudioScrobbler()
 
58
                if self.scrob_post is None:
 
59
                        self.scrob_post = self.scrob.post(self.config.as_username, self.config.as_password_md5, verbose=True)
 
60
                else:
 
61
                        if self.scrob_post.authenticated:
 
62
                                return # We are authenticated
 
63
                        else:
 
64
                                self.scrob_post = self.scrob.post(self.config.as_username, self.config.as_password_md5, verbose=True)
 
65
                try:
 
66
                        self.scrob_post.auth()
 
67
                except Exception, e:
 
68
                        print "Error authenticating audioscrobbler", e
 
69
                        self.scrob_post = None
 
70
                if self.scrob_post:
 
71
                        self.retrieve_cache()
 
72
 
 
73
        def iterate(self):
 
74
                """Update the running time"""
 
75
                self.scrob_time_now = time.time()
 
76
 
 
77
        def handle_change_status(self, playing, prevsonginfo, songinfo=None, switched_from_stop_to_play=None, mpd_time_now=None):
 
78
                """Handle changes to play status, submitting info as appropriate"""
 
79
                if prevsonginfo and 'time' in prevsonginfo:
 
80
                        prevsong_time = mpdh.get(prevsonginfo, 'time')
 
81
                else:
 
82
                        prevsong_time = None
 
83
 
 
84
                if playing:
 
85
                        elapsed_prev = self.elapsed_now
 
86
                        self.elapsed_now, length = [float(c) for c in mpd_time_now.split(':')]
 
87
                        current_file = mpdh.get(songinfo, 'file')
 
88
                        if switched_from_stop_to_play:
 
89
                                # Switched from stop to play, prepare current track:
 
90
                                self.prepare(songinfo)
 
91
                        elif (prevsong_time and 
 
92
                              (self.scrob_last_prepared != current_file or 
 
93
                               (self.scrob_last_prepared == current_file and elapsed_prev and
 
94
                                abs(elapsed_prev-length)<=2 and self.elapsed_now<=2 and length>0))):
 
95
                                # New song is playing, post previous track if time criteria is met.
 
96
                                # In order to account for the situation where the same song is played twice in
 
97
                                # a row, we will check if the previous time was the end of the song and we're
 
98
                                # now at the beginning of the same song.. this technically isn't right in
 
99
                                # the case where a user seeks back to the beginning, but that's an edge case.
 
100
                                if self.scrob_playing_duration > 4 * 60 or self.scrob_playing_duration > int(prevsong_time)/2:
 
101
                                        if self.scrob_start_time != "":
 
102
                                                self.post(prevsonginfo)
 
103
                                # Prepare current track:
 
104
                                self.prepare(songinfo)
 
105
                        elif self.scrob_time_now:
 
106
                                # Keep track of the total amount of time that the current song
 
107
                                # has been playing:
 
108
                                self.scrob_playing_duration += time.time() - self.scrob_time_now
 
109
                else: # stopped:
 
110
                        self.elapsed_now = 0
 
111
                        if prevsong_time:
 
112
                                if self.scrob_playing_duration > 4 * 60 or self.scrob_playing_duration > int(prevsong_time)/2:
 
113
                                        # User stopped the client, post previous track if time
 
114
                                        # criteria is met:
 
115
                                        if self.scrob_start_time != "":
 
116
                                                self.post(prevsonginfo)
 
117
 
 
118
        def auth_changed(self):
 
119
                """Try to re-authenticate"""
 
120
                if self.scrob_post:
 
121
                        if self.scrob_post.authenticated:
 
122
                                self.scrob_post = None
 
123
 
 
124
        def prepare(self, songinfo):
 
125
                if audioscrobbler is not None:
 
126
                        self.scrob_start_time = ""
 
127
                        self.scrob_last_prepared = ""
 
128
                        self.scrob_playing_duration = 0
 
129
 
 
130
                        if self.config.as_enabled and songinfo:
 
131
                                # No need to check if the song is 30 seconds or longer,
 
132
                                # audioscrobbler.py takes care of that.
 
133
                                if 'time' in songinfo:
 
134
                                        self.np(songinfo)
 
135
 
 
136
                                        self.scrob_start_time = str(int(time.time()))
 
137
                                        self.scrob_last_prepared = mpdh.get(songinfo, 'file')
 
138
                        
 
139
        def np(self, songinfo):
 
140
                thread = threading.Thread(target=self.do_np, args=(songinfo,))
 
141
                thread.setDaemon(True)
 
142
                thread.start()
 
143
                                   
 
144
        def do_np(self, songinfo):
 
145
                self.init()
 
146
                if self.config.as_enabled and self.scrob_post and songinfo:
 
147
                        if 'artist' in songinfo and \
 
148
                           'title' in songinfo and \
 
149
                           'time' in songinfo:
 
150
                                if not 'album' in songinfo:
 
151
                                        album = u''
 
152
                                else:
 
153
                                        album = mpdh.get(songinfo, 'album')
 
154
                                if not 'track' in songinfo:
 
155
                                        tracknumber = u''
 
156
                                else:
 
157
                                        tracknumber = mpdh.get(songinfo, 'track')
 
158
                                try:
 
159
                                        self.scrob_post.nowplaying(mpdh.get(songinfo, 'artist'),
 
160
                                                                                                mpdh.get(songinfo, 'title'),
 
161
                                                                                                mpdh.get(songinfo, 'time'),
 
162
                                                                                                tracknumber,
 
163
                                                                                                album,
 
164
                                                                                                self.scrob_start_time)
 
165
                                except:
 
166
                                        print sys.exc_info()[1]
 
167
                time.sleep(10)
 
168
                
 
169
        def post(self, prevsonginfo):
 
170
                self.init()
 
171
                if self.config.as_enabled and self.scrob_post and prevsonginfo:
 
172
                        if 'artist' in prevsonginfo and \
 
173
                           'title' in prevsonginfo and \
 
174
                           'time' in prevsonginfo:
 
175
                                if not 'album' in prevsonginfo:
 
176
                                        album = u''
 
177
                                else:
 
178
                                        album = mpdh.get(prevsonginfo, 'album')
 
179
                                if not 'track' in prevsonginfo:
 
180
                                        tracknumber = u''
 
181
                                else:
 
182
                                        tracknumber = mpdh.get(prevsonginfo, 'track')
 
183
                                try:
 
184
                                        self.scrob_post.addtrack(mpdh.get(prevsonginfo, 'artist'),
 
185
                                                                                                mpdh.get(prevsonginfo, 'title'),
 
186
                                                                                                mpdh.get(prevsonginfo, 'time'),
 
187
                                                                                                self.scrob_start_time,
 
188
                                                                                                tracknumber,
 
189
                                                                                                album)
 
190
                                except:
 
191
                                        print sys.exc_info()[1]
 
192
 
 
193
                                thread = threading.Thread(target=self.do_post)
 
194
                                thread.setDaemon(True)
 
195
                                thread.start()
 
196
                self.scrob_start_time = ""
 
197
                
 
198
        def do_post(self):
 
199
                for _i in range(0, 3):
 
200
                        if not self.scrob_post:
 
201
                                return
 
202
                        if len(self.scrob_post.cache) == 0:
 
203
                                return
 
204
                        try:
 
205
                                self.scrob_post.post()
 
206
                        except audioscrobbler.AudioScrobblerConnectionError, e:
 
207
                                print e
 
208
                        time.sleep(10)
 
209
        
 
210
        def save_cache(self):
 
211
                """Save the cache in a file"""
 
212
                filename = os.path.expanduser('~/.config/sonata/ascache')
 
213
                if self.scrob_post:
 
214
                        self.scrob_post.savecache(filename)
 
215
        
 
216
        def retrieve_cache(self):
 
217
                filename = os.path.expanduser('~/.config/sonata/ascache')
 
218
                if self.scrob_post:
 
219
                        self.scrob_post.retrievecache(filename)
 
220