예제 #1
0
파일: __init__.py 프로젝트: griffy/Pyap
    def play(self, player=None, rate=None, on_finish=None):
        if self.player is not None:
            self.stop()

        if player is None:
            from pyap.player import Player
            self.player = Player(on_finish=on_finish)
        else:
            self.player = player
            if on_finish is not None:
                self.player.connect('audio_finished', on_finish)
        def cleanup_player(audio):
            audio.stop()
        self.player.connect('audio_finished', cleanup_player)
        self.player.play(self, rate=rate)
예제 #2
0
파일: main.py 프로젝트: griffy/Muuse
    def __init__(self):
        Window.__init__(self)
        
        self.player = Player()
        self.player.connect('audio_ended', self.on_audio_end)

        self.library = Library()

        self.playlists = {}
        
        audio_list = self.library.all_audio()
        self.playlists['library'] = Playlist('Library', audio_list)

        # we want to see the library to start with
        self.set_playlist_view('library')
        
        self.show()
예제 #3
0
파일: __init__.py 프로젝트: griffy/Pyap
class Audio(object):
    """ Note: Title attribute will never be blank """
    def __init__(self, uri, **kwargs):
        self.type = kwargs['type'] if 'type' in kwargs else UNKNOWN
        self.artist = kwargs['artist'] if 'artist' in kwargs else u''
        self.title = kwargs['title'] if 'title' in kwargs else u''
        self.album = kwargs['album'] if 'album' in kwargs else u''
        self.track = int(kwargs['track']) if 'track' in kwargs else -1
        self.length = int(kwargs['length']) if 'length' in kwargs else -1
        self.year = kwargs['year'] if 'year' in kwargs else u''

        if self.type == UNKNOWN:
            self.type = uri_type(uri)

        self.uri = unicode(uri)
        if self.is_file():
            if self.uri.find(os.sep):
                self.uri = unicode(os.path.abspath(self.uri))

            if not kwargs:
                # analyze the track ourselves if no info was given
                info = audio_info(self.uri)
                if info is None:
                    raise Exception("Not an audio file")
                self.update(**info)
        elif self.is_stream():
            if not self.title:
                self.title = "Stream: %s" % self.uri

        self.player = None

    def update(self, **kwargs):
        if 'length' in kwargs:
            self.length = kwargs['length']
        if 'track' in kwargs:
            self.track = kwargs['track']
        if 'year' in kwargs:
            self.year = kwargs['year']
        if 'artist' in kwargs:
            self.artist = kwargs['artist']
        if 'title' in kwargs:
            self.title = kwargs['title']
        if 'album' in kwargs:
            self.album = kwargs['album']

    # FIXME: the on_finish function should only 
    #        be part of the player until the audio
    #        file is done playing, and not remain
    #        on the player (as it does now)
    def play(self, player=None, rate=None, on_finish=None):
        if self.player is not None:
            self.stop()

        if player is None:
            from pyap.player import Player
            self.player = Player(on_finish=on_finish)
        else:
            self.player = player
            if on_finish is not None:
                self.player.connect('audio_finished', on_finish)
        def cleanup_player(audio):
            audio.stop()
        self.player.connect('audio_finished', cleanup_player)
        self.player.play(self, rate=rate)

    def pause(self):
        if self.player is not None:
            self.player.pause()

    def resume(self):
        if self.player is not None:
            self.player.resume()

    def stop(self):
        if self.player is not None:
            self.player.stop()
            self.player = None

    def is_playing(self):
        if self.player is None:
            return False
        return self.player.is_playing()

    def is_streaming(self):
        if self.player is None:
            return False
        return self.player.is_streaming()

    def is_paused(self):
        if self.player is None:
            return False
        return self.player.is_paused()

    def is_stopped(self):
        if self.player is None:
            return True
        return self.player.is_stopped()

    def is_file(self):
        return self.type == FILE

    def is_stream(self):
        return self.type == STREAM

    # TODO: handle case where band name begins with "The" or other irrelevant words
    def __cmp__(self, audio):
        if self.artist and audio.artist:
            if self.artist == audio.artist:
                if self.album and audio.album:
                    if self.album == audio.album:
                        if self.track != -1 and audio.track != -1:
                            return cmp(self.track, audio.track)
                        else:
                            return cmp(self.title, audio.title)
                    else:
                        if self.year and audio.year:
                            return cmp(self.year, audio.year)
                        else:
                            return cmp(self.album, audio.album)
                else:
                    return cmp(self.title, audio.title) 
            else:
                return cmp(self.artist, audio.artist)

        return cmp(self.title, audio.title) 
        
    def __str__(self):
        if self.artist:
            return u"%s - %s" % (self.artist, self.title)
        return self.title

    def __repr__(self):
        id = ""
        if hasattr(self, 'id'):
            id = str(self.id)
        return u"<Audio('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')>" % (
            id,
            "Stream" if self.is_stream() else "File",
            self.uri,
            self.artist,
            self.title,
            self.album,
            str(self.track),
            str(self.length),
            self.year
        )
