Exemplo n.º 1
0
    def init_threads(self):
        self.thread = LyricsGrabberThread()
        self.thread.MainResult.connect(self.slot_lyrics_main)
        self.thread.MinorResult.connect(self.slot_lyrics_minor)
        self.thread.eof.connect(self.slot_lyrics_eof)
        self.thread.error.connect(self.slot_error)

        self.id3fetch_thread = MusicBrainzFetchThread()
        self.id3fetch_thread.output.connect(self.slot_setFreeDBData)
        self.id3fetch_thread.error.connect(self.slot_error)

        self.googleImages_thread = GoogleImagesGrabberThread()
        self.googleImages_thread.output.connect(self.slot_googleImagesResult)
Exemplo n.º 2
0
	def init_threads(self):
		self.thread = LyricsGrabberThread()
		self.thread.MainResult.connect(self.slot_lyrics_main)
		self.thread.MinorResult.connect(self.slot_lyrics_minor)
		self.thread.eof.connect(self.slot_lyrics_eof)
		self.thread.error.connect(self.slot_error)
		
		self.id3fetch_thread = MusicBrainzFetchThread()
		self.id3fetch_thread.output.connect(self.slot_setFreeDBData)
		self.id3fetch_thread.error.connect(self.slot_error)
		
		self.googleImages_thread = GoogleImagesGrabberThread()
		self.googleImages_thread.output.connect(self.slot_googleImagesResult)
