2018-02-27 18:06:05 +01:00
/*
* Strawberry Music Player
* This file was part of Clementine .
* Copyright 2010 , David Sansome < me @ davidsansome . com >
2021-03-20 21:14:47 +01:00
* Copyright 2018 - 2021 , Jonas Kvinge < jonas @ jkvinge . net >
2018-02-27 18:06:05 +01:00
*
* Strawberry is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* Strawberry is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with Strawberry . If not , see < http : //www.gnu.org/licenses/>.
2018-08-09 18:39:44 +02:00
*
2018-02-27 18:06:05 +01:00
*/
# include "config.h"
2020-07-19 22:43:58 +02:00
# include <functional>
2018-05-01 00:41:33 +02:00
# include <algorithm>
# include <iterator>
2018-02-27 18:06:05 +01:00
# include <limits>
2018-05-01 00:41:33 +02:00
# include <QtGlobal>
2020-07-18 16:28:39 +02:00
# include <QtConcurrent>
2020-07-19 19:07:12 +02:00
# include <QFuture>
2021-01-30 21:53:53 +01:00
# include <QFutureWatcher>
2018-05-01 00:41:33 +02:00
# include <QObject>
# include <QWidget>
# include <QDialog>
# include <QItemSelectionModel>
# include <QAbstractItemModel>
# include <QDir>
# include <QAction>
# include <QDateTime>
# include <QList>
2021-02-27 01:02:37 +01:00
# include <QMap>
2018-05-01 00:41:33 +02:00
# include <QVariant>
# include <QString>
# include <QStringBuilder>
# include <QUrl>
# include <QPixmap>
# include <QPalette>
# include <QColor>
# include <QFont>
2018-02-27 18:06:05 +01:00
# include <QLabel>
2018-05-01 00:41:33 +02:00
# include <QLineEdit>
# include <QListWidget>
# include <QLocale>
2018-02-27 18:06:05 +01:00
# include <QMenu>
# include <QMessageBox>
# include <QShortcut>
2018-05-01 00:41:33 +02:00
# include <QSize>
# include <QSpinBox>
2020-09-23 00:52:41 +02:00
# include <QCheckBox>
2018-05-01 00:41:33 +02:00
# include <QSplitter>
# include <QTabWidget>
# include <QTextEdit>
# include <QPlainTextEdit>
# include <QKeySequence>
# include <QDialogButtonBox>
# include <QPushButton>
# include <QAbstractButton>
# include <QtEvents>
# include <QSettings>
2018-02-27 18:06:05 +01:00
# include <QtDebug>
# include "core/application.h"
2018-05-01 00:41:33 +02:00
# include "core/iconloader.h"
2018-02-27 18:06:05 +01:00
# include "core/logging.h"
# include "core/tagreaderclient.h"
# include "core/utilities.h"
2021-02-26 21:03:51 +01:00
# include "core/imageutils.h"
2018-05-01 00:41:33 +02:00
# include "widgets/busyindicator.h"
# include "widgets/lineedit.h"
2018-02-27 18:06:05 +01:00
# include "collection/collectionbackend.h"
2018-05-01 00:41:33 +02:00
# include "playlist/playlist.h"
2018-02-27 18:06:05 +01:00
# include "playlist/playlistdelegates.h"
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2018-07-16 07:23:37 +02:00
# include "musicbrainz / tagfetcher.h"
2021-02-26 21:03:51 +01:00
# include "trackselectiondialog.h"
2018-07-16 07:23:37 +02:00
# endif
2018-05-01 00:41:33 +02:00
# include "covermanager/albumcoverchoicecontroller.h"
2018-02-27 18:06:05 +01:00
# include "covermanager/albumcoverloader.h"
2020-04-20 18:03:18 +02:00
# include "covermanager/albumcoverloaderoptions.h"
# include "covermanager/albumcoverloaderresult.h"
2018-02-27 18:06:05 +01:00
# include "covermanager/coverproviders.h"
2021-02-26 21:03:51 +01:00
# include "covermanager/currentalbumcoverloader.h"
# include "covermanager/albumcoverimageresult.h"
2018-05-01 00:41:33 +02:00
# include "edittagdialog.h"
# include "ui_edittagdialog.h"
2020-02-09 02:29:35 +01:00
# include "tagreadermessages.pb.h"
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
const char * EditTagDialog : : kTagsDifferentHintText = QT_TR_NOOP ( " (different across multiple songs) " ) ;
const char * EditTagDialog : : kArtDifferentHintText = QT_TR_NOOP ( " Different art across multiple songs. " ) ;
2018-02-27 18:06:05 +01:00
const char * EditTagDialog : : kSettingsGroup = " EditTagDialog " ;
EditTagDialog : : EditTagDialog ( Application * app , QWidget * parent )
: QDialog ( parent ) ,
ui_ ( new Ui_EditTagDialog ) ,
app_ ( app ) ,
album_cover_choice_controller_ ( new AlbumCoverChoiceController ( this ) ) ,
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2018-02-27 18:06:05 +01:00
tag_fetcher_ ( new TagFetcher ( this ) ) ,
2021-02-26 21:03:51 +01:00
results_dialog_ ( new TrackSelectionDialog ( this ) ) ,
2018-02-27 18:06:05 +01:00
# endif
2021-03-13 01:52:37 +01:00
image_no_cover_thumbnail_ ( ImageUtils : : GenerateNoCoverImage ( QSize ( 128 , 128 ) ) ) ,
2021-02-26 21:03:51 +01:00
loading_ ( false ) ,
ignore_edits_ ( false ) ,
summary_cover_art_id_ ( - 1 ) ,
tags_cover_art_id_ ( - 1 ) ,
2018-02-27 18:06:05 +01:00
cover_art_is_set_ ( false ) ,
2021-02-26 21:03:51 +01:00
save_tag_pending_ ( 0 ) ,
2021-07-11 07:40:57 +02:00
save_art_pending_ ( 0 ) {
2018-02-27 18:06:05 +01:00
2021-01-26 16:48:04 +01:00
QObject : : connect ( app_ - > album_cover_loader ( ) , & AlbumCoverLoader : : AlbumCoverLoaded , this , & EditTagDialog : : AlbumCoverLoaded ) ;
2018-02-27 18:06:05 +01:00
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2021-01-26 16:48:04 +01:00
QObject : : connect ( tag_fetcher_ , & TagFetcher : : ResultAvailable , results_dialog_ , & TrackSelectionDialog : : FetchTagFinished , Qt : : QueuedConnection ) ;
QObject : : connect ( tag_fetcher_ , & TagFetcher : : Progress , results_dialog_ , & TrackSelectionDialog : : FetchTagProgress ) ;
QObject : : connect ( results_dialog_ , & TrackSelectionDialog : : SongChosen , this , & EditTagDialog : : FetchTagSongChosen ) ;
QObject : : connect ( results_dialog_ , & TrackSelectionDialog : : finished , tag_fetcher_ , & TagFetcher : : Cancel ) ;
2018-02-27 18:06:05 +01:00
# endif
2019-07-07 21:14:24 +02:00
album_cover_choice_controller_ - > Init ( app_ ) ;
2018-02-27 18:06:05 +01:00
ui_ - > setupUi ( this ) ;
ui_ - > splitter - > setSizes ( QList < int > ( ) < < 200 < < width ( ) - 200 ) ;
ui_ - > loading_label - > hide ( ) ;
2021-02-26 21:03:51 +01:00
ui_ - > label_lyrics - > hide ( ) ;
2018-02-27 18:06:05 +01:00
ui_ - > fetch_tag - > setIcon ( QPixmap : : fromImage ( QImage ( " :/pictures/musicbrainz.png " ) ) ) ;
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2018-07-16 07:23:37 +02:00
ui_ - > fetch_tag - > setEnabled ( true ) ;
# else
ui_ - > fetch_tag - > setEnabled ( false ) ;
# endif
2018-02-27 18:06:05 +01:00
2019-02-20 21:27:53 +01:00
// An editable field is one that has a label as a buddy.
// The label is important because it gets turned bold when the field is changed.
2018-02-27 18:06:05 +01:00
for ( QLabel * label : findChildren < QLabel * > ( ) ) {
QWidget * widget = label - > buddy ( ) ;
if ( widget ) {
// Store information about the field
2021-06-20 19:04:08 +02:00
fields_ < < FieldData ( label , widget , widget - > objectName ( ) ) ; // clazy:exclude=reserve-candidates
2018-02-27 18:06:05 +01:00
// Connect the edited signal
2021-01-26 16:48:04 +01:00
if ( LineEdit * lineedit = qobject_cast < LineEdit * > ( widget ) ) {
QObject : : connect ( lineedit , & LineEdit : : textChanged , this , & EditTagDialog : : FieldValueEdited ) ;
QObject : : connect ( lineedit , & LineEdit : : Reset , this , & EditTagDialog : : ResetField ) ;
2018-02-27 18:06:05 +01:00
}
2021-01-26 16:48:04 +01:00
else if ( TextEdit * textedit = qobject_cast < TextEdit * > ( widget ) ) {
QObject : : connect ( textedit , & TextEdit : : textChanged , this , & EditTagDialog : : FieldValueEdited ) ;
QObject : : connect ( textedit , & TextEdit : : Reset , this , & EditTagDialog : : ResetField ) ;
2018-02-27 18:06:05 +01:00
}
2021-01-26 16:48:04 +01:00
else if ( SpinBox * spinbox = qobject_cast < SpinBox * > ( widget ) ) {
QObject : : connect ( spinbox , QOverload < int > : : of ( & SpinBox : : valueChanged ) , this , & EditTagDialog : : FieldValueEdited ) ;
QObject : : connect ( spinbox , & SpinBox : : Reset , this , & EditTagDialog : : ResetField ) ;
2018-02-27 18:06:05 +01:00
}
2021-01-26 16:48:04 +01:00
else if ( CheckBox * checkbox = qobject_cast < CheckBox * > ( widget ) ) {
QObject : : connect ( checkbox , & QCheckBox : : stateChanged , this , & EditTagDialog : : FieldValueEdited ) ;
QObject : : connect ( checkbox , & CheckBox : : Reset , this , & EditTagDialog : : ResetField ) ;
2020-09-23 00:52:41 +02:00
}
2021-10-24 16:08:17 +02:00
else if ( RatingBox * ratingbox = qobject_cast < RatingBox * > ( widget ) ) {
QObject : : connect ( ratingbox , & RatingWidget : : RatingChanged , this , & EditTagDialog : : FieldValueEdited ) ;
}
2018-02-27 18:06:05 +01:00
}
}
// Set the colour of all the labels on the summary page
const bool light = palette ( ) . color ( QPalette : : Base ) . value ( ) > 128 ;
const QColor color = palette ( ) . color ( QPalette : : WindowText ) ;
QPalette summary_label_palette ( palette ( ) ) ;
summary_label_palette . setColor ( QPalette : : WindowText , light ? color . lighter ( 150 ) : color . darker ( 150 ) ) ;
2021-02-26 21:03:51 +01:00
for ( QLabel * label : ui_ - > tab_summary - > findChildren < QLabel * > ( ) ) {
2018-02-27 18:06:05 +01:00
if ( label - > property ( " field_label " ) . toBool ( ) ) {
label - > setPalette ( summary_label_palette ) ;
}
}
2021-01-26 16:48:04 +01:00
QObject : : connect ( ui_ - > song_list - > selectionModel ( ) , & QItemSelectionModel : : selectionChanged , this , & EditTagDialog : : SelectionChanged ) ;
QObject : : connect ( ui_ - > button_box , & QDialogButtonBox : : clicked , this , & EditTagDialog : : ButtonClicked ) ;
QObject : : connect ( ui_ - > playcount_reset , & QPushButton : : clicked , this , & EditTagDialog : : ResetPlayCounts ) ;
2021-10-24 16:08:17 +02:00
QObject : : connect ( ui_ - > rating , & RatingWidget : : RatingChanged , this , & EditTagDialog : : SongRated ) ;
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2021-01-26 16:48:04 +01:00
QObject : : connect ( ui_ - > fetch_tag , & QPushButton : : clicked , this , & EditTagDialog : : FetchTag ) ;
2018-07-16 07:23:37 +02:00
# endif
2018-02-27 18:06:05 +01:00
// Set up the album cover menu
cover_menu_ = new QMenu ( this ) ;
QList < QAction * > actions = album_cover_choice_controller_ - > GetAllActions ( ) ;
2021-09-19 19:31:34 +02:00
QObject : : connect ( album_cover_choice_controller_ , & AlbumCoverChoiceController : : Error , this , & EditTagDialog : : Error ) ;
2021-01-26 16:48:04 +01:00
QObject : : connect ( album_cover_choice_controller_ - > cover_from_file_action ( ) , & QAction : : triggered , this , & EditTagDialog : : LoadCoverFromFile ) ;
QObject : : connect ( album_cover_choice_controller_ - > cover_to_file_action ( ) , & QAction : : triggered , this , & EditTagDialog : : SaveCoverToFile ) ;
QObject : : connect ( album_cover_choice_controller_ - > cover_from_url_action ( ) , & QAction : : triggered , this , & EditTagDialog : : LoadCoverFromURL ) ;
QObject : : connect ( album_cover_choice_controller_ - > search_for_cover_action ( ) , & QAction : : triggered , this , & EditTagDialog : : SearchForCover ) ;
QObject : : connect ( album_cover_choice_controller_ - > unset_cover_action ( ) , & QAction : : triggered , this , & EditTagDialog : : UnsetCover ) ;
2021-02-26 21:03:51 +01:00
QObject : : connect ( album_cover_choice_controller_ - > clear_cover_action ( ) , & QAction : : triggered , this , & EditTagDialog : : ClearCover ) ;
QObject : : connect ( album_cover_choice_controller_ - > delete_cover_action ( ) , & QAction : : triggered , this , & EditTagDialog : : DeleteCover ) ;
2021-01-26 16:48:04 +01:00
QObject : : connect ( album_cover_choice_controller_ - > show_cover_action ( ) , & QAction : : triggered , this , & EditTagDialog : : ShowCover ) ;
2021-02-27 01:32:45 +01:00
QObject : : connect ( ui_ - > checkbox_embedded_cover , & QCheckBox : : toggled , album_cover_choice_controller_ , & AlbumCoverChoiceController : : set_save_embedded_cover_override ) ;
2018-02-27 18:06:05 +01:00
cover_menu_ - > addActions ( actions ) ;
2021-02-26 21:03:51 +01:00
ui_ - > tags_art_button - > setMenu ( cover_menu_ ) ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
ui_ - > tags_art - > installEventFilter ( this ) ;
ui_ - > tags_art - > setAcceptDrops ( true ) ;
2018-02-27 18:06:05 +01:00
2021-03-07 23:23:58 +01:00
ui_ - > summary_art - > installEventFilter ( this ) ;
2018-02-27 18:06:05 +01:00
// Add the next/previous buttons
previous_button_ = new QPushButton ( IconLoader : : Load ( " go-previous " ) , tr ( " Previous " ) , this ) ;
next_button_ = new QPushButton ( IconLoader : : Load ( " go-next " ) , tr ( " Next " ) , this ) ;
ui_ - > button_box - > addButton ( previous_button_ , QDialogButtonBox : : ResetRole ) ;
ui_ - > button_box - > addButton ( next_button_ , QDialogButtonBox : : ResetRole ) ;
2021-01-26 16:48:04 +01:00
QObject : : connect ( previous_button_ , & QPushButton : : clicked , this , & EditTagDialog : : PreviousSong ) ;
QObject : : connect ( next_button_ , & QPushButton : : clicked , this , & EditTagDialog : : NextSong ) ;
2018-02-27 18:06:05 +01:00
// Set some shortcuts for the buttons
new QShortcut ( QKeySequence : : Back , previous_button_ , SLOT ( click ( ) ) ) ;
new QShortcut ( QKeySequence : : Forward , next_button_ , SLOT ( click ( ) ) ) ;
new QShortcut ( QKeySequence : : MoveToPreviousPage , previous_button_ , SLOT ( click ( ) ) ) ;
new QShortcut ( QKeySequence : : MoveToNextPage , next_button_ , SLOT ( click ( ) ) ) ;
// Show the shortcuts as tooltips
previous_button_ - > setToolTip ( QString ( " %1 (%2 / %3) " ) . arg (
previous_button_ - > text ( ) ,
QKeySequence ( QKeySequence : : Back ) . toString ( QKeySequence : : NativeText ) ,
QKeySequence ( QKeySequence : : MoveToPreviousPage ) . toString ( QKeySequence : : NativeText ) ) ) ;
next_button_ - > setToolTip ( QString ( " %1 (%2 / %3) " ) . arg (
next_button_ - > text ( ) ,
QKeySequence ( QKeySequence : : Forward ) . toString ( QKeySequence : : NativeText ) ,
QKeySequence ( QKeySequence : : MoveToNextPage ) . toString ( QKeySequence : : NativeText ) ) ) ;
new TagCompleter ( app_ - > collection_backend ( ) , Playlist : : Column_Artist , ui_ - > artist ) ;
new TagCompleter ( app_ - > collection_backend ( ) , Playlist : : Column_Album , ui_ - > album ) ;
new TagCompleter ( app_ - > collection_backend ( ) , Playlist : : Column_AlbumArtist , ui_ - > albumartist ) ;
new TagCompleter ( app_ - > collection_backend ( ) , Playlist : : Column_Genre , ui_ - > genre ) ;
new TagCompleter ( app_ - > collection_backend ( ) , Playlist : : Column_Composer , ui_ - > composer ) ;
new TagCompleter ( app_ - > collection_backend ( ) , Playlist : : Column_Performer , ui_ - > performer ) ;
new TagCompleter ( app_ - > collection_backend ( ) , Playlist : : Column_Grouping , ui_ - > grouping ) ;
2018-10-02 00:38:52 +02:00
2021-03-13 01:52:37 +01:00
cover_options_ . get_image_data_ = true ;
cover_options_ . get_image_ = true ;
cover_options_ . scale_output_image_ = true ;
cover_options_ . desired_height_ = 128 ;
2018-02-27 18:06:05 +01:00
}
EditTagDialog : : ~ EditTagDialog ( ) {
delete ui_ ;
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : showEvent ( QShowEvent * e ) {
if ( ! e - > spontaneous ( ) ) {
// Set the dialog's height to the smallest possible
resize ( width ( ) , sizeHint ( ) . height ( ) ) ;
// Restore the tab that was current last time.
QSettings s ;
s . beginGroup ( kSettingsGroup ) ;
if ( s . contains ( " geometry " ) ) {
restoreGeometry ( s . value ( " geometry " ) . toByteArray ( ) ) ;
}
ui_ - > tab_widget - > setCurrentIndex ( s . value ( " current_tab " ) . toInt ( ) ) ;
s . endGroup ( ) ;
album_cover_choice_controller_ - > ReloadSettings ( ) ;
}
QDialog : : showEvent ( e ) ;
}
void EditTagDialog : : hideEvent ( QHideEvent * e ) {
// Save the current tab
QSettings s ;
s . beginGroup ( kSettingsGroup ) ;
s . setValue ( " geometry " , saveGeometry ( ) ) ;
s . setValue ( " current_tab " , ui_ - > tab_widget - > currentIndex ( ) ) ;
s . endGroup ( ) ;
QDialog : : hideEvent ( e ) ;
}
void EditTagDialog : : accept ( ) {
// Show the loading indicator
if ( ! SetLoading ( tr ( " Saving tracks " ) + " ... " ) ) return ;
SaveData ( ) ;
}
bool EditTagDialog : : eventFilter ( QObject * o , QEvent * e ) {
if ( o = = ui_ - > tags_art ) {
switch ( e - > type ( ) ) {
2021-03-07 23:23:58 +01:00
case QEvent : : MouseButtonRelease : {
QMouseEvent * mouse_event = static_cast < QMouseEvent * > ( e ) ;
if ( mouse_event & & mouse_event - > button ( ) = = Qt : : RightButton ) {
2021-02-26 21:03:51 +01:00
# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
2021-03-07 23:23:58 +01:00
cover_menu_ - > popup ( mouse_event - > globalPosition ( ) . toPoint ( ) ) ;
2021-02-26 21:03:51 +01:00
# else
2021-03-07 23:26:08 +01:00
cover_menu_ - > popup ( mouse_event - > globalPos ( ) ) ;
2021-02-26 21:03:51 +01:00
# endif
2021-03-07 23:23:58 +01:00
}
break ;
}
case QEvent : : MouseButtonDblClick :
ShowCover ( ) ;
2021-02-26 21:03:51 +01:00
break ;
case QEvent : : DragEnter : {
QDragEnterEvent * event = static_cast < QDragEnterEvent * > ( e ) ;
if ( AlbumCoverChoiceController : : CanAcceptDrag ( event ) ) {
event - > acceptProposedAction ( ) ;
}
break ;
}
case QEvent : : Drop : {
const QDropEvent * event = static_cast < QDropEvent * > ( e ) ;
if ( event - > mimeData ( ) - > hasImage ( ) ) {
QImage image = qvariant_cast < QImage > ( event - > mimeData ( ) - > imageData ( ) ) ;
if ( ! image . isNull ( ) ) {
UpdateCover ( UpdateCoverAction_New , AlbumCoverImageResult ( image ) ) ;
}
}
break ;
}
default :
break ;
}
}
2021-03-07 23:23:58 +01:00
if ( o = = ui_ - > summary_art ) {
switch ( e - > type ( ) ) {
case QEvent : : MouseButtonDblClick :
ShowCover ( ) ;
break ;
default :
break ;
}
}
2021-06-20 19:04:08 +02:00
return QDialog : : eventFilter ( o , e ) ;
2021-02-26 21:03:51 +01:00
}
2018-02-27 18:06:05 +01:00
bool EditTagDialog : : SetLoading ( const QString & message ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
const bool loading = ! message . isEmpty ( ) ;
if ( loading = = loading_ ) return false ;
loading_ = loading ;
ui_ - > button_box - > setEnabled ( ! loading ) ;
ui_ - > tab_widget - > setEnabled ( ! loading ) ;
ui_ - > song_list - > setEnabled ( ! loading ) ;
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2018-02-27 18:06:05 +01:00
ui_ - > fetch_tag - > setEnabled ( ! loading ) ;
2018-07-16 07:23:37 +02:00
# endif
2018-02-27 18:06:05 +01:00
ui_ - > loading_label - > setVisible ( loading ) ;
ui_ - > loading_label - > set_text ( message ) ;
2021-02-26 21:03:51 +01:00
2018-02-27 18:06:05 +01:00
return true ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
2021-06-22 13:41:38 +02:00
QList < EditTagDialog : : Data > EditTagDialog : : LoadData ( const SongList & songs ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
QList < Data > ret ;
for ( const Song & song : songs ) {
if ( song . IsEditable ( ) ) {
// Try reloading the tags from file
Song copy ( song ) ;
TagReaderClient : : Instance ( ) - > ReadFileBlocking ( copy . url ( ) . toLocalFile ( ) , & copy ) ;
if ( copy . is_valid ( ) ) {
copy . MergeUserSetData ( song ) ;
ret < < Data ( copy ) ;
}
}
}
return ret ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : SetSongs ( const SongList & s , const PlaylistItemList & items ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
// Show the loading indicator
if ( ! SetLoading ( tr ( " Loading tracks " ) + " ... " ) ) return ;
data_ . clear ( ) ;
playlist_items_ = items ;
ui_ - > song_list - > clear ( ) ;
2021-02-26 21:03:51 +01:00
collection_songs_ . clear ( ) ;
2018-02-27 18:06:05 +01:00
// Reload tags in the background
2021-06-22 13:41:38 +02:00
QFuture < QList < Data > > future = QtConcurrent : : run ( & EditTagDialog : : LoadData , s ) ;
2021-01-30 21:53:53 +01:00
QFutureWatcher < QList < Data > > * watcher = new QFutureWatcher < QList < Data > > ( ) ;
QObject : : connect ( watcher , & QFutureWatcher < QList < Data > > : : finished , this , & EditTagDialog : : SetSongsFinished ) ;
2021-06-16 00:30:21 +02:00
watcher - > setFuture ( future ) ;
2019-02-20 21:27:53 +01:00
2018-02-27 18:06:05 +01:00
}
2021-01-30 21:53:53 +01:00
void EditTagDialog : : SetSongsFinished ( ) {
2018-02-27 18:06:05 +01:00
2021-01-30 21:53:53 +01:00
QFutureWatcher < QList < Data > > * watcher = static_cast < QFutureWatcher < QList < Data > > * > ( sender ( ) ) ;
QList < Data > result_data = watcher - > result ( ) ;
watcher - > deleteLater ( ) ;
2021-02-26 21:03:51 +01:00
if ( ! SetLoading ( QString ( ) ) ) return ;
2021-01-30 21:53:53 +01:00
data_ = result_data ;
2018-02-27 18:06:05 +01:00
if ( data_ . count ( ) = = 0 ) {
// If there were no valid songs, disable everything
ui_ - > song_list - > setEnabled ( false ) ;
ui_ - > tab_widget - > setEnabled ( false ) ;
// Show a summary with empty information
2021-02-26 21:03:51 +01:00
UpdateSummaryTab ( Song ( ) , UpdateCoverAction_None ) ;
ui_ - > tab_widget - > setCurrentWidget ( ui_ - > tab_summary ) ;
2018-02-27 18:06:05 +01:00
SetSongListVisibility ( false ) ;
return ;
}
// Add the filenames to the list
2020-04-23 21:08:28 +02:00
for ( const Data & tag_data : data_ ) {
ui_ - > song_list - > addItem ( tag_data . current_ . basefilename ( ) ) ;
2018-02-27 18:06:05 +01:00
}
// Select all
ui_ - > song_list - > setCurrentRow ( 0 ) ;
ui_ - > song_list - > selectAll ( ) ;
// Hide the list if there's only one song in it
SetSongListVisibility ( data_ . count ( ) ! = 1 ) ;
2019-02-20 21:27:53 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : SetSongListVisibility ( bool visible ) {
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
ui_ - > song_list - > setVisible ( visible ) ;
previous_button_ - > setEnabled ( visible ) ;
next_button_ - > setEnabled ( visible ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
QVariant EditTagDialog : : Data : : value ( const Song & song , const QString & id ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
if ( id = = " title " ) return song . title ( ) ;
if ( id = = " artist " ) return song . artist ( ) ;
if ( id = = " album " ) return song . album ( ) ;
if ( id = = " albumartist " ) return song . albumartist ( ) ;
if ( id = = " composer " ) return song . composer ( ) ;
if ( id = = " performer " ) return song . performer ( ) ;
if ( id = = " grouping " ) return song . grouping ( ) ;
if ( id = = " genre " ) return song . genre ( ) ;
if ( id = = " comment " ) return song . comment ( ) ;
2019-07-08 22:21:12 +02:00
if ( id = = " lyrics " ) return song . lyrics ( ) ;
2018-02-27 18:06:05 +01:00
if ( id = = " track " ) return song . track ( ) ;
if ( id = = " disc " ) return song . disc ( ) ;
if ( id = = " year " ) return song . year ( ) ;
2020-09-23 00:52:41 +02:00
if ( id = = " compilation " ) return song . compilation ( ) ;
2021-10-24 16:08:17 +02:00
if ( id = = " rating " ) { return song . rating ( ) ; }
2018-02-27 18:06:05 +01:00
qLog ( Warning ) < < " Unknown ID " < < id ;
return QVariant ( ) ;
}
void EditTagDialog : : Data : : set_value ( const QString & id , const QVariant & value ) {
if ( id = = " title " ) current_ . set_title ( value . toString ( ) ) ;
else if ( id = = " artist " ) current_ . set_artist ( value . toString ( ) ) ;
else if ( id = = " album " ) current_ . set_album ( value . toString ( ) ) ;
else if ( id = = " albumartist " ) current_ . set_albumartist ( value . toString ( ) ) ;
else if ( id = = " composer " ) current_ . set_composer ( value . toString ( ) ) ;
else if ( id = = " performer " ) current_ . set_performer ( value . toString ( ) ) ;
else if ( id = = " grouping " ) current_ . set_grouping ( value . toString ( ) ) ;
else if ( id = = " genre " ) current_ . set_genre ( value . toString ( ) ) ;
else if ( id = = " comment " ) current_ . set_comment ( value . toString ( ) ) ;
2019-07-08 22:21:12 +02:00
else if ( id = = " lyrics " ) current_ . set_lyrics ( value . toString ( ) ) ;
2018-02-27 18:06:05 +01:00
else if ( id = = " track " ) current_ . set_track ( value . toInt ( ) ) ;
else if ( id = = " disc " ) current_ . set_disc ( value . toInt ( ) ) ;
else if ( id = = " year " ) current_ . set_year ( value . toInt ( ) ) ;
2020-09-23 00:52:41 +02:00
else if ( id = = " compilation " ) current_ . set_compilation ( value . toBool ( ) ) ;
2021-10-24 16:08:17 +02:00
else if ( id = = " rating " ) { current_ . set_rating ( value . toDouble ( ) ) ; }
2018-02-27 18:06:05 +01:00
else qLog ( Warning ) < < " Unknown ID " < < id ;
}
bool EditTagDialog : : DoesValueVary ( const QModelIndexList & sel , const QString & id ) const {
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
QVariant value = data_ [ sel . first ( ) . row ( ) ] . current_value ( id ) ;
for ( int i = 1 ; i < sel . count ( ) ; + + i ) {
if ( value ! = data_ [ sel [ i ] . row ( ) ] . current_value ( id ) ) return true ;
}
return false ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
bool EditTagDialog : : IsValueModified ( const QModelIndexList & sel , const QString & id ) const {
2021-07-11 09:49:38 +02:00
return std : : any_of ( sel . begin ( ) , sel . end ( ) , [ this , id ] ( const QModelIndex & i ) { return data_ [ i . row ( ) ] . original_value ( id ) ! = data_ [ i . row ( ) ] . current_value ( id ) ; } ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : InitFieldValue ( const FieldData & field , const QModelIndexList & sel ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
const bool varies = DoesValueVary ( sel , field . id_ ) ;
if ( ExtendedEditor * editor = dynamic_cast < ExtendedEditor * > ( field . editor_ ) ) {
editor - > clear ( ) ;
editor - > clear_hint ( ) ;
if ( varies ) {
2021-02-26 21:03:51 +01:00
editor - > set_hint ( tr ( kTagsDifferentHintText ) ) ;
2020-09-23 00:52:41 +02:00
editor - > set_partially ( ) ;
2018-02-27 18:06:05 +01:00
}
else {
2020-09-23 00:52:41 +02:00
editor - > set_value ( data_ [ sel [ 0 ] . row ( ) ] . current_value ( field . id_ ) ) ;
2018-02-27 18:06:05 +01:00
}
}
2021-03-21 04:47:11 +01:00
else if ( field . editor_ ) {
2020-09-23 00:52:41 +02:00
qLog ( Error ) < < " Missing editor for " < < field . editor_ - > objectName ( ) ;
}
2018-02-27 18:06:05 +01:00
UpdateModifiedField ( field , sel ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : UpdateFieldValue ( const FieldData & field , const QModelIndexList & sel ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
// Get the value from the field
QVariant value ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
if ( ExtendedEditor * editor = dynamic_cast < ExtendedEditor * > ( field . editor_ ) ) {
2021-06-25 18:19:37 +02:00
value = editor - > value ( ) ;
2020-09-23 00:52:41 +02:00
}
2021-03-21 04:47:11 +01:00
else if ( field . editor_ ) {
2020-09-23 00:52:41 +02:00
qLog ( Error ) < < " Missing editor for " < < field . editor_ - > objectName ( ) ;
2018-02-27 18:06:05 +01:00
}
// Did we get it?
if ( ! value . isValid ( ) ) {
return ;
}
// Set it in each selected song
for ( const QModelIndex & i : sel ) {
data_ [ i . row ( ) ] . set_value ( field . id_ , value ) ;
}
UpdateModifiedField ( field , sel ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : UpdateModifiedField ( const FieldData & field , const QModelIndexList & sel ) {
const bool modified = IsValueModified ( sel , field . id_ ) ;
// Update the boldness
QFont new_font ( font ( ) ) ;
new_font . setBold ( modified ) ;
field . label_ - > setFont ( new_font ) ;
2021-03-21 04:47:11 +01:00
if ( field . editor_ ) field . editor_ - > setFont ( new_font ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : ResetFieldValue ( const FieldData & field , const QModelIndexList & sel ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
// Reset each selected song
for ( const QModelIndex & i : sel ) {
2020-04-23 21:08:28 +02:00
Data & tag_data = data_ [ i . row ( ) ] ;
tag_data . set_value ( field . id_ , tag_data . original_value ( field . id_ ) ) ;
2018-02-27 18:06:05 +01:00
}
// Reset the field
InitFieldValue ( field , sel ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : SelectionChanged ( ) {
2018-10-02 00:38:52 +02:00
2021-02-26 21:03:51 +01:00
const QModelIndexList indexes = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
if ( indexes . isEmpty ( ) ) return ;
2018-02-27 18:06:05 +01:00
// Set the editable fields
2021-02-26 21:03:51 +01:00
UpdateUI ( indexes ) ;
2018-02-27 18:06:05 +01:00
// If we're editing multiple songs then we have to disable certain tabs
2021-02-26 21:03:51 +01:00
const bool multiple = indexes . count ( ) > 1 ;
ui_ - > tab_widget - > setTabEnabled ( ui_ - > tab_widget - > indexOf ( ui_ - > tab_summary ) , ! multiple ) ;
ui_ - > tab_widget - > setTabEnabled ( ui_ - > tab_widget - > indexOf ( ui_ - > tab_lyrics ) , ! multiple ) ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
if ( multiple ) {
UpdateSummaryTab ( Song ( ) , UpdateCoverAction_None ) ;
UpdateStatisticsTab ( Song ( ) ) ;
}
else {
UpdateSummaryTab ( data_ [ indexes . first ( ) . row ( ) ] . original_ , data_ [ indexes . first ( ) . row ( ) ] . cover_action_ ) ;
UpdateStatisticsTab ( data_ [ indexes . first ( ) . row ( ) ] . original_ ) ;
}
const Song & first_song = data_ [ indexes . first ( ) . row ( ) ] . original_ ;
UpdateCoverAction first_cover_action = data_ [ indexes . first ( ) . row ( ) ] . cover_action_ ;
bool art_different = false ;
bool action_different = false ;
for ( const QModelIndex & idx : indexes ) {
if ( data_ [ idx . row ( ) ] . cover_action_ = = UpdateCoverAction_None ) {
data_ [ idx . row ( ) ] . cover_result_ = AlbumCoverImageResult ( ) ;
}
const Song & song = data_ [ idx . row ( ) ] . original_ ;
2021-02-27 00:58:43 +01:00
if ( data_ [ idx . row ( ) ] . cover_action_ ! = first_cover_action | | ( first_cover_action ! = UpdateCoverAction_None & & data_ [ idx . row ( ) ] . cover_result_ . image_data ! = data_ [ indexes . first ( ) . row ( ) ] . cover_result_ . image_data ) ) {
action_different = true ;
}
2021-02-26 21:03:51 +01:00
if ( data_ [ idx . row ( ) ] . cover_action_ ! = first_cover_action | |
song . art_manual ( ) ! = first_song . art_manual ( ) | |
song . has_embedded_cover ( ) ! = first_song . has_embedded_cover ( ) | |
( song . art_manual ( ) . isEmpty ( ) & & song . art_automatic ( ) ! = first_song . art_automatic ( ) ) | |
( song . has_embedded_cover ( ) & & first_song . has_embedded_cover ( ) & & ( first_song . effective_albumartist ( ) ! = song . effective_albumartist ( ) | | first_song . album ( ) ! = song . album ( ) ) )
) {
art_different = true ;
break ;
}
2018-02-27 18:06:05 +01:00
}
2018-03-10 13:02:56 +01:00
2021-02-26 21:03:51 +01:00
QString summary ;
if ( indexes . count ( ) = = 1 ) {
summary + = " <p><b> " + first_song . PrettyTitleWithArtist ( ) . toHtmlEscaped ( ) + " </b></p> " ;
}
else {
summary + = " <p><b> " ;
summary + = tr ( " %1 songs selected. " ) . arg ( indexes . count ( ) ) ;
summary + = " </b></p> " ;
}
2021-03-07 01:49:38 +01:00
const bool enable_change_art = first_song . is_collection_song ( ) ;
ui_ - > tags_art_button - > setEnabled ( enable_change_art ) ;
2021-03-07 02:36:50 +01:00
if ( ( art_different & & first_cover_action ! = UpdateCoverAction_New ) | | action_different ) {
2021-07-11 09:49:38 +02:00
tags_cover_art_id_ = - 1 ; // Cancels any pending art load.
2021-02-26 21:03:51 +01:00
ui_ - > tags_art - > clear ( ) ;
ui_ - > tags_art - > setText ( kArtDifferentHintText ) ;
album_cover_choice_controller_ - > show_cover_action ( ) - > setEnabled ( false ) ;
album_cover_choice_controller_ - > cover_to_file_action ( ) - > setEnabled ( false ) ;
2021-03-07 01:49:38 +01:00
album_cover_choice_controller_ - > cover_from_file_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > cover_from_url_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > search_for_cover_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > unset_cover_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > clear_cover_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > delete_cover_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > search_for_cover_action ( ) - > setEnabled ( enable_change_art ) ;
2021-02-26 21:03:51 +01:00
}
else {
ui_ - > tags_art - > clear ( ) ;
album_cover_choice_controller_ - > show_cover_action ( ) - > setEnabled ( first_song . has_valid_art ( ) & & ! first_song . has_manually_unset_cover ( ) ) ;
album_cover_choice_controller_ - > cover_to_file_action ( ) - > setEnabled ( first_song . has_valid_art ( ) & & ! first_song . has_manually_unset_cover ( ) ) ;
2021-03-07 01:49:38 +01:00
album_cover_choice_controller_ - > cover_from_file_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > cover_from_url_action ( ) - > setEnabled ( enable_change_art ) ;
album_cover_choice_controller_ - > search_for_cover_action ( ) - > setEnabled ( app_ - > cover_providers ( ) - > HasAnyProviders ( ) & & enable_change_art ) ;
album_cover_choice_controller_ - > unset_cover_action ( ) - > setEnabled ( enable_change_art & & ! first_song . has_manually_unset_cover ( ) ) ;
album_cover_choice_controller_ - > clear_cover_action ( ) - > setEnabled ( enable_change_art & & ! first_song . art_manual ( ) . isEmpty ( ) ) ;
2021-03-07 02:36:50 +01:00
album_cover_choice_controller_ - > delete_cover_action ( ) - > setEnabled ( enable_change_art & & first_song . has_valid_art ( ) & & ! first_song . has_manually_unset_cover ( ) ) ;
2021-02-26 21:03:51 +01:00
if ( data_ [ indexes . first ( ) . row ( ) ] . cover_action_ = = UpdateCoverAction_None ) {
tags_cover_art_id_ = app_ - > album_cover_loader ( ) - > LoadImageAsync ( cover_options_ , first_song ) ;
}
else {
tags_cover_art_id_ = app_ - > album_cover_loader ( ) - > LoadImageAsync ( cover_options_ , data_ [ indexes . first ( ) . row ( ) ] . cover_result_ ) ;
}
summary + = GetArtSummary ( first_song , first_cover_action ) ;
}
ui_ - > tags_summary - > setText ( summary ) ;
2021-02-27 01:32:45 +01:00
const bool embedded_cover = ( first_song . save_embedded_cover_supported ( ) & & ( first_song . has_embedded_cover ( ) | | album_cover_choice_controller_ - > get_collection_save_album_cover_type ( ) = = CollectionSettingsPage : : SaveCoverType_Embedded ) ) ;
2021-02-26 21:03:51 +01:00
ui_ - > checkbox_embedded_cover - > setChecked ( embedded_cover ) ;
album_cover_choice_controller_ - > set_save_embedded_cover_override ( embedded_cover ) ;
2018-02-27 18:06:05 +01:00
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : UpdateUI ( const QModelIndexList & indexes ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
ignore_edits_ = true ;
for ( const FieldData & field : fields_ ) {
2021-02-26 21:03:51 +01:00
InitFieldValue ( field , indexes ) ;
2018-02-27 18:06:05 +01:00
}
ignore_edits_ = false ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : SetText ( QLabel * label , const int value , const QString & suffix , const QString & def ) {
2018-02-27 18:06:05 +01:00
label - > setText ( value < = 0 ? def : ( QString : : number ( value ) + " " + suffix ) ) ;
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : SetDate ( QLabel * label , const uint time ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
if ( time = = std : : numeric_limits < uint > : : max ( ) ) { // -1
label - > setText ( QObject : : tr ( " Unknown " ) ) ;
}
else {
2020-07-18 04:09:36 +02:00
label - > setText ( QDateTime : : fromSecsSinceEpoch ( time ) . toString ( QLocale : : system ( ) . dateTimeFormat ( QLocale : : LongFormat ) ) ) ;
2018-02-27 18:06:05 +01:00
}
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : UpdateSummaryTab ( const Song & song , const UpdateCoverAction cover_action ) {
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
summary_cover_art_id_ = app_ - > album_cover_loader ( ) - > LoadImageAsync ( cover_options_ , song ) ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
QString summary = " <p><b> " + song . PrettyTitleWithArtist ( ) . toHtmlEscaped ( ) + " </b><p/> " ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
summary + = GetArtSummary ( song , cover_action ) ;
2018-02-27 18:06:05 +01:00
ui_ - > summary - > setText ( summary ) ;
ui_ - > length - > setText ( Utilities : : PrettyTimeNanosec ( song . length_nanosec ( ) ) ) ;
SetText ( ui_ - > samplerate , song . samplerate ( ) , " Hz " ) ;
SetText ( ui_ - > bitdepth , song . bitdepth ( ) , " Bit " ) ;
SetText ( ui_ - > bitrate , song . bitrate ( ) , tr ( " kbps " ) ) ;
SetDate ( ui_ - > mtime , song . mtime ( ) ) ;
SetDate ( ui_ - > ctime , song . ctime ( ) ) ;
if ( song . filesize ( ) = = - 1 ) {
ui_ - > filesize - > setText ( tr ( " Unknown " ) ) ;
}
else {
ui_ - > filesize - > setText ( Utilities : : PrettySize ( song . filesize ( ) ) ) ;
}
ui_ - > filetype - > setText ( song . TextForFiletype ( ) ) ;
2021-02-26 21:03:51 +01:00
if ( song . url ( ) . isLocalFile ( ) ) {
ui_ - > filename - > setText ( song . url ( ) . fileName ( ) ) ;
ui_ - > path - > setText ( QFileInfo ( QDir : : toNativeSeparators ( song . url ( ) . toLocalFile ( ) ) ) . path ( ) ) ;
}
else {
2018-02-27 18:06:05 +01:00
ui_ - > filename - > setText ( song . url ( ) . toString ( ) ) ;
2021-02-26 21:03:51 +01:00
ui_ - > path - > clear ( ) ;
}
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
if ( song . art_manual ( ) . isEmpty ( ) ) {
ui_ - > art_manual - > setText ( tr ( " None " ) ) ;
}
else if ( song . has_manually_unset_cover ( ) ) {
ui_ - > art_manual - > setText ( tr ( " Unset " ) ) ;
}
else {
ui_ - > art_manual - > setText ( song . art_manual ( ) . toString ( ) ) ;
}
if ( song . art_automatic ( ) . isEmpty ( ) ) {
ui_ - > art_automatic - > setText ( tr ( " None " ) ) ;
}
else if ( song . has_embedded_cover ( ) ) {
ui_ - > art_automatic - > setText ( tr ( " Embedded " ) ) ;
}
else {
ui_ - > art_automatic - > setText ( song . art_automatic ( ) . toString ( ) ) ;
}
}
QString EditTagDialog : : GetArtSummary ( const Song & song , const UpdateCoverAction cover_action ) {
QString summary ;
if ( cover_action ! = UpdateCoverAction_None ) {
switch ( cover_action ) {
case UpdateCoverAction_Clear :
summary = tr ( " Cover changed: Will be cleared when saved. " ) . toHtmlEscaped ( ) ;
break ;
case UpdateCoverAction_Unset :
summary = tr ( " Cover changed: Will be unset when saved. " ) . toHtmlEscaped ( ) ;
break ;
case UpdateCoverAction_Delete :
summary = tr ( " Cover changed: Will be deleted when saved. " ) . toHtmlEscaped ( ) ;
break ;
case UpdateCoverAction_New :
summary = tr ( " Cover changed: Will set new when saved. " ) . toHtmlEscaped ( ) ;
break ;
case UpdateCoverAction_None :
break ;
}
}
else if ( song . art_manual ( ) . isEmpty ( ) & & song . art_automatic ( ) . isEmpty ( ) ) {
summary = tr ( " Cover art not set " ) . toHtmlEscaped ( ) ;
}
else if ( song . has_manually_unset_cover ( ) ) {
summary = tr ( " Cover art manually unset " ) . toHtmlEscaped ( ) ;
}
else if ( song . art_manual_is_valid ( ) ) {
summary = tr ( " Manually set cover art from %1 " ) . arg ( song . art_manual ( ) . toString ( ) ) . toHtmlEscaped ( ) ;
}
else if ( song . has_embedded_cover ( ) ) {
summary = tr ( " Cover art from embedded image " ) ;
}
else if ( song . art_automatic_is_valid ( ) ) {
summary = tr ( " Cover art automatically loaded from %1 " ) . arg ( song . art_automatic ( ) . toString ( ) ) . toHtmlEscaped ( ) ;
}
else if ( ! song . art_manual ( ) . isEmpty ( ) ) {
summary = tr ( " Manually cover art from %1 is missing " ) . arg ( song . art_manual ( ) . toString ( ) ) . toHtmlEscaped ( ) ;
}
else if ( ! song . art_automatic ( ) . isEmpty ( ) ) {
summary = tr ( " Automatically cover art from %1 is missing " ) . arg ( song . art_automatic ( ) . toString ( ) ) . toHtmlEscaped ( ) ;
}
2021-03-07 01:49:38 +01:00
if ( ! song . is_collection_song ( ) ) {
if ( ! summary . isEmpty ( ) ) summary + = " <br /> " ;
summary = tr ( " Album cover editing is only available for collection songs. " ) ;
}
2021-02-26 21:03:51 +01:00
return summary ;
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : UpdateStatisticsTab ( const Song & song ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
ui_ - > playcount - > setText ( QString : : number ( qMax ( 0 , song . playcount ( ) ) ) ) ;
ui_ - > skipcount - > setText ( QString : : number ( qMax ( 0 , song . skipcount ( ) ) ) ) ;
2020-07-18 04:09:36 +02:00
ui_ - > lastplayed - > setText ( song . lastplayed ( ) < = 0 ? tr ( " Never " ) : QDateTime : : fromSecsSinceEpoch ( song . lastplayed ( ) ) . toString ( QLocale : : system ( ) . dateTimeFormat ( QLocale : : LongFormat ) ) ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
2020-04-20 18:03:18 +02:00
void EditTagDialog : : AlbumCoverLoaded ( const quint64 id , const AlbumCoverLoaderResult & result ) {
2019-09-15 20:27:32 +02:00
2021-02-26 21:03:51 +01:00
if ( id = = tags_cover_art_id_ ) {
ui_ - > tags_art - > clear ( ) ;
2021-03-07 01:49:38 +01:00
bool enable_change_art = false ;
2021-02-26 21:03:51 +01:00
if ( result . success & & ! result . image_scaled . isNull ( ) & & result . type ! = AlbumCoverLoaderResult : : Type_ManuallyUnset ) {
ui_ - > tags_art - > setPixmap ( QPixmap : : fromImage ( result . image_scaled ) ) ;
2021-02-26 23:10:36 +01:00
for ( const QModelIndex & idx : ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ) {
2021-02-26 21:03:51 +01:00
data_ [ idx . row ( ) ] . cover_result_ = result . album_cover ;
2021-03-07 01:49:38 +01:00
enable_change_art = data_ [ idx . row ( ) ] . original_ . is_collection_song ( ) ;
2021-02-26 21:03:51 +01:00
}
}
else {
ui_ - > tags_art - > setPixmap ( QPixmap : : fromImage ( image_no_cover_thumbnail_ ) ) ;
2021-03-07 07:01:22 +01:00
for ( const QModelIndex & idx : ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ) {
data_ [ idx . row ( ) ] . cover_result_ = AlbumCoverImageResult ( ) ;
enable_change_art = data_ [ idx . row ( ) ] . original_ . is_collection_song ( ) ;
}
2021-02-26 21:03:51 +01:00
}
tags_cover_art_id_ = - 1 ;
album_cover_choice_controller_ - > show_cover_action ( ) - > setEnabled ( result . success & & result . type ! = AlbumCoverLoaderResult : : Type_ManuallyUnset ) ;
album_cover_choice_controller_ - > cover_to_file_action ( ) - > setEnabled ( result . success & & result . type ! = AlbumCoverLoaderResult : : Type_ManuallyUnset ) ;
2021-03-07 01:49:38 +01:00
album_cover_choice_controller_ - > delete_cover_action ( ) - > setEnabled ( enable_change_art & & result . success & & result . type ! = AlbumCoverLoaderResult : : Type_ManuallyUnset ) ;
2021-02-26 21:03:51 +01:00
}
else if ( id = = summary_cover_art_id_ ) {
if ( result . success & & ! result . image_scaled . isNull ( ) & & result . type ! = AlbumCoverLoaderResult : : Type_ManuallyUnset ) {
ui_ - > summary_art - > setPixmap ( QPixmap : : fromImage ( result . image_scaled ) ) ;
}
else {
ui_ - > summary_art - > setPixmap ( QPixmap : : fromImage ( image_no_cover_thumbnail_ ) ) ;
}
summary_cover_art_id_ = - 1 ;
2018-02-27 18:06:05 +01:00
}
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : FieldValueEdited ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
if ( ignore_edits_ ) return ;
const QModelIndexList sel = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
2021-08-23 21:21:08 +02:00
if ( sel . isEmpty ( ) ) {
2018-02-27 18:06:05 +01:00
return ;
2021-08-23 21:21:08 +02:00
}
2018-02-27 18:06:05 +01:00
QWidget * w = qobject_cast < QWidget * > ( sender ( ) ) ;
// Find the field
for ( const FieldData & field : fields_ ) {
if ( field . editor_ = = w ) {
UpdateFieldValue ( field , sel ) ;
return ;
}
}
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : ResetField ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
const QModelIndexList sel = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
2021-08-23 21:21:08 +02:00
if ( sel . isEmpty ( ) ) {
2018-02-27 18:06:05 +01:00
return ;
2021-08-23 21:21:08 +02:00
}
2018-02-27 18:06:05 +01:00
QWidget * w = qobject_cast < QWidget * > ( sender ( ) ) ;
// Find the field
for ( const FieldData & field : fields_ ) {
if ( field . editor_ = = w ) {
ignore_edits_ = true ;
ResetFieldValue ( field , sel ) ;
ignore_edits_ = false ;
return ;
}
}
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
Song * EditTagDialog : : GetFirstSelected ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
const QModelIndexList sel = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
if ( sel . isEmpty ( ) ) return nullptr ;
2021-02-26 21:03:51 +01:00
return & data_ [ sel . first ( ) . row ( ) ] . current_ ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : LoadCoverFromFile ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
Song * song = GetFirstSelected ( ) ;
if ( ! song ) return ;
2021-02-26 21:03:51 +01:00
AlbumCoverImageResult result = album_cover_choice_controller_ - > LoadImageFromFile ( song ) ;
if ( result . is_valid ( ) ) UpdateCover ( UpdateCoverAction_New , result ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : SaveCoverToFile ( ) {
2018-10-02 00:38:52 +02:00
2021-02-26 21:03:51 +01:00
if ( ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) . isEmpty ( ) ) return ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
const Data & first_data = data_ [ ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) . first ( ) . row ( ) ] ;
album_cover_choice_controller_ - > SaveCoverToFileManual ( first_data . current_ , first_data . cover_result_ ) ;
2018-03-10 13:02:56 +01:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : LoadCoverFromURL ( ) {
2021-02-26 21:03:51 +01:00
if ( ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) . isEmpty ( ) ) return ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
AlbumCoverImageResult result = album_cover_choice_controller_ - > LoadImageFromURL ( ) ;
if ( result . is_valid ( ) ) UpdateCover ( UpdateCoverAction_New , result ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : SearchForCover ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
Song * song = GetFirstSelected ( ) ;
if ( ! song ) return ;
2021-02-26 21:03:51 +01:00
AlbumCoverImageResult result = album_cover_choice_controller_ - > SearchForImage ( song ) ;
if ( result . is_valid ( ) ) UpdateCover ( UpdateCoverAction_New , result ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : UnsetCover ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
Song * song = GetFirstSelected ( ) ;
if ( ! song ) return ;
2021-02-26 21:03:51 +01:00
song - > set_manually_unset_cover ( ) ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
UpdateCover ( UpdateCoverAction_Unset ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : ClearCover ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
Song * song = GetFirstSelected ( ) ;
2021-02-26 21:03:51 +01:00
if ( ! song ) return ;
song - > clear_art_automatic ( ) ;
song - > clear_art_manual ( ) ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
UpdateCover ( UpdateCoverAction_Clear ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : DeleteCover ( ) {
2018-10-02 00:38:52 +02:00
2021-02-26 21:03:51 +01:00
UpdateCover ( UpdateCoverAction_Delete ) ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
}
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
void EditTagDialog : : ShowCover ( ) {
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
if ( ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) . isEmpty ( ) ) return ;
const Data & first_data = data_ [ ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) . first ( ) . row ( ) ] ;
album_cover_choice_controller_ - > ShowCover ( first_data . current_ , first_data . cover_result_ . image ) ;
2020-10-19 21:05:59 +02:00
2021-02-26 21:03:51 +01:00
}
void EditTagDialog : : UpdateCover ( const UpdateCoverAction action , const AlbumCoverImageResult & result ) {
const QModelIndexList indexes = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
if ( indexes . isEmpty ( ) ) return ;
2020-10-19 21:05:59 +02:00
2021-02-26 21:03:51 +01:00
QString artist = data_ [ indexes . first ( ) . row ( ) ] . current_ . effective_albumartist ( ) ;
QString album = data_ [ indexes . first ( ) . row ( ) ] . current_ . album ( ) ;
for ( const QModelIndex & idx : indexes ) {
data_ [ idx . row ( ) ] . cover_action_ = action ;
data_ [ idx . row ( ) ] . cover_result_ = result ;
if ( action = = UpdateCoverAction_New ) {
data_ [ idx . row ( ) ] . current_ . clear_art_manual ( ) ;
}
else if ( action = = UpdateCoverAction_Unset ) {
data_ [ idx . row ( ) ] . current_ . set_manually_unset_cover ( ) ;
}
else if ( action = = UpdateCoverAction_Clear | | action = = UpdateCoverAction_Delete ) {
data_ [ idx . row ( ) ] . current_ . clear_art_manual ( ) ;
data_ [ idx . row ( ) ] . current_ . clear_art_automatic ( ) ;
}
if ( artist ! = data_ [ idx . row ( ) ] . current_ . effective_albumartist ( ) | | album ! = data_ [ idx . row ( ) ] . current_ . effective_albumartist ( ) ) {
artist . clear ( ) ;
album . clear ( ) ;
}
2018-02-27 18:06:05 +01:00
}
2018-10-02 00:38:52 +02:00
2021-02-26 21:03:51 +01:00
// Now check if we have any other songs cached that share that artist and album (and would therefore be changed as well)
if ( ! artist . isEmpty ( ) & & ! album . isEmpty ( ) ) {
for ( int i = 0 ; i < data_ . count ( ) ; + + i ) {
if ( data_ [ i ] . current_ . effective_albumartist ( ) = = artist & & data_ [ i ] . current_ . album ( ) = = album ) {
data_ [ i ] . cover_action_ = action ;
data_ [ i ] . cover_result_ = result ;
if ( action = = UpdateCoverAction_New ) {
data_ [ i ] . current_ . clear_art_manual ( ) ;
}
else if ( action = = UpdateCoverAction_Unset ) {
data_ [ i ] . current_ . set_manually_unset_cover ( ) ;
}
else if ( action = = UpdateCoverAction_Clear | | action = = UpdateCoverAction_Delete ) {
data_ [ i ] . current_ . clear_art_manual ( ) ;
data_ [ i ] . current_ . clear_art_automatic ( ) ;
}
}
}
}
UpdateSummaryTab ( data_ [ indexes . first ( ) . row ( ) ] . current_ , data_ [ indexes . first ( ) . row ( ) ] . cover_action_ ) ;
SelectionChanged ( ) ;
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : NextSong ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
if ( ui_ - > song_list - > count ( ) = = 0 ) {
return ;
}
int row = ( ui_ - > song_list - > currentRow ( ) + 1 ) % ui_ - > song_list - > count ( ) ;
ui_ - > song_list - > setCurrentRow ( row ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : PreviousSong ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
if ( ui_ - > song_list - > count ( ) = = 0 ) {
return ;
}
int row = ( ui_ - > song_list - > currentRow ( ) - 1 + ui_ - > song_list - > count ( ) ) % ui_ - > song_list - > count ( ) ;
ui_ - > song_list - > setCurrentRow ( row ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : ButtonClicked ( QAbstractButton * button ) {
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
if ( button = = ui_ - > button_box - > button ( QDialogButtonBox : : Discard ) ) {
reject ( ) ;
}
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : SaveData ( ) {
2018-10-02 00:38:52 +02:00
2021-02-27 00:58:43 +01:00
QMap < QString , QUrl > cover_urls ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
for ( int i = 0 ; i < data_ . count ( ) ; + + i ) {
Data & ref = data_ [ i ] ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
if ( ! ref . current_ . IsMetadataEqual ( ref . original_ ) ) {
+ + save_tag_pending_ ;
TagReaderReply * reply = TagReaderClient : : Instance ( ) - > SaveFile ( ref . current_ . url ( ) . toLocalFile ( ) , ref . current_ ) ;
QObject : : connect ( reply , & TagReaderReply : : Finished , this , [ this , reply , ref ] ( ) { SongSaveTagsComplete ( reply , ref . current_ . url ( ) . toLocalFile ( ) , ref . current_ ) ; } , Qt : : QueuedConnection ) ;
}
2018-10-02 00:38:52 +02:00
2021-10-24 16:08:17 +02:00
if ( ref . current_ . rating ( ) ! = ref . original_ . rating ( ) & & ref . current_ . is_collection_song ( ) ) {
app_ - > collection_backend ( ) - > UpdateSongRatingAsync ( ref . current_ . id ( ) , ref . current_ . rating ( ) , true ) ;
}
2021-03-07 07:01:22 +01:00
QString embedded_cover_from_file ;
2021-02-26 21:03:51 +01:00
// If embedded album cover is selected and it isn't saved to the tags, then save it even if no action was done.
2021-03-07 07:01:22 +01:00
if ( ui_ - > checkbox_embedded_cover - > isChecked ( ) & & ref . cover_action_ = = UpdateCoverAction_None & & ! ref . original_ . has_embedded_cover ( ) & & ref . original_ . save_embedded_cover_supported ( ) ) {
if ( ref . original_ . art_manual ( ) . isValid ( ) & & ref . original_ . art_manual ( ) . isLocalFile ( ) & & QFile : : exists ( ref . original_ . art_manual ( ) . toLocalFile ( ) ) ) {
ref . cover_action_ = UpdateCoverAction_New ;
embedded_cover_from_file = ref . original_ . art_manual ( ) . toLocalFile ( ) ;
}
else if ( ref . original_ . art_automatic ( ) . isValid ( ) & & ref . original_ . art_automatic ( ) . isLocalFile ( ) & & QFile : : exists ( ref . original_ . art_automatic ( ) . toLocalFile ( ) ) ) {
ref . cover_action_ = UpdateCoverAction_New ;
embedded_cover_from_file = ref . original_ . art_automatic ( ) . toLocalFile ( ) ;
}
2021-02-26 21:03:51 +01:00
}
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
if ( ref . cover_action_ ! = UpdateCoverAction_None ) {
switch ( ref . cover_action_ ) {
2021-02-27 02:12:26 +01:00
case UpdateCoverAction_None :
break ;
case UpdateCoverAction_New : {
if ( ( ! ref . current_ . effective_albumartist ( ) . isEmpty ( ) & & ! ref . current_ . album ( ) . isEmpty ( ) ) & &
( ! ui_ - > checkbox_embedded_cover - > isChecked ( ) | | ! ref . original_ . save_embedded_cover_supported ( ) ) ) {
QUrl cover_url ;
2021-03-07 02:36:50 +01:00
if ( ! ref . cover_result_ . cover_url . isEmpty ( ) & & ref . cover_result_ . cover_url . isLocalFile ( ) & & QFile : : exists ( ref . cover_result_ . cover_url . toLocalFile ( ) ) ) {
cover_url = ref . cover_result_ . cover_url ;
2021-02-27 02:12:26 +01:00
}
else {
2021-03-07 02:36:50 +01:00
QString cover_hash = Utilities : : Sha1CoverHash ( ref . current_ . effective_albumartist ( ) , ref . current_ . album ( ) ) . toHex ( ) ;
if ( cover_urls . contains ( cover_hash ) ) {
cover_url = cover_urls [ cover_hash ] ;
}
else {
cover_url = album_cover_choice_controller_ - > SaveCoverToFileAutomatic ( & ref . current_ , ref . cover_result_ ) ;
cover_urls . insert ( cover_hash , cover_url ) ;
}
2021-02-26 21:03:51 +01:00
}
2021-02-27 02:12:26 +01:00
ref . current_ . set_art_manual ( cover_url ) ;
2021-02-26 21:03:51 +01:00
}
2021-02-27 02:12:26 +01:00
break ;
}
case UpdateCoverAction_Unset :
ref . current_ . set_manually_unset_cover ( ) ;
break ;
case UpdateCoverAction_Clear :
ref . current_ . clear_art_manual ( ) ;
break ;
case UpdateCoverAction_Delete : {
if ( ! ref . original_ . art_automatic ( ) . isEmpty ( ) ) {
if ( ref . original_ . art_automatic ( ) . isValid ( ) & & ! ref . original_ . has_embedded_cover ( ) & & ref . original_ . art_automatic ( ) . isLocalFile ( ) ) {
QString art_automatic = ref . original_ . art_automatic ( ) . toLocalFile ( ) ;
if ( QFile : : exists ( art_automatic ) ) {
QFile : : remove ( art_automatic ) ;
}
2021-02-26 21:03:51 +01:00
}
2021-02-27 02:12:26 +01:00
ref . current_ . clear_art_automatic ( ) ;
}
if ( ! ref . original_ . art_manual ( ) . isEmpty ( ) & & ! ref . original_ . has_manually_unset_cover ( ) ) {
if ( ref . original_ . art_manual ( ) . isValid ( ) & & ref . original_ . art_manual ( ) . isLocalFile ( ) ) {
QString art_manual = ref . original_ . art_manual ( ) . toLocalFile ( ) ;
if ( QFile : : exists ( art_manual ) ) {
QFile : : remove ( art_manual ) ;
}
2021-02-26 21:03:51 +01:00
}
2021-02-27 02:12:26 +01:00
ref . current_ . clear_art_manual ( ) ;
2021-02-26 21:03:51 +01:00
}
2021-02-27 02:12:26 +01:00
break ;
}
2021-02-26 21:03:51 +01:00
}
if ( ui_ - > checkbox_embedded_cover - > isChecked ( ) & & ref . original_ . save_embedded_cover_supported ( ) ) {
2021-03-07 07:05:02 +01:00
if ( ref . cover_action_ = = UpdateCoverAction_New ) {
if ( ref . cover_result_ . is_jpeg ( ) ) { // Save JPEG data directly.
2021-03-07 07:01:22 +01:00
+ + save_art_pending_ ;
2021-02-26 21:03:51 +01:00
TagReaderReply * reply = TagReaderClient : : Instance ( ) - > SaveEmbeddedArt ( ref . current_ . url ( ) . toLocalFile ( ) , ref . cover_result_ . image_data ) ;
QObject : : connect ( reply , & TagReaderReply : : Finished , this , [ this , reply , ref ] ( ) {
SongSaveArtComplete ( reply , ref . current_ . url ( ) . toLocalFile ( ) , ref . current_ , ref . cover_action_ ) ;
} , Qt : : QueuedConnection ) ;
}
2021-03-07 07:01:22 +01:00
else if ( ! ref . cover_result_ . image . isNull ( ) ) { // Convert image data to JPEG.
+ + save_art_pending_ ;
2021-02-26 21:03:51 +01:00
QFuture < QByteArray > future = QtConcurrent : : run ( & ImageUtils : : SaveImageToJpegData , ref . cover_result_ . image ) ;
QFutureWatcher < QByteArray > * watcher = new QFutureWatcher < QByteArray > ( ) ;
QObject : : connect ( watcher , & QFutureWatcher < QByteArray > : : finished , this , [ = ] ( ) {
TagReaderReply * reply = TagReaderClient : : Instance ( ) - > SaveEmbeddedArt ( ref . current_ . url ( ) . toLocalFile ( ) , watcher - > result ( ) ) ;
QObject : : connect ( reply , & TagReaderReply : : Finished , this , [ this , reply , ref ] ( ) {
SongSaveArtComplete ( reply , ref . current_ . url ( ) . toLocalFile ( ) , ref . current_ , ref . cover_action_ ) ;
} , Qt : : QueuedConnection ) ;
watcher - > deleteLater ( ) ;
} ) ;
2021-06-16 00:30:21 +02:00
watcher - > setFuture ( future ) ;
2021-02-26 21:03:51 +01:00
}
2021-03-07 07:01:22 +01:00
else if ( ! embedded_cover_from_file . isEmpty ( ) ) { // Save existing file on disk as embedded cover.
+ + save_art_pending_ ;
QFuture < QByteArray > future = QtConcurrent : : run ( & ImageUtils : : FileToJpegData , embedded_cover_from_file ) ;
QFutureWatcher < QByteArray > * watcher = new QFutureWatcher < QByteArray > ( ) ;
QObject : : connect ( watcher , & QFutureWatcher < QByteArray > : : finished , this , [ = ] ( ) {
TagReaderReply * reply = TagReaderClient : : Instance ( ) - > SaveEmbeddedArt ( ref . current_ . url ( ) . toLocalFile ( ) , watcher - > result ( ) ) ;
QObject : : connect ( reply , & TagReaderReply : : Finished , this , [ this , reply , ref ] ( ) {
SongSaveArtComplete ( reply , ref . current_ . url ( ) . toLocalFile ( ) , ref . current_ , ref . cover_action_ ) ;
} , Qt : : QueuedConnection ) ;
watcher - > deleteLater ( ) ;
} ) ;
2021-06-16 00:30:21 +02:00
watcher - > setFuture ( future ) ;
2021-03-07 07:01:22 +01:00
}
}
else if ( ref . cover_action_ = = UpdateCoverAction_Delete ) {
+ + save_art_pending_ ;
TagReaderReply * reply = TagReaderClient : : Instance ( ) - > SaveEmbeddedArt ( ref . current_ . url ( ) . toLocalFile ( ) , QByteArray ( ) ) ;
QObject : : connect ( reply , & TagReaderReply : : Finished , this , [ this , reply , ref ] ( ) {
SongSaveArtComplete ( reply , ref . current_ . url ( ) . toLocalFile ( ) , ref . current_ , ref . cover_action_ ) ;
} , Qt : : QueuedConnection ) ;
2018-02-27 18:06:05 +01:00
}
}
2021-02-27 00:58:43 +01:00
else if ( ! ref . current_ . effective_albumartist ( ) . isEmpty ( ) & & ! ref . current_ . album ( ) . isEmpty ( ) ) {
2021-02-26 21:03:51 +01:00
if ( ref . current_ . is_collection_song ( ) ) {
collection_songs_ . insert ( ref . current_ . id ( ) , ref . current_ ) ;
}
if ( ref . current_ = = app_ - > current_albumcover_loader ( ) - > last_song ( ) ) {
app_ - > current_albumcover_loader ( ) - > LoadAlbumCover ( ref . current_ ) ;
2018-02-27 18:06:05 +01:00
}
}
}
}
2020-09-23 00:52:41 +02:00
2021-02-26 21:03:51 +01:00
if ( save_tag_pending_ < = 0 & & save_art_pending_ < = 0 ) AcceptFinished ( ) ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
}
2018-10-02 00:38:52 +02:00
2021-02-26 21:03:51 +01:00
void EditTagDialog : : AcceptFinished ( ) {
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
if ( ! collection_songs_ . isEmpty ( ) ) {
app_ - > collection_backend ( ) - > AddOrUpdateSongsAsync ( collection_songs_ . values ( ) ) ;
collection_songs_ . clear ( ) ;
2020-11-22 03:37:15 +01:00
}
2018-10-02 00:38:52 +02:00
2021-02-26 21:03:51 +01:00
if ( ! SetLoading ( QString ( ) ) ) return ;
2018-02-27 18:06:05 +01:00
2021-02-26 21:03:51 +01:00
QDialog : : accept ( ) ;
2020-09-23 00:52:41 +02:00
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : ResetPlayCounts ( ) {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
const QModelIndexList sel = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
2021-02-26 21:03:51 +01:00
if ( sel . isEmpty ( ) ) return ;
2018-02-27 18:06:05 +01:00
Song * song = & data_ [ sel . first ( ) . row ( ) ] . original_ ;
if ( ! song - > is_valid ( ) | | song - > id ( ) = = - 1 ) return ;
if ( QMessageBox : : question ( this , tr ( " Reset play counts " ) , tr ( " Are you sure you want to reset this song's statistics? " ) , QMessageBox : : Reset , QMessageBox : : Cancel ) ! = QMessageBox : : Reset ) {
return ;
}
song - > set_playcount ( 0 ) ;
song - > set_skipcount ( 0 ) ;
song - > set_lastplayed ( - 1 ) ;
2021-02-26 21:03:51 +01:00
if ( song - > is_collection_song ( ) ) {
2020-08-10 21:27:27 +02:00
app_ - > collection_backend ( ) - > ResetStatisticsAsync ( song - > id ( ) ) ;
2021-02-26 21:03:51 +01:00
}
2020-08-10 21:27:27 +02:00
2018-02-27 18:06:05 +01:00
UpdateStatisticsTab ( * song ) ;
2020-08-10 21:27:27 +02:00
2018-02-27 18:06:05 +01:00
}
2021-10-24 16:08:17 +02:00
void EditTagDialog : : SongRated ( const float rating ) {
const QModelIndexList indexes = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
if ( indexes . isEmpty ( ) ) return ;
for ( const QModelIndex & idx : indexes ) {
2021-10-24 16:11:53 +02:00
if ( ! data_ [ idx . row ( ) ] . current_ . is_valid ( ) | | data_ [ idx . row ( ) ] . current_ . id ( ) = = - 1 ) continue ;
2021-10-24 16:08:17 +02:00
data_ [ idx . row ( ) ] . current_ . set_rating ( rating ) ;
}
}
2018-02-27 18:06:05 +01:00
void EditTagDialog : : FetchTag ( ) {
2018-10-02 00:38:52 +02:00
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2019-10-03 23:29:52 +02:00
2018-02-27 18:06:05 +01:00
const QModelIndexList sel = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
SongList songs ;
2021-01-26 16:48:04 +01:00
for ( const QModelIndex & idx : sel ) {
Song song = data_ [ idx . row ( ) ] . original_ ;
2018-02-27 18:06:05 +01:00
if ( ! song . is_valid ( ) ) {
continue ;
}
songs < < song ;
}
if ( songs . isEmpty ( ) ) return ;
results_dialog_ - > Init ( songs ) ;
tag_fetcher_ - > StartFetch ( songs ) ;
results_dialog_ - > show ( ) ;
2018-03-10 13:02:56 +01:00
2019-10-03 23:29:52 +02:00
# endif
2018-02-27 18:06:05 +01:00
}
void EditTagDialog : : FetchTagSongChosen ( const Song & original_song , const Song & new_metadata ) {
2018-10-02 00:38:52 +02:00
2021-05-10 21:17:50 +02:00
# ifdef HAVE_MUSICBRAINZ
2019-10-03 23:29:52 +02:00
2018-02-27 18:06:05 +01:00
const QString filename = original_song . url ( ) . toLocalFile ( ) ;
// Find the song with this filename
2019-02-20 21:27:53 +01:00
auto data_it = std : : find_if ( data_ . begin ( ) , data_ . end ( ) , [ & filename ] ( const Data & d ) {
return d . original_ . url ( ) . toLocalFile ( ) = = filename ;
} ) ;
2018-02-27 18:06:05 +01:00
if ( data_it = = data_ . end ( ) ) {
qLog ( Warning ) < < " Could not find song to filename: " < < filename ;
return ;
}
// Update song data
data_it - > current_ . set_title ( new_metadata . title ( ) ) ;
data_it - > current_ . set_artist ( new_metadata . artist ( ) ) ;
data_it - > current_ . set_album ( new_metadata . album ( ) ) ;
data_it - > current_ . set_track ( new_metadata . track ( ) ) ;
data_it - > current_ . set_year ( new_metadata . year ( ) ) ;
// Is it currently being displayed in the UI?
if ( ui_ - > song_list - > currentRow ( ) = = std : : distance ( data_ . begin ( ) , data_it ) ) {
// Yes! Additionally update UI
const QModelIndexList sel = ui_ - > song_list - > selectionModel ( ) - > selectedIndexes ( ) ;
UpdateUI ( sel ) ;
}
2019-02-20 21:27:53 +01:00
2019-10-03 23:29:52 +02:00
# else
Q_UNUSED ( original_song )
Q_UNUSED ( new_metadata )
2018-02-27 18:06:05 +01:00
# endif
2019-02-20 21:27:53 +01:00
2019-10-03 23:29:52 +02:00
}
2021-02-26 21:03:51 +01:00
void EditTagDialog : : SongSaveTagsComplete ( TagReaderReply * reply , const QString & filename , Song song ) {
2019-02-20 21:27:53 +01:00
2021-02-26 21:03:51 +01:00
- - save_tag_pending_ ;
2019-02-20 21:27:53 +01:00
if ( ! reply - > message ( ) . save_file_response ( ) . success ( ) ) {
2019-02-22 20:24:38 +01:00
QString message = tr ( " An error occurred writing metadata to '%1' " ) . arg ( filename ) ;
2019-02-20 21:27:53 +01:00
emit Error ( message ) ;
}
2020-08-10 21:27:27 +02:00
else if ( song . is_collection_song ( ) ) {
2021-02-26 21:03:51 +01:00
if ( collection_songs_ . contains ( song . id ( ) ) ) {
Song old_song = collection_songs_ . take ( song . id ( ) ) ;
song . set_art_automatic ( old_song . art_automatic ( ) ) ;
song . set_art_manual ( old_song . art_manual ( ) ) ;
}
collection_songs_ . insert ( song . id ( ) , song ) ;
}
2021-09-09 21:53:14 +02:00
QMetaObject : : invokeMethod ( reply , " deleteLater " , Qt : : QueuedConnection ) ;
2021-02-26 21:03:51 +01:00
if ( save_tag_pending_ < = 0 & & save_art_pending_ < = 0 ) AcceptFinished ( ) ;
}
void EditTagDialog : : SongSaveArtComplete ( TagReaderReply * reply , const QString & filename , Song song , const UpdateCoverAction cover_action ) {
- - save_art_pending_ ;
if ( ! reply - > message ( ) . save_embedded_art_response ( ) . success ( ) ) {
QString message = tr ( " An error occurred writing cover art to '%1' " ) . arg ( filename ) ;
emit Error ( message ) ;
}
else if ( song . is_collection_song ( ) ) {
if ( collection_songs_ . contains ( song . id ( ) ) ) {
song = collection_songs_ . take ( song . id ( ) ) ;
}
switch ( cover_action ) {
case UpdateCoverAction_None :
break ;
case UpdateCoverAction_New :
song . clear_art_manual ( ) ;
song . set_embedded_cover ( ) ;
break ;
case UpdateCoverAction_Clear :
case UpdateCoverAction_Delete :
song . clear_art_automatic ( ) ;
song . clear_art_manual ( ) ;
break ;
case UpdateCoverAction_Unset :
song . clear_art_automatic ( ) ;
song . set_manually_unset_cover ( ) ;
break ;
}
collection_songs_ . insert ( song . id ( ) , song ) ;
}
if ( song = = app_ - > current_albumcover_loader ( ) - > last_song ( ) ) {
app_ - > current_albumcover_loader ( ) - > LoadAlbumCover ( song ) ;
2019-02-20 21:27:53 +01:00
}
2021-09-09 21:53:14 +02:00
QMetaObject : : invokeMethod ( reply , " deleteLater " , Qt : : QueuedConnection ) ;
2019-02-20 21:27:53 +01:00
2021-02-26 21:03:51 +01:00
if ( save_tag_pending_ < = 0 & & save_art_pending_ < = 0 ) AcceptFinished ( ) ;
2019-06-22 08:36:02 +02:00
2019-02-20 21:27:53 +01:00
}