douban.fm 是我常用的网络音乐电台,但因为是web应用,无法使用全局热键控制,官方又没有提供linux桌面版本,故打算自己开发一个linux doufan.fm播放器。

要实现的功能:

  1. 全局热键控制
  2. 开始播放时桌面通知
  3. 登录、选频道

想要实现并不一定能实现,但我会花时间来慢慢攻破困难,目前为止已经简单的实现了基本功能。

 

既然是简单实现,那么在保证功能的前提下应该尽量保持实现的简单,以便后续完善。在开发工具上我选择python,以便快速交付和测试我的想法。

如何获取要播放的音乐及相关信息是首要解决的问题,这就必须要研究douban.fm的工作流程。其实我们可以思考一下,如果要我们实现一个网络电台,该怎么实现?不可能把所有的播放列表都放在客户端,是了,一个很好的方案是先由客户端发送请求播放列表,服务端返回播放列表,客户端再根据播放列表中的url请求文件以播放。

当然,具体情况还是要实际分析过才可得知,我们可以用网页调试工具(firebug或chrome的开发者工具),甚至抓包软件,分析douban.fm的数据通信。

刷新后,可以看到douban.fm发出的所有http请求,接下来,我们要有目的的寻找携带播放列表的http响应:

从url可以明显的看到playlist字样,展开该请求响应,可以看出响应数据的格式是json,继续展开,终于找到了我们想要的数据。

接着,我们要研究该url的参数,以便定制我们的播放器。在新标签打开该url:

经过多次尝试,大概可以确定这几个参数的行为:

  • type:字面意义是类型,具体作用不明,由douban.fm发出的所有请求都使用了type=n,缺少该参数将无法正常获取播放列表;
  • sid:作用不明,可省,猜测与用户相关;
  • pt:作用不明,可省,猜测与音乐类型相关;
  • chanel:频道ID,经过测试发现,0:公共/私人(指定sid时)兆赫、1:华语、-3:红心兆赫;
  • from:客户端来源,可省;
  • r:从值来看,应该是random的缩写,可省;

所以,一条最简单的获取播放列表的url是:http://douban.fm/j/mine/playlist?type=n&channel=0

有了音乐文件,怎么用python播放呢?我们没有必要先把mp3文件下载到,又自己实现一个播放器。即使利用已有的模块或库都显得过于麻烦。linux如此多基于命令行的流媒体播放器,为什么不直接拿来用?比如我经常使用的mplayer。

使用mplayer:mplayer http://mr3.douban.com/201212101049/fbc67748d2c7791b86653220b2ccbd08/view/song/small/p1472407.mp3,一条命令就可以播放在线的音乐。然后,用python的subprocess模块调用即可。

至此已经可以写出一个最简单的douban.fm播放器:

  1. httpConnection = httplib.HTTPConnection(\'douban.fm\')
  2. httpConnection.request(\'GET\', \'/j/mine/playlist?type=n&channel=0\')
  3. song = json.loads(httpConnection.getresponse().read())[\'song\']
  4. subprocess.call([\'mplayer\', song[0][\'url\']])

激动人心的是,这一功能的实现只用了4行代码。

统一的桌面通知是很有意义的(想想windows下混乱的弹窗吧),从OS X v10.8 “Mountain Lion”开始,Mac开始统一Notification Center,与此同时各linux桌面也在完善各自的通知系统。

ubuntu unity拥有自己的桌面通知接口,打开~/.bashrc,可以发现有这样一条命令别名:

alias alert=\’notify-send –urgency=low -i “$([ $? = 0 ] && echo terminal || echo error)” “$(history|tail -n1|sed -e \’\\’\’s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//\’\\’\’)”\’

在shell中输入命令:alert “test”,可以看到如下效果:

alert是别名,核心命令是notify-send,通过man notify-send,我们可以查看其用法。

ubuntu下播放器开启notify插件后,每次切歌都会发出一个桌面提示:

这是一个很酷的功能,那么如何实现?

仔细想想的话并不难,图片可以由song[0][\’picture\’]下载,然后构造命令notify-send -i pictrue_path [\’title\’] [\’artist\’] [\’albumtitle\’]即可。其中涉及到图片文件的下载保存,可以用httplib+文件读写来完成,实际中我用的是wget。

用python来实现就是:

  1. picture = \'images/\' + song[0][\'picture\'].split(\'/\')[4]
  2. # 下载专辑封面
  3. if not os.path.exists(picture):
  4. subprocess.call([
  5. \'wget\',
  6. \'-P\',
  7. \'images\',
  8. song[0][\'picture\']])
  9. # 发送桌面通知
  10. subprocess.call([
  11. \'notify-send\',
  12. \'-i\',
  13. os.getcwd() + \'/\' + picture,
  14. song[0][\'title\'],
  15. song[0][\'artist\'] + \'\n\' + song[0][\'albumtitle\']])

获取图片名时小小的hack了一下,song[0][\’picture\’].split(\’/\’)[4] 这句用来获取url中的图片名,当然,这并不是一个很好的方法。

最后,结合前面的播放代码,已经算是基本完成了,虽然没有实现全局热键控制、登录,但已经可以使用了。

完整的代码:

  1. 1 #!/usr/bin/python
  2. 2 # coding: utf-8
  3. 3
  4. 4 import httplib
  5. 5 import json
  6. 6 import os
  7. 7 import sys
  8. 8 import subprocess
  9. 9 import time
  10. 10
  11. 11 reload(sys)
  12. 12 sys.setdefaultencoding(\'utf-8\')
  13. 13
  14. 14 while True:
  15. 15 # 获取播放列表
  16. 16 httpConnection = httplib.HTTPConnection(\'douban.fm\')
  17. 17 httpConnection.request(\'GET\', \'/j/mine/playlist?type=n&channel=0\')
  18. 18 song = json.loads(httpConnection.getresponse().read())[\'song\']
  19. 19
  20. 20 picture = \'images/\' + song[0][\'picture\'].split(\'/\')[4]
  21. 21
  22. 22 # 下载专辑封面
  23. 23 if not os.path.exists(picture):
  24. 24 subprocess.call([
  25. 25 \'wget\',
  26. 26 \'-P\',
  27. 27 \'images\',
  28. 28 song[0][\'picture\']])
  29. 29
  30. 30 # 发送桌面通知
  31. 31 subprocess.call([
  32. 32 \'notify-send\',
  33. 33 \'-i\',
  34. 34 os.getcwd() + \'/\' + picture,
  35. 35 song[0][\'title\'],
  36. 36 song[0][\'artist\'] + \'\n\' + song[0][\'albumtitle\']])
  37. 37
  38. 38 # 播放
  39. 39 player = subprocess.Popen([\'mplayer\', song[0][\'url\']])
  40. 40 time.sleep(song[0][\'length\'])
  41. 41 player.kill()

 

版权声明:本文为7c00原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/7c00/archive/2012/12/10/2811242.html