Exemplo n.º 3
0
class MainWin(QtGui.QDialog):
    def __init__(self, songPath, id3_action="", parent=None):
        super(MainWin, self).__init__(parent)
        self.isValid = True

        self.path = songPath
        self.id3_action = id3_action if id3_action else config.id3_action
        self.changed_AlbumArt = False
        self.pix_path = ""
        self.albumArt_task = "nothing"
        '''
		albumArt_task may be 'nothing', 'add' and 'delete':
		'nothing': Do not touch the APIC tag.
		'add': creates an APIC tag with self.pix_path.
		'delete': deletes an APIC tag, if exists.
		'''
        self.lyricsList = []  # The lyrics list
        self.lyricsListIndex = -1

        # Will be enabled when the last value of lyricsList will be fetched
        # and the servers will return EOF.
        # Until then, the lyrics groupbox will look like:
        # Lyrics (4/7+) (showing lyrics num. 4 out of seven, and more are available but not fetched yet)
        # When that switch will be enabled, the plus sign will get disappeared.
        self.lyricsList_EOF = False

        # If value is 0, notfound errors won't be suppressed. Any other value will suppress all errors.
        self.suppress_notfound_errors = 0

        self.setWindowTitle(tr("ID3 Editor"))
        self.resize(650, 430)
        self.setWindowIcon(QtGui.QIcon(r'pics\id3edit.png'))
        self.empty_cdbox_path = r"pics\empty_cdbox_%s.png" % config.lang if os.path.exists(
            "pics\empty_cdbox_%s.png" %
            config.lang) else r"pics\empty_cdbox.png"

        log.debug("Initializing SetID3Window Widgets...")
        self.init_threads()
        self.init_id3data()
        if not self.isValid:
            return
        if not [
                x for x in self.easyID3Obj.values() if x[0]
        ]:  # If all values are actually blank, we shall attemp to parse it from the filename.
            fn = os.path.splitext(os.path.basename(self.path))[0]
            title, artist = utils.parse_title_from_filename(fn)
            self.easyID3Obj['title'] = [title]
            self.easyID3Obj['artist'] = [artist]

        self.init_widgets()

        if self.id3_action == 'ask_albumart':
            self.fetch_all(autochoose_albumart=False)
        if self.id3_action == 'ask':
            self.fetch_all(autochoose_albumart=True)
        if self.id3_action == 'noask':
            self.close_with_timeout()
            self.fetch_all(autochoose_albumart=True)

    def init_threads(self):
        self.thread = LyricsGrabberThread()
        self.thread.MainResult.connect(self.slot_lyrics_main)
        self.thread.MinorResult.connect(self.slot_lyrics_minor)
        self.thread.eof.connect(self.slot_lyrics_eof)
        self.thread.error.connect(self.slot_error)

        self.id3fetch_thread = MusicBrainzFetchThread()
        self.id3fetch_thread.output.connect(self.slot_setFreeDBData)
        self.id3fetch_thread.error.connect(self.slot_error)

        self.googleImages_thread = GoogleImagesGrabberThread()
        self.googleImages_thread.output.connect(self.slot_googleImagesResult)

    def init_id3data(self):
        try:  # Add ID3 Tags if does not exist
            mp3Obj = MP3(self.path, ID3=ID3)
            mp3Obj.add_tags()
            mp3Obj.save()
        except error:
            pass
        except HeaderNotFoundError:
            log.warning(
                "This MP3 files seems to be faulty. Cannot edit it's ID3 data. (Error: HeaderNotFoundError)"
            )
            QtGui.QMessageBox.critical(
                self, tr("Error"),
                tr("This MP3 files seems to be faulty. Cannot edit it's ID3 data."
                   ), QtGui.QMessageBox.Ok)
            self.isValid = False
            return
        try:
            self.easyID3Obj = EasyID3(self.path)
            self.ID3Obj = ID3(self.path)
        except ID3NoHeaderError:
            # That means mp3Obj.add_tags() didn't work for a reason.
            utils.appendDummyID3(self.path)
            self.easyID3Obj = EasyID3(self.path)
            self.ID3Obj = ID3(self.path)

        USLT_Tag = [x for x in self.ID3Obj.keys() if x.startswith('USLT')]
        self.originalLyrics = self.ID3Obj[USLT_Tag[0]].text if USLT_Tag else ""

        APIC_Tag = [x for x in self.ID3Obj.keys() if x.startswith('APIC')]
        if APIC_Tag:
            APIC_Tag = APIC_Tag[0]

            mime = self.ID3Obj[APIC_Tag].mime
            if mime == u'image/jpeg':
                self.pix_path = os.path.join(config.temp_dir, 'album_art.jpg')
            elif mime == u'image/png':
                self.pix_path = os.path.join(config.temp_dir, 'album_art.png')
            else:
                self.pix_path = os.path.join(config.temp_dir, 'album_art.pic')

            with open(self.pix_path, 'wb') as f:
                f.write(self.ID3Obj[APIC_Tag].data)

    def init_widgets(self):
        self.title = QtGui.QLineEdit(
            utils.fix_faulty_unicode(self.easyID3Obj['title'][0])
        ) if 'title' in self.easyID3Obj.keys() else QtGui.QLineEdit("")
        self.artist = QtGui.QLineEdit(
            utils.fix_faulty_unicode(self.easyID3Obj['artist'][0])
        ) if 'artist' in self.easyID3Obj.keys() else QtGui.QLineEdit("")
        self.album = QtGui.QLineEdit(
            utils.fix_faulty_unicode(self.easyID3Obj['album'][0])
        ) if 'album' in self.easyID3Obj.keys() else QtGui.QLineEdit("")
        self.date = QtGui.QLineEdit(
            self.easyID3Obj['date']
            [0]) if 'date' in self.easyID3Obj.keys() else QtGui.QLineEdit("")

        self.original_title = unicode(self.title.displayText())
        self.original_artist = unicode(self.artist.displayText())
        self.original_album = unicode(self.album.displayText())
        self.original_date = unicode(self.date.displayText())

        pixmap = QtGui.QPixmap(
            self.pix_path) if self.pix_path else QtGui.QPixmap(
                self.empty_cdbox_path)

        if self.pix_path:
            fs = os.path.getsize(self.pix_path)
            s = tr("(%sx%s, %sKB)") % (pixmap.width(), pixmap.height(),
                                       fs / 1024)
            if fs > 250 * 1024:  # 250KB
                s = '<b>%s</b>' % s
            if fs > 500 * 1024:  # 250KB
                s = '<font color="green">%s</font>' % s
            self.albumArtData = QtGui.QLabel(s)
        else:
            self.albumArtData = QtGui.QLabel('')

        if max(pixmap.width(), pixmap.height()) > 200:
            pixmap = pixmap.scaled(
                200,
                200,
                aspectRatioMode=QtCore.Qt.KeepAspectRatio,
                transformMode=QtCore.Qt.SmoothTransformation)

        self.albumArtLayout = QtGui.QVBoxLayout()
        self.albumArt = QtGui.QLabel()
        self.albumArt.setPixmap(pixmap)
        self.albumArt.setStyleSheet(
            "border-style:solid; border-color:#999792; border-width:1px;")
        self.albumArtLayout.addWidget(self.albumArt,
                                      alignment=QtCore.Qt.AlignCenter)
        self.albumArtLayout.addWidget(self.albumArtData,
                                      alignment=QtCore.Qt.AlignCenter)

        self.remove_albumart_button = QtGui.QPushButton(
            QtGui.QIcon(r'pics\cancel.png'), "")
        self.remove_albumart_button.clicked.connect(self.remove_albumart_slot)
        self.remove_albumart_button.setFlat(True)
        if not self.pix_path:
            self.remove_albumart_button.setEnabled(False)

        # Row Layouts
        buttonLayout = QtGui.QHBoxLayout()
        applyButton = QtGui.QPushButton(tr("Apply"))
        applyButton.clicked.connect(self.slot_apply)
        closeButton = QtGui.QPushButton(tr("Dismiss"))
        closeButton.clicked.connect(self.close)
        buttonLayout.addWidget(applyButton)
        buttonLayout.addWidget(closeButton)

        self.changeImageButton = QtGui.QPushButton(tr("Fetch From The Web"))
        self.changeImageButton.setSizePolicy(QtGui.QSizePolicy.Expanding,
                                             QtGui.QSizePolicy.Minimum)
        self.changeImageButton.clicked.connect(self.slot_changeImage)
        self.loadLocalImageButton = QtGui.QPushButton(
            tr("Choose Image Locally"))
        self.loadLocalImageButton.setSizePolicy(QtGui.QSizePolicy.Expanding,
                                                QtGui.QSizePolicy.Minimum)
        self.loadLocalImageButton.clicked.connect(self.slot_loadLocalImage)
        self.loadFreeDBButton = QtGui.QPushButton(tr("Load From MusicBrainz"))
        self.loadFreeDBButton.clicked.connect(self.slot_loadFreeDBButton)
        self.restoreOriginalButton = QtGui.QPushButton(tr("Restore Original"))
        self.restoreOriginalButton.clicked.connect(self.slot_restoreOriginal)

        # Layouts
        mainLayout = QtGui.QVBoxLayout()
        formLayout = QtGui.QHBoxLayout()

        ### RIGHT ###
        rightLayout = QtGui.QVBoxLayout()

        basicTagsGroup = QtGui.QGroupBox(tr("ID3 Tags"))
        layout = QtGui.QFormLayout()
        layout.addRow(tr("Title:"), self.title)
        layout.addRow(tr("Artist:"), self.artist)
        layout.addRow(tr("Album:"), self.album)
        layout.addRow(tr("Date:"), self.date)
        buttons = QtGui.QHBoxLayout()
        buttons.addWidget(self.loadFreeDBButton)
        buttons.addWidget(self.restoreOriginalButton)
        layout.addRow(buttons)
        basicTagsGroup.setLayout(layout)

        albumArtGroup = QtGui.QGroupBox(tr("Album Art"))
        layout = QtGui.QGridLayout()
        layout.addLayout(self.albumArtLayout, 0, 0, 1, 3,
                         QtCore.Qt.AlignCenter)
        layout.addWidget(self.changeImageButton, 1, 0)
        layout.addWidget(self.loadLocalImageButton, 1, 1)
        layout.addWidget(self.remove_albumart_button, 1, 2)
        albumArtGroup.setLayout(layout)

        rightLayout.addWidget(basicTagsGroup)
        rightLayout.addWidget(albumArtGroup)

        ### LEFT ###
        self.changeLyricsButton = QtGui.QPushButton(
            tr("Load Lyrics From The Web"))
        self.changeLyricsButton.clicked.connect(self.slot_changeLyrics)
        self.changeLyricsButton.setSizePolicy(QtGui.QSizePolicy.Expanding,
                                              QtGui.QSizePolicy.Minimum)
        self.deleteLyricsButton = QtGui.QPushButton(
            QtGui.QIcon(r'pics\cancel.png'), "")
        self.deleteLyricsButton.setFlat(True)
        self.deleteLyricsButton.clicked.connect(self.slot_deleteLyrics)

        self.lyrics_left_button = QtGui.QPushButton("<<")
        self.lyrics_left_button.setEnabled(False)
        self.lyrics_left_button.clicked.connect(self.slot_lyrics_left_button)
        self.lyrics_right_button = QtGui.QPushButton(">>")
        self.lyrics_right_button.setEnabled(False)
        self.lyrics_right_button.clicked.connect(self.slot_lyrics_right_button)
        self.changeLyricsLabel = QtGui.QLabel(
            tr("* Loaded from file's tags *"
               )) if self.originalLyrics else QtGui.QLabel("")
        self.changeLyricsLabel.setFrameStyle(QtGui.QFrame.Panel
                                             | QtGui.QFrame.Sunken)
        self.changeLyricsLabel.setWordWrap(True)

        self.lyrics = QtGui.QPlainTextEdit(self.originalLyrics)
        leftLayout = QtGui.QGroupBox(tr("Lyrics"))
        self.lyricsGroupBox = leftLayout  # For manipulating the title name
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.lyrics)
        inner_layout = QtGui.QHBoxLayout()
        inner_layout.addWidget(self.lyrics_left_button)
        inner_layout.addWidget(self.changeLyricsLabel)
        inner_layout.addWidget(self.lyrics_right_button)
        layout.addLayout(inner_layout)
        inner_layout = QtGui.QHBoxLayout()
        inner_layout.addWidget(self.changeLyricsButton)
        inner_layout.addWidget(self.deleteLyricsButton,
                               alignment=QtCore.Qt.AlignRight)
        layout.addLayout(inner_layout)
        leftLayout.setLayout(layout)

        ### Exit Counter Layout ###
        exitCounterLayout = QtGui.QHBoxLayout()
        self.exitCounterLabel = QtGui.QLabel(
            tr("<h1>Closing window in %d seconds...</h1>") %
            (config.id3_noask_wait_interval + 1))
        self.exitCounterButton = QtGui.QPushButton(tr("Don't close"))
        self.exitCounterButton.clicked.connect(self.slot_close_cancel)
        exitCounterLayout.addWidget(self.exitCounterLabel)
        exitCounterLayout.addWidget(self.exitCounterButton)

        ### OTHERS ###
        formLayout.addLayout(rightLayout)
        formLayout.addWidget(leftLayout)
        mainLayout.addLayout(formLayout)
        if self.id3_action == 'noask':
            mainLayout.addLayout(exitCounterLayout)
        mainLayout.addLayout(buttonLayout)
        self.setLayout(mainLayout)

    def fetch_all(self, autochoose_albumart):
        title = unicode(self.title.displayText())
        artist = unicode(self.artist.displayText())

        if title or artist:
            self.suppress_notfound_errors = 3
            self.slot_changeImage(autochoose_albumart)
            self.slot_changeLyrics()
            self.slot_loadFreeDBButton()

    def close_with_timeout(self):
        "Function shows a message to the user for 5 seconds, then closes window."
        self.close_waitthreads_calls = 0
        self.close_timer = QtCore.QTimer()
        self.close_timer.timeout.connect(self.close_waitthreads)
        self.close_timer.start(1000)

    def close_waitthreads(self):
        sec_to_wait = config.id3_noask_wait_interval

        if sec_to_wait - self.close_waitthreads_calls > 0:
            self.exitCounterLabel.setText(
                tr("<h1>Closing window in %d seconds...</h1>") %
                (sec_to_wait - self.close_waitthreads_calls))
        else:
            self.exitCounterLabel.setText(tr("<h1>Closing Window...</h1>"))
            if not any([
                    self.thread.isRunning(),
                    self.id3fetch_thread.isRunning(),
                    self.googleImages_thread.isRunning()
            ]):
                self.close_timer.stop()
                self.slot_apply()

        self.close_waitthreads_calls += 1

    def slot_close_cancel(self):
        if hasattr(self, 'close_timer'):
            self.close_timer.stop()
            self.exitCounterLabel.setText(
                tr("<h1>Automatic Closing Canceled.</h1>"))
            self.exitCounterButton.setEnabled(False)

    def slot_error(self, s):
        if hasattr(self, 'splash') and self.suppress_notfound_errors <= 1:
            self.splash.finish(self)
        if self.suppress_notfound_errors == 0:
            QtGui.QMessageBox.warning(self, tr("Error"), "<h2>%s</h2>" % s,
                                      QtGui.QMessageBox.Ok)
        else:
            self.suppress_notfound_errors -= 1

        self.changeLyricsButton.setEnabled(True)
        self.changeImageButton.setEnabled(True)
        self.loadFreeDBButton.setEnabled(True)
        self.restoreOriginalButton.setEnabled(True)

    def slot_apply(self):
        self.ID3TagsToEdit = {}
        log.debug("Saving ID3 Data in cache...")

        self.ID3TagsToEdit['TIT2'] = TIT2(encoding=3,
                                          text=unicode(
                                              self.title.displayText()))
        self.ID3TagsToEdit['TPE1'] = TPE1(encoding=3,
                                          text=unicode(
                                              self.artist.displayText()))
        self.ID3TagsToEdit['TALB'] = TALB(encoding=3,
                                          text=unicode(
                                              self.album.displayText()))
        self.ID3TagsToEdit['TDRC'] = TDRC(encoding=3,
                                          text=unicode(
                                              self.date.displayText()))
        self.ID3TagsToEdit['COMM'] = COMM(encoding=3,
                                          text=unicode(
                                              config.id3tags_whitemark),
                                          desc='')
        lyrics = unicode(self.lyrics.toPlainText()).replace('\r\n',
                                                            '\n').replace(
                                                                '\n', '\r\n')

        if lyrics:
            if utils.isHebrew(lyrics):
                self.ID3TagsToEdit['USLT'] = USLT(encoding=3,
                                                  lang=u'heb',
                                                  desc=u'',
                                                  text=lyrics)
            else:
                self.ID3TagsToEdit['USLT'] = USLT(encoding=3,
                                                  lang=u'eng',
                                                  desc=u'',
                                                  text=lyrics)
        else:
            self.ID3TagsToEdit['USLT'] = ''

        if self.albumArt_task == 'add' and self.pix_path:
            with open(self.pix_path, 'rb') as f:
                data = f.read()
            self.ID3TagsToEdit['APIC'] = APIC(encoding=0,
                                              mime=utils.guess_image_mime_type(
                                                  self.pix_path),
                                              type=0,
                                              desc="",
                                              data=data)
        elif self.albumArt_task == 'delete':
            self.ID3TagsToEdit[
                'APIC'] = ''  # empty value doesn't add a new APIC tag. All other APIC tags are removed piror to insertion.
        self.close()

    def slot_changeImage(self, autochoose=False):
        title = unicode(self.title.displayText())
        artist = unicode(self.artist.displayText())
        if title and artist:
            s = "%s - %s" % (artist, title)
        elif title:
            s = title
        elif artist:
            s = artist
        else:
            self.slot_error(tr("No title or artist values were given."))
            return
        self.last_search_string = s

        splash_movie = QtGui.QMovie(r'pics\loading_row.gif')
        self.splash = utils.qt.MovieSplashScreen(
            splash_movie, QtCore.Qt.WindowStaysOnTopHint)
        self.splash.show()

        self.changeImageButton.setEnabled(False)

        if autochoose:
            self.googleImages_thread.search(s, 1)
        else:
            self.googleImages_thread.search(s, 3)

    def slot_loadLocalImage(self):
        dialog = QtGui.QFileDialog()
        dialog.setFileMode(QtGui.QFileDialog.ExistingFile)
        f = unicode(
            dialog.getOpenFileName(caption=tr('Choose the artwork'),
                                   filter=tr("Images (*.png *.jpg)")))
        if f:
            self.pix_path = f
            pixmap = QtGui.QPixmap(self.pix_path)

            fs = os.path.getsize(self.pix_path)
            s = tr("(%sx%s, %sKB)") % (pixmap.width(), pixmap.height(),
                                       fs / 1024)
            if fs > 250 * 1024:  # 250KB
                s = '<b>%s</b>' % s
            if fs > 500 * 1024:  # 250KB
                s = '<font color="green">%s</font>' % s
            self.albumArtData.setText(s)

            if max(pixmap.width(), pixmap.height()) > 200:
                pixmap = pixmap.scaled(
                    200,
                    200,
                    aspectRatioMode=QtCore.Qt.KeepAspectRatio,
                    transformMode=QtCore.Qt.SmoothTransformation)
            self.albumArt.setPixmap(pixmap)
            self.albumArt_task = 'add'
            self.remove_albumart_button.setEnabled(True)

    def slot_googleImagesResult(self, fn_list):
        self.changeImageButton.setEnabled(True)

        if self.suppress_notfound_errors <= 1:
            self.splash.finish(self)

        if not fn_list and self.suppress_notfound_errors == 0:
            QtGui.QMessageBox.warning(self, "Error",
                                      tr("<h2>No photos were found.</h2>"),
                                      QtGui.QMessageBox.Ok)
            return

        if self.suppress_notfound_errors > 0:
            self.suppress_notfound_errors -= 1

        if len(fn_list) == 1:
            self.pix_path = fn_list[0]
        else:
            w = AlbumArtWin(fn_list, self.last_search_string)
            w.exec_()
            if hasattr(w, 'pix'):
                self.pix_path = w.pix
            if hasattr(w, 'search_string'):
                splash_movie = QtGui.QMovie(r'pics\loading_row.gif')
                self.splash = utils.qt.MovieSplashScreen(
                    splash_movie, QtCore.Qt.WindowStaysOnTopHint)
                self.splash.show()

                self.last_search_string = w.search_string
                self.googleImages_thread.search(w.search_string, 3)
                return

        if self.pix_path:
            pixmap = QtGui.QPixmap(self.pix_path)

            fs = os.path.getsize(self.pix_path)
            s = tr("(%sx%s, %sKB)") % (pixmap.width(), pixmap.height(),
                                       fs / 1024)
            if fs > 250 * 1024:  # 250KB
                s = '<b>%s</b>' % s
            if fs > 500 * 1024:  # 250KB
                s = '<font color="green">%s</font>' % s
            self.albumArtData.setText(s)

            if max(pixmap.width(), pixmap.height()) > 200:
                pixmap = pixmap.scaled(
                    200,
                    200,
                    aspectRatioMode=QtCore.Qt.KeepAspectRatio,
                    transformMode=QtCore.Qt.SmoothTransformation)
            self.albumArt.setPixmap(pixmap)
            self.albumArt_task = 'add'
            self.remove_albumart_button.setEnabled(True)

    def slot_changeLyrics(self):
        title = unicode(self.title.displayText())
        artist = unicode(self.artist.displayText())

        if not title and not artist:
            self.slot_error(tr("No title or artist values were given."))
            return

        self.lyricsList = []
        self.lyricsListIndex = -1

        splash_movie = QtGui.QMovie(r'pics\loading_row.gif')
        self.splash = utils.qt.MovieSplashScreen(
            splash_movie, QtCore.Qt.WindowStaysOnTopHint)
        self.splash.show()

        self.changeLyricsButton.setEnabled(False)

        self.thread.search(title, artist)

    def slot_deleteLyrics(self):
        self.changeLyricsLabel.setText("")
        self.lyrics.setPlainText("")

    def remove_albumart_slot(self):
        self.albumArt_task = 'delete'
        fs = os.path.getsize(self.pix_path)
        self.albumArtData.setText("")
        self.pix_path = ""
        self.albumArt.setPixmap(QtGui.QPixmap(self.empty_cdbox_path))
        self.remove_albumart_button.setEnabled(False)
        log.debug('AlbumArt is marked for deletion.')

    def slot_lyrics_main(self, lyrics):
        self.changeLyricsButton.setEnabled(True)

        if self.suppress_notfound_errors <= 1:
            self.splash.finish(self)

        if self.suppress_notfound_errors > 0:
            self.suppress_notfound_errors -= 1

        self.lyricsList.append(lyrics)
        self.lyricsListIndex = len(self.lyricsList) - 1
        if self.lyricsListIndex > 0:
            self.lyrics_left_button.setEnabled(True)

        # We're dealing with LyricsData object here
        self.lyrics.setPlainText(lyrics.lyrics)
        self.changeLyricsLabel.setText("%s - %s" %
                                       (lyrics.artist, lyrics.title))
        if self.lyricsList_EOF:
            self.lyricsGroupBox.setTitle(
                tr("Lyrics (%d/%d)") %
                (self.lyricsListIndex + 1, len(self.lyricsList)))
        else:
            self.lyricsGroupBox.setTitle(
                tr("Lyrics (%d/%d+)") %
                (self.lyricsListIndex + 1, len(self.lyricsList)))

    def slot_lyrics_minor(self, lyrics):
        self.changeLyricsButton.setEnabled(True)

        if self.suppress_notfound_errors <= 1:
            self.splash.finish(self)

        self.lyricsList.append(lyrics)
        self.lyrics_right_button.setEnabled(True)
        self.lyricsGroupBox.setTitle(
            tr("Lyrics (%d/%d+)") %
            (self.lyricsListIndex + 1, len(self.lyricsList)))

    def slot_lyrics_eof(self):
        self.changeLyricsButton.setEnabled(True)

        if self.suppress_notfound_errors <= 1:
            self.splash.finish(self)

        self.lyricsList_EOF = True
        self.lyricsGroupBox.setTitle(
            tr("Lyrics") + " (%d/%d)" %
            (self.lyricsListIndex + 1, len(self.lyricsList)))
        self.lyrics_right_button.setEnabled(False)

    def slot_lyrics_left_button(self):
        self.lyricsListIndex -= 1
        lyrics = self.lyricsList[self.lyricsListIndex]

        # We're dealing with LyricsData object here
        self.lyrics.setPlainText(lyrics.lyrics)
        self.changeLyricsLabel.setText("%s - %s" %
                                       (lyrics.artist, lyrics.title))
        if self.lyricsList_EOF:
            self.lyricsGroupBox.setTitle(
                tr("Lyrics") + " (%d/%d)" %
                (self.lyricsListIndex + 1, len(self.lyricsList)))
        else:
            self.lyricsGroupBox.setTitle(
                tr("Lyrics") + " (%d/%d+)" %
                (self.lyricsListIndex + 1, len(self.lyricsList)))

        self.lyrics_right_button.setEnabled(True)
        if self.lyricsListIndex == 0:
            self.lyrics_left_button.setEnabled(False)

    def slot_lyrics_right_button(self):
        self.lyricsListIndex += 1
        lyrics = self.lyricsList[self.lyricsListIndex]

        # We're dealing with LyricsData object here
        self.lyrics.setPlainText(lyrics.lyrics)
        self.changeLyricsLabel.setText("%s - %s" %
                                       (lyrics.artist, lyrics.title))
        if self.lyricsList_EOF:
            self.lyricsGroupBox.setTitle(
                tr("Lyrics") + " (%d/%d)" %
                (self.lyricsListIndex + 1, len(self.lyricsList)))
        else:
            self.lyricsGroupBox.setTitle(
                tr("Lyrics") + " (%d/%d+)" %
                (self.lyricsListIndex + 1, len(self.lyricsList)))

        self.lyrics_left_button.setEnabled(True)
        if self.lyricsListIndex == len(self.lyricsList) - 1:
            self.thread.nextLyrics()
            self.lyrics_right_button.setEnabled(False)

    def slot_restoreOriginal(self):
        self.title.setText(self.original_title)
        self.artist.setText(self.original_artist)
        self.album.setText(self.original_album)
        self.date.setText(self.original_date)

    def slot_loadFreeDBButton(self):
        title = unicode(self.title.displayText())
        artist = unicode(self.artist.displayText())

        if not title and not artist:
            self.slot_error(tr("No title or artist values were given."))
            return

        splash_movie = QtGui.QMovie(r'pics\loading_row.gif')
        self.splash = utils.qt.MovieSplashScreen(
            splash_movie, QtCore.Qt.WindowStaysOnTopHint)
        self.splash.show()

        self.loadFreeDBButton.setEnabled(False)
        self.restoreOriginalButton.setEnabled(False)

        self.id3fetch_thread.fetch(title, artist)

    def slot_setFreeDBData(self, album, date, tag, artist):
        self.loadFreeDBButton.setEnabled(True)
        self.restoreOriginalButton.setEnabled(True)

        if self.suppress_notfound_errors <= 1:
            self.splash.finish(self)

        if self.suppress_notfound_errors > 0:
            self.suppress_notfound_errors -= 1

        if album:
            self.album.setText(album)
        if date:
            self.date.setText(date)
        if artist:
            self.artist.setText(artist)
