├── .project ├── .pydevproject ├── Groove ├── README.md ├── fm_ui.py ├── image ├── Selection_003.png ├── Selection_004.png ├── Selection_005.png ├── cover.jpg ├── douban.jpg ├── grep.png ├── grepheart.png ├── ic_play_current1a.gif ├── logo.jpg ├── mute.png ├── play.png ├── redheart.png ├── splay.png ├── trash.png ├── user.png ├── volumnbutton.png ├── wrench.png └── x_alt.png ├── main.py ├── sDialog.py ├── setting.py └── songinfo.py /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | douban 4 | 5 | 6 | 7 | 8 | 9 | org.python.pydev.PyDevBuilder 10 | 11 | 12 | 13 | 14 | 15 | org.python.pydev.pythonNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.pydevproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /douban 5 | 6 | python 2.7 7 | Default 8 | 9 | -------------------------------------------------------------------------------- /Groove: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | script_path=$(dirname "$0") 4 | cd $script_path 5 | 6 | python3 main.py 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##Groove 豆瓣FM客户端 2 | 使用python3+pyqt编写的豆瓣FM客户端 3 | 4 | ##依赖 5 | * python3 6 | 7 | * python3-bs4 8 | 9 | * python3-PyQt4 10 | 11 | ##截图 12 | 13 | 14 | ##运行 15 | >在ubuntu 12.04 Gnome和Kde环境下都可运行 16 | 17 | >下载后运行 18 | 19 | >`chmod +x Groove` 20 | 21 | >然后运行Groove就可以启动程序 22 | 23 | ##后续版本要增加的功能 24 | * 歌词 25 | * 完善收藏兆赫 26 | 27 | ##Bug 28 | * 连续点击下一首,可能出现歌与歌曲信息不符合的情况 29 | -------------------------------------------------------------------------------- /fm_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'doubanFM_revision.ui' 4 | # 5 | # Created: Wed May 29 22:26:19 2013 6 | # by: PyQt4 UI code generator 4.9.1 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PyQt4 import QtCore, QtGui 11 | 12 | try: 13 | _fromUtf8 = QtCore.QString.fromUtf8 14 | except AttributeError: 15 | _fromUtf8 = lambda s: s 16 | 17 | class Ui_Form(object): 18 | def setupUi(self, Form): 19 | Form.setObjectName(_fromUtf8("Form")) 20 | Form.setWindowModality(QtCore.Qt.NonModal) 21 | Form.resize(250, 550) 22 | Form.setMinimumSize(QtCore.QSize(250, 550)) 23 | Form.setMaximumSize(QtCore.QSize(250, 550)) 24 | icon = QtGui.QIcon() 25 | icon.addPixmap(QtGui.QPixmap(_fromUtf8("image/logo.jpg")), QtGui.QIcon.Normal, QtGui.QIcon.Off) 26 | Form.setWindowIcon(icon) 27 | Form.setAutoFillBackground(False) 28 | Form.setObjectName(_fromUtf8("")) 29 | self.layoutWidget = QtGui.QWidget(Form) 30 | self.layoutWidget.setGeometry(QtCore.QRect(12, 12, 226, 52)) 31 | self.layoutWidget.setObjectName(_fromUtf8("layoutWidget")) 32 | self.opLayout = QtGui.QHBoxLayout(self.layoutWidget) 33 | self.opLayout.setSpacing(7) 34 | self.opLayout.setMargin(0) 35 | self.opLayout.setObjectName(_fromUtf8("opLayout")) 36 | self.stopButton = HexLabel(self.layoutWidget) 37 | self.stopButton.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 38 | self.stopButton.setPixmap(QtGui.QPixmap(_fromUtf8("image/Selection_005.png"))) 39 | self.stopButton.setObjectName(_fromUtf8("stopButton")) 40 | self.opLayout.addWidget(self.stopButton) 41 | self.nextButton = HexLabel(self.layoutWidget) 42 | self.nextButton.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 43 | self.nextButton.setTextFormat(QtCore.Qt.AutoText) 44 | self.nextButton.setPixmap(QtGui.QPixmap(_fromUtf8("image/Selection_004.png"))) 45 | self.nextButton.setObjectName(_fromUtf8("nextButton")) 46 | self.opLayout.addWidget(self.nextButton) 47 | self.likeButton = HexLabel(self.layoutWidget) 48 | self.likeButton.setEnabled(False) 49 | self.likeButton.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 50 | self.likeButton.setTextFormat(QtCore.Qt.AutoText) 51 | self.likeButton.setPixmap(QtGui.QPixmap(_fromUtf8("image/grepheart.png"))) 52 | self.likeButton.setObjectName(_fromUtf8("likeButton")) 53 | self.opLayout.addWidget(self.likeButton) 54 | self.notlikeButton = HexLabel(self.layoutWidget) 55 | self.notlikeButton.setEnabled(False) 56 | self.notlikeButton.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 57 | self.notlikeButton.setTextFormat(QtCore.Qt.AutoText) 58 | self.notlikeButton.setPixmap(QtGui.QPixmap(_fromUtf8("image/Selection_003.png"))) 59 | self.notlikeButton.setObjectName(_fromUtf8("notlikeButton")) 60 | self.opLayout.addWidget(self.notlikeButton) 61 | self.opLayout.setStretch(0, 10) 62 | self.tline = QtGui.QFrame(Form) 63 | self.tline.setGeometry(QtCore.QRect(0, 468, 250, 16)) 64 | self.tline.setFrameShape(QtGui.QFrame.HLine) 65 | self.tline.setFrameShadow(QtGui.QFrame.Sunken) 66 | self.tline.setObjectName(_fromUtf8("tline")) 67 | self.cover = QtGui.QLabel(Form) 68 | self.cover.setGeometry(QtCore.QRect(0, 110, 250, 250)) 69 | self.cover.setText(_fromUtf8("")) 70 | self.cover.setPixmap(QtGui.QPixmap(_fromUtf8("image/cover.jpg"))) 71 | self.cover.setScaledContents(True) 72 | self.cover.setObjectName(_fromUtf8("cover")) 73 | self.title = QtGui.QLabel(Form) 74 | self.title.setGeometry(QtCore.QRect(10, 370, 150, 30)) 75 | self.title.setMinimumSize(QtCore.QSize(250, 30)) 76 | self.title.setText(_fromUtf8("")) 77 | self.title.setObjectName(_fromUtf8("title")) 78 | self.artist = QtGui.QLabel(Form) 79 | self.artist.setGeometry(QtCore.QRect(10, 410, 150, 20)) 80 | self.artist.setMinimumSize(QtCore.QSize(250, 10)) 81 | self.artist.setText(_fromUtf8("")) 82 | self.artist.setObjectName(_fromUtf8("artist")) 83 | self.channel = HexLabel(Form) 84 | self.channel.setGeometry(QtCore.QRect(10, 478, 240, 30)) 85 | self.channel.setMinimumSize(QtCore.QSize(50, 30)) 86 | self.channel.setObjectName(_fromUtf8("channel")) 87 | self.choice = QtGui.QLabel(self.channel) 88 | self.choice.setGeometry(QtCore.QRect(220, 5, 10, 20)) 89 | self.choice.setText(_fromUtf8("")) 90 | self.choice.setPixmap(QtGui.QPixmap(_fromUtf8("image/play.png"))) 91 | self.choice.setObjectName(_fromUtf8("choice")) 92 | self.bline = QtGui.QFrame(Form) 93 | self.bline.setGeometry(QtCore.QRect(0, 503, 250, 16)) 94 | self.bline.setFrameShape(QtGui.QFrame.HLine) 95 | self.bline.setFrameShadow(QtGui.QFrame.Sunken) 96 | self.bline.setObjectName(_fromUtf8("bline")) 97 | self.layoutWidget1 = QtGui.QWidget(Form) 98 | self.layoutWidget1.setGeometry(QtCore.QRect(0, 70, 254, 31)) 99 | self.layoutWidget1.setObjectName(_fromUtf8("layoutWidget1")) 100 | self.volumnLayout = QtGui.QHBoxLayout(self.layoutWidget1) 101 | self.volumnLayout.setMargin(0) 102 | self.volumnLayout.setObjectName(_fromUtf8("volumnLayout")) 103 | self.volumn = HexLabel(self.layoutWidget1) 104 | self.volumn.setMinimumSize(QtCore.QSize(20, 0)) 105 | self.volumn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 106 | self.volumn.setPixmap(QtGui.QPixmap(_fromUtf8("image/volumnbutton.png"))) 107 | self.volumn.setObjectName(_fromUtf8("volumn")) 108 | self.volumnLayout.addWidget(self.volumn) 109 | self.volumnSlider = QtGui.QSlider(self.layoutWidget1) 110 | self.volumnSlider.setMinimumSize(QtCore.QSize(100, 0)) 111 | self.volumnSlider.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 112 | self.volumnSlider.setOrientation(QtCore.Qt.Horizontal) 113 | self.volumnSlider.setObjectName(_fromUtf8("volumnSlider")) 114 | self.volumnSlider.setFixedWidth(150) 115 | self.volumnLayout.addWidget(self.volumnSlider) 116 | self.tpos = QtGui.QLabel(self.layoutWidget1) 117 | self.tpos.setMinimumSize(QtCore.QSize(70, 0)) 118 | self.tpos.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) 119 | self.tpos.setObjectName(_fromUtf8("tpos")) 120 | self.volumnLayout.addWidget(self.tpos) 121 | self.userLabel = HexLabel(Form) 122 | self.userLabel.setGeometry(QtCore.QRect(10, 520, 20, 20)) 123 | self.userLabel.setMinimumSize(QtCore.QSize(20, 0)) 124 | self.userLabel.setPixmap(QtGui.QPixmap(_fromUtf8("image/user.png"))) 125 | self.userLabel.setObjectName(_fromUtf8("userLabel")) 126 | self.username = QtGui.QLabel(Form) 127 | self.username.setGeometry(QtCore.QRect(38, 522, 70, 17)) 128 | self.username.setMinimumSize(QtCore.QSize(70, 0)) 129 | self.username.setObjectName(_fromUtf8("username")) 130 | self.quitLabel = HexLabel(Form) 131 | self.quitLabel.setGeometry(QtCore.QRect(220, 520, 20, 20)) 132 | self.quitLabel.setPixmap(QtGui.QPixmap(_fromUtf8("image/x_alt.png"))) 133 | self.quitLabel.setObjectName(_fromUtf8("quitLabel")) 134 | self.setting = HexLabel(Form) 135 | self.setting.setGeometry(QtCore.QRect(192, 520, 20, 20)) 136 | self.setting.setPixmap(QtGui.QPixmap(_fromUtf8("image/wrench.png"))) 137 | self.setting.setObjectName(_fromUtf8("setting")) 138 | self.redLabel = HexLabel(Form) 139 | self.redLabel.setGeometry(QtCore.QRect(102, 520, 25, 20)) 140 | self.redLabel.setPixmap(QtGui.QPixmap(_fromUtf8("image/grep.png"))) 141 | self.redLabel.setObjectName(_fromUtf8("redLabel")) 142 | self.rednum = QtGui.QLabel(Form) 143 | self.rednum.setGeometry(QtCore.QRect(127, 520, 50, 20)) 144 | self.rednum.setMinimumSize(QtCore.QSize(50, 0)) 145 | self.rednum.setObjectName(_fromUtf8("rednum")) 146 | 147 | self.current_channel = QtGui.QLabel(Form) 148 | self.current_channel.setGeometry(QtCore.QRect(10, 442, 240, 30)) 149 | self.current_channel.setMinimumSize(QtCore.QSize(50, 30)) 150 | self.current_channel.setObjectName(_fromUtf8("current_channel")) 151 | self.current_channel.setStyleSheet("font-size:10pt") 152 | self.choice_2 = QtGui.QLabel(self.current_channel) 153 | self.choice_2.setGeometry(QtCore.QRect(220, 5, 10, 20)) 154 | self.choice_2.setText(_fromUtf8("")) 155 | self.choice_2.setPixmap(QtGui.QPixmap(_fromUtf8("image/ic_play_current1a.gif"))) 156 | self.choice_2.setObjectName(_fromUtf8("choice_2")) 157 | 158 | #Popup menu 159 | self.menu = QtGui.QMenu(Form) 160 | self.redheartChannel = QtGui.QAction(_fromUtf8("红心兆赫"),self) 161 | self.redheartChannel.setEnabled(False) 162 | self.privateChannel = QtGui.QAction(_fromUtf8("私人兆赫"),self) 163 | self.privateChannel.setEnabled(False) 164 | self.collectChannel = QtGui.QMenu("收藏兆赫") 165 | self.collectChannel.setEnabled(False) 166 | self.moreChannel = QtGui.QMenu("公共兆赫") 167 | self.chineseChannel = QtGui.QAction("华语MHz",self) 168 | self.westernChannel = QtGui.QAction("欧美MHz",self) 169 | self.seventyChannel = QtGui.QAction("70MHz",self) 170 | self.eightyChannel = QtGui.QAction("80MHz",self) 171 | self.nintyChannel = QtGui.QAction("90MHz",self) 172 | self.cantoneseChannel = QtGui.QAction("粤语MHz",self) 173 | self.rockChannel = QtGui.QAction("摇滚MHz",self) 174 | self.folkChannel = QtGui.QAction("民谣MHz",self) 175 | self.lightmusicChannnel = QtGui.QAction("轻音乐MHz",self) 176 | self.movieChannel = QtGui.QAction("电影原声MHz",self) 177 | self.jazzChannel = QtGui.QAction("爵士MHz",self) 178 | self.elecChannel = QtGui.QAction("电子MHz",self) 179 | self.rapChannel = QtGui.QAction("说唱MHz",self) 180 | self.rnbChannel = QtGui.QAction("R&BMHz",self) 181 | self.japanChannel = QtGui.QAction("日语MHz",self) 182 | self.koreanChannel = QtGui.QAction("韩语MHz",self) 183 | self.womenChannel = QtGui.QAction("女声MHz",self) 184 | self.franceChannel = QtGui.QAction("法语MHz",self) 185 | self.moreChannel.addAction(self.chineseChannel) 186 | self.moreChannel.addAction(self.westernChannel) 187 | self.moreChannel.addAction(self.seventyChannel) 188 | self.moreChannel.addAction(self.eightyChannel) 189 | self.moreChannel.addAction(self.nintyChannel) 190 | self.moreChannel.addAction(self.cantoneseChannel) 191 | self.moreChannel.addAction(self.rockChannel) 192 | self.moreChannel.addAction(self.folkChannel) 193 | self.moreChannel.addAction(self.lightmusicChannnel) 194 | self.moreChannel.addAction(self.movieChannel) 195 | self.moreChannel.addAction(self.jazzChannel) 196 | self.moreChannel.addAction(self.elecChannel) 197 | self.moreChannel.addAction(self.rapChannel) 198 | self.moreChannel.addAction(self.rnbChannel) 199 | self.moreChannel.addAction(self.japanChannel) 200 | self.moreChannel.addAction(self.koreanChannel) 201 | self.moreChannel.addAction(self.womenChannel) 202 | self.moreChannel.addAction(self.franceChannel) 203 | self.menu.addMenu(self.moreChannel) 204 | self.menu.addAction(self.redheartChannel) 205 | self.menu.addAction(self.privateChannel) 206 | self.menu.addMenu(self.collectChannel) 207 | 208 | self.retranslateUi(Form) 209 | QtCore.QMetaObject.connectSlotsByName(Form) 210 | 211 | def retranslateUi(self, Form): 212 | Form.setWindowTitle(QtGui.QApplication.translate("Form", "Groove", None, QtGui.QApplication.UnicodeUTF8)) 213 | self.channel.setText(QtGui.QApplication.translate("Form", "