예제 #4
0
파일: main.py 프로젝트: griffy/Muuse
class Muuse(Window):
    def __init__(self):
        Window.__init__(self)
        
        self.player = Player()
        self.player.connect('audio_ended', self.on_audio_end)

        self.library = Library()

        self.playlists = {}
        
        audio_list = self.library.all_audio()
        self.playlists['library'] = Playlist('Library', audio_list)

        # we want to see the library to start with
        self.set_playlist_view('library')
        
        self.show()
        
    def set_playlist_view(self, playlist):
        self._playlist_view = playlist
        self.listbox.clear()
        self.extend_listbox(self.playlists[playlist])
        # in case the repeat state was changed in a previous playlist,
        # make sure the button matches the state for this playlist
        self.set_repeat(self.playlists[playlist].get_repeat())
        # same for shuffle
        self.set_shuffle(self.playlists[playlist].is_shuffling())
        # only the library playlist is persistent
        if playlist == 'library':
            #self.playlist_listbox.hide()
            #self.library_listbox.show()
            # (re)enable all buttons affecting library
            self.add_file_btn.set_sensitive(True)
            self.add_folder_btn.set_sensitive(True)
            self.rem_sel_btn.set_sensitive(True)
            self.rem_all_btn.set_sensitive(True)
            self.sel_inverse_btn.set_sensitive(True)
            self.sel_all_btn.set_sensitive(True)
            self.sel_none_btn.set_sensitive(True)
            self.list_load_btn.set_sensitive(True)
            self.list_save_btn.set_sensitive(True)
        else:
            #self.library_listbox.hide()
            #self.playlist_listbox.show()
            # disable all buttons that affect the library
            self.add_file_btn.set_sensitive(False)
            self.add_folder_btn.set_sensitive(False)
            self.rem_sel_btn.set_sensitive(False)
            self.rem_all_btn.set_sensitive(False)
            self.sel_inverse_btn.set_sensitive(False)
            self.sel_all_btn.set_sensitive(False)
            self.sel_none_btn.set_sensitive(False)
            self.list_load_btn.set_sensitive(False)
            self.list_save_btn.set_sensitive(False)

    def playlist_view(self):
        return self._playlist_view
      
    def current_playlist(self):
        return self.playlists[self.playlist_view()]
         
    def extend_listbox(self, audio_list):
        text_list = [str(audio) for audio in audio_list]
        self.listbox.extend(text_list)
        
    def extend_library(self, audio_list):
        self.playlists['library'].extend(audio_list)
        self.library.add_audio(audio_list)
        if self.playlist_view() == 'library':
            self.extend_listbox(audio_list)

    def set_repeat(self, state):
        self.current_playlist().set_repeat(state)
        if state == playlist.REPEAT_ALL:
            self.repeat_btn.set_image(get_icon('repeat_all'))
        elif state == playlist.REPEAT_ONE:
            self.repeat_btn.set_image(get_icon('repeat_one'))
        elif state == playlist.REPEAT_OFF:
            self.repeat_btn.set_image(get_icon('repeat_off'))

    def set_shuffle(self, shuffling):
        self.current_playlist().set_shuffle(shuffling)
        if shuffling:
            self.shuffle_btn.set_image(get_icon('shuffle_on'))
        else:
            self.shuffle_btn.set_image(get_icon('shuffle_off'))
            
    def set_volume(self, volume):
        self.player.set_volume(volume)
        if volume >= 0.7:
            self.volume_btn.set_image(get_icon('volume_max'))
        elif volume >= 0.3:
            self.volume_btn.set_image(get_icon('volume_mid'))
        else:
            self.volume_btn.set_image(get_icon('volume_min'))
            
    def pause(self):
        self.player.pause()
        self.play_btn.set_image(get_icon('play'))
        self.status_icon.set_tooltip(u'Paused: %s' % self.player.current_audio())
        
    def resume(self):
        self.player.resume()
        self.play_btn.set_image(get_icon('pause'))
        self.status_icon.set_tooltip(u'Playing: %s' % self.player.current_audio())
        
    def play(self, audio, focus=False):
        self.player.stop()
        index = self.current_playlist().current_index
        self.listbox.select_and_scroll(index, focus)
        self.audio_label.set_text(str(audio))
        self.status_icon.set_tooltip(u'Playing: %s' % audio)
        update_progress = self.progress_updater()
        gobject.timeout_add(250, update_progress.next)
        self.player.play(audio)
        self.play_btn.set_image(get_icon('pause'))

    def stop(self):
        self.player.stop()
        self.audio_label.set_text('')
        self.status_icon.set_tooltip('Muuse')
        self.play_btn.set_image(get_icon('play'))
        self.audio_slider.set_fraction(0)
        self.audio_slider.set_text('')
        
    def progress_updater(self):
        position = self.player.position()
        duration = self.player.audio_duration()
        while position < duration:
            pos_fmt = format_time(position)
            dur_fmt = format_time(duration)
            self.audio_slider.set_fraction(position * 1.0 / duration)
            self.audio_slider.set_text("%s / %s" % (pos_fmt, dur_fmt))
            position = self.player.position()
            duration = self.player.audio_duration()
            yield True
        yield False
        
    def on_window_close(self, *arg, **kwargs):
        gtk.main_quit()

    def on_icon_click(self, *args, **kwargs):
        if self.window.props.visible:
            self.window.hide()
        else:
            self.window.show()   
                  
    def on_icon_right_click(self, *args, **kwargs):
        if 'data' in kwargs:
            menu.popup(self.menu, None, None, 3, args[2])
        
    def on_show_click(self, *args, **kwargs):
        if self.window.props.visible:
            self.window.hide()
        else:
            self.window.show()
        
    def on_quit_click(self, *args, **kwargs):
        gtk.main_quit()
        
    def on_audio_end(self, audio):
        next_audio = self.current_playlist().next()
        if next_audio:
            self.play(next_audio, focus=True)
            
    def on_slider_click(self, widget, event, **kwargs):
        audio_duration = self.player.audio_duration()
        if audio_duration:
            slider_width = self.audio_slider.get_allocation()[2]
            seek_time = event.x / slider_width * audio_duration
            self.player.set_position(seek_time)  
        
    def on_play_click(self, *args, **kwargs):
        if self.player.is_playing():
            self.pause()
        elif self.player.is_paused():
            self.resume()
        else:
            # the user manually selected a song to play
            index = self.listbox.get_selected_row()
            playlist = self.current_playlist()
            playlist.current_index = index
            self.play(playlist[index], focus=False)
                
    def on_stop_click(self, *args, **kwargs):
        self.stop()
      
    def on_previous_click(self, *args, **kwargs):
        audio = self.current_playlist().previous()
        if audio:
            self.play(audio)
      
    def on_next_click(self, *args, **kwargs):
        audio = self.current_playlist().next()
        if audio:
            self.play(audio)
              
    def on_repeat_click(self, *args, **kwargs):
        # Order by click: NO_REPEAT, REPEAT_ONE, REPEAT_ALL
        if self.current_playlist().is_repeating_one():
            self.set_repeat(playlist.REPEAT_ALL)
        elif self.current_playlist().is_repeating_all():
            self.set_repeat(playlist.REPEAT_OFF)
        else:
            self.set_repeat(playlist.REPEAT_ONE)
            
    def on_shuffle_click(self, *args, **kwargs):
        self.set_shuffle(not self.current_playlist().is_shuffling())
        
    def on_volume_change(self, widget, value):
        self.set_volume(value)

    def on_audio_click(self, widget, path, *args, **kwargs):
        index = path[0]
        playlist = self.current_playlist()
        playlist.current_index = index
        self.play(playlist[index], focus=False)   
            
    # TODO: handle playlist files
    def on_add_file_click(self, *args, **kwargs):
        dialog = gtk.FileChooserDialog("Select File(s)", 
                                       None, 
                                       gtk.FILE_CHOOSER_ACTION_OPEN,
                                       (gtk.STOCK_CANCEL,
                                        gtk.RESPONSE_CANCEL, 
                                        gtk.STOCK_OPEN,
                                        gtk.RESPONSE_OK))
        dialog.set_default_response(gtk.RESPONSE_OK)
        dialog.set_select_multiple(True)
        
        filter = gtk.FileFilter()
        filter.set_name('Audio')
        for ext_items in extensions['audio'].items():
            for ext in ext_items:
                filter.add_pattern('*.%s' % ext)
        dialog.add_filter(filter)

        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            uris = dialog.get_filenames()
            audio_list = [Audio(uri) for uri in uris]
            self.extend_library(audio_list)
        dialog.destroy()
       
    def on_add_folder_click(self, widget, data=None):
        dialog = gtk.FileChooserDialog("Select Folder(s)", 
                                       None,
                                       gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
                                       (gtk.STOCK_CANCEL,
                                        gtk.RESPONSE_CANCEL, 
                                        gtk.STOCK_OPEN,
                                        gtk.RESPONSE_OK))
        dialog.set_default_response(gtk.RESPONSE_OK)
        dialog.set_select_multiple(True)
        
        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            uris = []
            folders = dialog.get_filenames()
            for folder in folders:
                for root, dirs, files in os.walk(folder):
                    for file in files:
                        ext = os.path.splitext(file)[1].replace('.', '')
                        if util.is_audio(ext):
                            uri = os.path.join(root, file)
                            uris.append(uri)
            audio_list = [Audio(uri) for uri in uris]
            self.extend_library(audio_list)
        dialog.destroy()
        
    # TODO
    def on_remove_selected_click(self, *args, **kwargs):
        pass
        
    # TODO
    def on_remove_all_click(self, *args, **kwargs):
        pass
        
    def on_select_inverse_click(self, *args, **kwargs):
        self.listbox.select_inverse()
        
    def on_select_all_click(self, *args, **kwargs):
        self.listbox.select_all()

    def on_select_none_click(self, *args, **kwargs):
        self.listbox.unselect_all()
        
    def on_list_save_click(self, *args, **kwargs):
        dialog = gtk.FileChooserDialog("Save Playlist", 
                                       None, 
                                       gtk.FILE_CHOOSER_ACTION_SAVE,
                                       (gtk.STOCK_CANCEL, 
                                        gtk.RESPONSE_CANCEL, 
                                        gtk.STOCK_SAVE,
                                        gtk.RESPONSE_OK))
        dialog.set_default_response(gtk.RESPONSE_OK)
        
        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            uri = dialog.get_filename()
            # FIXME: it's not that simple
            self.current_playlist().export('m3u', uri)
        dialog.destroy()

    def on_list_load_click(self, *args, **kwargs):
        dialog = gtk.FileChooserDialog("Load Playlist", 
                                       None, 
                                       gtk.FILE_CHOOSER_ACTION_OPEN,
                                       (gtk.STOCK_CANCEL, 
                                        gtk.RESPONSE_CANCEL, 
                                        gtk.STOCK_OPEN,
                                        gtk.RESPONSE_OK))
        dialog.set_default_response(gtk.RESPONSE_OK)
        
        filter = gtk.FileFilter()
        filter.set_name('Playlists')
        for ext_items in extensions['playlist'].items():
            for ext in ext_items:
                filter.add_pattern('*.%s' % ext)
        dialog.add_filter(filter)

        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            uri = dialog.get_filename()
            playlist = import_playlist(uri)
            self.playlists[str(playlist)] = playlist
        dialog.destroy()
      
    def main(self):
        gtk.main()
