Ejemplo n.º 1
0
 def __init__(self, user_email, user_password, system_proxies, system_notification, show_lyric, channels_file):
     self.current_channel = None
     self.channel_list = None
     self.channels_file = channels_file
     self.dbfm = DoubanFM(system_proxies)
     self.has_lyric = False
     self.download_manager = DownloadManager(system_proxies)
     self.current_song_info = None
     self.setup_main_win()
     self.setup_left_win()
     self.setup_right_win()
     self.setup_console_win()
     self.user_email = user_email
     self.user_password = user_password
     self.enable_notification = system_notification
     self.show_lyric = show_lyric
     self.console_log = deque()
Ejemplo n.º 2
0
import sys
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQuick import QQuickView
from PyQt5.QtQml import QQmlApplicationEngine
from DoubanFM import DoubanFM
import Logger

# Main Function
if __name__ == '__main__':
    # Create main app
    main_app = QGuiApplication(sys.argv)
    # Create a label and set its properties
    douban = DoubanFM()
    logger = Logger.Logger()

    engine = QQmlApplicationEngine(parent=main_app)
    root_context = engine.rootContext()
    root_context.setContextProperty("douban", douban)
    root_context.setContextProperty("logger", logger)
    mainurl = "qml/main.qml"
    engine.load(QUrl(mainurl))

    # Execute the Application and Exit
    rc = main_app.exec_()
    sys.exit(rc)
