Merge lp:~hermann-kaser/pogo/lastfm-plugin into lp:~jendrikseipp/pogo/trunk
- lastfm-plugin
- Merge into trunk
Proposed by
Hermann Kaser
Status: | Needs review |
---|---|
Proposed branch: | lp:~hermann-kaser/pogo/lastfm-plugin |
Merge into: | lp:~jendrikseipp/pogo/trunk |
Diff against target: |
407 lines (+398/-0) 2 files modified
res/Lastfm.glade (+276/-0) src/modules/Lastfm.py (+122/-0) |
To merge this branch: | bzr merge lp:~hermann-kaser/pogo/lastfm-plugin |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jendrik Seipp | Needs Fixing | ||
Review via email: mp+90511@code.launchpad.net |
Commit message
Description of the change
Last.fm scrobbler thingie plugin.
To post a comment you must log in.
Unmerged revisions
- 449. By Hermann Kaser
-
Add Last.fm plugin
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'res/Lastfm.glade' |
2 | --- res/Lastfm.glade 1970-01-01 00:00:00 +0000 |
3 | +++ res/Lastfm.glade 2012-01-27 19:34:24 +0000 |
4 | @@ -0,0 +1,276 @@ |
5 | +<?xml version="1.0" encoding="UTF-8"?> |
6 | +<glade-interface> |
7 | + <!-- interface-requires gtk+ 2.6 --> |
8 | + <!-- interface-naming-policy toplevel-contextual --> |
9 | + <widget class="GtkWindow" id="win-main"> |
10 | + <property name="can_focus">False</property> |
11 | + <property name="window_position">center</property> |
12 | + <child> |
13 | + <widget class="GtkVBox" id="vbox1"> |
14 | + <property name="can_focus">False</property> |
15 | + <property name="border_width">12</property> |
16 | + <property name="spacing">18</property> |
17 | + <child> |
18 | + <widget class="GtkVBox" id="vbox2"> |
19 | + <property name="can_focus">False</property> |
20 | + <property name="spacing">18</property> |
21 | + <child> |
22 | + <widget class="GtkFrame" id="frame1"> |
23 | + <property name="can_focus">False</property> |
24 | + <property name="label_xalign">0</property> |
25 | + <property name="shadow_type">none</property> |
26 | + <child> |
27 | + <widget class="GtkAlignment" id="alignment1"> |
28 | + <property name="can_focus">False</property> |
29 | + <property name="top_padding">6</property> |
30 | + <property name="left_padding">12</property> |
31 | + <child> |
32 | + <widget class="GtkVBox" id="vbox4"> |
33 | + <property name="can_focus">False</property> |
34 | + <property name="spacing">6</property> |
35 | + <child> |
36 | + <widget class="GtkVBox" id="vbox5"> |
37 | + <property name="can_focus">False</property> |
38 | + <child> |
39 | + <widget class="GtkHBox" id="hbox4"> |
40 | + <property name="can_focus">False</property> |
41 | + <property name="spacing">6</property> |
42 | + <child> |
43 | + <widget class="GtkLabel" id="label5"> |
44 | + <property name="can_focus">False</property> |
45 | + <property name="label" translatable="yes">Username:</property> |
46 | + </widget> |
47 | + <packing> |
48 | + <property name="expand">False</property> |
49 | + <property name="fill">True</property> |
50 | + <property name="position">0</property> |
51 | + </packing> |
52 | + </child> |
53 | + <child> |
54 | + <widget class="GtkEntry" id="txt-username"> |
55 | + <property name="can_focus">True</property> |
56 | + <property name="tooltip" translatable="yes">Filenames to look for (e.g., folder, cover)</property> |
57 | + <property name="primary_icon_activatable">False</property> |
58 | + <property name="secondary_icon_activatable">False</property> |
59 | + <property name="primary_icon_sensitive">True</property> |
60 | + <property name="secondary_icon_sensitive">True</property> |
61 | + </widget> |
62 | + <packing> |
63 | + <property name="expand">True</property> |
64 | + <property name="fill">True</property> |
65 | + <property name="position">1</property> |
66 | + </packing> |
67 | + </child> |
68 | + </widget> |
69 | + <packing> |
70 | + <property name="expand">True</property> |
71 | + <property name="fill">True</property> |
72 | + <property name="position">0</property> |
73 | + </packing> |
74 | + </child> |
75 | + </widget> |
76 | + <packing> |
77 | + <property name="expand">True</property> |
78 | + <property name="fill">True</property> |
79 | + <property name="position">0</property> |
80 | + </packing> |
81 | + </child> |
82 | + <child> |
83 | + <widget class="GtkVBox" id="vbox3"> |
84 | + <property name="can_focus">False</property> |
85 | + <child> |
86 | + <widget class="GtkHBox" id="hbox2"> |
87 | + <property name="can_focus">False</property> |
88 | + <property name="spacing">6</property> |
89 | + <child> |
90 | + <widget class="GtkLabel" id="label2"> |
91 | + <property name="can_focus">False</property> |
92 | + <property name="label" translatable="yes">Password:</property> |
93 | + </widget> |
94 | + <packing> |
95 | + <property name="expand">False</property> |
96 | + <property name="fill">True</property> |
97 | + <property name="position">0</property> |
98 | + </packing> |
99 | + </child> |
100 | + <child> |
101 | + <widget class="GtkEntry" id="txt-password"> |
102 | + <property name="can_focus">True</property> |
103 | + <property name="has_tooltip">True</property> |
104 | + <property name="tooltip" translatable="yes">Filenames to look for (e.g., folder, cover)</property> |
105 | + <property name="visibility">False</property> |
106 | + <property name="invisible_char">•</property> |
107 | + <property name="invisible_char_set">True</property> |
108 | + <property name="primary_icon_activatable">False</property> |
109 | + <property name="secondary_icon_activatable">False</property> |
110 | + <property name="primary_icon_sensitive">True</property> |
111 | + <property name="secondary_icon_sensitive">True</property> |
112 | + </widget> |
113 | + <packing> |
114 | + <property name="expand">True</property> |
115 | + <property name="fill">True</property> |
116 | + <property name="position">1</property> |
117 | + </packing> |
118 | + </child> |
119 | + </widget> |
120 | + <packing> |
121 | + <property name="expand">True</property> |
122 | + <property name="fill">True</property> |
123 | + <property name="position">0</property> |
124 | + </packing> |
125 | + </child> |
126 | + </widget> |
127 | + <packing> |
128 | + <property name="expand">True</property> |
129 | + <property name="fill">True</property> |
130 | + <property name="position">1</property> |
131 | + </packing> |
132 | + </child> |
133 | + <child> |
134 | + <widget class="GtkHButtonBox" id="hbuttonbox4"> |
135 | + <property name="visible">True</property> |
136 | + <property name="can_focus">False</property> |
137 | + <property name="layout_style">end</property> |
138 | + <child> |
139 | + <widget class="GtkImage" id="img-lastfm"> |
140 | + <property name="visible">True</property> |
141 | + <property name="can_focus">False</property> |
142 | + <property name="stock">gtk-missing-image</property> |
143 | + </widget> |
144 | + <packing> |
145 | + <property name="expand">False</property> |
146 | + <property name="fill">False</property> |
147 | + <property name="pack_type">end</property> |
148 | + <property name="position">0</property> |
149 | + </packing> |
150 | + </child> |
151 | + </widget> |
152 | + <packing> |
153 | + <property name="expand">False</property> |
154 | + <property name="fill">False</property> |
155 | + <property name="position">2</property> |
156 | + </packing> |
157 | + </child> |
158 | + </widget> |
159 | + </child> |
160 | + </widget> |
161 | + </child> |
162 | + <child> |
163 | + <widget class="GtkLabel" id="label1"> |
164 | + <property name="can_focus">False</property> |
165 | + <property name="label" translatable="yes"><b>Last.fm login</b></property> |
166 | + <property name="use_markup">True</property> |
167 | + </widget> |
168 | + <packing> |
169 | + <property name="type">label_item</property> |
170 | + </packing> |
171 | + </child> |
172 | + </widget> |
173 | + <packing> |
174 | + <property name="expand">False</property> |
175 | + <property name="fill">True</property> |
176 | + <property name="position">0</property> |
177 | + </packing> |
178 | + </child> |
179 | + <child> |
180 | + <placeholder/> |
181 | + </child> |
182 | + <child> |
183 | + <placeholder/> |
184 | + </child> |
185 | + </widget> |
186 | + <packing> |
187 | + <property name="expand">True</property> |
188 | + <property name="fill">True</property> |
189 | + <property name="position">0</property> |
190 | + </packing> |
191 | + </child> |
192 | + <child> |
193 | + <widget class="GtkHSeparator" id="hseparator1"> |
194 | + <property name="can_focus">False</property> |
195 | + </widget> |
196 | + <packing> |
197 | + <property name="expand">False</property> |
198 | + <property name="fill">True</property> |
199 | + <property name="position">1</property> |
200 | + </packing> |
201 | + </child> |
202 | + <child> |
203 | + <widget class="GtkHBox" id="hbox1"> |
204 | + <property name="can_focus">False</property> |
205 | + <property name="spacing">6</property> |
206 | + <child> |
207 | + <widget class="GtkHButtonBox" id="hbuttonbox1"> |
208 | + <property name="can_focus">False</property> |
209 | + <child> |
210 | + <widget class="GtkButton" id="btn-help"> |
211 | + <property name="label">gtk-help</property> |
212 | + <property name="can_focus">True</property> |
213 | + <property name="receives_default">True</property> |
214 | + <property name="use_action_appearance">False</property> |
215 | + <property name="use_stock">True</property> |
216 | + </widget> |
217 | + <packing> |
218 | + <property name="expand">False</property> |
219 | + <property name="fill">False</property> |
220 | + <property name="position">0</property> |
221 | + </packing> |
222 | + </child> |
223 | + </widget> |
224 | + <packing> |
225 | + <property name="expand">False</property> |
226 | + <property name="fill">True</property> |
227 | + <property name="position">0</property> |
228 | + </packing> |
229 | + </child> |
230 | + <child> |
231 | + <widget class="GtkHButtonBox" id="hbuttonbox2"> |
232 | + <property name="can_focus">False</property> |
233 | + <property name="spacing">12</property> |
234 | + <property name="layout_style">end</property> |
235 | + <child> |
236 | + <widget class="GtkButton" id="btn-cancel"> |
237 | + <property name="label">gtk-cancel</property> |
238 | + <property name="can_focus">True</property> |
239 | + <property name="receives_default">True</property> |
240 | + <property name="use_action_appearance">False</property> |
241 | + <property name="use_stock">True</property> |
242 | + </widget> |
243 | + <packing> |
244 | + <property name="expand">False</property> |
245 | + <property name="fill">False</property> |
246 | + <property name="position">0</property> |
247 | + </packing> |
248 | + </child> |
249 | + <child> |
250 | + <widget class="GtkButton" id="btn-ok"> |
251 | + <property name="label">gtk-ok</property> |
252 | + <property name="can_focus">True</property> |
253 | + <property name="receives_default">True</property> |
254 | + <property name="use_action_appearance">False</property> |
255 | + <property name="use_stock">True</property> |
256 | + </widget> |
257 | + <packing> |
258 | + <property name="expand">False</property> |
259 | + <property name="fill">False</property> |
260 | + <property name="position">1</property> |
261 | + </packing> |
262 | + </child> |
263 | + </widget> |
264 | + <packing> |
265 | + <property name="expand">True</property> |
266 | + <property name="fill">True</property> |
267 | + <property name="position">1</property> |
268 | + </packing> |
269 | + </child> |
270 | + </widget> |
271 | + <packing> |
272 | + <property name="expand">False</property> |
273 | + <property name="fill">True</property> |
274 | + <property name="position">2</property> |
275 | + </packing> |
276 | + </child> |
277 | + </widget> |
278 | + </child> |
279 | + </widget> |
280 | +</glade-interface> |
281 | |
282 | === added file 'src/modules/Lastfm.py' |
283 | --- src/modules/Lastfm.py 1970-01-01 00:00:00 +0000 |
284 | +++ src/modules/Lastfm.py 2012-01-27 19:34:24 +0000 |
285 | @@ -0,0 +1,122 @@ |
286 | +# -*- coding: utf-8 -*- |
287 | +# |
288 | +# Copyright (c) 2007 François Ingelrest (Francois.Ingelrest@gmail.com) |
289 | +# Copyright (c) 2010 Jendrik Seipp (jendrikseipp@web.de) |
290 | +# |
291 | +# This program is free software; you can redistribute it and/or modify |
292 | +# it under the terms of the GNU General Public License as published by |
293 | +# the Free Software Foundation; either version 2 of the License, or |
294 | +# (at your option) any later version. |
295 | +# |
296 | +# This program is distributed in the hope that it will be useful, |
297 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
298 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
299 | +# GNU Library General Public License for more details. |
300 | +# |
301 | +# You should have received a copy of the GNU General Public License |
302 | +# along with this program; if not, write to the Free Software |
303 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
304 | + |
305 | +import modules, os, tools, traceback, pylast, time |
306 | + |
307 | +from tools import consts, prefs |
308 | +from gettext import gettext as _ |
309 | +from tools.log import logger |
310 | + |
311 | + |
312 | +# Module information |
313 | +MOD_INFO = ('Last.fm', _('Last.fm Scrobbler'), _('The last.fm scrobbler'), [], False, True) |
314 | +MOD_NAME = MOD_INFO[modules.MODINFO_NAME] |
315 | + |
316 | +AS_API_KEY = 'f3fb2121e6e62a01db3bfc7957ee88a1' # Hermann's Audioscrobbler key |
317 | +AS_API_SECRET = 'fd330f4dee8a10c44096e94e36774e39' |
318 | + |
319 | +class Lastfm(modules.ThreadedModule): |
320 | + |
321 | + def __init__(self): |
322 | + """ Constructor """ |
323 | + handlers = { |
324 | + consts.MSG_EVT_NEW_TRACK: self.onNewTrack, |
325 | + consts.MSG_EVT_TRACK_POSITION: self.trackPos, |
326 | + } |
327 | + |
328 | + modules.ThreadedModule.__init__(self, handlers) |
329 | + |
330 | + self._track = False |
331 | + self._track_sent = False |
332 | + self._to_scrobble = False |
333 | + |
334 | + self._lastfmUsr = prefs.get(__name__, 'lastfm-username', "") |
335 | + self._lastfmPwd = prefs.get(__name__, 'lastfm-password', "") |
336 | + |
337 | + # --== Message handlers ==-- |
338 | + |
339 | + def onNewTrack(self, track): |
340 | + self._track_sent = False |
341 | + if track.getArtist() != consts.UNKNOWN_ARTIST and track.getAlbum() != consts.UNKNOWN_ALBUM \ |
342 | + and self._lastfmUsr and self._lastfmPwd: |
343 | + self._track = track |
344 | + |
345 | + # calculate submission point |
346 | + # track must be longer than 30 seconds |
347 | + if track.getLength() < 30: |
348 | + self._track_sent = True |
349 | + |
350 | + # and then scrobbling shall be done halfway through the track |
351 | + # or at the 4 minute mark (240 seconds), whichever comes first |
352 | + if track.getLength() > 480: |
353 | + self._to_scrobble = 240 |
354 | + else: |
355 | + self._to_scrobble = int(track.getLength() / 2) |
356 | + |
357 | + def trackPos(self, seconds): |
358 | + if self._track and not self._track_sent: |
359 | + if self._to_scrobble <= seconds: |
360 | + self._track_sent = True |
361 | + |
362 | + network = pylast.LastFMNetwork(api_key = AS_API_KEY, api_secret = AS_API_SECRET, |
363 | + username = self._lastfmUsr, password_hash = pylast.md5(self._lastfmPwd)) |
364 | + try: |
365 | + network.scrobble( |
366 | + artist = self._track.getArtist(), |
367 | + title = self._track.getTitle(), |
368 | + timestamp = int(time.time()) - self._to_scrobble, |
369 | + duration = self._track.getLength() |
370 | + ) |
371 | + except Exception, e: |
372 | + pass |
373 | + |
374 | + # --== Configuration ==-- |
375 | + |
376 | + def configure(self, parent): |
377 | + if self.cfgWin is None: |
378 | + from gui.window import Window |
379 | + |
380 | + self.cfgWin = Window('Lastfm.glade', 'vbox1', __name__, MOD_INFO[modules.MODINFO_L10N], 320, 265) |
381 | + self.cfgWin.getWidget('img-lastfm').set_from_file(os.path.join(consts.dirPix, 'audioscrobbler.png')) |
382 | + self.cfgWin.getWidget('btn-ok').connect('clicked', self.onBtnOk) |
383 | + self.cfgWin.getWidget('btn-help').connect('clicked', self.onBtnHelp) |
384 | + self.cfgWin.getWidget('btn-cancel').connect('clicked', lambda btn: self.cfgWin.hide()) |
385 | + |
386 | + if not self.cfgWin.isVisible(): |
387 | + lastfmUsr = prefs.get(__name__, 'lastfm-username', "") |
388 | + lastfmPwd = prefs.get(__name__, 'lastfm-password', "") |
389 | + |
390 | + self.cfgWin.getWidget('btn-ok').grab_focus() |
391 | + self.cfgWin.getWidget('txt-username').set_text(lastfmUsr) |
392 | + self.cfgWin.getWidget('txt-password').set_text(lastfmPwd) |
393 | + |
394 | + self.cfgWin.show() |
395 | + |
396 | + def onBtnOk(self, btn): |
397 | + """ Save configuration """ |
398 | + self._lastfmUsr = self.cfgWin.getWidget('txt-username').get_text() |
399 | + self._lastfmPwd = self.cfgWin.getWidget('txt-password').get_text() |
400 | + |
401 | + prefs.set(__name__, 'lastfm-username', self._lastfmUsr) |
402 | + prefs.set(__name__, 'lastfm-password', self._lastfmPwd) |
403 | + |
404 | + self.cfgWin.hide() |
405 | + |
406 | + def onBtnHelp(self, btn): |
407 | + pass |
Thanks for your work Hermann. I think we can make the plugin even better however by using code from decibel, the player pogo was forked from. There a scrobbling plugin was already included. The major benefit is that it doesn't need the additional pylast dependency and has a nice ui integration already. The code would have to be adapted slightly but I guess it should be straightforward.
If you like you can try copying http:// bazaar. launchpad. net/~athropos/ decibel- audio-player/ trunk/view/ head:/src/ modules/ AudioScrobbler. py into pogos modules directory. You will have to remove consts. MODCAT_ INTERNET from MOD_INFO, I don't know about other changes.