예제 #5
0
파일: test_player.py 프로젝트: griffy/Pyap
 def setUp(self):
     self.player = Player()
예제 #6
0
파일: test_player.py 프로젝트: griffy/Pyap
class TestPlayer(unittest.TestCase):
    def setUp(self):
        self.player = Player()

    def test_play(self):
        self.player.play(Audio('http://mp1.somafm.com:2020'))
        self.assertTrue(self.player.is_streaming())

    def test_play_uri(self):
        self.player.play_uri('http://mp1.somafm.com:2020', True)
        self.assertTrue(self.player.is_streaming())

    def test_pause(self):
        self.player.play(Audio('http://mp1.somafm.com:2020'))
        self.player.pause()
        self.assertTrue(self.player.is_paused())

    def test_resume(self):
        self.player.play(Audio('http://mp1.somafm.com:2020'))
        self.player.pause()
        self.assertTrue(self.player.is_paused())

    def test_stop(self):
        self.player.play(Audio('http://mp1.somafm.com:2020'))
        self.player.stop()
        self.assertTrue(self.player.is_stopped())

    def test_set_position(self):
        pass

    def test_get_position(self):
        pass

    def test_get_duration(self):
        pass

    def test_get_progress(self):
        pass

    def test_set_volume(self):
        self.player.play(Audio('http://mp1.somafm.com:2020'))
        self.player.set_volume(10)
        self.assertEqual(self.player.get_volume(), 10)

    def test_get_volume(self):
        self.player.play(Audio('http://mp1.somafm.com:2020'))
        self.player.set_volume(10)
        self.assertEqual(self.player.get_volume(), 10)