Exemplo n.º 4
0
class MainWin(QtGui.QDialog):
	def __init__(self, songPath, id3_action="", parent=None):
		super(MainWin, self).__init__(parent)
		self.isValid = True
		
		self.path = songPath
		self.id3_action = id3_action if id3_action else config.id3_action
		self.changed_AlbumArt = False
		self.pix_path = ""
		self.albumArt_task = "nothing"
		
		'''
		albumArt_task may be 'nothing', 'add' and 'delete':
		'nothing': Do not touch the APIC tag.
		'add': creates an APIC tag with self.pix_path.
		'delete': deletes an APIC tag, if exists.
		'''
		self.lyricsList = [] # The lyrics list
		self.lyricsListIndex = -1
		
		# Will be enabled when the last value of lyricsList will be fetched
		# and the servers will return EOF.
		# Until then, the lyrics groupbox will look like:
		# Lyrics (4/7+) (showing lyrics num. 4 out of seven, and more are available but not fetched yet)
		# When that switch will be enabled, the plus sign will get disappeared.
		self.lyricsList_EOF = False
		
		# If value is 0, notfound errors won't be suppressed. Any other value will suppress all errors.
		self.suppress_notfound_errors = 0
		
		self.setWindowTitle(tr("ID3 Editor"))
		self.resize(650, 430)
		self.setWindowIcon(QtGui.QIcon(r'pics\id3edit.png'))
		self.empty_cdbox_path = r"pics\empty_cdbox_%s.png" % config.lang if os.path.exists("pics\empty_cdbox_%s.png" % config.lang) else r"pics\empty_cdbox.png"
		
		log.debug("Initializing SetID3Window Widgets...")
		self.init_threads()
		self.init_id3data()
		if not self.isValid:
			return
		if not [x for x in self.easyID3Obj.values() if x[0]]: # If all values are actually blank, we shall attemp to parse it from the filename.
			fn = os.path.splitext(os.path.basename(self.path))[0]
			title, artist = utils.parse_title_from_filename(fn)
			self.easyID3Obj['title'] = [title]
			self.easyID3Obj['artist'] = [artist]
			
		self.init_widgets()
		
		if self.id3_action == 'ask_albumart':
			self.fetch_all(autochoose_albumart=False)
		if self.id3_action == 'ask':
			self.fetch_all(autochoose_albumart=True)
		if self.id3_action == 'noask':
			self.close_with_timeout()
			self.fetch_all(autochoose_albumart=True)
		
	def init_threads(self):
		self.thread = LyricsGrabberThread()
		self.thread.MainResult.connect(self.slot_lyrics_main)
		self.thread.MinorResult.connect(self.slot_lyrics_minor)
		self.thread.eof.connect(self.slot_lyrics_eof)
		self.thread.error.connect(self.slot_error)
		
		self.id3fetch_thread = MusicBrainzFetchThread()
		self.id3fetch_thread.output.connect(self.slot_setFreeDBData)
		self.id3fetch_thread.error.connect(self.slot_error)
		
		self.googleImages_thread = GoogleImagesGrabberThread()
		self.googleImages_thread.output.connect(self.slot_googleImagesResult)
		
	def init_id3data(self):
		try: # Add ID3 Tags if does not exist
			mp3Obj = MP3(self.path, ID3=ID3)
			mp3Obj.add_tags()
			mp3Obj.save()
		except error:
			pass
		except HeaderNotFoundError:
			log.warning("This MP3 files seems to be faulty. Cannot edit it's ID3 data. (Error: HeaderNotFoundError)")
			QtGui.QMessageBox.critical(self, tr("Error"), tr("This MP3 files seems to be faulty. Cannot edit it's ID3 data."), QtGui.QMessageBox.Ok)
			self.isValid = False
			return
		try:
			self.easyID3Obj = EasyID3(self.path)
			self.ID3Obj = ID3(self.path)
		except ID3NoHeaderError:
			# That means mp3Obj.add_tags() didn't work for a reason.
			utils.appendDummyID3(self.path)
			self.easyID3Obj = EasyID3(self.path)
			self.ID3Obj = ID3(self.path)

		USLT_Tag = [x for x in self.ID3Obj.keys() if x.startswith('USLT')]
		self.originalLyrics = self.ID3Obj[USLT_Tag[0]].text if USLT_Tag else ""
		
		APIC_Tag = [x for x in self.ID3Obj.keys() if x.startswith('APIC')]
		if APIC_Tag:
			APIC_Tag = APIC_Tag[0]
			
			mime = self.ID3Obj[APIC_Tag].mime
			if mime == u'image/jpeg':
				self.pix_path = os.path.join(config.temp_dir, 'album_art.jpg')
			elif mime == u'image/png':
				self.pix_path = os.path.join(config.temp_dir, 'album_art.png')
			else:
				self.pix_path = os.path.join(config.temp_dir, 'album_art.pic')
			
			with open(self.pix_path, 'wb') as f:
				f.write(self.ID3Obj[APIC_Tag].data)
		
	def init_widgets(self):
		self.title = QtGui.QLineEdit(utils.fix_faulty_unicode(self.easyID3Obj['title'][0])) if 'title' in self.easyID3Obj.keys() else QtGui.QLineEdit("")
		self.artist = QtGui.QLineEdit(utils.fix_faulty_unicode(self.easyID3Obj['artist'][0])) if 'artist' in self.easyID3Obj.keys() else QtGui.QLineEdit("")
		self.album = QtGui.QLineEdit(utils.fix_faulty_unicode(self.easyID3Obj['album'][0])) if 'album' in self.easyID3Obj.keys() else QtGui.QLineEdit("")
		self.date = QtGui.QLineEdit(self.easyID3Obj['date'][0]) if 'date' in self.easyID3Obj.keys() else QtGui.QLineEdit("")
		
		self.original_title = unicode(self.title.displayText())
		self.original_artist = unicode(self.artist.displayText())
		self.original_album = unicode(self.album.displayText())
		self.original_date = unicode(self.date.displayText())
		
		pixmap = QtGui.QPixmap(self.pix_path) if self.pix_path else QtGui.QPixmap(self.empty_cdbox_path)
		
		if self.pix_path:
			fs = os.path.getsize(self.pix_path)
			s = tr("(%sx%s, %sKB)") % (pixmap.width(), pixmap.height(), fs/1024)
			if fs > 250*1024: # 250KB
				s = '<b>%s</b>' % s
			if fs > 500*1024: # 250KB
				s = '<font color="green">%s</font>' % s
			self.albumArtData = QtGui.QLabel(s)
		else:
			self.albumArtData = QtGui.QLabel('')
		
		if max(pixmap.width(), pixmap.height()) > 200:
			pixmap = pixmap.scaled(200, 200, aspectRatioMode=QtCore.Qt.KeepAspectRatio, transformMode=QtCore.Qt.SmoothTransformation)

		self.albumArtLayout = QtGui.QVBoxLayout()
		self.albumArt = QtGui.QLabel()
		self.albumArt.setPixmap(pixmap)
		self.albumArt.setStyleSheet("border-style:solid; border-color:#999792; border-width:1px;")
		self.albumArtLayout.addWidget(self.albumArt, alignment=QtCore.Qt.AlignCenter)
		self.albumArtLayout.addWidget(self.albumArtData, alignment=QtCore.Qt.AlignCenter)
		
		self.remove_albumart_button = QtGui.QPushButton(QtGui.QIcon(r'pics\cancel.png'), "")
		self.remove_albumart_button.clicked.connect(self.remove_albumart_slot)
		self.remove_albumart_button.setFlat(True)
		if not self.pix_path:
			self.remove_albumart_button.setEnabled(False)

		# Row Layouts
		buttonLayout = QtGui.QHBoxLayout()
		applyButton = QtGui.QPushButton(tr("Apply"))
		applyButton.clicked.connect(self.slot_apply)
		closeButton = QtGui.QPushButton(tr("Dismiss"))
		closeButton.clicked.connect(self.close)
		buttonLayout.addWidget(applyButton)
		buttonLayout.addWidget(closeButton)
		
		self.changeImageButton = QtGui.QPushButton(tr("Fetch From The Web"))
		self.changeImageButton.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
		self.changeImageButton.clicked.connect(self.slot_changeImage)
		self.loadLocalImageButton = QtGui.QPushButton(tr("Choose Image Locally"))
		self.loadLocalImageButton.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
		self.loadLocalImageButton.clicked.connect(self.slot_loadLocalImage)
		self.loadFreeDBButton = QtGui.QPushButton(tr("Load From MusicBrainz"))
		self.loadFreeDBButton.clicked.connect(self.slot_loadFreeDBButton)
		self.restoreOriginalButton = QtGui.QPushButton(tr("Restore Original"))
		self.restoreOriginalButton.clicked.connect(self.slot_restoreOriginal)

		# Layouts
		mainLayout = QtGui.QVBoxLayout()
		formLayout = QtGui.QHBoxLayout()
		
		### RIGHT ###
		rightLayout = QtGui.QVBoxLayout()
		
		basicTagsGroup = QtGui.QGroupBox(tr("ID3 Tags"))
		layout = QtGui.QFormLayout()
		layout.addRow(tr("Title:"), self.title)
		layout.addRow(tr("Artist:"), self.artist)
		layout.addRow(tr("Album:"), self.album)
		layout.addRow(tr("Date:"), self.date)
		buttons = QtGui.QHBoxLayout()
		buttons.addWidget(self.loadFreeDBButton)
		buttons.addWidget(self.restoreOriginalButton)
		layout.addRow(buttons)
		basicTagsGroup.setLayout(layout)
		
		albumArtGroup = QtGui.QGroupBox(tr("Album Art"))
		layout = QtGui.QGridLayout()
		layout.addLayout(self.albumArtLayout, 0, 0, 1, 3, QtCore.Qt.AlignCenter)
		layout.addWidget(self.changeImageButton, 1, 0)
		layout.addWidget(self.loadLocalImageButton, 1, 1)
		layout.addWidget(self.remove_albumart_button, 1, 2)
		albumArtGroup.setLayout(layout)
		
		rightLayout.addWidget(basicTagsGroup)
		rightLayout.addWidget(albumArtGroup)
		
		### LEFT ###
		self.changeLyricsButton = QtGui.QPushButton(tr("Load Lyrics From The Web"))
		self.changeLyricsButton.clicked.connect(self.slot_changeLyrics)
		self.changeLyricsButton.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
		self.deleteLyricsButton = QtGui.QPushButton(QtGui.QIcon(r'pics\cancel.png'), "")
		self.deleteLyricsButton.setFlat(True)
		self.deleteLyricsButton.clicked.connect(self.slot_deleteLyrics)
		
		self.lyrics_left_button = QtGui.QPushButton("<<")
		self.lyrics_left_button.setEnabled(False)
		self.lyrics_left_button.clicked.connect(self.slot_lyrics_left_button)
		self.lyrics_right_button = QtGui.QPushButton(">>")
		self.lyrics_right_button.setEnabled(False)
		self.lyrics_right_button.clicked.connect(self.slot_lyrics_right_button)
		self.changeLyricsLabel = QtGui.QLabel(tr("* Loaded from file's tags *")) if self.originalLyrics else QtGui.QLabel("")
		self.changeLyricsLabel.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Sunken)
		self.changeLyricsLabel.setWordWrap(True)
		
		self.lyrics = QtGui.QPlainTextEdit(self.originalLyrics)
		leftLayout = QtGui.QGroupBox(tr("Lyrics"))
		self.lyricsGroupBox = leftLayout # For manipulating the title name
		layout = QtGui.QVBoxLayout()
		layout.addWidget(self.lyrics)
		inner_layout = QtGui.QHBoxLayout()
		inner_layout.addWidget(self.lyrics_left_button)
		inner_layout.addWidget(self.changeLyricsLabel)
		inner_layout.addWidget(self.lyrics_right_button)
		layout.addLayout(inner_layout)
		inner_layout = QtGui.QHBoxLayout()
		inner_layout.addWidget(self.changeLyricsButton)
		inner_layout.addWidget(self.deleteLyricsButton, alignment=QtCore.Qt.AlignRight)
		layout.addLayout(inner_layout)
		leftLayout.setLayout(layout)
		
		### Exit Counter Layout ###
		exitCounterLayout = QtGui.QHBoxLayout()
		self.exitCounterLabel = QtGui.QLabel(tr("<h1>Closing window in %d seconds...</h1>") % (config.id3_noask_wait_interval+1))
		self.exitCounterButton = QtGui.QPushButton(tr("Don't close"))
		self.exitCounterButton.clicked.connect(self.slot_close_cancel)
		exitCounterLayout.addWidget(self.exitCounterLabel)
		exitCounterLayout.addWidget(self.exitCounterButton)
		
		### OTHERS ###
		formLayout.addLayout(rightLayout)
		formLayout.addWidget(leftLayout)
		mainLayout.addLayout(formLayout)
		if self.id3_action == 'noask':
			mainLayout.addLayout(exitCounterLayout)
		mainLayout.addLayout(buttonLayout)
		self.setLayout(mainLayout)
	
	def fetch_all(self, autochoose_albumart):
		title = unicode(self.title.displayText())
		artist = unicode(self.artist.displayText())
		
		if title or artist:
			self.suppress_notfound_errors = 3
			self.slot_changeImage(autochoose_albumart)
			self.slot_changeLyrics()
			self.slot_loadFreeDBButton()
	
	def close_with_timeout(self):
		"Function shows a message to the user for 5 seconds, then closes window."
		self.close_waitthreads_calls = 0
		self.close_timer = QtCore.QTimer()
		self.close_timer.timeout.connect(self.close_waitthreads)
		self.close_timer.start(1000)

	def close_waitthreads(self):
		sec_to_wait = config.id3_noask_wait_interval
		
		if sec_to_wait-self.close_waitthreads_calls > 0:
			self.exitCounterLabel.setText(tr("<h1>Closing window in %d seconds...</h1>") % (sec_to_wait-self.close_waitthreads_calls))
		else:
			self.exitCounterLabel.setText(tr("<h1>Closing Window...</h1>"))
			if not any([self.thread.isRunning(), self.id3fetch_thread.isRunning(), self.googleImages_thread.isRunning()]):
				self.close_timer.stop()
				self.slot_apply()
		
		self.close_waitthreads_calls += 1
	
	def slot_close_cancel(self):
		if hasattr(self, 'close_timer'):
			self.close_timer.stop()
			self.exitCounterLabel.setText(tr("<h1>Automatic Closing Canceled.</h1>"))
			self.exitCounterButton.setEnabled(False)
		
	def slot_error(self, s):
		if hasattr(self, 'splash') and self.suppress_notfound_errors <= 1:
			self.splash.finish(self)
		if self.suppress_notfound_errors == 0:
			QtGui.QMessageBox.warning(self, tr("Error"), "<h2>%s</h2>" % s, QtGui.QMessageBox.Ok)
		else:
			self.suppress_notfound_errors -= 1
			
		self.changeLyricsButton.setEnabled(True)
		self.changeImageButton.setEnabled(True)
		self.loadFreeDBButton.setEnabled(True)
		self.restoreOriginalButton.setEnabled(True)
	
	def slot_apply(self):
		self.ID3TagsToEdit = {}
		log.debug("Saving ID3 Data in cache...")

		self.ID3TagsToEdit['TIT2'] = TIT2(encoding=3, text=unicode(self.title.displayText()))
		self.ID3TagsToEdit['TPE1'] = TPE1(encoding=3, text=unicode(self.artist.displayText()))
		self.ID3TagsToEdit['TALB'] = TALB(encoding=3, text=unicode(self.album.displayText()))
		self.ID3TagsToEdit['TDRC'] = TDRC(encoding=3, text=unicode(self.date.displayText()))
		self.ID3TagsToEdit['COMM'] = COMM(encoding=3, text=unicode(config.id3tags_whitemark), desc='')
		lyrics = unicode(self.lyrics.toPlainText()).replace('\r\n','\n').replace('\n','\r\n')

		if lyrics:
			if utils.isHebrew(lyrics):
				self.ID3TagsToEdit['USLT'] = USLT(encoding=3, lang=u'heb', desc=u'', text=lyrics)
			else:
				self.ID3TagsToEdit['USLT'] = USLT(encoding=3, lang=u'eng', desc=u'', text=lyrics)
		else:
			self.ID3TagsToEdit['USLT'] = ''

		if self.albumArt_task == 'add' and self.pix_path:
			with open(self.pix_path, 'rb') as f:
				data = f.read()
			self.ID3TagsToEdit['APIC'] = APIC(encoding=0, mime=utils.guess_image_mime_type(self.pix_path),
												type=0, desc="", data=data)
		elif self.albumArt_task == 'delete':
			self.ID3TagsToEdit['APIC'] = '' # empty value doesn't add a new APIC tag. All other APIC tags are removed piror to insertion.
		self.close()

	def slot_changeImage(self, autochoose=False):
		title = unicode(self.title.displayText())
		artist = unicode(self.artist.displayText())
		if title and artist:
			s = "%s - %s" % (artist, title)
		elif title:
			s = title
		elif artist:
			s = artist
		else:
			self.slot_error(tr("No title or artist values were given."))
			return
		self.last_search_string = s
			
		splash_movie = QtGui.QMovie(r'pics\loading_row.gif')
		self.splash = utils.qt.MovieSplashScreen(splash_movie, QtCore.Qt.WindowStaysOnTopHint)
		self.splash.show()
		
		self.changeImageButton.setEnabled(False)
		
		if autochoose:
			self.googleImages_thread.search(s, 1)
		else:
			self.googleImages_thread.search(s, 3)
			
	def slot_loadLocalImage(self):
		dialog = QtGui.QFileDialog()
		dialog.setFileMode(QtGui.QFileDialog.ExistingFile)
		f = unicode(dialog.getOpenFileName(caption=tr('Choose the artwork'), filter=tr("Images (*.png *.jpg)")))
		if f:
			self.pix_path = f
			pixmap = QtGui.QPixmap(self.pix_path)
			
			fs = os.path.getsize(self.pix_path)
			s = tr("(%sx%s, %sKB)") % (pixmap.width(), pixmap.height(), fs/1024)
			if fs > 250*1024: # 250KB
				s = '<b>%s</b>' % s
			if fs > 500*1024: # 250KB
				s = '<font color="green">%s</font>' % s
			self.albumArtData.setText(s)
			
			if max(pixmap.width(), pixmap.height()) > 200:
				pixmap = pixmap.scaled(200, 200, aspectRatioMode=QtCore.Qt.KeepAspectRatio, transformMode=QtCore.Qt.SmoothTransformation)
			self.albumArt.setPixmap(pixmap)
			self.albumArt_task = 'add'
			self.remove_albumart_button.setEnabled(True)
	
	def slot_googleImagesResult(self, fn_list):
		self.changeImageButton.setEnabled(True)
		
		if self.suppress_notfound_errors <= 1:
			self.splash.finish(self)
		
		if not fn_list and self.suppress_notfound_errors == 0:
			QtGui.QMessageBox.warning(self, "Error", tr("<h2>No photos were found.</h2>"), QtGui.QMessageBox.Ok)
			return
			
		if self.suppress_notfound_errors > 0:
			self.suppress_notfound_errors -= 1
			
		if len(fn_list) == 1:
			self.pix_path = fn_list[0]
		else:
			w = AlbumArtWin(fn_list, self.last_search_string)
			w.exec_()
			if hasattr(w, 'pix'):
				self.pix_path = w.pix
			if hasattr(w, 'search_string'):
				splash_movie = QtGui.QMovie(r'pics\loading_row.gif')
				self.splash = utils.qt.MovieSplashScreen(splash_movie, QtCore.Qt.WindowStaysOnTopHint)
				self.splash.show()
				
				self.last_search_string = w.search_string
				self.googleImages_thread.search(w.search_string, 3)
				return

		if self.pix_path:
			pixmap = QtGui.QPixmap(self.pix_path)
			
			fs = os.path.getsize(self.pix_path)
			s = tr("(%sx%s, %sKB)") % (pixmap.width(), pixmap.height(), fs/1024)
			if fs > 250*1024: # 250KB
				s = '<b>%s</b>' % s
			if fs > 500*1024: # 250KB
				s = '<font color="green">%s</font>' % s
			self.albumArtData.setText(s)
		
			if max(pixmap.width(), pixmap.height()) > 200:
				pixmap = pixmap.scaled(200, 200, aspectRatioMode=QtCore.Qt.KeepAspectRatio, transformMode=QtCore.Qt.SmoothTransformation)
			self.albumArt.setPixmap(pixmap)
			self.albumArt_task = 'add'
			self.remove_albumart_button.setEnabled(True)
	
	def slot_changeLyrics(self):
		title = unicode(self.title.displayText())
		artist = unicode(self.artist.displayText())
		
		if not title and not artist:
			self.slot_error(tr("No title or artist values were given."))
			return
		
		self.lyricsList = []
		self.lyricsListIndex = -1
		
		splash_movie = QtGui.QMovie(r'pics\loading_row.gif')
		self.splash = utils.qt.MovieSplashScreen(splash_movie, QtCore.Qt.WindowStaysOnTopHint)
		self.splash.show()
		
		self.changeLyricsButton.setEnabled(False)
		
		self.thread.search(title, artist)
		
	def slot_deleteLyrics(self):
		self.changeLyricsLabel.setText("")
		self.lyrics.setPlainText("")
	
	def remove_albumart_slot(self):
		self.albumArt_task = 'delete'
		fs = os.path.getsize(self.pix_path)
		self.albumArtData.setText("")
		self.pix_path = ""
		self.albumArt.setPixmap(QtGui.QPixmap(self.empty_cdbox_path))
		self.remove_albumart_button.setEnabled(False)
		log.debug('AlbumArt is marked for deletion.')
	
	def slot_lyrics_main(self, lyrics):
		self.changeLyricsButton.setEnabled(True)
		
		if self.suppress_notfound_errors <= 1:
			self.splash.finish(self)
		
		if self.suppress_notfound_errors > 0:
			self.suppress_notfound_errors -= 1
		
		self.lyricsList.append(lyrics)
		self.lyricsListIndex = len(self.lyricsList)-1
		if self.lyricsListIndex > 0:
			self.lyrics_left_button.setEnabled(True)
		
		# We're dealing with LyricsData object here
		self.lyrics.setPlainText(lyrics.lyrics)
		self.changeLyricsLabel.setText("%s - %s" % (lyrics.artist, lyrics.title))
		if self.lyricsList_EOF:
			self.lyricsGroupBox.setTitle(tr("Lyrics (%d/%d)") % (self.lyricsListIndex+1, len(self.lyricsList)))
		else:
			self.lyricsGroupBox.setTitle(tr("Lyrics (%d/%d+)") % (self.lyricsListIndex+1, len(self.lyricsList)))
		
	def slot_lyrics_minor(self, lyrics):
		self.changeLyricsButton.setEnabled(True)
		
		if self.suppress_notfound_errors <= 1:
			self.splash.finish(self)
			
		self.lyricsList.append(lyrics)
		self.lyrics_right_button.setEnabled(True)
		self.lyricsGroupBox.setTitle(tr("Lyrics (%d/%d+)") % (self.lyricsListIndex+1, len(self.lyricsList)))
	
	def slot_lyrics_eof(self):
		self.changeLyricsButton.setEnabled(True)
		
		if self.suppress_notfound_errors <= 1:
			self.splash.finish(self)
			
		self.lyricsList_EOF = True
		self.lyricsGroupBox.setTitle(tr("Lyrics") + " (%d/%d)" % (self.lyricsListIndex+1, len(self.lyricsList)))
		self.lyrics_right_button.setEnabled(False)
			
	def slot_lyrics_left_button(self):
		self.lyricsListIndex -= 1
		lyrics = self.lyricsList[self.lyricsListIndex]
		
		# We're dealing with LyricsData object here
		self.lyrics.setPlainText(lyrics.lyrics)
		self.changeLyricsLabel.setText("%s - %s" % (lyrics.artist, lyrics.title))
		if self.lyricsList_EOF:
			self.lyricsGroupBox.setTitle(tr("Lyrics") + " (%d/%d)" % (self.lyricsListIndex+1, len(self.lyricsList)))
		else:
			self.lyricsGroupBox.setTitle(tr("Lyrics") + " (%d/%d+)" % (self.lyricsListIndex+1, len(self.lyricsList)))
		
		self.lyrics_right_button.setEnabled(True)
		if self.lyricsListIndex == 0:
			self.lyrics_left_button.setEnabled(False)
	
	def slot_lyrics_right_button(self):
		self.lyricsListIndex += 1
		lyrics = self.lyricsList[self.lyricsListIndex]
		
		# We're dealing with LyricsData object here
		self.lyrics.setPlainText(lyrics.lyrics)
		self.changeLyricsLabel.setText("%s - %s" % (lyrics.artist, lyrics.title))
		if self.lyricsList_EOF:
			self.lyricsGroupBox.setTitle(tr("Lyrics") + " (%d/%d)" % (self.lyricsListIndex+1, len(self.lyricsList)))
		else:
			self.lyricsGroupBox.setTitle(tr("Lyrics") + " (%d/%d+)" % (self.lyricsListIndex+1, len(self.lyricsList)))
		
		self.lyrics_left_button.setEnabled(True)
		if self.lyricsListIndex == len(self.lyricsList)-1:
			self.thread.nextLyrics()
			self.lyrics_right_button.setEnabled(False)
		
	def slot_restoreOriginal(self):
		self.title.setText(self.original_title)
		self.artist.setText(self.original_artist)
		self.album.setText(self.original_album)
		self.date.setText(self.original_date)
	
	def slot_loadFreeDBButton(self):
		title = unicode(self.title.displayText())
		artist = unicode(self.artist.displayText())
		
		if not title and not artist:
			self.slot_error(tr("No title or artist values were given."))
			return
		
		splash_movie = QtGui.QMovie(r'pics\loading_row.gif')
		self.splash = utils.qt.MovieSplashScreen(splash_movie, QtCore.Qt.WindowStaysOnTopHint)
		self.splash.show()
		
		self.loadFreeDBButton.setEnabled(False)
		self.restoreOriginalButton.setEnabled(False)
		
		self.id3fetch_thread.fetch(title, artist)
		
	def slot_setFreeDBData(self, album, date, tag, artist):
		self.loadFreeDBButton.setEnabled(True)
		self.restoreOriginalButton.setEnabled(True)
		
		if self.suppress_notfound_errors <= 1:
			self.splash.finish(self)
		
		if self.suppress_notfound_errors > 0:
			self.suppress_notfound_errors -= 1
		
		if album:
			self.album.setText(album)
		if date:
			self.date.setText(date)
		if artist:
			self.artist.setText(artist)