兆赫选择

", None, QtGui.QApplication.UnicodeUTF8)) 214 | self.tpos.setText(QtGui.QApplication.translate("Form", " ", None, QtGui.QApplication.UnicodeUTF8)) 215 | self.username.setText(QtGui.QApplication.translate("Form", "

未登录

", None, QtGui.QApplication.UnicodeUTF8)) 216 | self.rednum.setText(QtGui.QApplication.translate("Form", "0", None, QtGui.QApplication.UnicodeUTF8)) 217 | 218 | class HexLabel(QtGui.QLabel): 219 | def __init__(self,parent=None): 220 | super(HexLabel,self).__init__(parent) 221 | self.checked = False; 222 | 223 | clicked = QtCore.pyqtSignal() 224 | toggled = QtCore.pyqtSignal(bool) 225 | mousemove = QtCore.pyqtSignal(QtGui.QMouseEvent) 226 | 227 | def mousePressEvent(self,event): 228 | if event.button() == QtCore.Qt.LeftButton: 229 | self.clicked.emit() 230 | self.checked = not self.checked 231 | self.toggled.emit(self.checked) 232 | def mouseMoveEvent(self,event): 233 | self.mousemove.emit(event) -------------------------------------------------------------------------------- /image/Selection_003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/Selection_003.png -------------------------------------------------------------------------------- /image/Selection_004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/Selection_004.png -------------------------------------------------------------------------------- /image/Selection_005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/Selection_005.png -------------------------------------------------------------------------------- /image/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/cover.jpg -------------------------------------------------------------------------------- /image/douban.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/douban.jpg -------------------------------------------------------------------------------- /image/grep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/grep.png -------------------------------------------------------------------------------- /image/grepheart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/grepheart.png -------------------------------------------------------------------------------- /image/ic_play_current1a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/ic_play_current1a.gif -------------------------------------------------------------------------------- /image/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/logo.jpg -------------------------------------------------------------------------------- /image/mute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/mute.png -------------------------------------------------------------------------------- /image/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/play.png -------------------------------------------------------------------------------- /image/redheart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/redheart.png -------------------------------------------------------------------------------- /image/splay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/splay.png -------------------------------------------------------------------------------- /image/trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/trash.png -------------------------------------------------------------------------------- /image/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/user.png -------------------------------------------------------------------------------- /image/volumnbutton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/volumnbutton.png -------------------------------------------------------------------------------- /image/wrench.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/wrench.png -------------------------------------------------------------------------------- /image/x_alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vstorm/Groove/cdce4f2af7320268f5c38d3681db90a428a5f1a4/image/x_alt.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import fm_ui 4 | import time 5 | import urllib.request #python3 6 | import http.cookiejar #python3 7 | from PyQt4 import QtCore,QtGui 8 | from songinfo import getSongInfo 9 | from sDialog import sDialog 10 | from bs4 import BeautifulSoup 11 | 12 | class Widget(QtGui.QWidget,fm_ui.Ui_Form): 13 | def __init__(self,parent=None): 14 | QtGui.QWidget.__init__(self,parent) 15 | self.initUi() 16 | 17 | def initUi(self): 18 | self.setupUi(self) 19 | # http.client.HTTPConnection.debuglevel = 1 20 | url = '1' 21 | cookiefile = "cookies.txt" 22 | collectChannel = {} 23 | self.tochannel() #QSignalMapper 24 | self.get = 0 25 | if os.path.isfile(cookiefile): #cookie文件是否存在 26 | self.likeButton.setEnabled(True) 27 | self.notlikeButton.setEnabled(True) 28 | self.redheartChannel.setEnabled(True) 29 | self.privateChannel.setEnabled(True) 30 | self.collectChannel.setEnabled(True) 31 | self.install_opener(cookiefile) 32 | collectChannel=self.getcollect() 33 | self.tocollect(collectChannel) 34 | url = '0' 35 | if url == '0': 36 | self.current_channel.setText("私人兆赫") 37 | else: 38 | self.current_channel.setText("华语HMz") 39 | self.url = "http://douban.fm/j/mine/playlist?type=n&channel="+url+"&from=mainsite" 40 | 41 | self.sDialog = None 42 | self.songprocess = QtCore.QProcess() 43 | self.songprocess.setProcessChannelMode(QtCore.QProcess.MergedChannels) 44 | self.songprocess.started.connect(self.info) 45 | self.songprocess.finished[int].connect(self.finish) 46 | self.nextButton.clicked.connect(self.next) #下一首 47 | self.stopButton.toggled.connect(self.stop) #暂停 48 | self.volumnSlider.valueChanged.connect(self.changeVolumn) #改变音量 49 | self.volumn.clicked.connect(self.mute) #改变音量 50 | self.setting.clicked.connect(self.changeSetting) 51 | self.likeButton.clicked.connect(self.like) 52 | self.notlikeButton.clicked.connect(self.notlike) 53 | self.channel.mousemove.connect(self.showChannel) 54 | self.quitLabel.clicked.connect(self.clearCookie) 55 | 56 | if url == "1": 57 | self.cnum = 1 58 | else: 59 | self.cnum = -3 60 | 61 | self.songlist = getSongInfo(self.url) 62 | self.s = next(self.songlist) 63 | song = self.s["url"] 64 | self.songprocess.start('mplayer',['-slave','-quiet',song]) 65 | self.vnum = 50 66 | self.volumnSlider.setValue(self.vnum) 67 | 68 | def getcollect(self): 69 | indexurl = "http://fm.douban.com" 70 | r = urllib.request.urlopen(indexurl) 71 | soup = BeautifulSoup(r) 72 | self.username.setText(soup.find(id="user_name").get_text()) 73 | self.rednum.setText(soup.find(id="rec_liked").get_text()) 74 | if not self.get: 75 | ul = soup.find(id="fav_chls") 76 | collect={} 77 | for li in ul.find_all('li'): 78 | if li.get('class') == ['channel']: 79 | cid = li.get('cid') 80 | cname = li.a.get_text() 81 | action = QtGui.QAction(cname,self) 82 | self.collectChannel.addAction(action) 83 | collect[cid]=action 84 | self.get = 1 85 | return collect 86 | 87 | def tocollect(self,collectChannel): 88 | if len(collectChannel): 89 | for num,channel in collectChannel.items(): 90 | text = channel.text() 91 | # collectChannel 92 | self.sMapper = QtCore.QSignalMapper(self) 93 | self.tMapper = QtCore.QSignalMapper(self) 94 | self.sMapper.setMapping(channel,num) 95 | self.tMapper.setMapping(channel,text) 96 | channel.triggered.connect(self.sMapper.map) 97 | channel.triggered.connect(self.tMapper.map) 98 | self.sMapper.mapped[str].connect(self.changeChannel) 99 | self.tMapper.mapped[str].connect(self.currentChannel) 100 | 101 | def install_opener(self,cookiefile): 102 | cookie = http.cookiejar.LWPCookieJar(cookiefile) 103 | cookie.load(cookiefile, ignore_discard=True, ignore_expires=True) 104 | opener= urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cookie)) 105 | urllib.request.install_opener(opener) 106 | 107 | def tochannel(self): #QSignalMapper 108 | channellist = {'-3':self.redheartChannel,'0':self.privateChannel,'1':self.chineseChannel,'2':self.westernChannel, 109 | '3':self.seventyChannel,'4':self.eightyChannel,'5':self.nintyChannel, 110 | '6':self.cantoneseChannel,'7':self.rockChannel,'8':self.folkChannel, 111 | '9':self.lightmusicChannnel,'10':self.movieChannel,'13':self.jazzChannel, 112 | '14':self.elecChannel,'15':self.rapChannel,'16':self.rnbChannel,'17':self.japanChannel, 113 | '18':self.koreanChannel,'20':self.womenChannel,"22":self.franceChannel} 114 | for num,channel in channellist.items(): 115 | text = channel.text() 116 | self.sMapper = QtCore.QSignalMapper(self) 117 | self.tMapper = QtCore.QSignalMapper(self) 118 | self.sMapper.setMapping(channel,num) 119 | self.tMapper.setMapping(channel,text) 120 | channel.triggered.connect(self.sMapper.map) 121 | channel.triggered.connect(self.tMapper.map) 122 | self.sMapper.mapped[str].connect(self.changeChannel) 123 | self.tMapper.mapped[str].connect(self.currentChannel) 124 | 125 | def song(self): #获取歌曲 126 | url = self.s["picture"].replace("mpic","lpic") 127 | picture = urllib.request.urlopen(url).read() 128 | with open("image/cover.jpg",mode="wb") as img_file: 129 | img_file.write(picture) 130 | title = self.s["title"] 131 | artist = self.s["artist"] 132 | if len(title)>25: 133 | title = title[0:25]+'...' 134 | if len(artist)>25: 135 | artist = artist[0:25]+'...' 136 | self.cover.setPixmap(QtGui.QPixmap("image/cover.jpg")) 137 | self.artist.setText('

