├── README.md ├── captcha.py ├── staredtrack.py ├── transfer_stared.py └── xiami.py /README.md: -------------------------------------------------------------------------------- 1 | 虾米工具包 2 | ============ 3 | 4 | ## 更新 5 | * [2015/1/30] 初始化Xiami类时可以加入`captcha_handler`指定验证码处理函数,该函数需要返回验证码,交互和显示都用户实现的函数内自行处理 6 | * [2014/8/12] 1.替换掉android接口中 7 | * [2014/8/2] 增加虾米的淘宝帐号登录. 调用方法 `Xiami(username, password, taobao=True)`. 8 | 以及用 cookies 登录的办法 (解决接口不能用的终极方案) `Xiami(username, password, cookies=COOKIES字典)` 9 | 10 | ## 提示 11 | * 想要下载自己的收藏歌曲列表登录后比未登录获取的速度快 (接口不同) 12 | 13 | 14 | 把虾米的一些接口做成了库方便其他程序调用 15 | 16 | * `Xiami.get_stared_song(self, uid=None, full=False)` 返回某用户所有收藏曲目列表, uid不写默认为登录用户. 17 | * `Xiami.get_stared_collection(self, uid=None, full=False)` 返回某用户所有收藏精选集列表, uid不写默认为登录用户. 18 | * `Xiami.get_stared_album(self, uid=None, full=False)` 返回某用户所有收藏专集列表, uid不写默认为登录用户. 19 | * `Xiami.set_320k()` 设置当前用户默认下载曲目为高音质 20 | * `Xiami.download_song(self, song_id)` 返回编号为 *song_id* 的曲目的相关信息和下载地址, 详细返回请看范例 21 | * `Xiami.download_album(self, album_id)` 返回编号为 *album_id* 的专辑的相关信息和专辑内曲目下载地址, 详细返回请看范例 22 | * `Xiami.download_playlist(self, col_id)` 同上 23 | * `Xiami.star_song(self, songid)` 收藏曲目编号为 *songid* 的歌曲 24 | * `Xiami.get_session(self)` 获得当前 *Xiami* 实例的requests.Session对象 25 | * `Xiami.get_random_songs(self, uid=None, full=False)` 不知道干什么用的 26 | * `Xiami.get_artist_topsongs(self, artist_id, full=False)` 获得艺术家最热门的一些曲目 27 | * `Xiami.get_artist_albums(self, artist_id, full=False)` 获得艺术家的专辑列表 28 | 29 | 30 | get_ 类的函数可以指定 *full* 参数来确定是否返回详细信息 31 | download_ 类的函数返回一个简略但是足够信息的曲目列表 32 | 33 | **注意** 慎用 *full* 参数, 对于vip会员来说, *full* 参数可能查询高清音质的地址速度过快而导致需要输入验证码 (当然你可以直接指定你自己喜欢的处理函数 `self.captcha_handler = Your handler of captcha` 解决这个问题) 34 | 35 | #### 范例 36 | 37 | ```python 38 | xiami = Xiami('username', 'password') 39 | print xiami.download_song(1773330029) 40 | 41 | {'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330029_15326599_l.mp3?auth_key=xxx', 'header': {'Referer': 'http://img.xiami.com/static/swf/seiya/player.swf?v=1394535902294', 'User-Agent': 'Mozilla/5.0'}, 'cookies': {'member_auth': 'xxx', '_unsign_token': 'xxx', '__XIAMI_SESSID': 'xxx', 'user': 'xxx', '_xiamitoken': '88c977565b6f57d85833b488a43ac31c'}, 'data': [{'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'260', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Decision', 'album_id': u'204202267', 'id': 1773330029}]} 42 | ``` 43 | 44 | ```python 45 | xiami = Xiami('username', 'password') 46 | print xiami.download_song(1773330029) 47 | 48 | {'header': {'Referer': 'http://img.xiami.com/static/swf/seiya/player.swf?v=1394535902294', 'User-Agent': 'Mozilla/5.0'}, 'cookies': {'member_auth': '2W6bT45I621jiqifSIhiIC0f5O3VGGCFlo4Fj7Ev5VFwcdtaYYX%2FkauSRAJO2iKWrmFDReHNgWo', '_unsign_token': '1d753ca50994f5f5c07da420e4204430', '__XIAMI_SESSID': '047df5f493fdc1da653b74c48cdd8f08', 'user': '36962256%22lalala%22%222%22680%22%3Ca+href%3D%27%2Fwebsitehelp%23help9_3%27+%3Efa%3C%2Fa%3E%220%220%221597%223bba224459%221406222306', '_xiamitoken': '144df029292e13bec17db6feb9315be0'}, 'data': [{'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'260', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Decision', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330029_15326599_l.mp3?auth_key=25169b0959a1a19efe7ef44a74875867-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330029'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'126', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Best Thing That Ever Happened', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330030_15326600_l.mp3?auth_key=7607a2c3a8db52aa6442be2f35052c19-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330030'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'306', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'I'm an Autobot', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330031_15326601_l.mp3?auth_key=19153a432b71ccfa55f02af6f7d3ee55-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330031'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'137', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Optimus Is Alive', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330032_15326602_l.mp3?auth_key=b510b3a5d023b31ec8182e5ca4c80746-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330032'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'353', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Cemetery Wind', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330033_15326603_l.mp3?auth_key=91411ca8bb8c972fd17ed9e323030d2c-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330033'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'317', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'His Name Is Shane and He Drives', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330034_15326604_l.mp3?auth_key=f46ee29ea1772555e48c27ad3298b35f-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330034'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'125', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Hacking the Drone', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330035_15326605_l.mp3?auth_key=885105789eab92cf93c8f91ee53236d0-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330035'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'204', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Transformium', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330036_15326606_l.mp3?auth_key=efb1be0087183fb01bcdade229e84a52-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330036'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'116', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Galvatron Is Online', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330037_15326607_l.mp3?auth_key=635937f4669a918b31051e01f1574324-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330037'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'206', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Your Creators Want You Back', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330038_15326608_l.mp3?auth_key=e131ed686ea5f3a8388f294879d34321-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330038'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'247', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'The Final Knight', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330039_15326609_l.mp3?auth_key=130bc1d5ab965c517c40e0617ab787d0-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330039'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'132', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Punch Hold Slide Repeat', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330040_15326610_l.mp3?auth_key=a32fc095a187b5c500cd926c7b8bf548-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330040'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'171', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'The Presence of Megatron', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330041_15326611_l.mp3?auth_key=0d1e881234f0e261900f5a882f9410c6-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330041'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'254', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Galvatron Is Active', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330042_15326612_l.mp3?auth_key=4efaeb48223726ed63556cefd52ddaa1-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330042'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'89', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Have Faith Prime', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330043_15326613_l.mp3?auth_key=ada5dcabfd8932ca8a9719933e7308cc-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330043'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'103', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Hong Kong Chase', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330044_15326614_l.mp3?auth_key=0aa27f22ad1dc4b03930b652c76bf974-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330044'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'76', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'The Legend Exists', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330045_15326615_l.mp3?auth_key=bc0e82e0ad64a4d6b906fef0a1b998d2-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330045'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'397', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Dinobot Charge', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330046_15326616_l.mp3?auth_key=4f0bdf9e12fef0ec036c1b85362b50e1-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330046'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'171', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'That's a Big Magnet', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330047_15326617_l.mp3?auth_key=9e3e0aec036311608b3a453cb321700f-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330047'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'125', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Drive Backwards', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330048_15326618_l.mp3?auth_key=e80004b10da22b2dab85d1d26cd63f96-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330048'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'318', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': u'http://img.xiami.net/lyric/49/1773330049_14053219446364.txt', 'title': u'Honor to the End', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330049_15326619_l.mp3?auth_key=9a32e62f902254ab97055a9d463a70dd-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330049'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'227', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'Leave Planet Earth Alone', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330050_15326620_l.mp3?auth_key=07ce405bcc788d61d26798aece8e8163-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330050'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'201', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': None, 'title': u'The Knight Ship', 'url': u'http://m5.file.xiami.com/566/58566/1004526490/1773330051_15326621_l.mp3?auth_key=454f33bf388c817ce7e1a4ddbeb87268-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773330051'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'357', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': u'http://img.xiami.net/lyric/66/1773320966_14044862092251.lrc', 'title': u'Hunted', 'url': u'http://m5.file.xiami.com/566/58566/204202267/1773320966_15316504_l.mp3?auth_key=99a82d6c8dadd3f1b23df91908ee803e-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773320966'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'345', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': u'http://img.xiami.net/lyric/67/1773320967_14044862169970.lrc', 'title': u'Tessa', 'url': u'http://m5.file.xiami.com/566/58566/204202267/1773320967_15316505_l.mp3?auth_key=1353d214b36798b6240e9bf83df11b88-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773320967'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'177', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': u'http://img.xiami.net/lyric/68/1773320968_14044862276405.lrc', 'title': u'Autobots Reunite', 'url': u'http://m5.file.xiami.com/566/58566/204202267/1773320968_15316506_l.mp3?auth_key=84ecad921c74588ffe8ff34374f9974e-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773320968'}, {'artist': u'Steve Jablonsky', 'album_pic': u'http://img.xiami.net/images/album/img66/58566/2042022671404550070.jpg', 'length': u'294', 'album_name': u'Transformers: Age of Extinction (Music from the Motion Picture)', 'lyric': u'http://img.xiami.net/lyric/69/1773320969_14044862358934.lrc', 'title': u'Lockdown', 'url': u'http://m5.file.xiami.com/566/58566/204202267/1773320969_15316507_l.mp3?auth_key=89334aa199686bda6ac580a04fac93da-1406332800-0-null', 'album_id': u'204202267', 'id': u'1773320969'}]} 49 | ``` 50 | 51 | 一些其他好玩的组合 52 | ============ 53 | * 转移用户收藏数据 54 | -------------------------------------------------------------------------------- /captcha.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | # -*- coding: utf-8 -*- 3 | try: 4 | import Image 5 | except: 6 | from PIL import Image 7 | import sys 8 | from cStringIO import StringIO 9 | import time 10 | 11 | bash = False 12 | width = 0 13 | height = 0 14 | imgWidth = 0 15 | imgHeight = 0 16 | character = u'▄' 17 | verbose = False 18 | native = "_xterm256.c" 19 | 20 | CUBE_STEPS = [0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF] 21 | BASIC16 = ((0, 0, 0), (205, 0, 0), (0, 205, 0), (205, 205, 0), 22 | (0, 0, 238), (205, 0, 205), (0, 205, 205), (229, 229, 229), 23 | (127, 127, 127), (255, 0, 0), (0, 255, 0), (255, 255, 0), 24 | (92, 92, 255), (255, 0, 255), (0, 255, 255), (255, 255, 255)) 25 | 26 | def usage(): 27 | print("\n "+sys.argv[0]+" [-whcbvn] image") 28 | print(" Options:") 29 | print(" -w --width - width in pixels, if specified without height, the image") 30 | print(" will be scaled proportionally") 31 | print(" -h --height - see width") 32 | print(" -c --character - character to use for foreground") 33 | print(" -b --as-bash-script - output is a bash script") 34 | print(" -v --verbose - verbose output") 35 | print(" -n --native - name of the C-file which will be used to replace xterm_to_rgb") 36 | print(" and rgb_to_xterm by native methods") 37 | print(" --help - this") 38 | print(" image - Any image or gif (output will be animated)\n") 39 | 40 | def xterm_to_rgb(xcolor): 41 | assert 0 <= xcolor <= 255 42 | if xcolor < 16: 43 | # basic colors 44 | return BASIC16[xcolor] 45 | elif 16 <= xcolor <= 231: 46 | # color cube 47 | xcolor -= 16 48 | return (CUBE_STEPS[(xcolor / 36) % 6], 49 | CUBE_STEPS[(xcolor / 6) % 6], 50 | CUBE_STEPS[xcolor % 6]) 51 | elif 232 <= xcolor <= 255: 52 | # gray tone 53 | c = 8 + (xcolor - 232) * 0x0A 54 | return (c, c, c) 55 | 56 | COLOR_TABLE = [xterm_to_rgb(i) for i in xrange(256)] 57 | 58 | def rgb_to_xterm(r, g, b): 59 | if r < 5 and g < 5 and b < 5: 60 | return 16 61 | best_match = 0 62 | smallest_distance = 10000000000 63 | for c in xrange(16, 256): 64 | d = (COLOR_TABLE[c][0] - r) ** 2 + \ 65 | (COLOR_TABLE[c][1] - g) ** 2 + \ 66 | (COLOR_TABLE[c][2] - b) ** 2 67 | if d < smallest_distance: 68 | smallest_distance = d 69 | best_match = c 70 | return best_match 71 | 72 | def printPixels(rgb1,rgb2): 73 | c1 = rgb_to_xterm(rgb1[0], rgb1[1],rgb1[2]) 74 | c2 = rgb_to_xterm(rgb2[0], rgb2[1],rgb2[2]) 75 | sys.stdout.write('\x1b[48;5;%d;38;5;%dm' % (c1, c2)) 76 | sys.stdout.write(character) 77 | 78 | def printImage(im): 79 | global line 80 | for y in range(0,height-1,2): 81 | for x in range(width): 82 | p1 = im.getpixel((x,y)) 83 | p2 = im.getpixel((x,y+1)) 84 | printPixels(p1, p2) 85 | print('\x1b[0m') 86 | 87 | def iterateImages(im): 88 | if bash: 89 | print("echo '\x1b[s'") 90 | else: 91 | sys.stdout.write('\x1b[s') 92 | 93 | while True: 94 | if bash: 95 | print('cat <<"EOF"') 96 | sys.stdout.write('\x1b[u') 97 | printImage(getFrame(im)) 98 | if bash: 99 | print("EOF") 100 | 101 | try: 102 | im.seek(im.tell()+1) 103 | if bash: 104 | print('sleep '+str(im.info['duration']/1000.0)) 105 | else: 106 | time.sleep(im.info['duration']/1000.0) 107 | except EOFError: 108 | break 109 | 110 | def getFrame(im): 111 | if width!=imgWidth or height!=imgHeight: 112 | return im.resize((width,height), Image.ANTIALIAS).convert('RGB') 113 | else: 114 | return im.convert('RGB') 115 | 116 | def compile_speedup(): 117 | import os 118 | import ctypes 119 | from os.path import join, dirname, getmtime, exists, expanduser 120 | # library = join(dirname(__file__), '_xterm256.so') 121 | library = expanduser('~/.xterm256.so') 122 | sauce = join(dirname(__file__), native) 123 | if not exists(library) or getmtime(sauce) > getmtime(library): 124 | build = "gcc -fPIC -shared -o %s %s" % (library, sauce) 125 | assert os.system(build + " >/dev/null 2>&1") == 0 126 | xterm256_c = ctypes.cdll.LoadLibrary(library) 127 | xterm256_c.init() 128 | def xterm_to_rgb(xcolor): 129 | res = xterm256_c.xterm_to_rgb_i(xcolor) 130 | return ((res >> 16) & 0xFF, (res >> 8) & 0xFF, res & 0xFF) 131 | return (xterm256_c.rgb_to_xterm, xterm_to_rgb) 132 | 133 | 134 | def show(img): 135 | jpeg_data = StringIO(img) 136 | im = Image.open(jpeg_data) 137 | imgWidth = im.size[0] 138 | imgHeight = im.size[1] 139 | global width 140 | global height 141 | 142 | try: 143 | (rgb_to_xterm, xterm_to_rgb) = compile_speedup() 144 | except: 145 | if verbose and not bash: 146 | print("Failed to compile code, no speedup") 147 | 148 | if width!=0 or height!=0: 149 | if width==0: 150 | width=int(imgWidth*(height/float(imgHeight))) 151 | if height==0: 152 | height=int(imgHeight*(width/float(imgWidth))) 153 | else: 154 | width = imgWidth 155 | height = imgHeight 156 | """ 157 | width = imgWidth/2 158 | height = imgHeight/2 159 | """ 160 | iterateImages(im) 161 | -------------------------------------------------------------------------------- /staredtrack.py: -------------------------------------------------------------------------------- 1 | # coding=utf8 2 | """ 3 | 该脚本用于下载所有用户加星(收藏)的歌曲到 ./stared 目录下,并且内嵌专辑图片 4 | """ 5 | 6 | import os 7 | import re 8 | import time 9 | import random 10 | import urllib2 11 | from xiami import * 12 | import captcha 13 | from mutagen.mp3 import MP3 14 | from mutagen.id3 import ID3, TIT2, TPE1, TALB, APIC 15 | 16 | # source: baidupcsapi repo Issue 3 on github 17 | def upload_img42(img): 18 | url = 'http://img42.com' 19 | req = urllib2.Request(url, data=img) 20 | msg = urllib2.urlopen(req).read() 21 | return '%s/%s' % (url, json.loads(msg)['id']) 22 | 23 | def captcha(jpeg): 24 | print '* captcha needed' 25 | print 'captcha url:', upload_img42(jpeg) 26 | foo = raw_input('captcha >') 27 | return foo 28 | 29 | class Mutagentools: 30 | 31 | @staticmethod 32 | def add_tags(mp3, title=None, album=None, artist=None, 33 | image_cover=None, image_artist=None, image_type=u'image/jpeg'): 34 | _id3 = { 35 | 'mp3_title': TIT2(encoding=3, text=title), 36 | 'mp3_artist': TPE1(encoding=3, text=artist), 37 | 'mp3_album': TALB(encoding=3, text=album), 38 | 'mp3_cover': APIC(3, image_type, 3, u'Front cover', image_cover), 39 | 'mp3_artist_image': APIC(3, image_type, 8, u'Artist picture', image_artist), 40 | } 41 | # 尝试打开文件 42 | audio = MP3(mp3, ID3=ID3) 43 | try: 44 | audio.add_tags() 45 | except: 46 | pass 47 | 48 | for i in _id3.values(): 49 | audio.tags.add(i) 50 | 51 | audio.save() 52 | 53 | 54 | def safe_get(url): 55 | while True: 56 | try: 57 | foo = urllib2.urlopen(url).read() 58 | return foo 59 | except urllib2.HTTPError as e: 60 | if 'HTTP Error 503' in str(e): 61 | print '%s does not exist.' % url 62 | return None 63 | else: 64 | continue 65 | except: 66 | continue 67 | 68 | xiami = Xiami(username='', password='', captcha_handler=captcha, taobao=True) 69 | # xiami = Xiami(username='taobaousername', password='taobaopassword', taobao=True, captcha_handler=captcha) 70 | 71 | xiami.set_320k() 72 | cookies = ';'.join(['%s=%s' % (k, v) 73 | for k, v in xiami.session.cookies.items()]) 74 | 75 | stared_list = xiami.get_stared_songs() 76 | 77 | for entry in stared_list: 78 | song = xiami.download_song(entry) 79 | filepath = './stared/{0}-{1}.mp3'.format( 80 | Utils.text_validate(song['album_name']), 81 | Utils.text_validate(song['title'])) 82 | cmd = 'axel --alternate -n5 -H "Cookies:{2}" "{0}" -o "{1}"'.format( 83 | song['location'], filepath, cookies) 84 | 85 | song_logo = re.sub('_\d+', '', song['album_pic']) 86 | os.system(cmd) 87 | 88 | logo_song = safe_get(song_logo) 89 | 90 | # mutagen area 91 | Mutagentools.add_tags(filepath, song['title'], song['album_name'], song[ 92 | 'artist'], logo_song) 93 | 94 | -------------------------------------------------------------------------------- /transfer_stared.py: -------------------------------------------------------------------------------- 1 | """ 2 | 用途:转移 transferer 加星的歌曲到 transferee 3 | 专业免费2个月vip 20年 =w=, 我一般用于新账户的推荐歌曲功能 4 | """ 5 | from xiami import * 6 | 7 | # source: baidupcsapi repo Issue 3 on github 8 | def upload_img42(img): 9 | url = 'http://img42.com' 10 | req = urllib2.Request(url, data=img) 11 | msg = urllib2.urlopen(req).read() 12 | return '%s/%s' % (url, json.loads(msg)['id']) 13 | 14 | def captcha(jpeg): 15 | print '* captcha needed' 16 | print 'captcha url:', upload_img42(jpeg) 17 | foo = raw_input('captcha >') 18 | return foo 19 | 20 | xiami1 = Xiami(username='transferer', password='psasword1', taobao=True, captcha_handler=captcha) 21 | xiami2 = Xiami(username='transferee', password='password2', taobao=True, captcha_handler=captcha) 22 | 23 | stared = xiami1.get_stared_songs() 24 | for item in stared: 25 | print 'staring song:', item 26 | xiami2.star_song(item) 27 | -------------------------------------------------------------------------------- /xiami.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import sys 4 | from urllib2 import unquote 5 | import json 6 | import time 7 | import re 8 | import logging 9 | from functools import partial 10 | import xmltodict 11 | import requests 12 | from BeautifulSoup import BeautifulSoup 13 | import captcha 14 | import rsa 15 | 16 | logger = logging.getLogger() 17 | formatter = logging.Formatter( 18 | '%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',) 19 | stream_handler = logging.StreamHandler(sys.stderr) 20 | stream_handler.setFormatter(formatter) 21 | logger.addHandler(stream_handler) 22 | logger.setLevel(logging.NOTSET) 23 | 24 | reload(sys) 25 | sys.setdefaultencoding('utf-8') 26 | 27 | 28 | class Xiamiexp(Exception): 29 | """ 30 | 异常类 31 | """ 32 | def __init__(self, err): 33 | super(Xiamiexp, self).__init__() 34 | self.err = err 35 | 36 | def __str__(self): 37 | return self.err_type() 38 | 39 | def __expr__(self): 40 | return self.__str__() 41 | 42 | def err_type(self): 43 | """ 44 | xiami_account 虾米帐号密码错误 45 | 46 | taobao_account 淘宝帐号密码错误 47 | taobao_captcha 淘宝帐号验证码输入错误 48 | """ 49 | 50 | if self.err == 'xiami_account': 51 | return u'虾米帐号密码错误' 52 | elif self.err == 'taobao_account': 53 | return u'淘宝帐号密码输入错误' 54 | elif self.err == 'taobao_captcha': 55 | return u'淘宝验证码输入错误' 56 | elif self.err == 'generic_user_err': 57 | return u'Cookies 无效, 请确认用户名密码或者 cookies 的有效性.' 58 | 59 | 60 | 61 | class Utils: 62 | 63 | @staticmethod 64 | def text_validate(text): 65 | return text.replace('\'', '').replace('\\', '').replace('/', '') 66 | 67 | @staticmethod 68 | def url_decrypt(s): 69 | start = s.find('h') 70 | row = int(s[0:start]) 71 | length = len(s[start:]) 72 | column = length / row 73 | output = '' 74 | real_s = list(s[1:]) 75 | 76 | sucks = [] 77 | suck = length % row # = 0 -> good! if not , sucks! 78 | for i in range(1, suck + 1): 79 | sucks.append(real_s[i * (column)]) 80 | real_s[i * (column)] = 'sucks' 81 | real_s.remove('sucks') 82 | for i in range(column): 83 | output += ''.join(real_s[i:][slice(0, length, column)]) 84 | output += ''.join(sucks) 85 | return unquote(output).replace('^', '0') 86 | 87 | @staticmethod 88 | def check_username(func, *args): 89 | def _wrapper(*args): 90 | obj = args[0] 91 | if (not obj.username or not obj.password) and not obj.taobao: 92 | logger.debug('用户未登录') 93 | raise Exception('用户未登录') 94 | else: 95 | return func(*args) 96 | return _wrapper 97 | 98 | header = {'User-Agent': 'Mozilla/5.0', 99 | 'Referer': 'http://img.xiami.com/static/swf/seiya/player.swf?v=1394535902294'} 100 | 101 | 102 | class Xiamibase(object): 103 | """基类定义了基本的get和post访问 104 | """ 105 | header = {'User-Agent': 'Mozilla/5.0', 106 | 'Referer': 'http://img.xiami.com/static/swf/seiya/player.swf?v=1394535902294'} 107 | 108 | def __init__(self, captcha_handler=None): 109 | self.session = requests.session() 110 | self.captcha_handler = self._captcha_handler 111 | if captcha_handler: 112 | self.captcha_func = captcha_handler 113 | else: 114 | self.captcha_func = captcha.show 115 | 116 | def _safe_get(self, *args, **kwargs): 117 | while True: 118 | try: 119 | data = self.session.get(*args, **kwargs) 120 | 121 | if data.status_code == 403: 122 | sec = re.findall('