Ejemplo n.º 3
0
class CursesUI(object):
    def __init__(self, user_email, user_password, system_proxies, system_notification, show_lyric, channels_file):
        self.current_channel = None
        self.channel_list = None
        self.channels_file = channels_file
        self.dbfm = DoubanFM(system_proxies)
        self.has_lyric = False
        self.download_manager = DownloadManager(system_proxies)
        self.current_song_info = None
        self.setup_main_win()
        self.setup_left_win()
        self.setup_right_win()
        self.setup_console_win()
        self.user_email = user_email
        self.user_password = user_password
        self.enable_notification = system_notification
        self.show_lyric = show_lyric
        self.console_log = deque()
    def setup_main_win(self):
        #locale
        locale.setlocale(locale.LC_CTYPE, '')
        global code
        code = locale.getpreferredencoding()
        ## initialize
        self.stdscr = stdscr = curses.initscr()
        ## use color
        if curses.has_colors():
            curses.start_color()
        ## set options
        curses.noecho()
        curses.cbreak()
        stdscr.keypad(1)
        ## set colors
        curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_CYAN)
        curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLUE)
        curses.init_pair(13, curses.COLOR_CYAN, curses.COLOR_BLACK)

    def end_curses_app(self):
        curses.nocbreak()
        self.left_win.keypad(0)
        self.right_win.keypad(0)
        self.stdscr.keypad(0)
        curses.echo()
        curses.endwin()

    def left_win_restore(self):
        self.left_win.border(0)
        l_title = "channel"
        self.left_win.addstr(0, 0, l_title.encode(code), curses.A_BOLD)
    def right_win_restore(self):
        self.right_win.border(0)
        r_title = "Playing"
        self.right_win.addstr(0, 0, r_title.encode(code), curses.A_BOLD)
    def console_win_restore(self):
        self.console_win.border(0)
        self.console_win.addstr(0, 0, 'Console'.encode(code), curses.A_BOLD)
        
    def setup_console_win(self):
        console_x, console_y = 30, 16
        console_height, console_width = 8, 49
        self.console_win = console_win = curses.newwin(console_height, console_width, console_y, console_x)
        self.console_win_restore()
        console_win.refresh()
        
    def setup_left_win(self):
        ## left window
        l_begin_x, l_begin_y = 0, 0
        l_height, l_width = 24, 29
        self.left_win = left_win = curses.newwin(l_height, l_width, l_begin_y, l_begin_x)
        left_win.keypad(1)
        self.left_win_restore()
        # Add Hearted Radio, which is not in the API's returning list
        self.channel_list = [{'abbr_en': 'Hearted',
                              'channel_id': -3,
                              'name': u'红心兆赫',
                              'name_en': 'Hearted Radio',
                              'seq_id': -1}] + self.dbfm.get_channel_list()
        if DEBUG: logging.info(self.channel_list)
        # Below is dirty and quick hack...
        if os.path.exists(self.channels_file):
            with open(self.channels_file) as fin:
                extra_channel = eval(fin.readline())
                extra_channel['channel_id'] = extra_channel['id']
                self.channel_list[MAX_CHANNEL - 1] = extra_channel;

        for idx, channel in enumerate(self.channel_list):
            if idx >= MAX_CHANNEL: # TODO: List all channels
                break
            left_win.addstr((idx + 1) * 2, 2, channel['name'].strip())
        left_win.addstr(19, 2, '-' * (l_width - 4))
        left_win.addstr(20, 2, "上移: k或↑, 下移: j或↓")
        left_win.addstr(21, 2, "登录: l, 选择: c")
        left_win.addstr(22, 2, "退出: q")
        
        left_win.refresh()
    
    def setup_right_win(self):
        ## right window
        r_begin_x = 30; r_begin_y = 0
        r_height = 16; r_width = 49
        self.right_win = right_win = curses.newwin(r_height, r_width, r_begin_y, r_begin_x)
        self.right_win_restore()

        right_win.addstr(13, 10, "取消喜欢(u)", curses.color_pair(1))
        right_win.addstr(13, 24, "加红心(r)", curses.color_pair(1))
        right_win.addstr(13, 36, "下一首(n)", curses.color_pair(1))
        right_win.addstr(10, 2, '[')
        right_win.addstr(10, 44, ']')
        
        right_win.refresh()
        
    def set_like_status(self):
        if True:
            right_win.addstr(14, 30, "加红心(f)")
        else:
            right_win.addstr(14, 29, "删除红心(d)")
    def set_progress(self, position_int):
        length = position_int * 40 / 1000000000 / self.current_song_info['length']
        if length > 40:
            length = 40         # Force it!
        self.right_win.addstr(10, 3, '-' * length + '>' + ' ' * (40 - length))
        position_text = self.convert_seconds(position_int, 1000000000)
        self.right_win.addstr(11, 34, position_text)
        if self.has_lyric:
            lyric_line = self.get_lyric_line(position_text)
            self.right_win.addstr(7, 2, '%', curses.color_pair(13))
            self.right_win.addstr(7, 4, lyric_line +
                                  ' ' * (LYRIC_LENGTH - display_len2(lyric_line.decode('utf-8'))))
        else:
            self.right_win.addstr(7, 2, ' ' * (LYRIC_LENGTH + 2))
        self.right_win.refresh()
         # Really really really ugly... But it seems the problem was caused by
         # the library... Well, I'm not sure though.
        return abs(position_int / 100000000 - self.current_song_info['length'] * 10) <= 5
        
    def add_console_output(self, message):
        self.console_log.append(message)
        while len(self.console_log) > 5:
            self.console_log.popleft()
        for (idx, msg) in enumerate(reversed(self.console_log)):
            self.console_win.addstr(2 + idx, 2, (str(msg)).encode(code))
            self.console_win.clrtoeol()
        self.console_win_restore()
        self.console_win.refresh()
        
    def show_song_info(self, song_info):
        self.right_win.addstr(2, 2, song_info['artist'].encode(code))
        self.right_win.clrtoeol()
        self.right_win.addstr(3, 2, ('%s %s' %(song_info['albumtitle'], song_info['public_time'])).encode(code))
        self.right_win.clrtoeol()
        self.right_win.move(4, 1)
        self.right_win.clrtoeol()
        self.right_win.addstr(5, 2, song_info['title'].encode(code), curses.A_BOLD)
        self.right_win.clrtoeol()
        self.right_win.move(6, 1)
        self.right_win.clrtoeol()
        self.right_win.addstr(13, 2, ('♡' if song_info['like'] == 0 else '♥').encode(code), curses.A_BOLD)
        
        duration_text = self.convert_seconds(self.current_song_info['length'], 1)
        self.right_win.addstr(11, 34, "00:00/%s" %duration_text)
        self.right_win_restore()
        self.right_win.refresh()
        
    def convert_seconds(self, t, times):
        # This method was submitted by Sam Mason.
        # It's much shorter than the original one.
        s, _ = divmod(t, times)
        m, s = divmod(s, 60)
        if m < 60:
            return "%02i:%02i" %(m,s)
        else:
            h, m = divmod(m, 60)
            return "%i:%02i:%02i" %(h,m,s)


    def get_channel_to_play(self, cursor_y):
        selected_idx = cursor_y / 2 - 1
        selected_channel = self.channel_list[selected_idx]
        if selected_idx == 0 and not self.dbfm.is_logined():
            self.add_console_output('Please log in first! Press "l".')
            return None
        for idx, channel in enumerate(self.channel_list):
            if idx == selected_idx:
                self.left_win.addstr((selected_idx + 1) * 2, 2,
                                     selected_channel['name'],
                                     curses.color_pair(1))
            elif idx < MAX_CHANNEL:
                self.left_win.addstr((idx + 1) * 2, 2, channel['name'])
        self.left_win.refresh()
        return selected_channel

    def play_next_song(self, p):
        self.has_lyric = False
        self.current_song_info = self.dbfm.get_next_song_info()
        self.show_song_info(self.current_song_info)
        # show lyric?
        if not self.show_lyric:
            self.has_lyric = False
        else:
            ret = self.download_manager.download_lyric(self.current_song_info)
            if ret == None:
                self.has_lyric = False
                self.add_console_output('No lyric found!')
            else:
                self.has_lyric = True
                self.lyrics = ret
                self.add_console_output('Lyric downloaded.')
        # send notifications?
        if self.enable_notification:
            self.send_notification(self.current_song_info)
        p.play_song(self.current_song_info)
        
    def stop_and_remove(self, player, song_info):
        player.stop_song()
        local_filename = make_local_filename(self.current_song_info)
        if player.is_http_player() and os.path.exists(local_filename):
            os.remove(local_filename)
        
    def run(self):
        try:
            self.left_win.move(2, 2)
            p = MusicPlayer()
            player_started = False
            while True:
                self.right_win.refresh()
                self.left_win.refresh()
                if player_started:
                    try:
                        position_int = p.position
                        if self.set_progress(position_int):
                            if DEBUG: logging.info("set_progress true")
                            p.stop_song()
                            self.play_next_song(p)
                    except:
                        continue
                self.left_win.refresh()
                rlist, _, _ = select([sys.stdin], [], [], 1)
                if not rlist:
                    continue
                ch = self.left_win.getch()
                if DEBUG: logging.info("getch: %c" %ch)
                cursor_y, cursor_x = self.left_win.getyx()
                if ch == ord('c'):
                    if cursor_x != 2:
                        self.left_win.move(2, 2)
                        continue
                    if player_started:
                        self.stop_and_remove(p, self.current_song_info['url'])
                    player_started = False # Can't leave out this
                    current_channel = self.get_channel_to_play(cursor_y)
                    if current_channel == None:
                        continue
                    self.current_channel = current_channel
                    player_started = True
                    self.dbfm.change_channel(self.current_channel)
                    self.play_next_song(p)
                    self.left_win.move(cursor_y, cursor_x)
                elif ch == ord('n'):
                    if not player_started:
                        continue
                    self.stop_and_remove(p, self.current_song_info['url'])
                    self.play_next_song(p)
                elif ch == ord('r'):
                    if not self.dbfm.is_logined():
                        self.add_console_output('Please log in first! Press "l".')
                        continue
                    if self.current_song_info['like'] == 1:
                        self.add_console_output('Already hearted!')
                        continue
                    if self.dbfm.rate_song(self.current_channel['channel_id'], True)['r'] == 0:
                        self.add_console_output("Hearted %s successfully!" %self.current_song_info['title'])
                        self.current_song_info['like'] = 1
                        self.show_song_info(self.current_song_info)
                    else:
                        self.add_console_output("Hearted %s failure!" %self.current_song_info['title'])
                elif ch == ord('u'):
                    if not self.dbfm.is_logined():
                        self.add_console_output('Please log in first! Press "l".')
                        continue
                    if self.current_song_info['like'] == 0:
                        self.add_console_output("Haven't hearted yet!")
                        continue
                    if self.dbfm.rate_song(self.current_channel['channel_id'], False)['r'] == 0:
                        self.add_console_output("Unhearted %s successfully!" %self.current_song_info['title'])
                        self.current_song_info['like'] = 0
                        self.show_song_info(self.current_song_info)
                    else:
                        self.add_console_output("Unhearted %s failure!" %self.current_song_info['title'])

                elif ch == ord('j') or ch == curses.KEY_DOWN:
                    if cursor_y <= (MAX_CHANNEL + 2) * 2:
                        self.left_win.move(cursor_y + 2, 2)
                    else:
                        continue
                elif ch == ord('k') or ch == curses.KEY_UP:
                    if cursor_y > 2:
                        self.left_win.move(cursor_y - 2, 2)
                    else:
                        continue
                elif ch == ord('p'):
                    p.toggle_paused_song()
                elif ch == ord('l'):
                    if self.dbfm.login(self.user_email, self.user_password):
                        self.add_console_output("Successfully log in!")
                    else:
                        self.add_console_output('Failed to log in!')
                else:
                    self.stop_and_remove(p, self.current_song_info['url'])
                    break
        except Exception, e:
            if player_started and self.current_song_info != None:
                self.stop_and_remove(p, self.current_song_info['url'])
            if DEBUG: logging.info(e)
        finally: