class AsyncMusicPlayer(threading.Thread): '''Music Player. MusicPlayer가 동기적이기 때문에 이것을 비동기적으로 명령을 처리 하기 위해서 Thread로 만듬 외부와의 데이터 교환: - playlist : 플레이 리스트 - command_list : 명령 수행 ''' def __init__(self): threading.Thread.__init__(self) # 현재 플레이 중인 Url self.url = '' # 현재 플레이 되고 있는 노래 self.current_song = None # webbrowser player 인스턴스 self.player = MusicPlayer() # DB self.store = Store() def run(self): '이 쓰레드에서 수행할 로직' while True: try: # 0.5초를 주기로 명령 수행 time.sleep(0.5) # 광고가 나오는지 확인해서 넘긴다. # 보통 youtube 광고는 5초다. # TODO: 광고가 나오면 volume을 줄이도록 한다. self.player.skip_if_exists_ad() if self.url == '': self.play_next_song() # URL이 변경되었으면 표시한다. url = self.player.current_url() if( self.url != url ): self.url = url # TODO: # - 여기에서 곡을 추가하는 로직을 넣으면 # - 내가 듣는 링크를 플레이 리스트로 만들 수 있다. print(self.url) # 노래가 중지되면 다음 노래 if self.player.is_finished() : # played_count 수를 하나 증가 시킨다. if self.current_song: self.current_song.played_count += 1 self.store.update(self.current_song) self.play_next_song() elif self.player.is_unplable(): self.play_next_song() self.handle_cmd(); except queue.Empty: pass except KeyboardInterrupt: break except Exception as e: # 에러가 발생하면 에러의 종료를 출력 #print('----') #print(type(e)) #print(e) break def play_next_song(self): " 다음 곡 " # 플레이 리스트에서 하나 꺼낸다. playlist_lock.acquire() try: song = playlist.pop(0) url = song.url except: url = None playlist_lock.release() # url이 있으면 다음곡 플레이 if url: self.current_song = song self.player.play_url(song.url) def stop(self): " 일시 정지 " self.player.stop() def play(self): " 다시 플레이" self.player.play() def handle_cmd(self): "명령 처리" data = command_list.get(block=False) if data: cmd, param = data if cmd == 'forward': self.play_next_song() elif cmd == 'stop': self.stop() elif cmd == 'play': self.play()
class GTK_Main: def __init__(self): window = gtk.Window(gtk.WINDOW_TOPLEVEL) window.set_title("Yamp!") window.set_default_size(450, 300) window.connect("destroy", gtk.main_quit, "WM destroy") window.set_position(gtk.WIN_POS_CENTER) self.player = MusicPlayer() self.player.add_observer(self) self.mb = gtk.MenuBar() self.filemenu = gtk.Menu() self.filem = gtk.MenuItem("File") self.filem.set_submenu(self.filemenu) self.menuOpen = gtk.MenuItem("Open") self.menuOpen.connect("activate", self.open_file_dialog) self.menuExit = gtk.MenuItem("Exit") self.menuExit.connect("activate", gtk.main_quit) self.filemenu.append(self.menuOpen) self.filemenu.append(self.menuExit) self.mb.append(self.filem) vbox = gtk.VBox(False, 2) window.add(vbox) vbox.pack_start(self.mb, False, False, 0) self.imgStop = gtk.Image() self.imgPlay = gtk.Image() self.imgNext = gtk.Image() self.imgPrev = gtk.Image() self.imgPause = gtk.Image() self.imgStop.set_from_stock(gtk.STOCK_MEDIA_STOP, gtk.ICON_SIZE_BUTTON) self.imgPause.set_from_stock(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_BUTTON) self.imgPlay.set_from_stock(gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_BUTTON) self.imgPrev.set_from_stock(gtk.STOCK_MEDIA_PREVIOUS, gtk.ICON_SIZE_BUTTON) self.imgNext.set_from_stock(gtk.STOCK_MEDIA_NEXT, gtk.ICON_SIZE_BUTTON) self.lblArtist = gtk.Label() self.lblAlbum = gtk.Label() self.lblSongTitle = gtk.Label() self.lblArtist.set_use_markup(True) self.lblAlbum.set_use_markup(True) self.lblSongTitle.set_use_markup(True) self.buttonPlay = gtk.Button() self.buttonNext = gtk.Button() self.buttonPrev = gtk.Button() self.buttonStop = gtk.Button() self.buttonStop.set_relief(gtk.RELIEF_NONE) self.buttonPlay.set_relief(gtk.RELIEF_NONE) self.buttonPrev.set_relief(gtk.RELIEF_NONE) self.buttonNext.set_relief(gtk.RELIEF_NONE) self.buttonStop.add(self.imgStop) self.buttonPlay.add(self.imgPlay) self.buttonPrev.add(self.imgPrev) self.buttonNext.add(self.imgNext) self.buttonPlay.connect("clicked", self.play_action) self.buttonNext.connect("clicked", self.next_action) self.buttonPrev.connect("clicked", self.prev_action) self.buttonStop.connect("clicked", self.stop_action) self.imgCover = gtk.Image() pixbuf = gtk.gdk.pixbuf_new_from_file("./music_cd.png") scaled_buf = pixbuf.scale_simple(84, 84, gtk.gdk.INTERP_BILINEAR) self.imgCover.set_from_pixbuf(scaled_buf) self.sw = gtk.ScrolledWindow() self.sw.set_shadow_type(gtk.SHADOW_ETCHED_IN) self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) store = self.create_model() self.treeView = gtk.TreeView(store) self.treeView.connect("row-activated", self.on_activated) self.treeView.set_rules_hint(True) self.sw.add(self.treeView) self.create_columns(self.treeView) hboxButtons = gtk.HBox(False, 5) hboxButtons.pack_start(self.buttonPrev, False, False, 0) hboxButtons.pack_start(self.buttonPlay, False, False, 0) hboxButtons.pack_start(self.buttonStop, False, False, 0) hboxButtons.pack_start(self.buttonNext, False, False, 0) vboxInfos = gtk.VBox(False, 0) self.lblSongTitle.set_alignment(0, 0.5) self.lblArtist.set_alignment(0, 0.5) self.lblAlbum.set_alignment(0, 0.5) vboxInfos.pack_start(hboxButtons, True, True, 0) vboxInfos.pack_start(self.lblSongTitle, True, True, 0) vboxInfos.pack_start(self.lblArtist, True, True, 0) vboxInfos.pack_start(self.lblAlbum, True, True, 0) hbox = gtk.HBox(False, 10) hbox.pack_start(self.imgCover, False, False, 0) hbox.pack_start(vboxInfos, False, False, 0) vbox.pack_start(hbox, False, False, 5) vbox.pack_start(self.sw, True, True, 0) window.show_all() def update(self, message=None, song=None): if message == "reset": self.buttonPlay.set_image(self.imgPlay) elif message == "pause": self.buttonPlay.set_image(self.imgPlay) elif message == "play": if song is not None: self.lblArtist.set_markup("<b>" + song.get_artist() + "</b>") self.lblAlbum.set_markup("<b>" + song.get_album() + "</b>") self.lblSongTitle.set_markup("<b>#{} - {}</b>".format(song.get_track(), song.get_title())) self.buttonPlay.set_image(self.imgPause) elif message == "update_playlist": self.update_model() else: print("Error : unknow message {}".format(message)) def update_model(self): for song in self.player.get_playlist(): self.treeView.get_model().append( [song.get_track(), song.get_title(), song.get_artist(), song.get_album(), song.get_path()] ) def create_model(self): store = gtk.ListStore(str, str, str, str, str) return store def on_activated(self, widget, row, col): model = widget.get_model() song = Song(model[row][1], model[row][2], model[row][3], model[row][0], model[row][4]) if self.player.is_playing(): self.player.stop() self.player.play(song) def create_columns(self, treeView): rendererText = gtk.CellRendererText() column = gtk.TreeViewColumn("Track #", rendererText, text=0) column.set_sort_column_id(0) treeView.append_column(column) rendererText = gtk.CellRendererText() column = gtk.TreeViewColumn("Title", rendererText, text=1) column.set_sort_column_id(1) treeView.append_column(column) rendererText = gtk.CellRendererText() column = gtk.TreeViewColumn("Artist", rendererText, text=2) column.set_sort_column_id(2) treeView.append_column(column) rendererText = gtk.CellRendererText() column = gtk.TreeViewColumn("Album", rendererText, text=3) column.set_sort_column_id(3) treeView.append_column(column) def play_action(self, w): if w == self.buttonPlay: if self.player.is_playing(): self.player.pause() else: self.player.play() def next_action(self, w): if w == self.buttonNext: self.player.play_next() def prev_action(self, w): if w == self.buttonPrev: self.player.play_prev() def stop_action(self, w): if w == self.buttonStop: self.player.stop() def open_file_dialog(self, w): chooser = gtk.FileChooserDialog( "Open", action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK), ) chooser.set_select_multiple(True) response = chooser.run() if response == gtk.RESPONSE_OK: filenames = chooser.get_filenames() self.player.stop() self.update("reset") self.player.set_playlist(filenames) chooser.destroy()
class AsyncMusicPlayer(threading.Thread): '''Music Player. MusicPlayer가 동기적이기 때문에 이것을 비동기적으로 명령을 처리 하기 위해서 Thread로 만듬 외부와의 데이터 교환: - playlist : 플레이 리스트 - command_list : 명령 수행 ''' def __init__(self): threading.Thread.__init__(self) # 현재 플레이 중인 Url self.url = '' # 현재 플레이 되고 있는 노래 self.current_song = None # webbrowser player 인스턴스 self.player = MusicPlayer() # DB self.store = Store() def run(self): '이 쓰레드에서 수행할 로직' while True: try: # 0.5초를 주기로 명령 수행 time.sleep(0.5) # 광고가 나오는지 확인해서 넘긴다. # 보통 youtube 광고는 5초다. # TODO: 광고가 나오면 volume을 줄이도록 한다. self.player.skip_if_exists_ad() if self.url == '': self.play_next_song() # URL이 변경되었으면 표시한다. url = self.player.current_url() if (self.url != url): self.url = url # TODO: # - 여기에서 곡을 추가하는 로직을 넣으면 # - 내가 듣는 링크를 플레이 리스트로 만들 수 있다. print(self.url) # 노래가 중지되면 다음 노래 if self.player.is_finished(): # played_count 수를 하나 증가 시킨다. if self.current_song: self.current_song.played_count += 1 self.store.update(self.current_song) self.play_next_song() elif self.player.is_unplable(): self.play_next_song() self.handle_cmd() except queue.Empty: pass except KeyboardInterrupt: break except Exception as e: # 에러가 발생하면 에러의 종료를 출력 #print('----') #print(type(e)) #print(e) break def play_next_song(self): " 다음 곡 " # 플레이 리스트에서 하나 꺼낸다. playlist_lock.acquire() try: song = playlist.pop(0) url = song.url except: url = None playlist_lock.release() # url이 있으면 다음곡 플레이 if url: self.current_song = song self.player.play_url(song.url) def stop(self): " 일시 정지 " self.player.stop() def play(self): " 다시 플레이" self.player.play() def handle_cmd(self): "명령 처리" data = command_list.get(block=False) if data: cmd, param = data if cmd == 'forward': self.play_next_song() elif cmd == 'stop': self.stop() elif cmd == 'play': self.play()
class GTK_Main: def __init__(self): window = gtk.Window(gtk.WINDOW_TOPLEVEL) window.set_title("Yamp!") window.set_default_size(450, 300) window.connect("destroy", gtk.main_quit, "WM destroy") window.set_position(gtk.WIN_POS_CENTER) self.player = MusicPlayer() self.player.add_observer(self) self.mb = gtk.MenuBar() self.filemenu = gtk.Menu() self.filem = gtk.MenuItem("File") self.filem.set_submenu(self.filemenu) self.menuOpen = gtk.MenuItem("Open") self.menuOpen.connect("activate", self.open_file_dialog) self.menuExit = gtk.MenuItem("Exit") self.menuExit.connect("activate", gtk.main_quit) self.filemenu.append(self.menuOpen) self.filemenu.append(self.menuExit) self.mb.append(self.filem) vbox = gtk.VBox(False, 2) window.add(vbox) vbox.pack_start(self.mb, False, False, 0) self.imgStop = gtk.Image() self.imgPlay = gtk.Image() self.imgNext = gtk.Image() self.imgPrev = gtk.Image() self.imgPause = gtk.Image() self.imgStop.set_from_stock(gtk.STOCK_MEDIA_STOP, gtk.ICON_SIZE_BUTTON) self.imgPause.set_from_stock(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_BUTTON) self.imgPlay.set_from_stock(gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_BUTTON) self.imgPrev.set_from_stock(gtk.STOCK_MEDIA_PREVIOUS, gtk.ICON_SIZE_BUTTON) self.imgNext.set_from_stock(gtk.STOCK_MEDIA_NEXT, gtk.ICON_SIZE_BUTTON) self.lblArtist = gtk.Label() self.lblAlbum = gtk.Label() self.lblSongTitle = gtk.Label() self.lblArtist.set_use_markup(True) self.lblAlbum.set_use_markup(True) self.lblSongTitle.set_use_markup(True) self.buttonPlay = gtk.Button() self.buttonNext = gtk.Button() self.buttonPrev = gtk.Button() self.buttonStop = gtk.Button() self.buttonStop.set_relief(gtk.RELIEF_NONE) self.buttonPlay.set_relief(gtk.RELIEF_NONE) self.buttonPrev.set_relief(gtk.RELIEF_NONE) self.buttonNext.set_relief(gtk.RELIEF_NONE) self.buttonStop.add(self.imgStop) self.buttonPlay.add(self.imgPlay) self.buttonPrev.add(self.imgPrev) self.buttonNext.add(self.imgNext) self.buttonPlay.connect("clicked", self.play_action) self.buttonNext.connect("clicked", self.next_action) self.buttonPrev.connect("clicked", self.prev_action) self.buttonStop.connect("clicked", self.stop_action) self.imgCover = gtk.Image() pixbuf = gtk.gdk.pixbuf_new_from_file("./music_cd.png") scaled_buf = pixbuf.scale_simple(84, 84, gtk.gdk.INTERP_BILINEAR) self.imgCover.set_from_pixbuf(scaled_buf) self.sw = gtk.ScrolledWindow() self.sw.set_shadow_type(gtk.SHADOW_ETCHED_IN) self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) store = self.create_model() self.treeView = gtk.TreeView(store) self.treeView.connect("row-activated", self.on_activated) self.treeView.set_rules_hint(True) self.sw.add(self.treeView) self.create_columns(self.treeView) hboxButtons = gtk.HBox(False, 5) hboxButtons.pack_start(self.buttonPrev, False, False, 0) hboxButtons.pack_start(self.buttonPlay, False, False, 0) hboxButtons.pack_start(self.buttonStop, False, False, 0) hboxButtons.pack_start(self.buttonNext, False, False, 0) vboxInfos = gtk.VBox(False, 0) self.lblSongTitle.set_alignment(0, 0.5) self.lblArtist.set_alignment(0, 0.5) self.lblAlbum.set_alignment(0, 0.5) vboxInfos.pack_start(hboxButtons, True, True, 0) vboxInfos.pack_start(self.lblSongTitle, True, True, 0) vboxInfos.pack_start(self.lblArtist, True, True, 0) vboxInfos.pack_start(self.lblAlbum, True, True, 0) hbox = gtk.HBox(False, 10) hbox.pack_start(self.imgCover, False, False, 0) hbox.pack_start(vboxInfos, False, False, 0) vbox.pack_start(hbox, False, False, 5) vbox.pack_start(self.sw, True, True, 0) window.show_all() def update(self, message=None, song=None): if message == "reset": self.buttonPlay.set_image(self.imgPlay) elif message == "pause": self.buttonPlay.set_image(self.imgPlay) elif message == "play": if song is not None: self.lblArtist.set_markup("<b>" + song.get_artist() + "</b>") self.lblAlbum.set_markup("<b>" + song.get_album() + "</b>") self.lblSongTitle.set_markup("<b>#{} - {}</b>".format( song.get_track(), song.get_title())) self.buttonPlay.set_image(self.imgPause) elif message == "update_playlist": self.update_model() else: print("Error : unknow message {}".format(message)) def update_model(self): for song in self.player.get_playlist(): self.treeView.get_model().append([ song.get_track(), song.get_title(), song.get_artist(), song.get_album(), song.get_path() ]) def create_model(self): store = gtk.ListStore(str, str, str, str, str) return store def on_activated(self, widget, row, col): model = widget.get_model() song = Song(model[row][1], model[row][2], model[row][3], model[row][0], model[row][4]) if self.player.is_playing(): self.player.stop() self.player.play(song) def create_columns(self, treeView): rendererText = gtk.CellRendererText() column = gtk.TreeViewColumn("Track #", rendererText, text=0) column.set_sort_column_id(0) treeView.append_column(column) rendererText = gtk.CellRendererText() column = gtk.TreeViewColumn("Title", rendererText, text=1) column.set_sort_column_id(1) treeView.append_column(column) rendererText = gtk.CellRendererText() column = gtk.TreeViewColumn("Artist", rendererText, text=2) column.set_sort_column_id(2) treeView.append_column(column) rendererText = gtk.CellRendererText() column = gtk.TreeViewColumn("Album", rendererText, text=3) column.set_sort_column_id(3) treeView.append_column(column) def play_action(self, w): if w == self.buttonPlay: if self.player.is_playing(): self.player.pause() else: self.player.play() def next_action(self, w): if w == self.buttonNext: self.player.play_next() def prev_action(self, w): if w == self.buttonPrev: self.player.play_prev() def stop_action(self, w): if w == self.buttonStop: self.player.stop() def open_file_dialog(self, w): chooser = gtk.FileChooserDialog( "Open", action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) chooser.set_select_multiple(True) response = chooser.run() if response == gtk.RESPONSE_OK: filenames = chooser.get_filenames() self.player.stop() self.update("reset") self.player.set_playlist(filenames) chooser.destroy()