Example #1
0
    def __init__(self, version, window=QMainWindow(), player=Player()):
        QMainWindow.__init__(self)
        self.ui = Ui_Main()
        self.window = window
        self.ui.setupUi(window)
        self.player = player
        self.version = version

        # Init Timer
        self.timer = QtCore.QTimer()
        self.timer.start(1000)  # Update every second
        self.timer.timeout.connect(self.track_elapsed)

        # Button icons
        window_icon = QIcon()
        window_icon.addPixmap(QPixmap(os.path.dirname(os.path.realpath(__file__))
                                    + "/images/window/icon.png"), QIcon.Normal, QIcon.Off)
        self.window.setWindowIcon(window_icon)

        self.play_icon = QIcon()
        self.play_icon.addPixmap(QPixmap(os.path.dirname(os.path.realpath(__file__))
                                    + "/images/buttons/play.png"), QIcon.Normal, QIcon.Off)
        self.pause_icon = QIcon()
        self.pause_icon.addPixmap(QPixmap(os.path.dirname(os.path.realpath(__file__))
                                    + "/images/buttons/pause.png"), QIcon.Normal, QIcon.Off)
        self.next_icon = QIcon()
        self.next_icon.addPixmap(QPixmap(os.path.dirname(os.path.realpath(__file__))
                                    + "/images/buttons/next.png"), QIcon.Normal, QIcon.Off)
        self.prev_icon = QIcon()
        self.prev_icon.addPixmap(QPixmap(os.path.dirname(os.path.realpath(__file__))
                                    + "/images/buttons/previous.png"), QIcon.Normal, QIcon.Off)
        self.stop_icon = QIcon()
        self.stop_icon.addPixmap(QPixmap(os.path.dirname(os.path.realpath(__file__))
                                    + "/images/buttons/stop.png"), QIcon.Normal, QIcon.Off)

        # Set buttons
        self.ui.playpauseButton.setIcon(self.play_icon)
        self.ui.nextButton.setIcon(self.next_icon)
        self.ui.previousButton.setIcon(self.prev_icon)
        self.ui.stopButton.setIcon(self.stop_icon)

        # CheckBox for showing / hiding Expanded Lists
        self.ui.expandCheck.stateChanged.connect(self.lists)
        # And for showing / hiding video window
        self.ui.videoCheck.stateChanged.connect(self.video_window)

        # For dragging the slider
        self.was_it_playing = None
        self.ui.TrackSlider.sliderPressed.connect(self.slider_pressed)
        self.ui.TrackSlider.sliderReleased.connect(self.play_when_slider_released)
        self.ui.TrackSlider.sliderMoved.connect(self.player.change_item_pos)

        # Model to hold track list
        self.queueTrackListModel = QStandardItemModel(self.ui.trackList)
        self.ui.trackList.setModel(self.queueTrackListModel)
        self.recentTrackListModel = QStandardItemModel(self.ui.recentTracksList)
        self.ui.recentTracksList.setModel(self.recentTrackListModel)
        self.ui.trackList.doubleClicked.connect(self.choose_track_from_queue)
        self.ui.recentTracksList.doubleClicked.connect(self.choose_track_from_recent)

        self.ui.trackList.hide()  # we start the list hidden
        self.ui.recentTracksList.hide()  # likewise

        # Connect GUI buttons
        self.ui.playpauseButton.clicked.connect(self.play_track)  # Play / Pause button
        self.ui.stopButton.clicked.connect(self.stop_track)  # Stop button
        self.ui.nextButton.clicked.connect(self.next_track)  # Next button
        self.ui.previousButton.clicked.connect(self.prev_track)  # Next button

        self.ui.actionQuit.triggered.connect(sys.exit)  # Quit from menu
        self.ui.actionOpen.triggered.connect(self.choose_track)  # Open single item from menu
        self.ui.actionOpen_Several.triggered.connect(self.choose_tracks)  # Open several items from menu
        self.ui.actionOpen_Directory.triggered.connect(self.choose_directory)  # Open a directory from menu
        self.ui.actionAbout_LiMu.triggered.connect(self.about_dialog)

        # Keyboard shortcuts
        self.shortcut_next_track = QShortcut(QKeySequence("Right"), self.window)
        self.shortcut_next_track.activated.connect(self.next_track)
        self.shortcut_prev_track = QShortcut(QKeySequence("Left"), self.window)
        self.shortcut_prev_track.activated.connect(self.prev_track)
        self.shortcut_play_track = QShortcut(QKeySequence("Up"), self.window)
        self.shortcut_play_track.activated.connect(self.play_track)
        self.shortcut_stop_track = QShortcut(QKeySequence("Down"), self.window)
        self.shortcut_stop_track.activated.connect(self.stop_track)
        self.shortcut_open_folder = QShortcut(QKeySequence("Ctrl+O"), self.window)
        self.shortcut_open_folder.activated.connect(self.choose_directory)
        self.shortcut_open_track = QShortcut(QKeySequence("Ctrl+I"), self.window)
        self.shortcut_open_track.activated.connect(self.choose_track)
        self.shortcut_open_tracks = QShortcut(QKeySequence("Ctrl+L"), self.window)
        self.shortcut_open_tracks.activated.connect(self.choose_tracks)
        self.shortcut_quit = QShortcut(QKeySequence("Ctrl+Q"), self.window)
        self.shortcut_quit.activated.connect(sys.exit)

        # Starts expanded if set in settings.conf
        if config['Expanded'] is True:
            self.ui.expandCheck.nextCheckState()
            self.window.setMaximumSize(QSize(_expX, _expY))
            self.window.setMinimumSize(QSize(_expX, _expY))
            self.window.resize(_expX, _expY)
            self.ui.trackList.show()
            self.ui.recentTracksList.show()

        # Stuck Counter ( A BUG )
        self.stuck_counter = 0