'+artist+'

') 138 | self.title.setText('

'+title+'

') 139 | self.sid = self.s['sid'] 140 | if self.s["like"] == 1: 141 | self.likeButton.setPixmap(QtGui.QPixmap("image/redheart.png")) 142 | self.like = 1 143 | else: 144 | self.likeButton.setPixmap(QtGui.QPixmap("image/grepheart.png")) 145 | self.like = 0 146 | 147 | def nextsong(self): 148 | try: 149 | self.s = next(self.songlist) 150 | except StopIteration: 151 | self.songlist = getSongInfo(self.url) 152 | self.s = next(self.songlist) 153 | 154 | @QtCore.pyqtSlot() 155 | def like(self): #加心或去掉加心 156 | num = int(self.rednum.text()) 157 | if self.like: 158 | t = "u" 159 | self.likeButton.setPixmap(QtGui.QPixmap("image/grepheart.png")) 160 | self.like = 0 161 | num = num -1 162 | else: 163 | t = "r" 164 | self.likeButton.setPixmap(QtGui.QPixmap("image/redheart.png")) 165 | self.like = 1 166 | num = num + 1 167 | self.rednum.setText(str(num)) 168 | url = "http://douban.fm/j/mine/playlist?type=" + t +"&sid=" + self.sid +"&channel=" + str(self.cnum) + "&from=mainsite" 169 | urllib.request.urlopen(url) 170 | 171 | @QtCore.pyqtSlot() #不再收听 172 | def notlike(self): 173 | url = "http://douban.fm/j/mine/playlist?type=b&sid=" + self.sid +"&channel=" + str(self.cnum) + "&from=mainsite" 174 | urllib.request.urlopen(url) 175 | if self.like: 176 | num = int(self.rednum.text()) - 1 177 | self.rednum.setText(str(num)) 178 | self.next() 179 | 180 | @QtCore.pyqtSlot() 181 | def changeSetting(self): #打开设置 182 | if self.sDialog is None: 183 | self.sDialog = sDialog(self) 184 | self.sDialog.show() 185 | self.sDialog.raise_() 186 | self.sDialog.activateWindow() 187 | 188 | @QtCore.pyqtSlot() 189 | def info(self): 190 | self.song() 191 | 192 | @QtCore.pyqtSlot(bool) 193 | def stop(self,stop): #暂停 194 | if stop: 195 | self.stopButton.setPixmap(QtGui.QPixmap("image/splay.png")) 196 | else: 197 | self.stopButton.setPixmap(QtGui.QPixmap("image/Selection_005")) 198 | self.songprocess.write("pause\n") 199 | 200 | @QtCore.pyqtSlot(int) 201 | def changeVolumn(self,num): #更改音量 202 | self.songprocess.write("volume {0} 1\n".format(num)) 203 | if not num: 204 | self.volumn.setPixmap(QtGui.QPixmap('image/mute.png')) 205 | else: 206 | self.volumn.setPixmap(QtGui.QPixmap('image/volumnbutton.png')) 207 | @QtCore.pyqtSlot() 208 | def mute(self): #静音 209 | num = self.volumnSlider.value() 210 | if(num): 211 | self.volumnSlider.setValue(0) 212 | self.vnum = num 213 | self.volumn.setPixmap(QtGui.QPixmap('image/mute.png')) 214 | else: 215 | self.volumnSlider.setValue(self.vnum) 216 | self.volumn.setPixmap(QtGui.QPixmap('image/volumnbutton.png')) 217 | # self.volumn.repaint() 218 | 219 | @QtCore.pyqtSlot() 220 | def next(self): 221 | self.nextsong() 222 | song = self.s["url"] 223 | self.songprocess.write("loadfile {0}\n".format(song)) 224 | time.sleep(1) 225 | self.song() 226 | # self.repaint() 227 | @QtCore.pyqtSlot(int) 228 | def finish(self,error): 229 | if not error: 230 | self.nextsong() 231 | song = self.s["url"] 232 | self.songprocess.start('mplayer',['-slave','-quiet',song]) 233 | 234 | @QtCore.pyqtSlot(QtGui.QMouseEvent) 235 | def showChannel(self,event): #显示兆赫选择菜单 236 | pos = QtCore.QPoint(250,478) 237 | self.menu.exec_(self.mapToParent(pos)) 238 | 239 | @QtCore.pyqtSlot(str) 240 | def changeChannel(self,num): 241 | self.url = "http://douban.fm/j/mine/playlist?type=n&sid=&pt=0.0&channel="+ num + "&from=mainsite" 242 | self.cnum = num 243 | self.songlist = getSongInfo(self.url) 244 | self.s = next(self.songlist) 245 | song = self.s["url"] 246 | self.songprocess.write("loadfile {0}\n".format(song)) 247 | self.song() 248 | 249 | @QtCore.pyqtSlot(str) 250 | def currentChannel(self,text): 251 | self.current_channel.setText(text) 252 | @QtCore.pyqtSlot() 253 | def clearCookie(self): 254 | if os.path.isfile("cookies.txt"): 255 | r = QtGui.QMessageBox.warning(self,"Groove","退出登录?", buttons=QtGui.QMessageBox.Yes|QtGui.QMessageBox.No, defaultButton=QtGui.QMessageBox.No) 256 | if r == QtGui.QMessageBox.Yes: 257 | os.remove("cookies.txt") 258 | self.likeButton.setEnabled(False) 259 | self.notlikeButton.setEnabled(False) 260 | self.redheartChannel.setEnabled(False) 261 | self.privateChannel.setEnabled(False) 262 | self.collectChannel.setEnabled(False) 263 | self.username.setText("未登录") 264 | self.rednum.setText("0") 265 | self.changeChannel('1') 266 | self.currentChannel("华语HMz") 267 | def closeEvent(self,ev): 268 | self.songprocess.write("quit\n") 269 | self.songprocess.waitForFinished(msecs=30000) 270 | ev.accept() 271 | 272 | def main(): 273 | app = QtGui.QApplication(sys.argv) 274 | widget = Widget() 275 | widget.show() 276 | app.lastWindowClosed.connect(app.quit) 277 | sys.exit(app.exec_()) 278 | 279 | if __name__ == "__main__": 280 | main() 281 | 282 | 283 | -------------------------------------------------------------------------------- /sDialog.py: -------------------------------------------------------------------------------- 1 | import os 2 | import http.cookiejar 3 | import urllib.request 4 | import setting 5 | import json 6 | from PyQt4 import QtGui,QtCore 7 | # from opener import install_opener 8 | 9 | 10 | class sDialog(QtGui.QDialog,setting.Ui_Dialog): 11 | def __init__(self,parent=None): 12 | QtGui.QDialog.__init__(self,parent) 13 | self.initUi() 14 | 15 | def initUi(self): 16 | self.setupUi(self) 17 | self.setFixedSize(QtCore.QSize(400,350)) 18 | self.glink.setText("github") 19 | self.dlink.setText("小峰") 20 | self.captchaurl_id = None 21 | self.codeButton.clicked.connect(self.getCode) 22 | self.buttonBox.accepted.connect(self.getAuth) 23 | self.buttonBox.rejected.connect(self.close) 24 | self.glink.linkActivated.connect(self.openUrl) 25 | self.dlink.linkActivated.connect(self.openUrl) 26 | def openUrl(self,url): 27 | QtGui.QDesktopServices.openUrl(QtCore.QUrl(url)) 28 | 29 | def getCode(self): 30 | captchaurl = "http://douban.fm/j/new_captcha" 31 | data = urllib.request.urlopen(captchaurl) 32 | self.captchaurl_id = data.read().decode('utf-8').split('"')[1] 33 | requrl = "http://douban.fm/misc/captcha?size=m&id={0}".format(self.captchaurl_id) 34 | urllib.request.urlretrieve(requrl,'chaptucha.jpg') 35 | self.codeImage.setPixmap(QtGui.QPixmap("chaptucha.jpg")) 36 | 37 | def getAuth(self): 38 | if self.captchaurl_id is None: 39 | QtGui.QMessageBox.information(self,"Groove","请先获取验证码") 40 | else: 41 | loginurl = "http://douban.fm/j/login" 42 | params = { 43 | "source": "radio", 44 | "alias": self.emailEdit.text(), 45 | "form_password": self.passwordEdit.text(), 46 | "captcha_solution":self.codeEdit.text(), 47 | "captcha_id":self.captchaurl_id, 48 | "task": "sync_channel_list" 49 | } 50 | user_agent = 'Mozilla/5.0 (compatible; MSIE 5.5; Windows NT)' 51 | 52 | cookiefile = "cookies.txt" 53 | cookie = http.cookiejar.LWPCookieJar(cookiefile) 54 | if os.path.isfile(cookiefile): 55 | cookie.revert(cookiefile, ignore_discard=False, ignore_expires=False) 56 | else: 57 | cookie.save(cookiefile, ignore_discard=False, ignore_expires=False) 58 | opener= urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cookie)) 59 | data = urllib.parse.urlencode(params).encode('unicode_escape') 60 | opener.addheaders = [('User-Agent',user_agent)] 61 | response = opener.open(loginurl, data) 62 | ans = json.loads(response.read().decode('utf-8')) 63 | if 'err_no' in ans: 64 | error = ans['err_msg'] 65 | if error == "验证码不正确": 66 | error += ",请刷新验证码" 67 | QtGui.QMessageBox.information(self,"Groove",error) 68 | os.remove("cookies.txt") 69 | else: 70 | self.parent().likeButton.setEnabled(True) 71 | self.parent().notlikeButton.setEnabled(True) 72 | self.parent().redheartChannel.setEnabled(True) 73 | self.parent().privateChannel.setEnabled(True) 74 | self.parent().collectChannel.setEnabled(True) 75 | self.parent().username.setText(ans['user_info']['name']) 76 | self.parent().rednum.setText(str(ans['user_info']['play_record']['liked'])) 77 | cookie.save(cookiefile, ignore_discard=True, ignore_expires=True) 78 | self.parent().install_opener("cookies.txt") 79 | self.parent().changeChannel("0") 80 | self.parent().currentChannel("私人兆赫") 81 | if not self.parent().get: 82 | collect = self.parent().getcollect() 83 | self.parent().tocollect(collect) 84 | self.close() -------------------------------------------------------------------------------- /setting.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'setting.ui' 4 | # 5 | # Created: Sun May 26 22:05:20 2013 6 | # by: PyQt4 UI code generator 4.9.1 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PyQt4 import QtCore, QtGui 11 | 12 | try: 13 | _fromUtf8 = QtCore.QString.fromUtf8 14 | except AttributeError: 15 | _fromUtf8 = lambda s: s 16 | 17 | class Ui_Dialog(object): 18 | def setupUi(self, Dialog): 19 | Dialog.setObjectName(_fromUtf8("Dialog")) 20 | Dialog.resize(400, 349) 21 | self.setting = QtGui.QTabWidget(Dialog) 22 | self.setting.setGeometry(QtCore.QRect(10, 20, 371, 291)) 23 | self.setting.setObjectName(_fromUtf8("setting")) 24 | # self.general = QtGui.QWidget() 25 | # self.general.setObjectName(_fromUtf8("general")) 26 | # self.setting.addTab(self.general, _fromUtf8("")) 27 | self.account = QtGui.QWidget() 28 | self.account.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) 29 | self.account.setObjectName(_fromUtf8("account")) 30 | self.widget = QtGui.QWidget(self.account) 31 | self.widget.setGeometry(QtCore.QRect(30, 30, 261, 211)) 32 | self.widget.setObjectName(_fromUtf8("widget")) 33 | self.verticalLayout = QtGui.QVBoxLayout(self.widget) 34 | self.verticalLayout.setMargin(0) 35 | self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) 36 | self.horizontalLayout = QtGui.QHBoxLayout() 37 | self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) 38 | spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) 39 | self.horizontalLayout.addItem(spacerItem) 40 | self.email = QtGui.QLabel(self.widget) 41 | self.email.setObjectName(_fromUtf8("email")) 42 | self.horizontalLayout.addWidget(self.email) 43 | self.emailEdit = QtGui.QLineEdit(self.widget) 44 | self.emailEdit.setObjectName(_fromUtf8("emailEdit")) 45 | self.horizontalLayout.addWidget(self.emailEdit) 46 | self.verticalLayout.addLayout(self.horizontalLayout) 47 | self.horizontalLayout_2 = QtGui.QHBoxLayout() 48 | self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2")) 49 | spacerItem1 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) 50 | self.horizontalLayout_2.addItem(spacerItem1) 51 | self.password = QtGui.QLabel(self.widget) 52 | self.password.setObjectName(_fromUtf8("password")) 53 | self.horizontalLayout_2.addWidget(self.password) 54 | self.passwordEdit = QtGui.QLineEdit(self.widget) 55 | self.passwordEdit.setEchoMode(QtGui.QLineEdit.Password) 56 | self.passwordEdit.setObjectName(_fromUtf8("passwordEdit")) 57 | self.horizontalLayout_2.addWidget(self.passwordEdit) 58 | self.verticalLayout.addLayout(self.horizontalLayout_2) 59 | self.horizontalLayout_3 = QtGui.QHBoxLayout() 60 | self.horizontalLayout_3.setObjectName(_fromUtf8("horizontalLayout_3")) 61 | spacerItem2 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) 62 | self.horizontalLayout_3.addItem(spacerItem2) 63 | self.code = QtGui.QLabel(self.widget) 64 | self.code.setObjectName(_fromUtf8("code")) 65 | self.horizontalLayout_3.addWidget(self.code) 66 | self.codeEdit = QtGui.QLineEdit(self.widget) 67 | self.codeEdit.setObjectName(_fromUtf8("codeEdit")) 68 | self.horizontalLayout_3.addWidget(self.codeEdit) 69 | self.verticalLayout.addLayout(self.horizontalLayout_3) 70 | self.horizontalLayout_4 = QtGui.QHBoxLayout() 71 | self.horizontalLayout_4.setObjectName(_fromUtf8("horizontalLayout_4")) 72 | self.codeButton = QtGui.QPushButton(self.widget) 73 | self.codeButton.setObjectName(_fromUtf8("codeButton")) 74 | self.horizontalLayout_4.addWidget(self.codeButton) 75 | self.codeImage = QtGui.QLabel(self.widget) 76 | self.codeImage.setText(_fromUtf8("")) 77 | self.codeImage.setObjectName(_fromUtf8("codeImage")) 78 | self.codeImage.setScaledContents(True) 79 | self.horizontalLayout_4.addWidget(self.codeImage) 80 | self.verticalLayout.addLayout(self.horizontalLayout_4) 81 | self.buttonBox = QtGui.QDialogButtonBox(self.widget) 82 | self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) 83 | self.buttonBox.setObjectName(_fromUtf8("buttonBox")) 84 | self.verticalLayout.addWidget(self.buttonBox) 85 | self.setting.addTab(self.account, _fromUtf8("")) 86 | # self.shortcut = QtGui.QWidget() 87 | # self.shortcut.setObjectName(_fromUtf8("shortcut")) 88 | # self.setting.addTab(self.shortcut, _fromUtf8("")) 89 | self.help = QtGui.QWidget() 90 | self.help.setObjectName(_fromUtf8("help")) 91 | self.widget = QtGui.QWidget(self.help) 92 | self.widget.setGeometry(QtCore.QRect(70, 50, 221, 61)) 93 | self.widget.setObjectName(_fromUtf8("widget")) 94 | self.verticalLayout_2 = QtGui.QVBoxLayout(self.widget) 95 | self.verticalLayout_2.setMargin(0) 96 | self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2")) 97 | self.horizontalLayout_5 = QtGui.QHBoxLayout() 98 | self.horizontalLayout_5.setObjectName(_fromUtf8("horizontalLayout_5")) 99 | self.github = QtGui.QLabel(self.widget) 100 | self.github.setObjectName(_fromUtf8("github")) 101 | self.horizontalLayout_5.addWidget(self.github) 102 | self.glink = QtGui.QLabel(self.widget) 103 | self.glink.setText(_fromUtf8("")) 104 | self.glink.setObjectName(_fromUtf8("glink")) 105 | self.horizontalLayout_5.addWidget(self.glink) 106 | self.verticalLayout_2.addLayout(self.horizontalLayout_5) 107 | self.horizontalLayout_6 = QtGui.QHBoxLayout() 108 | self.horizontalLayout_6.setObjectName(_fromUtf8("horizontalLayout_6")) 109 | self.douban = QtGui.QLabel(self.widget) 110 | self.douban.setObjectName(_fromUtf8("douban")) 111 | self.horizontalLayout_6.addWidget(self.douban) 112 | self.dlink = QtGui.QLabel(self.widget) 113 | self.dlink.setText(_fromUtf8("")) 114 | self.dlink.setObjectName(_fromUtf8("dlink")) 115 | self.horizontalLayout_6.addWidget(self.dlink) 116 | self.verticalLayout_2.addLayout(self.horizontalLayout_6) 117 | self.setting.addTab(self.help, _fromUtf8("")) 118 | 119 | self.retranslateUi(Dialog) 120 | self.setting.setCurrentIndex(0) 121 | QtCore.QMetaObject.connectSlotsByName(Dialog) 122 | 123 | def retranslateUi(self, Dialog): 124 | Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "首选项", None, QtGui.QApplication.UnicodeUTF8)) 125 | # self.setting.setTabText(self.setting.indexOf(self.general), QtGui.QApplication.translate("Dialog", "常规", None, QtGui.QApplication.UnicodeUTF8)) 126 | self.email.setText(QtGui.QApplication.translate("Dialog", "邮箱:", None, QtGui.QApplication.UnicodeUTF8)) 127 | self.password.setText(QtGui.QApplication.translate("Dialog", "密码:", None, QtGui.QApplication.UnicodeUTF8)) 128 | self.code.setText(QtGui.QApplication.translate("Dialog", "验证码:", None, QtGui.QApplication.UnicodeUTF8)) 129 | self.codeButton.setText(QtGui.QApplication.translate("Dialog", "获取验证码", None, QtGui.QApplication.UnicodeUTF8)) 130 | self.setting.setTabText(self.setting.indexOf(self.account), QtGui.QApplication.translate("Dialog", "帐号", None, QtGui.QApplication.UnicodeUTF8)) 131 | self.github.setText(QtGui.QApplication.translate("Dialog", "项目主页:", None, QtGui.QApplication.UnicodeUTF8)) 132 | self.douban.setText(QtGui.QApplication.translate("Dialog", "豆瓣主页:", None, QtGui.QApplication.UnicodeUTF8)) 133 | # self.setting.setTabText(self.setting.indexOf(self.shortcut), QtGui.QApplication.translate("Dialog", "快捷键", None, QtGui.QApplication.UnicodeUTF8)) 134 | self.setting.setTabText(self.setting.indexOf(self.help), QtGui.QApplication.translate("Dialog", "帮助", None, QtGui.QApplication.UnicodeUTF8)) 135 | 136 | -------------------------------------------------------------------------------- /songinfo.py: -------------------------------------------------------------------------------- 1 | import urllib.request #python3 2 | import json 3 | 4 | def getSongInfo(url): 5 | req = urllib.request.Request(url) 6 | response = urllib.request.urlopen(req) #注意:返回字节串而不是字符串 7 | responseJson = json.loads(response.read().decode("utf-8")) #应用byte的decode方法解码 8 | songList = responseJson["song"] 9 | for s in songList: 10 | yield s 11 | --------------------------------------------------------------------------------