Beispiel #1
0
class Doubanfm:
    def __init__(self):
        self._load_config()
        self.douban = Douban(self.email, self.password, self.user_id, self.expire, self.token, self.user_name)
        self.player = Player()
        self.current_channel = 0
        self.current_song = None
        self.current_play_list = None
        self.get_channels()
        
        self.palette = [('selected', 'bold', 'default'),
                        ('title', 'yellow', 'default')]
        self.selected_button = None
        self.main_loop = None
        self.song_change_alarm = None
        
        if self.scrobbling:
            self.scrobbler = Scrobbler(self.last_fm_username, self.last_fm_password)
            r =  self.scrobbler.handshake()
            if r:
                print("Last.FM logged in.")
            else:
                print("Last.FM login failed")
        if self.douban_account:
            r, err = self.douban.do_login()
            if r:
                print("Douban logged in.")
                self._save_cache()
            else:
                print("Douban login failed: " + err)
        
    def _load_config(self):
        self.email = None
        self.password = None
        self.user_id = None
        self.expire = None
        self.token = None
        self.user_name = None
        self.lasf_fm_username = None
        self.last_fm_password = None
        self.scrobbling = True
        self.douban_account = True
        self.channels = None
        
        config = None
        token = None
        try:
            f = open('config.json', 'r')
            config = json.load(f)
            
            self.email = config['email']
            self.password = config['password']
            
        except (KeyError,ValueError):
            self.douban_account = False
            print("Douban account not found. Personal FM disabled.")
        
        try:
            if config == None:
                raise ValueError 
            self.last_fm_username = config['last_fm_username']
            self.last_fm_password = config['last_fm_password']
        except (KeyError,ValueError):
            self.scrobbling = False
            print("Last.fm account not found. Scrobbling disabled.")
            
        try:
            f = open('channels.json', 'r')
            self.channels = json.load(f)
            print("Load channel file.")
        except FileNotFoundError:
            print("Channels file not found.")
         
    def _save_cache(self):
        f = None
        try:
            f = open('cache.json', 'w')
            f2 = open('channels.json', 'w')
            json.dump({
                'user_name': self.douban.user_name,
                'user_id': self.douban.user_id,
                'expire': self.douban.expire,
                'token': self.douban.token
            }, f)
            json.dump(self.channels, f2)
        except IOError:
            raise Exception("Unable to write cache file")
            
    def get_channels(self):
        if self.channels is None:
            self.channels = self.douban.get_channels()
        return self.channels
    
    def _choose_channel(self, channel):
        self.current_channel = channel
        self.current_play_list = deque(self.douban.get_new_play_list(self.current_channel))
        
    def _play_track(self):
        _song = self.current_play_list.popleft()                
        self.current_song = Song(_song)
        self.song_change_alarm = self.main_loop.set_alarm_in(self.current_song.length_in_sec,
                                   self.next_song, None);
        self.selected_button.set_text(self.selected_button.text[0:7].strip())
        heart = u'\N{WHITE HEART SUIT}';
        if self.current_song.like:
            heart = u'\N{BLACK HEART SUIT}'
        self.selected_button.set_text(self.selected_button.text + '                 ' + heart + '  ' +
                                        self.current_song.artist + ' - ' + 
                                        self.current_song.title)
        if self.scrobbling:
            self.scrobbler.now_playing(self.current_song.artist, self.current_song.title,
                                       self.current_song.album_title, self.current_song.length_in_sec)
        
        self.player.stop()
        self.player.play(self.current_song)
        # Currently playing the second last song in queue
        if len(self.current_play_list) == 1:
            playing_list = self.douban.get_playing_list(self.current_song.sid, self.current_channel)
            self.current_play_list.extend(deque(playing_list))
    
    def next_song(self, loop, user_data):
        # Scrobble the track if scrobbling is enabled 
        # and total playback time of the track > 30s
        if self.scrobbling and self.current_song.length_in_sec > 30:
            self.scrobbler.submit(self.current_song.artist, self.current_song.title,
                                  self.current_song.album_title, self.current_song.length_in_sec)
        
        self.douban.end_song(self.current_song.sid, self.current_channel)
        if self.song_change_alarm:
            self.main_loop.remove_alarm(self.song_change_alarm)
        self._play_track()
    
    def skip_current_song(self):
        self.douban.skip_song(self.current_song.sid, self.current_channel)
        if self.song_change_alarm:
            self.main_loop.remove_alarm(self.song_change_alarm)
        self._play_track()
          
    def rate_current_song(self):
        self.douban.rate_song(self.current_song.sid, self.current_channel)
        self.selected_button.set_text(self.selected_button.text.replace(u'\N{WHITE HEART SUIT}', u'\N{BLACK HEART SUIT}'))
        
        
    def unrate_current_song(self):
        self.douban.unrate_song(self.current_song.sid, self.current_channel)
        self.selected_button.set_text(self.selected_button.text.replace(u'\N{BLACK HEART SUIT}', u'\N{WHITE HEART SUIT}'))
        
    def quit(self):
        self.player.stop()
        
    def start(self):
        title = urwid.AttrMap(urwid.Text('豆瓣FM'), 'title')
        divider = urwid.Divider()
        pile = urwid.Padding(urwid.Pile([divider, title, divider]), left=4, right=4)
        box = urwid.Padding(self.ChannelListBox(), left=2, right=4)
        
        frame = urwid.Frame(box, header=pile, footer=divider)
        
        self.main_loop = urwid.MainLoop(frame, self.palette, handle_mouse=False)
        self.main_loop.run()

    def ChannelListBox(self):
        body = []
        for c in self.channels:
                _channel = ChannelButton(c['name'])
                urwid.connect_signal(_channel, 'click', self.channel_chosen, c['channel_id'])
                body.append(urwid.AttrMap(_channel, None, focus_map="channel"))
        return MyListBox(urwid.SimpleFocusListWalker(body), self)

    def channel_chosen(self, button, choice):
        # Choose the channel which is playing right now
        # ignore this
        if self.selected_button == button:
            return
        # Choose a different channel
        if self.player.is_playing:
            self.player.stop()
        self._choose_channel(choice)
        if self.selected_button != None and button != self.selected_button:
            self.selected_button.set_text(self.selected_button.text[0:7].strip())
        self.selected_button = button
        self._play_track()