Example #2
0
class Application(QMainWindow):
    def __init__(self, version, window=QMainWindow(), player=Player()):
        QMainWindow.__init__(self)
        self.ui = Ui_Main()
        self.window = window
        self.ui.setupUi(window)
        self.player = player
        self.version = version

        # Init Timer
        self.timer = QtCore.QTimer()
        self.timer.start(1000)  # Update every second
        self.timer.timeout.connect(self.track_elapsed)

        # Button icons
        window_icon = QIcon()
        window_icon.addPixmap(QPixmap(os.path.dirname(os.path.realpath(__file__))
                                    + "/images/window/icon.png"), QIcon.Normal, QIcon.Off)
        self.window.setWindowIcon(window_icon)

        self.play_icon = QIcon()
        self.play_icon.addPixmap(QPixmap(os.path.dirname(os.path.realpath(__file__))
                                    + "/images/buttons/play.png"), QIcon.Normal, QIcon.Off)
        self.pause_icon = QIcon()
        self.pause_icon.addPixmap(QPixmap(os.path.dirname(os.path.realpath(__file__))
                                    + "/images/buttons/pause.png"), QIcon.Normal, QIcon.Off)
        self.next_icon = QIcon()
        self.next_icon.addPixmap(QPixmap(os.path.dirname(os.path.realpath(__file__))
                                    + "/images/buttons/next.png"), QIcon.Normal, QIcon.Off)
        self.prev_icon = QIcon()
        self.prev_icon.addPixmap(QPixmap(os.path.dirname(os.path.realpath(__file__))
                                    + "/images/buttons/previous.png"), QIcon.Normal, QIcon.Off)
        self.stop_icon = QIcon()
        self.stop_icon.addPixmap(QPixmap(os.path.dirname(os.path.realpath(__file__))
                                    + "/images/buttons/stop.png"), QIcon.Normal, QIcon.Off)

        # Set buttons
        self.ui.playpauseButton.setIcon(self.play_icon)
        self.ui.nextButton.setIcon(self.next_icon)
        self.ui.previousButton.setIcon(self.prev_icon)
        self.ui.stopButton.setIcon(self.stop_icon)

        # CheckBox for showing / hiding Expanded Lists
        self.ui.expandCheck.stateChanged.connect(self.lists)
        # And for showing / hiding video window
        self.ui.videoCheck.stateChanged.connect(self.video_window)

        # For dragging the slider
        self.was_it_playing = None
        self.ui.TrackSlider.sliderPressed.connect(self.slider_pressed)
        self.ui.TrackSlider.sliderReleased.connect(self.play_when_slider_released)
        self.ui.TrackSlider.sliderMoved.connect(self.player.change_item_pos)

        # Model to hold track list
        self.queueTrackListModel = QStandardItemModel(self.ui.trackList)
        self.ui.trackList.setModel(self.queueTrackListModel)
        self.recentTrackListModel = QStandardItemModel(self.ui.recentTracksList)
        self.ui.recentTracksList.setModel(self.recentTrackListModel)
        self.ui.trackList.doubleClicked.connect(self.choose_track_from_queue)
        self.ui.recentTracksList.doubleClicked.connect(self.choose_track_from_recent)

        self.ui.trackList.hide()  # we start the list hidden
        self.ui.recentTracksList.hide()  # likewise

        # Connect GUI buttons
        self.ui.playpauseButton.clicked.connect(self.play_track)  # Play / Pause button
        self.ui.stopButton.clicked.connect(self.stop_track)  # Stop button
        self.ui.nextButton.clicked.connect(self.next_track)  # Next button
        self.ui.previousButton.clicked.connect(self.prev_track)  # Next button

        self.ui.actionQuit.triggered.connect(sys.exit)  # Quit from menu
        self.ui.actionOpen.triggered.connect(self.choose_track)  # Open single item from menu
        self.ui.actionOpen_Several.triggered.connect(self.choose_tracks)  # Open several items from menu
        self.ui.actionOpen_Directory.triggered.connect(self.choose_directory)  # Open a directory from menu
        self.ui.actionAbout_LiMu.triggered.connect(self.about_dialog)

        # Keyboard shortcuts
        self.shortcut_next_track = QShortcut(QKeySequence("Right"), self.window)
        self.shortcut_next_track.activated.connect(self.next_track)
        self.shortcut_prev_track = QShortcut(QKeySequence("Left"), self.window)
        self.shortcut_prev_track.activated.connect(self.prev_track)
        self.shortcut_play_track = QShortcut(QKeySequence("Up"), self.window)
        self.shortcut_play_track.activated.connect(self.play_track)
        self.shortcut_stop_track = QShortcut(QKeySequence("Down"), self.window)
        self.shortcut_stop_track.activated.connect(self.stop_track)
        self.shortcut_open_folder = QShortcut(QKeySequence("Ctrl+O"), self.window)
        self.shortcut_open_folder.activated.connect(self.choose_directory)
        self.shortcut_open_track = QShortcut(QKeySequence("Ctrl+I"), self.window)
        self.shortcut_open_track.activated.connect(self.choose_track)
        self.shortcut_open_tracks = QShortcut(QKeySequence("Ctrl+L"), self.window)
        self.shortcut_open_tracks.activated.connect(self.choose_tracks)
        self.shortcut_quit = QShortcut(QKeySequence("Ctrl+Q"), self.window)
        self.shortcut_quit.activated.connect(sys.exit)

        # Starts expanded if set in settings.conf
        if config['Expanded'] is True:
            self.ui.expandCheck.nextCheckState()
            self.window.setMaximumSize(QSize(_expX, _expY))
            self.window.setMinimumSize(QSize(_expX, _expY))
            self.window.resize(_expX, _expY)
            self.ui.trackList.show()
            self.ui.recentTracksList.show()

        # Stuck Counter ( A BUG )
        self.stuck_counter = 0

    def lists(self):
        if self.ui.expandCheck.isChecked():
            self.window.setMaximumSize(QSize(_expX, _expY))
            self.window.setMinimumSize(QSize(_expX, _expY))
            self.window.resize(_expX, _expY)
            self.ui.trackList.show()
            self.ui.recentTracksList.show()
        else:
            self.window.setMaximumSize(QSize(_norX, _norY))
            self.window.setMinimumSize(QSize(_norX, _norY))
            self.window.resize(_norX, _norY)
            self.ui.trackList.hide()
            self.ui.recentTracksList.hide()

    def video_window(self):
        self.check_video()
        if self.player.video.isEnabled():
            if self.ui.videoCheck.isChecked():
                self.player.video.show()
            else:
                self.player.video.hide()

    def check_video(self):
        if self.player.get_item() is not None:
            video = False
            for v in video_files:
                if self.player.get_item().lower().endswith(v):
                    self.ui.videoCheck.setEnabled(True)
                    video = True
            if video is False:
                self.ui.videoCheck.setChecked(False)
                self.ui.videoCheck.setEnabled(False)

    def slider_pressed(self):
        if self.player.status == "playing":
            self.play_track()
            self.was_it_playing = True
        else:
            self.was_it_playing = False

    def play_when_slider_released(self):
        if self.was_it_playing is True:
            self.play_track()

    def choose_track_from_queue(self, chosen):
        self.player.stop_item()
        if self.player.get_item() is not None:
            self.current_to_recent()
        location = chosen.data(1)
        self.player.set_item(location)
        print("loaded: " + self.player.get_item())
        location = chosen.data(1)
        self.queueTrackListModel.removeRow(chosen.row())
        self.meta_info(location, active=True)
        self.play_track()

    def choose_track_from_recent(self, chosen):
        self.player.stop_item()
        location = chosen.data(1)
        self.recentTrackListModel.removeRow(chosen.row())
        if self.player.get_item() is not None:
            self.current_to_recent()
        self.player.set_item(location)
        self.play_track()
        self.meta_info(location, active=True)

    @pyqtSlot()
    def play_track(self):
        if self.player.get_item() is not None:
            self.player.play_item()
            self.video_window()
            print(self.player.status)
            if self.player.status == "paused":
                self.ui.playpauseButton.setIcon(self.play_icon)
            elif self.player.status == "playing":
                self.ui.playpauseButton.setIcon(self.pause_icon)
        else:
            print("No track to play")

    @pyqtSlot()
    def stop_track(self):
        if self.player.get_item() is not None:
            self.player.stop_item()
            print(self.player.status)
            self.ui.playpauseButton.setIcon(self.play_icon)

    @pyqtSlot()
    def next_track(self):
        error = "There's no track"
        try:
            self.current_to_recent()
            self.get_next_track()
            self.play_track()
        except AttributeError:
            print(error + " (" + AttributeError.__name__ + ")")
        except TypeError:
            print(error + " (" + TypeError.__name__ + ")")

    @pyqtSlot()
    def prev_track(self):
        previous_track = self.recentTrackListModel.takeItem(0)
        if previous_track is not None or previous_track is not "":
            try:
                location = previous_track.data(1)
                try:
                    self.player.stop_item()
                    print(self.player.get_item())
                    playing_track = self.player.get_item()
                    playing_meta = self.meta_info(playing_track)
                    item = QStandardItem(playing_track)
                    item.setFlags(item.flags() & ~QtCore.Qt.ItemIsDropEnabled)  # This is necessary to drag'n'drop within QueueList
                    item.setText(playing_meta)
                    item.setData(playing_track, 1)
                    self.queueTrackListModel.insertRow(0, item)
                    print("goes back one step..")
                except FileNotFoundError:
                    print("no current track..")
                self.player.set_item(location)
                print("loaded: " + self.player.get_item())
                self.meta_info(location, active=True)
                self.recentTrackListModel.removeRow(0)
                print(previous_track.data(0) + " taken back from recent.")
                self.play_track()
            except AttributeError:
                print("nope")
        elif previous_track is None or previous_track is "":
            print("there's no track - cannot go backwards")

    def current_to_recent(self):
        track = self.player.get_item()
        if track is not "":
            item = QStandardItem(track)
            track_meta = self.meta_info(track)
            item.setText(track_meta)
            item.setData(track, 1)
            self.recentTrackListModel.insertRow(0, item)

    @pyqtSlot()
    def choose_track(self, arg=False):
        if arg is not False:
            chosen = [arg]
        elif arg is False:
            chosen = QFileDialog.getOpenFileName(parent=self.window, filter="Media (*.mp3 *.ogg *.oga *.wav *.flac *.wma *.mkv *.mp4 *.avi *.mpg *.ogv)", directory=os.getenv('HOME'), caption="Open single track")
        if chosen[0] != "":
            if self.player.status != "stopped":
                self.player.stop_item()
            if self.player.get_item() is not None:
                self.current_to_recent()
            location = chosen[0]
            self.player.set_item(location)
            print("loaded: " + self.player.get_item())
            self.meta_info(location, active=True)
            self.play_track()

    @pyqtSlot()
    def choose_tracks(self):
        chosen = QFileDialog.getOpenFileNames(parent=self.window, directory=os.getenv('HOME'), caption="Open one or more tracks")
        tracks = chosen[0]
        count = len(tracks)
        start_all = time.time()
        for track in tracks:
            is_audio = False
            is_video = False
            for a in audio_files:
                if track.lower().endswith(a):
                    is_audio = True
                    break
            for v in video_files:
                if track.lower().endswith(v):
                    is_video = True
                    break
            if is_audio:
                queue.put(track)
            # Adds it directly, bypassing threading (doesn't play nice)
            elif is_video:
                self.process_track(track)
            # The track wasn't a playable track at all!
            else:
                count -= 1
        self.start_working(start_all, count)

    @pyqtSlot()
    def choose_directory(self, arg=False):
        dir_files = ''
        if arg is not False:
            directory = [arg][0]
            dir_files = os.listdir(directory)
        else:
            directory = QFileDialog.getExistingDirectory(directory=os.getenv('HOME'), caption="Open directory with tracks")
            if directory != '':
                dir_files = os.listdir(directory)
        count = len(dir_files)
        start_all = time.time()
        for track in dir_files:
            is_audio = False
            is_video = False
            for a in audio_files:
                if track.lower().endswith(a):
                    is_audio = True
                    break
            for v in video_files:
                if track.lower().endswith(v):
                    is_video = True
                    break
            if is_audio:
                fullpath = os.path.realpath(directory) + "/" + track
                queue.put(fullpath)
            # # Adds it directly, bypassing threading (doesn't play nice)
            elif is_video:
                fullpath = os.path.realpath(directory) + "/" + track
                self.process_track(fullpath)
            # Track was no track at all!
            else:
                count -= 1
        self.start_working(start_all, count)

    def get_next_track(self):
        try:
            next_track = self.queueTrackListModel.takeItem(0)
            print("takes next track from queue..")
            location = next_track.data(1)
            self.player.set_item(location)
            print("loaded: " + self.player.get_item())
            self.meta_info(location, active=True)
            self.queueTrackListModel.removeRow(0)
            print(next_track.data(0) + " removed from queue.")
            self.play_track()
            self.ui.playpauseButton.setIcon(self.pause_icon)
        except AttributeError:
            print("queue's empty.. I'll hang back for now")
            self.player.reset()
            self.ui.playpauseButton.setIcon(self.play_icon)

    def meta_info(self, location, active=False):
        taginfo = os.path.basename(location)
        try:
            tagger = read_tag(location)
            taginfo = tagger.artist + " - " + tagger.title + " (" + tagger.album + ")"# + " (" + tagger.date + ")"
            if active is True:
                if len(taginfo) > 54:
                    taginfo = taginfo[:52]
                    self.ui.TrackInformationText.setText(taginfo + "..")
                else:
                    self.ui.TrackInformationText.setText(taginfo)
        except NameError:  # If NoTagError couldn't import because of lack of stagger
            if active is True:
                self.ui.TrackInformationText.setText(os.path.basename(location))
        except NoTagError:
            if active is True:
                self.ui.TrackInformationText.setText(os.path.basename(location))
        return taginfo

    def track_elapsed(self):
        track = self.player.track_item_pos()
        if self.player.status is "playing":
            # Song finished
            if track[3] >= track[4]:
                if track[4] != 0:  # test-fix for early track switching | booo
                    print("finished playing song")
                    self.current_to_recent()
                    self.player.stop_item()
                    self.get_next_track()
            # -||- but placeholder fix for 1sec left bug
            if track[3] == track[4] - 1:
                stuck = self.is_it()
                if stuck is True:
                    print("finished playing song (IN AN UGLY WAY)")
                    self.current_to_recent()
                    self.player.stop_item()
                    self.get_next_track()
            if track[4] != 0:
                self.ui.TrackElapsedText.setText("+" + track[0])
                self.ui.TrackLengthText.setText(track[1])
                self.ui.TrackLeftText.setText("-" + track[2])
                self.ui.TrackSlider.setMaximum(track[4])
                if self.player.status is not "paused":
                    self.ui.TrackSlider.setValue(track[3])

    def is_it(self):
            self.stuck_counter += 1
            if self.stuck_counter == 3:
                self.stuck_counter = 0
                return True
            else:
                return False

    def about_dialog(self):
        from PyQt5.QtWidgets import QDialog, QLabel, QWidget
        from PyQt5.QtCore import QRect

        d = QDialog(self.window)
        d.setWindowTitle("About LiMu")
        container = QWidget(d)
        container.setGeometry(QRect(0, 0, 200, 80))
        intro = QLabel(container)
        intro.setText("the Liothe Music Player")
        made_by = QLabel(container)
        made_by.setText("\nCreated by: Liothe")
        version = QLabel(container)
        version.setText("\n\nVersion: " + self.version)
        d.show()

    def process_track(self, track):
        with lock:
            start_single = time.time()
            item = QStandardItem(track)
            item.setFlags(item.flags() & ~QtCore.Qt.ItemIsDropEnabled)  # This is necessary to drag'n'drop within QueueList
            track_info = self.meta_info(track)
            item.setText(track_info)
            item.setData(track, 1)
            self.queueTrackListModel.appendRow(item)
            stop_single = time.time() - start_single
            print("queued:", os.path.basename(item.data(0)), "(" + str(stop_single.__round__(5)) + ")" + " --- " + threading.current_thread().name)
            if self.player.status is None:
                self.get_next_track()

    def start_working(self, start_time, count):
        for i in range(os.cpu_count()):
            threading.Thread(target=self.workerbee_do, daemon=True, name="workerbee " + str(i + 1)).start()
        stop_all = time.time() - start_time
        queue.join()
        with lock:
            print("Adding", str(count), "tracks took", str(stop_all.__round__(5)) + "seconds")

    def workerbee_do(self):
        while True:
            item = queue.get()
            self.process_track(item)
            queue.task_done()