Beispiel #2
0
class Doubanfm:

    def __init__(self):
        self.email = None
        self.password = None
        self.user_name = None
        self.user_id = None
        self.expire = None
        self.token = None
        self.cookies = None
        
        self.last_fm_username = None
        self.last_fm_password = None
        self.scrobbling = True
        self.douban_account = True
        self.channels = None
        
        # Set up config
        try:
            arg = sys.argv[1]
            self._do_config()
        except IndexError:
            self._load_config()
        
        # Init API tools
        self.douban = Douban(
            self.email, self.password, self.user_id, self.expire, self.token, self.user_name, self.cookies)
        self.player = Player()
        self.current_channel = 0
        self.current_song = None
        self.current_play_list = None

        # Init terminal ui
        self.palette = [('selected', 'bold', 'default'),
                        ('title', 'yellow', 'default')]
        self.selected_button = None
        self.main_loop = None
        self.song_change_alarm = None
        
        # Try to login
        if self.last_fm_username is None or self.last_fm_username == "":
            self.scrobbling = False
        if (self.email is None or self.email == "") and self.cookies == None:
            self.douban_account = False
            
        if self.scrobbling:
            self.scrobbler = Scrobbler(
                self.last_fm_username, self.last_fm_password)
            r, err = self.scrobbler.handshake()
            if r:
                print("Last.FM 已登陆")
            else:
                print("Last.FM 登录失败: " + err)
        if self.douban_account:
            r, err = self.douban.do_login()
            if r:
                print("Douban 已登陆")
            else:
                print("Douban 登录失败: " + err)
        self.get_channels()
        self._save_cache()

    def _do_config(self):
        self.email = input('豆瓣账户 (Email地址): ') or None
        self.password = getpass('豆瓣密码: ') or None
        self.last_fm_username = input('Last.fm 用户名: ') or None
        password = getpass('Last.fm 密码: ') or None
        self.last_fm_password = md5(password.encode('utf-8')).hexdigest() 
                
    def _load_config(self):
        try:
            f = open('channels.json', 'r')
            self.channels = deque(json.load(f))
            logger.debug("Load channel file.")
        except FileNotFoundError:
            logger.debug("Channels file not found.")
            
        try: 
            f = open('cache.json', 'r')
            cache = json.load(f)
            try:
                self.user_name = cache['user_name']
                self.user_id = cache['user_id']
                self.expire = cache['expire']
                self.token = cache['token']
                self.cookies = cache['cookies']
            except (KeyError, ValueError):
                self.douban_account = False
            try:
                self.last_fm_username = cache['last_fm_username']
                self.last_fm_password = cache['last_fm_password']
            except (KeyError, ValueError):
                self.scrobbling = False
    
        except FileNotFoundError:
            logger.debug("Cache file not found.")

    def _save_cache(self):
        f = None
        try:
            f = open('cache.json', 'w')
            f2 = open('channels.json', 'w')
            json.dump({
                'user_name': self.douban.user_name,
                'user_id': self.douban.user_id,
                'expire': self.douban.expire,
                'token': self.douban.token,
                'cookies': self.douban.cookies,
                'last_fm_username': self.last_fm_username,
                'last_fm_password': self.last_fm_password
            }, f)
            json.dump(list(self.channels), f2)
        except IOError:
            raise Exception("Unable to write cache file")

    def get_channels(self):
        if self.channels is None:
            self.channels = deque(self.douban.get_channels())

    def _choose_channel(self, channel):
        self.current_channel = channel
        self.current_play_list = deque(
            self.douban.get_new_play_list(self.current_channel))

    def _play_track(self):
        _song = self.current_play_list.popleft()
        self.current_song = Song(_song)
        logger.debug('Playing Track')
        logger.debug('Artist: ' + self.current_song.artist)
        logger.debug('Title: ' + self.current_song.song_title)
        logger.debug('Album: ' + self.current_song.album_title)
        logger.debug('Length: ' + self.current_song.length_in_str)
        logger.debug('Sid: ' + self.current_song.sid)

        logger.debug(
            '{0} tracks remaining in the playlist'.format(len(self.current_play_list)))

        self.song_change_alarm = self.main_loop.set_alarm_in(self.current_song.length_in_sec,
                                                             self.next_song, None)
        self.selected_button.set_text(self.selected_button.text[0:11].strip())
        heart = u'\N{WHITE HEART SUIT}'
        if self.current_song.like:
            heart = u'\N{BLACK HEART SUIT}'
        if not self.douban_account:
            heart = ' '
        self.selected_button.set_text(self.selected_button.text + '                 ' + heart + '  ' +
                                      self.current_song.artist + ' - ' +
                                      self.current_song.song_title)
        if self.scrobbling:
            self.scrobbler.now_playing(self.current_song.artist, self.current_song.song_title,
                                       self.current_song.album_title, self.current_song.length_in_sec)

        self.player.stop()
        self.player.play(self.current_song)
        # Currently playing the second last song in queue
        if len(self.current_play_list) == 1:
            # Extend the playing list
            playing_list = self.douban.get_playing_list(
                self.current_song.sid, self.current_channel)
            logger.debug('Got {0} more tracks'.format(len(playing_list)))
            self.current_play_list.extend(deque(playing_list))

    def next_song(self, loop, user_data):
        # Scrobble the track if scrobbling is enabled
        # and total playback time of the track > 30s
        if self.scrobbling and self.current_song.length_in_sec > 30:
            self.scrobbler.submit(self.current_song.artist, self.current_song.song_title,
                                  self.current_song.album_title, self.current_song.length_in_sec)

        if self.douban_account:
            r, err = self.douban.end_song(
                self.current_song.sid, self.current_channel)
            if r:
                logger.debug('End song OK')
            else:
                logger.error(err)
        if self.song_change_alarm:
            self.main_loop.remove_alarm(self.song_change_alarm)
        self._play_track()

    def skip_current_song(self):
        if self.douban_account:
            r, err = self.douban.skip_song(
                self.current_song.sid, self.current_channel)
            if r:
                logger.debug('Skip song OK')
            else:
                logger.error(err)
        if self.song_change_alarm:
            self.main_loop.remove_alarm(self.song_change_alarm)
        self._play_track()

    def rate_current_song(self):
        if not self.douban_account:
            return
        r, err = self.douban.rate_song(
            self.current_song.sid, self.current_channel)
        if r:
            self.current_song.like = True
            self.selected_button.set_text(self.selected_button.text.replace(
                u'\N{WHITE HEART SUIT}', u'\N{BLACK HEART SUIT}'))
            logger.debug('Rate song OK')
        else:
            logger.error(err)

    def unrate_current_song(self):
        if not self.douban_account:
            return
        r, err = self.douban.unrate_song(
            self.current_song.sid, self.current_channel)
        if r:
            self.current_song.like = False
            self.selected_button.set_text(self.selected_button.text.replace(
                u'\N{BLACK HEART SUIT}', u'\N{WHITE HEART SUIT}'))
            logger.debug('Unrate song OK')
        else:
            logger.error(err)

    def trash_current_song(self):
        if not self.douban_account:
            return
        r, err = self.douban.bye_song(
            self.current_song.sid, self.current_channel)
        if r:
            # play next song
            if self.song_change_alarm:
                self.main_loop.remove_alarm(self.song_change_alarm)
            self._play_track()
            logger.debug('Trash song OK')
        else:
            logger.error(err)

    def quit(self):
        logger.debug('Quit')
        self.player.stop()

    def start(self):
        title = urwid.AttrMap(urwid.Text('豆瓣FM'), 'title')
        divider = urwid.Divider()
        pile = urwid.Padding(
            urwid.Pile([divider, title, divider]), left=4, right=4)
        box = urwid.Padding(self.ChannelListBox(), left=2, right=4)

        frame = urwid.Frame(box, header=pile, footer=divider)

        self.main_loop = urwid.MainLoop(
            frame, self.palette, handle_mouse=False)
        self.main_loop.run()

    def ChannelListBox(self):
        body = []
        for c in self.channels:
            _channel = ChannelButton(c['name'])
            urwid.connect_signal(
                _channel, 'click', self.channel_chosen, c['channel_id'])
            body.append(urwid.AttrMap(_channel, None, focus_map="channel"))
        return MyListBox(urwid.SimpleFocusListWalker(body), self)

    def channel_chosen(self, button, choice):
        # Choose the channel which is playing right now
        # ignore this
        if self.selected_button == button:
            return
        # Choose a different channel
        if self.player.is_playing:
            self.player.stop()
        self._choose_channel(choice)
        if self.selected_button != None and button != self.selected_button:
            self.selected_button.set_text(
                self.selected_button.text[0:11].strip())
        self.selected_button = button
        if self.song_change_alarm:
            self.main_loop.remove_alarm(self.song_change_alarm)
        self._play_track()