diff --git a/CMakeLists.txt b/CMakeLists.txt index fbbb338498..c88cd39627 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,268 +1,260 @@ cmake_minimum_required(VERSION 2.8.12) project(Amarok) # Remove all warnings, ease the porting to cmake 3.x if (POLICY CMP0028) cmake_policy(SET CMP0028 NEW) endif() ############### find_package(PkgConfig REQUIRED) find_package(ECM 1.7.0 REQUIRED CONFIG) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(FeatureSummary) include(ECMInstallIcons) include(ECMSetupVersion) include(ECMAddTests) include(ECMAddAppIcon) include(FindPkgConfig) find_package( Qt5 REQUIRED COMPONENTS Core DBus Gui Quick QuickWidgets Qml Script ScriptTools Sql Svg Test Widgets Xml ) find_package( KF5 REQUIRED COMPONENTS Archive Codecs CoreAddons DBusAddons Declarative DNSSD GlobalAccel GuiAddons I18n IconThemes KCMUtils KIO NewStuff Notifications NotifyConfig Package Solid TextEditor ThreadWeaver WindowSystem ) ############### option(WITH_UTILITIES "Enable building of utilities" ON) option(WITH_PLAYER "Enable building of main Amarok player" ON) option(WITH_MP3Tunes "Enable mp3tunes in the Amarok player, requires multiple extra dependencies" ON) option(WITH_IPOD "Enable iPod support in Amarok" ON) option(WITH_MYSQL_EMBEDDED "Build the embedded database library -- highly recommended" ON) option(WITH_PLAYGROUND "Enable building of playground scripts and applets (WARNING: some of them might have legal issues!)" OFF) ############### Taglib set(TAGLIB_MIN_VERSION "1.7") find_package(Taglib REQUIRED) +set_package_properties( Taglib PROPERTIES DESCRIPTION "Support for Audio metadata." URL "http://developer.kde.org/~wheeler/taglib.html" TYPE REQUIRED PURPOSE "Required for tag reading" ) # Check if TagLib is built with ASF and MP4 support include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES "${TAGLIB_INCLUDES}") set(CMAKE_REQUIRED_LIBRARIES "${TAGLIB_LIBRARIES}") check_cxx_source_compiles("#include int main() { TagLib::ASF::Tag tag; return 0;}" TAGLIB_ASF_FOUND) if( NOT TAGLIB_ASF_FOUND ) message(FATAL_ERROR "TagLib does not have ASF support compiled in.") endif() check_cxx_source_compiles("#include int main() { TagLib::MP4::Tag tag(0, 0); return 0;}" TAGLIB_MP4_FOUND) if( NOT TAGLIB_MP4_FOUND ) message(FATAL_ERROR "TagLib does not have MP4 support compiled in.") endif() check_cxx_source_compiles("#include #include #include #include #include using namespace TagLib; int main() { char *s; Mod::Tag tag; Mod::File modfile(s); S3M::File s3mfile(s); IT::File itfile(s); XM::File xmfile(s); return 0; }" TAGLIB_MOD_FOUND) check_cxx_source_compiles("#include int main() { char *s; TagLib::Ogg::Opus::File opusfile(s); return 0;}" TAGLIB_OPUS_FOUND) set(CMAKE_REQUIRED_INCLUDES) set(CMAKE_REQUIRED_LIBRARIES) set(TAGLIB-EXTRAS_MIN_VERSION "1.0") find_package(Taglib-Extras) set(TAGLIB_EXTRAS_FOUND ${TAGLIB-EXTRAS_FOUND}) # we need a c-compatible name for the include file include(CheckTagLibFileName) check_taglib_filename(COMPLEX_TAGLIB_FILENAME) ############### #Needed to conditionally build tests and gui if(BUILD_TESTING) add_definitions(-DDEBUG) endif() if(WITH_DESKTOP_UI) add_definitions(-DDESKTOP_UI) endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fmessage-length=0") if (CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmessage-length=0") if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--as-needed") endif() endif () include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/shared ${CMAKE_CURRENT_BINARY_DIR}/shared ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") # Require C++11 # WORKAROUND for Clang bug: http://llvm.org/bugs/show_bug.cgi?id=15651 if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND WIN32) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-delayed-template-parsing") endif () add_definitions(-DQT_NO_URL_CAST_FROM_STRING) find_package(Phonon4Qt5 4.6.60 REQUIRED NO_MODULE) include_directories(BEFORE ${PHONON_INCLUDES}) find_package( LibLastFm ) set( LIBLASTFM_MIN_VERSION "1.0.0" ) if( LIBLASTFM_FOUND ) if ( ${LIBLASTFM_MIN_VERSION} VERSION_LESS ${LIBLASTFM_VERSION} ) set( LIBLASTFM_FOUND TRUE ) endif() endif() find_package( LibOFA ) if( LIBOFA_FOUND ) PKG_SEARCH_MODULE(AVCODEC libavcodec) PKG_SEARCH_MODULE(AVFORMAT libavformat) PKG_SEARCH_MODULE(AVUTIL libavutil) endif() string( TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_TOLOWER ) if( CMAKE_BUILD_TYPE_TOLOWER MATCHES debug ) set( DEBUG_BUILD_TYPE ON ) add_definitions(-Wall -Wextra) endif() # this needs to be here because also code in shared/ needs config.h. This is also the # reason why various checks are above why they belong under if( WITH_PLAYER ) configure_file( shared/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/shared/config.h ) add_subdirectory( data ) add_subdirectory( images ) add_subdirectory( shared ) if( WITH_PLAYER ) - set_package_properties( QTOPENGL PROPERTIES DESCRIPTION "Required for the spectrum analyzer" URL "http://qt-project.org" TYPE OPTIONAL ) - find_package(X11) find_package(MySQLAmarok REQUIRED) if( WITH_MYSQL_EMBEDDED ) set( BUILD_MYSQLE_COLLECTION TRUE ) set_package_properties( MYSQLD PROPERTIES DESCRIPTION "Embedded MySQL Libraries" URL "http://www.mysql.com" TYPE REQUIRED ) else() add_definitions( "-DNO_MYSQL_EMBEDDED" ) endif() set_package_properties( MYSQL PROPERTIES DESCRIPTION "MySQL Server Libraries" URL "http://www.mysql.com" TYPE REQUIRED ) # zlib is required for mysql embedded find_package(ZLIB REQUIRED) set_package_properties( ZLIB PROPERTIES DESCRIPTION "zlib" TYPE REQUIRED ) # QJson is required for the PlaydarCollection find_package(QJSON) set_package_properties( QJSON PROPERTIES DESCRIPTION "Qt JSON Parser used for the Playdar Collection" URL "http://qjson.sourceforge.net/" TYPE OPTIONAL ) # We tell users that we need 1.0.3, but we really check just >= 1.0.0. This is because # upstream forgot to update version in lastfm/global.h, so it looks like 1.0.2. :-( # will be fixed in liblastfm-1.0.4 set( LIBLASTFM_MIN_VERSION "1.0.3" ) - set_package_properties( LIBLASTFM PROPERTIES DESCRIPTION "Enable Last.Fm service, including scrobbling, song submissions, and suggested song dynamic playlists" URL "http://cdn.last.fm/client/liblastfm-${LIBLASTFM_MIN_VERSION}.tar.gz" TYPE OPTIONAL ) + set_package_properties( LibLastFm PROPERTIES DESCRIPTION "Enable Last.Fm service, including scrobbling, song submissions, and suggested song dynamic playlists" URL "http://cdn.last.fm/client/liblastfm-${LIBLASTFM_MIN_VERSION}.tar.gz" TYPE OPTIONAL ) if( LIBOFA_FOUND ) - set_package_properties(LIBOFA PROPERTIES DESCRIPTION "Enable MusicDNS service" URL "http://code.google.com/p/musicip-libofa/" TYPE OPTIONAL) + set_package_properties(LibOFA PROPERTIES DESCRIPTION "Enable MusicDNS service" URL "http://code.google.com/p/musicip-libofa/" TYPE OPTIONAL) endif() ## gpodder Service - ## Currently disabled as it can crash Amarok on startup, during dynamic linking phase, before main() is reached. - # find_package( Mygpo-qt 1.0.7 QUIET ) - # set_package_properties( LIBMYGPO_QT PROPERTIES DESCRIPTION "Enable gpodder.net service" URL "http://wiki.gpodder.org/wiki/Libmygpo-qt" TYPE OPTIONAL ) - # macro_bool_to_01( LIBMYGPO_QT_FOUND HAVE_LIBMYGPOQT ) + find_package( Mygpo-qt5 ) + set_package_properties( Mygpo-qt5 PROPERTIES DESCRIPTION "Enable gpodder.net service" URL "http://wiki.gpodder.org/wiki/Libmygpo-qt" TYPE OPTIONAL ) if( WITH_IPOD ) find_package(Ipod) set(IPOD_MIN_VERSION "0.8.2") if( IPOD_FOUND AND NOT WIN32 ) if ( ${IPOD_MIN_VERSION} VERSION_LESS ${IPOD_VERSION} ) set( IPOD_FOUND TRUE ) endif() endif() - set_package_properties( IPOD PROPERTIES DESCRIPTION "Support Apple iPod/iPad/iPhone audio devices" URL "http://sourceforge.net/projects/gtkpod/" TYPE OPTIONAL ) + set_package_properties( Ipod PROPERTIES DESCRIPTION "Support Apple iPod/iPad/iPhone audio devices" URL "http://sourceforge.net/projects/gtkpod/" TYPE OPTIONAL ) find_package(GDKPixBuf) - set_package_properties( GDKPIXBUF PROPERTIES DESCRIPTION "Support for artwork on iPod audio devices via GDK-PixBuf" URL "http://developer.gnome.org/arch/imaging/gdkpixbuf.html" TYPE OPTIONAL ) + set_package_properties( GDKPixBuf PROPERTIES DESCRIPTION "Support for artwork on iPod audio devices via GDK-PixBuf" URL "http://developer.gnome.org/arch/imaging/gdkpixbuf.html" TYPE OPTIONAL ) endif() find_package(Mtp) - set_package_properties(MTP PROPERTIES DESCRIPTION "Enable Support for portable media devices that use the media transfer protocol" URL "http://libmtp.sourceforge.net/" TYPE OPTIONAL ) + set_package_properties( Mtp PROPERTIES DESCRIPTION "Enable Support for portable media devices that use the media transfer protocol" URL "http://libmtp.sourceforge.net/" TYPE OPTIONAL ) if( WITH_MP3Tunes ) find_package(CURL) - set_package_properties( MTP PROPERTIES DESCRIPTION "Enable Support for portable media devices that use the media transfer protocol" URL "http://libmtp.sourceforge.net/" TYPE OPTIONAL ) + set_package_properties( CURL PROPERTIES DESCRIPTION "Used to transfer data with URLs" URL "https://curl.haxx.se/" TYPE OPTIONAL ) find_package(LibXml2) - set_package_properties( LIBXML2 PROPERTIES DESCRIPTION "LibXML2 is an XML parser required by mp3tunes." URL "http://www.xmlsoft.org" TYPE OPTIONAL ) + set_package_properties( LibXml2 PROPERTIES DESCRIPTION "LibXML2 is an XML parser required by mp3tunes." URL "http://www.xmlsoft.org" TYPE OPTIONAL ) find_package(OpenSSL) find_package(Libgcrypt) if ( OPENSSL_FOUND OR LIBGCRYPT_FOUND ) set (_mp3tunes_crypto TRUE ) else () message( SEND_ERROR "Building with mp3tunes support REQUIRES either OpenSSL or GNU Libgcrypt" ) endif () - set_package_properties( OPENSSL_OR_LIBGCRYPT PROPERTIES DESCRIPTION "OpenSSL or GNU Libgcrypt provides cryptographic functions required by mp3tunes." URL "http://www.openssl.org/ or http://www.gnupg.org/download/#libgcrypt" TYPE OPTIONAL ) + set_package_properties( OpenSSL PROPERTIES DESCRIPTION "OpenSSL or GNU Libgcrypt provides cryptographic functions required by mp3tunes." URL "http://www.openssl.org/ or http://www.gnupg.org/download/#libgcrypt" TYPE OPTIONAL ) + set_package_properties( Libgcrypt PROPERTIES DESCRIPTION "OpenSSL or GNU Libgcrypt provides cryptographic functions required by mp3tunes." URL "http://www.openssl.org/ or http://www.gnupg.org/download/#libgcrypt" TYPE OPTIONAL ) find_package(Loudmouth) - set_package_properties( LOUDMOUTH PROPERTIES DESCRIPTION "Loudmouth is the communication backend needed by mp3tunes for syncing." URL "http://www.loudmouth-project.org" TYPE OPTIONAL ) + set_package_properties( Loudmouth PROPERTIES DESCRIPTION "Loudmouth is the communication backend needed by mp3tunes for syncing." URL "http://www.loudmouth-project.org" TYPE OPTIONAL ) include(CheckQtGlib) set_package_properties( QT5_GLIB PROPERTIES DESCRIPTION "Qt5 must be compiled with glib support for mp3tunes" URL "http://www.trolltech.com" TYPE OPTIONAL ) endif() if( WITH_IPOD OR WITH_MP3Tunes ) - pkg_search_module(GOBJECT REQUIRED gobject-2.0) - set_package_properties( GOBJECT PROPERTIES DESCRIPTION "Required by libgpod and mp3tunes." URL "http://www.gtk.org" TYPE OPTIONAL ) - pkg_search_module(GLIB2 glib-2.0) - set_package_properties( GLIB2 PROPERTIES DESCRIPTION "Required by libgpod and mp3tunes" URL "http://www.gtk.org" TYPE OPTIONAL ) + pkg_search_module( GOBJECT REQUIRED gobject-2.0 ) + set_package_properties( GOBJECT PROPERTIES DESCRIPTION "Required by libgpod and mp3tunes." URL "http://www.gtk.org" TYPE OPTIONAL ) + pkg_search_module( GLIB2 glib-2.0 ) + set_package_properties( GLIB2 PROPERTIES DESCRIPTION "Required by libgpod and mp3tunes" URL "http://www.gtk.org" TYPE OPTIONAL ) endif() find_program( CLAMZ_FOUND clamz PATH ) set_package_properties( CLAMZ PROPERTIES DESCRIPTION "Optional requirement to download songs from the Amazon MP3 store. Highly recommended on Linux, as the official downloader from Amazon is quite broken on many systems." URL "https://code.google.com/p/clamz/" TYPE OPTIONAL ) find_package(PythonInterp) set_package_properties( PYTHON PROPERTIES DESCRIPTION "Required for generating the autocompletion file for the script console" URL "https://www.python.org" TYPE OPTIONAL ) if( BUILD_TESTING AND NOT WIN32 ) enable_testing() # add_subdirectory( tests ) endif() add_subdirectory( src ) - # Also display taglib in the feature log - set_package_properties( TAGLIB PROPERTIES DESCRIPTION "Support for Audio metadata." URL "http://developer.kde.org/~wheeler/taglib.html" TYPE REQUIRED PURPOSE "Required for tag reading" ) - # following line is here (and not near TAGLIB_MOD_FOUND) because there may be no MacroLogFeature without kdelibs - set_package_properties( TAGLIB_MOD PROPERTIES DESCRIPTION "Additional support for Audio metadata of mod, s3m, it and xm files." URL "http://developer.kde.org/~wheeler/taglib.html" TYPE OPTIONAL ) - set_package_properties( TAGLIB_OPUS PROPERTIES DESCRIPTION "Additional support for Audio metadata of opus files." URL "http://developer.kde.org/~wheeler/taglib.html" TYPE OPTIONAL ) - feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) #Do not remove or modify these. The release script substitutes in for these #comments with appropriate doc and translation directories. #PO_SUBDIR #DOC_SUBDIR endif() if( WITH_UTILITIES ) set(EXEC_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} CACHE PATH "Base directory for executables and libraries" FORCE) set(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" CACHE PATH "The subdirectory to the binaries prefix (default prefix/bin)" FORCE) add_subdirectory( utilities ) endif() if( WITH_PLAYGROUND ) add_subdirectory( playground ) message(STATUS "Included playground subdirectory in configuration") endif() include(CTest) diff --git a/cmake/modules/FindLibmygpo-qt.cmake b/cmake/modules/FindLibmygpo-qt.cmake deleted file mode 100644 index a1af5426eb..0000000000 --- a/cmake/modules/FindLibmygpo-qt.cmake +++ /dev/null @@ -1,48 +0,0 @@ - -# - Find libmygpo-qt -# Find the libmygpo-qt includes and the libmygpo-qt libraries -# This module defines -# LIBMYGPO_QT_INCLUDE_DIR, root mygpo-qt include dir -# LIBMYGPO_QT_LIBRARY, the path to libmygpo-qt -# LIBMYGPO_QT_FOUND, whether libmygpo-qt was found - - -find_path(LIBMYGPO_QT_INCLUDE_DIR NAMES ApiRequest.h - HINTS - ~/usr/include - /opt/local/include - /usr/include - /usr/local/include - /opt/kde4/include - ~/kde/include - PATH_SUFFIXES mygpo-qt -) - -find_library( LIBMYGPO_QT_LIBRARY NAMES mygpo-qt5 - PATHS - ~/usr/lib - ~/usr/lib64 - /opt/local/lib - /opt/local/lib64 - /usr/lib - /usr/lib64 - /usr/local/lib - /usr/local/lib64 - /opt/kde4/lib - /opt/kde4/lib64 - ~/kde/lib - ~/kde/lib64 -) - - -if(LIBMYGPO_QT_INCLUDE_DIR AND LIBMYGPO_QT_LIBRARY) - set(LIBMYGPO_QT_FOUND TRUE) - message(STATUS "Found libmygpo-qt: ${LIBMYGPO_QT_INCLUDE_DIR}, ${LIBMYGPO_QT_LIBRARY}") -else() - set(LIBMYGPO_QT_FOUND FALSE) - if (LIBMYGPO_QT_FIND_REQUIRED) - message(FATAL_ERROR "Could NOT find required package libmygpo-qt") - endif() -endif() - -mark_as_advanced(LIBMYGPO_QT_INCLUDE_DIR LIBMYGPO_QT_LIBRARY) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a1954b7777..5fab447fed 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,978 +1,978 @@ # Improves speed of string concatenation add_definitions(-DQT_USE_FAST_CONCATENATION) add_definitions(-DQT_USE_FAST_OPERATOR_PLUS) if(NOT MSVC) add_definitions(-DQT_STRICT_ITERATORS) endif() if(APPLE) set(mac_SRCS app_mac.cpp mac/GrowlInterface.cpp ) # Notification Center Appeared in 10.8, or Darwin 12 if( CMAKE_SYSTEM_VERSION VERSION_GREATER "11.9.9") list(APPEND mac_SRCS mac/MacSystemNotify.mm) add_definitions(-DHAVE_NOTIFICATION_CENTER) endif() include_directories ( services/lastfm/ ) set( MAC_FILES_DIR ${CMAKE_SOURCE_DIR}/src/mac ) endif() include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) add_subdirectory( core ) add_subdirectory( core-impl/collections ) add_subdirectory( core-impl/storage/sql ) add_subdirectory( context ) -#add_subdirectory( services ) #FIXME: disabled temporarily for KF5 porting: Port this to KF5::Plasma after the rest of the code has been ported to the other KF5 components. +add_subdirectory( services ) add_subdirectory( scripting/scripts ) add_subdirectory( aboutdialog/libattica-ocsclient ) add_subdirectory( transcoding ) add_subdirectory( kconf_update ) #add_subdirectory( importers ) #FIXME: disabled temporarily for KF5 porting: Port this to KF5::Plasma after the rest of the code has been ported to the other KF5 components. ##################################################################### # PROXYCOLLECTION ##################################################################### set(aggregatecollection_SRCS core-impl/collections/aggregate/AggregateCollection.cpp core-impl/collections/aggregate/AggregateMeta.cpp core-impl/collections/aggregate/AggregateQueryMaker.cpp ) ##################################################################### # MEDIADEVICEFRAMEWORK ##################################################################### set(libmediadeviceframework_SRCS core-impl/collections/mediadevicecollection/MediaDeviceCollection.cpp core-impl/collections/mediadevicecollection/MediaDeviceCollectionLocation.cpp core-impl/collections/mediadevicecollection/MediaDeviceMeta.cpp core-impl/collections/mediadevicecollection/MediaDeviceTrackEditor.cpp core-impl/collections/mediadevicecollection/handler/MediaDeviceHandler.cpp core-impl/collections/mediadevicecollection/handler/MediaDeviceHandlerCapability.cpp core-impl/collections/mediadevicecollection/handler/capabilities/ArtworkCapability.cpp core-impl/collections/mediadevicecollection/handler/capabilities/PlaylistCapability.cpp core-impl/collections/mediadevicecollection/handler/capabilities/PodcastCapability.cpp core-impl/collections/mediadevicecollection/handler/capabilities/ReadCapability.cpp core-impl/collections/mediadevicecollection/handler/capabilities/WriteCapability.cpp core-impl/collections/mediadevicecollection/playlist/MediaDevicePlaylist.cpp core-impl/collections/mediadevicecollection/playlist/MediaDeviceUserPlaylistProvider.cpp core-impl/collections/mediadevicecollection/podcast/MediaDevicePodcastProvider.cpp core-impl/collections/mediadevicecollection/support/ConnectionAssistant.cpp core-impl/collections/mediadevicecollection/support/MediaDeviceInfo.cpp ) ##################################################################### # SERVICEFRAMEWORK ##################################################################### set(libserviceframework_SRCS services/DynamicServiceQueryMaker.cpp services/InfoParserBase.cpp services/ServiceAlbumCoverDownloader.cpp services/ServiceBase.cpp services/ServiceCapabilities.cpp services/ServiceCollection.cpp services/ServiceCollectionLocation.cpp services/ServiceCollectionTreeView.cpp services/ServiceMetaBase.cpp services/ServicePluginManager.cpp services/ServiceSqlCollection.cpp services/ServiceSqlQueryMaker.cpp services/ServiceSqlRegistry.cpp ) ##################################################################### # SERVICEBROWSER ##################################################################### set(libservicebrowser_SRCS browsers/servicebrowser/ServiceBrowser.cpp ) ##################################################################### # AMAROKURL ##################################################################### set(libamarokurl_SRCS amarokurls/AmarokUrl.cpp amarokurls/AmarokUrlAction.cpp amarokurls/AmarokUrlHandler.cpp amarokurls/BookmarkCurrentButton.cpp amarokurls/ContextUrlGenerator.cpp amarokurls/ContextUrlRunner.cpp amarokurls/NavigationUrlRunner.cpp amarokurls/NavigationUrlGenerator.cpp amarokurls/PlayUrlRunner.cpp amarokurls/PlayUrlGenerator.cpp amarokurls/BookmarkManager.cpp amarokurls/BookmarkManagerWidget.cpp amarokurls/BookmarkGroup.cpp amarokurls/BookmarkModel.cpp amarokurls/BookmarkTreeView.cpp amarokurls/BookmarkMetaActions.cpp ) ##################################################################### # SCRIPTABLESERVICE ##################################################################### set(libscriptableservice_SRCS services/scriptable/ScriptableService.cpp services/scriptable/ScriptableServiceCollection.cpp services/scriptable/ScriptableServiceCollectionTreeModel.cpp services/scriptable/ScriptableServiceInfoParser.cpp services/scriptable/ScriptableServiceManager.cpp services/scriptable/ScriptableServiceMeta.cpp services/scriptable/ScriptableServiceQueryMaker.cpp ) ##################################################################### # CONFIGDIALOG ##################################################################### set(libconfigdialog_SRCS configdialog/ConfigDialog.cpp configdialog/ConfigDialogBase.cpp configdialog/dialogs/CollectionConfig.cpp configdialog/dialogs/ExcludedLabelsDialog.cpp configdialog/dialogs/GeneralConfig.cpp configdialog/dialogs/MetadataConfig.cpp configdialog/dialogs/NotificationsConfig.cpp configdialog/dialogs/PlaybackConfig.cpp configdialog/dialogs/PluginsConfig.cpp configdialog/dialogs/ScriptsConfig.cpp configdialog/dialogs/ScriptSelector.cpp configdialog/dialogs/DatabaseConfig.cpp ) ki18n_wrap_ui(libconfigdialog_SRCS configdialog/dialogs/CollectionConfig.ui configdialog/dialogs/GeneralConfig.ui configdialog/dialogs/MetadataConfig.ui configdialog/dialogs/ExcludedLabelsDialog.ui configdialog/dialogs/NotificationsConfig.ui configdialog/dialogs/PlaybackConfig.ui configdialog/dialogs/DatabaseConfig.ui configdialog/dialogs/ScriptsConfig.ui ) set(libbrowserframework_SRCS browsers/BrowserBreadcrumbItem.cpp browsers/BrowserBreadcrumbWidget.cpp browsers/BrowserCategory.cpp browsers/BrowserCategoryList.cpp browsers/BrowserCategoryListModel.cpp browsers/BrowserCategoryListSortFilterProxyModel.cpp browsers/BrowserDock.cpp browsers/BrowserMessageArea.cpp browsers/CollectionSortFilterProxyModel.cpp browsers/CollectionTreeItem.cpp browsers/CollectionTreeItemModel.cpp browsers/CollectionTreeItemModelBase.cpp browsers/CollectionTreeView.cpp browsers/InfoProxy.cpp browsers/SingleCollectionTreeItemModel.cpp ) ##################################################################### # COLLECTIONBROWSER ##################################################################### set(libcollectionbrowser_SRCS browsers/collectionbrowser/CollectionBrowserTreeView.cpp browsers/collectionbrowser/CollectionWidget.cpp ) ##################################################################### # SYNCHRONIZATION ##################################################################### set(libsynchronization_SRCS synchronization/MasterSlaveSynchronizationJob.cpp synchronization/OneWaySynchronizationJob.cpp synchronization/SynchronizationBaseJob.cpp synchronization/UnionJob.cpp ) ##################################################################### # STATUSBAR ##################################################################### set(libstatusbar_SRCS statusbar/ProgressBar.cpp statusbar/KJobProgressBar.cpp statusbar/NetworkProgressBar.cpp statusbar/CompoundProgressBar.cpp statusbar/PopupWidget.cpp statusbar/LongMessageWidget.cpp ) ##################################################################### # META ##################################################################### set(libmetaimpl_SRCS core-impl/playlists/providers/user/UserPlaylistProvider.cpp core-impl/playlists/types/file/asx/ASXPlaylist.cpp core-impl/playlists/types/file/m3u/M3UPlaylist.cpp core-impl/playlists/types/file/pls/PLSPlaylist.cpp core-impl/playlists/types/file/PlaylistFileLoaderJob.cpp core-impl/playlists/types/file/PlaylistFileSupport.cpp core-impl/playlists/types/file/xspf/XSPFPlaylist.cpp core-impl/capabilities/AlbumActionsCapability.cpp core-impl/capabilities/timecode/TimecodeBoundedPlaybackCapability.cpp core-impl/capabilities/timecode/TimecodeLoadCapability.cpp core-impl/capabilities/timecode/TimecodeWriteCapability.cpp core-impl/capabilities/multisource/MultiSourceCapabilityImpl.cpp core-impl/meta/file/File.cpp core-impl/meta/file/FileTrackProvider.cpp core-impl/meta/multi/MultiTrack.cpp core-impl/meta/cue/CueFileSupport.cpp core-impl/meta/proxy/MetaProxy.cpp core-impl/meta/proxy/MetaProxyWorker.cpp core-impl/meta/stream/Stream.cpp core-impl/playlists/types/file/PlaylistFile.cpp core-impl/support/PersistentStatisticsStore.cpp core-impl/support/TagStatisticsStore.cpp core-impl/support/UrlStatisticsStore.cpp ) ##################################################################### # COLLECTION ##################################################################### set(collection_SRCS core-impl/collections/support/jobs/WriteTagsJob.cpp core-impl/collections/support/ArtistHelper.cpp core-impl/collections/support/CollectionManager.cpp core-impl/collections/support/CollectionLocationDelegateImpl.cpp core-impl/collections/support/MemoryCustomValue.cpp core-impl/collections/support/MemoryFilter.cpp core-impl/collections/support/MemoryMatcher.cpp core-impl/collections/support/MemoryMeta.cpp core-impl/collections/support/MemoryQueryMaker.cpp core-impl/collections/support/MemoryQueryMakerInternal.cpp core-impl/collections/support/MemoryQueryMakerHelper.cpp core-impl/collections/support/TrashCollectionLocation.cpp core-impl/collections/support/XmlQueryReader.cpp core-impl/collections/support/FileCollectionLocation.cpp core-impl/collections/support/Expression.cpp core-impl/collections/support/TextualQueryFilter.cpp ) ##################################################################### # STORAGE ##################################################################### set(storage_SRCS core-impl/storage/StorageManager.cpp ) ##################################################################### # SCANNER ##################################################################### set( scanner_SRCS scanner/GenericScanManager.cpp scanner/GenericScannerJob.cpp scanner/AbstractDirectoryWatcher.cpp scanner/AbstractScanResultProcessor.cpp ) ##################################################################### # CONTEXT ##################################################################### set( libcontextview_SRCS context/AmarokContextPackageStructure.cpp context/AppletLoader.cpp context/AppletModel.cpp context/ContextDock.cpp context/ContextView.cpp context/LyricsManager.cpp ) ##################################################################### # PODCASTS ##################################################################### set(libpodcasts_SRCS core-impl/podcasts/sql/SqlPodcastMeta.cpp core-impl/podcasts/sql/SqlPodcastProvider.cpp core-impl/podcasts/sql/PodcastSettingsDialog.cpp core-impl/podcasts/sql/PodcastFilenameLayoutConfigDialog.cpp ) ##################################################################### # PLAYLISTBROWSER ##################################################################### set(libplaylistbrowser_SRCS browsers/playlistbrowser/APGCategory.cpp browsers/playlistbrowser/DynamicCategory.cpp browsers/playlistbrowser/DynamicBiasDelegate.cpp browsers/playlistbrowser/DynamicBiasDialog.cpp browsers/playlistbrowser/DynamicView.cpp browsers/playlistbrowser/PlaylistBrowserFilterProxy.cpp browsers/playlistbrowser/PlaylistBrowserModel.cpp browsers/playlistbrowser/PlaylistBrowserCategory.cpp browsers/playlistbrowser/QtGroupingProxy.cpp browsers/playlistbrowser/PlaylistBrowser.cpp browsers/playlistbrowser/PlaylistBrowserView.cpp browsers/playlistbrowser/UserPlaylistCategory.cpp browsers/playlistbrowser/PlaylistsInFoldersProxy.cpp browsers/playlistbrowser/PlaylistsByProviderProxy.cpp browsers/playlistbrowser/PodcastModel.cpp browsers/playlistbrowser/PodcastCategory.cpp browsers/playlistbrowser/UserPlaylistModel.cpp ) ##################################################################### # PLAYLISTMANAGER ##################################################################### set(libplaylistmanager_SRCS playlistmanager/PlaylistManager.cpp playlistmanager/file/PlaylistFileProvider.cpp playlistmanager/file/KConfigSyncRelStore.cpp playlistmanager/sql/SqlUserPlaylistProvider.cpp playlistmanager/sql/SqlPlaylist.cpp playlistmanager/sql/SqlPlaylistGroup.cpp playlistmanager/SyncedPlaylist.cpp playlistmanager/SyncedPodcast.cpp playlistmanager/SyncRelationStorage.cpp ) ##################################################################### # PLAYLIST ##################################################################### set(libplaylist_SRCS playlist/PlaylistActions.cpp playlist/PlaylistBreadcrumbItem.cpp playlist/PlaylistBreadcrumbItemSortButton.cpp playlist/PlaylistBreadcrumbLevel.cpp playlist/PlaylistDefines.cpp playlist/PlaylistController.cpp playlist/PlaylistInfoWidget.cpp playlist/PlaylistItem.cpp playlist/PlaylistModel.cpp playlist/PlaylistModelStack.cpp playlist/PlaylistRestorer.cpp playlist/PlaylistQueueEditor.cpp playlist/PlaylistSortWidget.cpp playlist/PlaylistViewUrlGenerator.cpp playlist/PlaylistViewUrlRunner.cpp playlist/PlaylistDock.cpp playlist/PlaylistToolBar.cpp playlist/ProgressiveSearchWidget.cpp playlist/UndoCommands.cpp playlist/layouts/LayoutEditDialog.cpp playlist/layouts/LayoutEditWidget.cpp playlist/layouts/LayoutConfigAction.cpp playlist/layouts/LayoutItemConfig.cpp playlist/layouts/LayoutManager.cpp playlist/layouts/PlaylistLayoutEditDialog.cpp playlist/navigators/AlbumNavigator.cpp playlist/navigators/DynamicTrackNavigator.cpp playlist/navigators/FavoredRandomTrackNavigator.cpp playlist/navigators/NavigatorConfigAction.cpp playlist/navigators/NonlinearTrackNavigator.cpp playlist/navigators/RandomAlbumNavigator.cpp playlist/navigators/RandomTrackNavigator.cpp playlist/navigators/RepeatAlbumNavigator.cpp playlist/navigators/RepeatTrackNavigator.cpp playlist/navigators/StandardTrackNavigator.cpp playlist/navigators/TrackNavigator.cpp playlist/view/PlaylistViewCommon.cpp playlist/view/listview/InlineEditorWidget.cpp playlist/view/listview/PrettyItemDelegate.cpp playlist/view/listview/PrettyListView.cpp playlist/view/listview/SourceSelectionPopup.cpp playlist/proxymodels/GroupingProxy.cpp playlist/proxymodels/ProxyBase.cpp playlist/proxymodels/SortAlgorithms.cpp playlist/proxymodels/SortFilterProxy.cpp playlist/proxymodels/SortScheme.cpp playlist/proxymodels/SearchProxy.cpp ) ki18n_wrap_ui(libplaylist_SRCS playlist/PlaylistQueueEditor.ui ) ##################################################################### # DYNAMIC ##################################################################### set(libdynamic_SRCS dynamic/TrackSet.cpp dynamic/BiasFactory.cpp dynamic/BiasedPlaylist.cpp dynamic/BiasSolver.cpp dynamic/DynamicPlaylist.cpp dynamic/DynamicModel.cpp # biases dynamic/Bias.cpp dynamic/biases/AlbumPlayBias.cpp dynamic/biases/EchoNestBias.cpp dynamic/biases/IfElseBias.cpp dynamic/biases/PartBias.cpp dynamic/biases/QuizPlayBias.cpp dynamic/biases/TagMatchBias.cpp dynamic/biases/SearchQueryBias.cpp ) ##################################################################### # DBUS ##################################################################### set(dbus_SRCS dbus/mpris1/RootHandler.cpp dbus/mpris1/PlayerHandler.cpp dbus/mpris1/TrackListHandler.cpp dbus/mpris2/DBusAbstractAdaptor.cpp dbus/mpris2/Mpris2.cpp dbus/mpris2/MediaPlayer2.cpp dbus/mpris2/MediaPlayer2Player.cpp dbus/mpris2/MediaPlayer2AmarokExtensions.cpp dbus/mpris2/DBusAmarokApp.cpp dbus/CollectionDBusHandler.cpp dbus/DBusQueryHelper.cpp ) ##################################################################### # SCRIPTING INTERFACE ##################################################################### set(scriptengine_SRCS scripting/scriptengine/AmarokBookmarkScript.cpp scripting/scriptengine/AmarokCollectionScript.cpp scripting/scriptengine/AmarokCollectionViewScript.cpp scripting/scriptengine/AmarokEngineScript.cpp scripting/scriptengine/AmarokEqualizerScript.cpp scripting/scriptengine/AmarokInfoScript.cpp scripting/scriptengine/AmarokKNotifyScript.cpp scripting/scriptengine/AmarokLyricsScript.cpp scripting/scriptengine/AmarokNetworkScript.cpp scripting/scriptengine/AmarokOSDScript.cpp scripting/scriptengine/AmarokPlaylistManagerScript.cpp scripting/scriptengine/AmarokPlaylistScript.cpp scripting/scriptengine/AmarokScript.cpp scripting/scriptengine/AmarokScriptConfig.cpp scripting/scriptengine/AmarokScriptableServiceScript.cpp scripting/scriptengine/AmarokServicePluginManagerScript.cpp scripting/scriptengine/AmarokStatusbarScript.cpp scripting/scriptengine/AmarokStreamItemScript.cpp scripting/scriptengine/AmarokWindowScript.cpp scripting/scriptengine/AmarokScriptXml.cpp scripting/scriptengine/ScriptImporter.cpp scripting/scriptengine/ScriptingDefines.cpp scripting/scriptengine/exporters/CollectionTypeExporter.cpp scripting/scriptengine/exporters/MetaTypeExporter.cpp scripting/scriptengine/exporters/PlaylistExporter.cpp scripting/scriptengine/exporters/PlaylistProviderExporter.cpp scripting/scriptengine/exporters/QueryMakerExporter.cpp scripting/scriptengine/exporters/ScriptableBiasExporter.cpp ) set(scriptconsole_SRCS scripting/scriptconsole/CompletionModel.cpp scripting/scriptconsole/ScriptConsole.cpp scripting/scriptconsole/ScriptEditorDocument.cpp scripting/scriptconsole/ScriptConsoleItem.cpp ) if (PYTHONINTERP_FOUND) execute_process(COMMAND "${PYTHON_EXECUTABLE}" ${CMAKE_SOURCE_DIR}/src/scripting/scriptengine/PHAACG2.py ${CMAKE_SOURCE_DIR}/src/scripting/scriptengine ${CMAKE_BINARY_DIR}/scriptconsole) install(FILES ${CMAKE_BINARY_DIR}/scriptconsole/AutoComplete.txt DESTINATION ${DATA_INSTALL_DIR}/amarok/scriptconsole) endif() ##################################################################### # PLAYLIST GENERATOR ##################################################################### set(apg_SRCS playlistgenerator/Constraint.cpp playlistgenerator/ConstraintGroup.cpp playlistgenerator/ConstraintFactory.cpp playlistgenerator/ConstraintNode.cpp playlistgenerator/ConstraintSolver.cpp playlistgenerator/Preset.cpp playlistgenerator/PresetEditDialog.cpp playlistgenerator/PresetModel.cpp playlistgenerator/TreeController.cpp playlistgenerator/TreeModel.cpp playlistgenerator/constraints/Checkpoint.cpp playlistgenerator/constraints/Matching.cpp playlistgenerator/constraints/PlaylistDuration.cpp playlistgenerator/constraints/PlaylistFileSize.cpp playlistgenerator/constraints/PlaylistLength.cpp playlistgenerator/constraints/PreventDuplicates.cpp playlistgenerator/constraints/TagMatch.cpp playlistgenerator/constraints/TagMatchSupport.cpp playlistgenerator/constraints/TrackSpreader.cpp ) ki18n_wrap_ui(apg_SRCS playlistgenerator/ConstraintGroupEditWidget.ui playlistgenerator/PresetEditDialog.ui playlistgenerator/constraints/CheckpointEditWidget.ui playlistgenerator/constraints/PlaylistDurationEditWidget.ui playlistgenerator/constraints/PlaylistFileSizeEditWidget.ui playlistgenerator/constraints/PlaylistLengthEditWidget.ui playlistgenerator/constraints/PreventDuplicatesEditWidget.ui playlistgenerator/constraints/TagMatchEditWidget.ui ) ##################################################################### # NETWORK ACCESS ##################################################################### set(network_access_SRCS network/NetworkAccessManagerProxy.cpp ) if( CMAKE_BUILD_TYPE_TOLOWER MATCHES debug ) set(network_access_SRCS ${network_access_SRCS} network/NetworkAccessViewer.cpp ) ki18n_wrap_ui(network_access_SRCS network/NetworkRequests.ui ) endif() ##################################################################### # STATISTICS SYNCHRONIZATION ##################################################################### set( statsyncing_SRCS statsyncing/Config.cpp statsyncing/Controller.cpp statsyncing/Options.cpp statsyncing/Process.cpp statsyncing/Provider.cpp statsyncing/ProviderFactory.cpp statsyncing/ScrobblingService.cpp statsyncing/SimpleTrack.cpp statsyncing/SimpleWritableTrack.cpp statsyncing/Track.cpp statsyncing/TrackTuple.cpp statsyncing/collection/CollectionProvider.cpp statsyncing/collection/CollectionTrack.cpp statsyncing/jobs/MatchTracksJob.cpp statsyncing/jobs/SynchronizeTracksJob.cpp statsyncing/models/CommonModel.cpp statsyncing/models/MatchedTracksModel.cpp statsyncing/models/ProvidersModel.cpp statsyncing/models/SingleTracksModel.cpp statsyncing/ui/ChooseProvidersPage.cpp statsyncing/ui/CreateProviderDialog.cpp statsyncing/ui/ConfigureProviderDialog.cpp statsyncing/ui/MatchedTracksPage.cpp statsyncing/ui/TrackDelegate.cpp ) ki18n_wrap_ui( statsyncing_SRCS statsyncing/ui/ChooseProvidersPage.ui statsyncing/ui/MatchedTracksPage.ui ) ##################################################################### # STATISTICS IMPORTERS ##################################################################### set( importers_SRCS importers/ImporterManager.cpp importers/ImporterProvider.cpp importers/ImporterSqlConnection.cpp importers/SimpleImporterConfigWidget.cpp ) ##################################################################### # LIBAMAROK ##################################################################### set(amaroklib_LIB_SRCS ${libscriptableservice_SRCS} ${libbrowserframework_SRCS} ${libcontextview_SRCS} ${libcollectionbrowser_SRCS} ${libconfigdialog_SRCS} ${libplaylist_SRCS} ${aggregatecollection_SRCS} ${libpodcasts_SRCS} ${libmediadeviceframework_SRCS} ${libserviceframework_SRCS} ${libservicebrowser_SRCS} ${libdynamic_SRCS} ${libmetaimpl_SRCS} ${apg_SRCS} ${collection_SRCS} ${storage_SRCS} ${scanner_SRCS} ${mac_SRCS} ${network_access_SRCS} ${libplaylistbrowser_SRCS} ${libplaylistmanager_SRCS} ${dbus_SRCS} ${scriptengine_SRCS} ${scriptconsole_SRCS} ${libstatusbar_SRCS} ${libamarokurl_SRCS} ${libsynchronization_SRCS} ${statsyncing_SRCS} # ${importers_SRCS} #FIXME: disabled temporarily for KF5 porting: Port this to KF5::Plasma after the rest of the code has been ported to the other KF5 components. core-impl/logger/ProxyLogger.cpp aboutdialog/AnimatedBarWidget.cpp aboutdialog/AnimatedWidget.cpp aboutdialog/ExtendedAboutDialog.cpp aboutdialog/FramedLabel.cpp aboutdialog/OcsData.cpp aboutdialog/OcsPersonItem.cpp aboutdialog/OcsPersonListWidget.cpp ActionClasses.cpp AmarokMimeData.cpp AmarokProcess.cpp App.cpp CaseConverter.cpp EngineController.cpp KNotificationBackend.cpp MainWindow.cpp MediaDeviceCache.cpp MediaDeviceMonitor.cpp PluginManager.cpp QStringx.cpp scripting/scriptmanager/ScriptManager.cpp scripting/scriptmanager/ScriptItem.cpp scripting/scriptmanager/ScriptUpdater.cpp SvgHandler.cpp SvgTinter.cpp TrayIcon.cpp core-impl/meta/timecode/TimecodeObserver.cpp core-impl/meta/timecode/TimecodeMeta.cpp core-impl/meta/timecode/TimecodeTrackProvider.cpp core-impl/support/TrackLoader.cpp covermanager/CoverCache.cpp covermanager/CoverFetcher.cpp covermanager/CoverFetchingActions.cpp covermanager/CoverFetchQueue.cpp covermanager/CoverFetchUnit.cpp covermanager/CoverFoundDialog.cpp covermanager/CoverManager.cpp covermanager/CoverViewDialog.cpp databaseimporter/SqlBatchImporter.cpp databaseimporter/SqlBatchImporterConfig.cpp dialogs/CollectionSetup.cpp dialogs/DatabaseImporterDialog.cpp dialogs/DiagnosticDialog.cpp dialogs/EditFilterDialog.cpp dialogs/EqualizerDialog.cpp dialogs/MusicBrainzTagger.cpp dialogs/OrganizeCollectionDialog.cpp dialogs/TrackOrganizer.cpp dialogs/TagDialog.cpp dialogs/TagGuesser.cpp dialogs/TagGuesserDialog.cpp dialogs/LabelListModel.cpp equalizer/EqualizerPresets.cpp browsers/filebrowser/DirPlaylistTrackFilterProxyModel.cpp browsers/filebrowser/FileBrowser.cpp browsers/filebrowser/FileView.cpp musicbrainz/MusicBrainzFinder.cpp musicbrainz/MusicBrainzTagsItem.cpp musicbrainz/MusicBrainzTagsModel.cpp musicbrainz/MusicBrainzTagsModelDelegate.cpp musicbrainz/MusicBrainzTagsView.cpp musicbrainz/MusicBrainzXmlParser.cpp OpmlOutline.cpp OpmlParser.cpp OpmlWriter.cpp PaletteHandler.cpp PopupDropperFactory.cpp playback/DelayedDoers.cpp playback/EqualizerController.cpp playback/Fadeouter.cpp playback/PowerManager.cpp statemanagement/ApplicationController.cpp statemanagement/DefaultApplicationController.cpp toolbar/CurrentTrackToolbar.cpp toolbar/SlimToolbar.cpp toolbar/VolumePopupButton.cpp toolbar/MainToolbar.cpp widgets/AlbumBreadcrumbWidget.cpp widgets/AmarokDockWidget.cpp widgets/AnimatedLabelStack.cpp widgets/BoxWidget.cpp widgets/BreadcrumbItemButton.cpp widgets/ClearSpinBox.cpp widgets/CoverLabel.cpp widgets/HintLineEdit.cpp widgets/kdatecombo.cpp widgets/TokenDropTarget.cpp widgets/EditDeleteComboBoxView.cpp widgets/EditDeleteDelegate.cpp widgets/ElidingButton.cpp widgets/FilenameLayoutWidget.cpp widgets/FlowLayout.cpp widgets/HorizontalDivider.cpp widgets/IconButton.cpp widgets/ComboBox.cpp widgets/LineEdit.cpp widgets/Osd.cpp widgets/TimeLabel.cpp widgets/PixmapViewer.cpp widgets/PlayPauseButton.cpp widgets/PrettyTreeView.cpp widgets/PrettyTreeDelegate.cpp widgets/ProgressWidget.cpp widgets/SearchWidget.cpp widgets/SliderWidget.cpp widgets/StarManager.cpp widgets/TokenPool.cpp widgets/Token.cpp widgets/TokenWithLayout.cpp widgets/VolumeDial.cpp widgets/TrackActionButton.cpp widgets/BookmarkTriangle.cpp widgets/BookmarkPopup.cpp widgets/TrackSelectWidget.cpp widgets/MetaQueryWidget.cpp GlobalCollectionActions.cpp GlobalCurrentTrackActions.cpp moodbar/MoodbarManager.cpp ) if( LIBMYGPO_QT_FOUND ) set( EXTRA_LIBS ${LIBMYGPO_QT_LIBRARIES} ) include_directories( ${LIBMYGPO_QT_INCLUDE_DIRS} ${LIBMYGPO_QT_INCLUDE_DIRS}/../ ) endif() if( LIBLASTFM_FOUND ) set(amaroklib_LIB_SRCS ${amaroklib_LIB_SRCS} LastfmReadLabelCapability.cpp ) include_directories( ${LIBLASTFM_INCLUDE_DIR}/.. ${LIBLASTFM_INCLUDE_DIR}) set( EXTRA_LIBS ${LIBLASTFM_LIBRARY} ) endif() if( LIBOFA_FOUND AND AVCODEC_FOUND AND AVFORMAT_FOUND AND AVUTIL_FOUND ) add_definitions( ${AVCODEC_DEFINITIONS} ${AVFORMAT_DEFINITIONS} ${AVUTIL_DEFINITIONS} ) include_directories( ${AVCODEC_INCLUDE_DIRS} ${AVFORMAT_INCLUDE_DIRS} ${AVUTIL_INCLUDE_DIRS} ) set( EXTRA_LIBS ${EXTRA_LIBS} ${LIBOFA_LIBRARY} ${AVFORMAT_LIBRARIES} ${AVCODEC_LIBRARIES} ${AVUTIL_LIBRARIES} ) set( amaroklib_LIB_SRCS ${amaroklib_LIB_SRCS} musicbrainz/MusicDNSAudioDecoder.cpp musicbrainz/MusicDNSFinder.cpp musicbrainz/MusicDNSXmlParser.cpp ) endif() qt5_add_dbus_adaptor( AMAROK_MPRIS1_ADAPTOR_SRCS dbus/mpris1/org.freedesktop.MediaPlayer.root.xml dbus/mpris1/RootHandler.h Mpris1::RootHandler Mpris1RootAdaptor Mpris1RootAdaptor ) qt5_add_dbus_adaptor( AMAROK_MPRIS1_ADAPTOR_SRCS dbus/mpris1/org.freedesktop.MediaPlayer.player.xml dbus/mpris1/PlayerHandler.h Mpris1::PlayerHandler Mpris1PlayerAdaptor Mpris1PlayerAdaptor ) qt5_add_dbus_adaptor( AMAROK_MPRIS1_ADAPTOR_SRCS dbus/mpris1/org.freedesktop.MediaPlayer.tracklist.xml dbus/mpris1/TrackListHandler.h Mpris1::TrackListHandler Mpris1TrackListAdaptor Mpris1TrackListAdaptor ) qt5_add_dbus_adaptor( AMAROK_MPRIS1_ADAPTOR_SRCS dbus/org.kde.amarok.App.xml dbus/mpris1/RootHandler.h Mpris1::RootHandler Mpris1AmarokAppAdaptor Mpris1AmarokAppAdaptor ) qt5_add_dbus_adaptor( AMAROK_MPRIS1_ADAPTOR_SRCS dbus/mpris1/org.kde.amarok.Mpris1Extensions.Player.xml dbus/mpris1/PlayerHandler.h Mpris1::PlayerHandler Mpris1AmarokPlayerAdaptor Mpris1AmarokPlayerAdaptor ) qt5_add_dbus_adaptor( AMAROK_MPRIS1_ADAPTOR_SRCS dbus/org.kde.amarok.Collection.xml dbus/CollectionDBusHandler.h CollectionDBusHandler CollectionAdaptor CollectionAdaptor ) # suppress deprecated methods warnings if(NOT WIN32) set_source_files_properties(${AMAROK_MPRIS1_ADAPTOR_SRCS} PROPERTIES COMPILE_FLAGS -Wno-deprecated-declarations) endif() set( amaroklib_DEPENDS "amarokpud" ) set( amaroklib_DEPENDS "amarokcore" ) set( amaroklib_DEPENDS "amarok-transcoding" ) # depends on generated ui_*.h file kconfig_add_kcfg_files(amaroklib_LIB_SRCS amarokconfig.kcfgc) add_custom_target(amarokconfig_h DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/amarokconfig.h) ki18n_wrap_ui(amaroklib_LIB_SRCS aboutdialog/OcsPersonItem.ui dialogs/EditFilterDialog.ui dialogs/EqualizerDialog.ui dialogs/MusicBrainzTagger.ui dialogs/TagDialogBase.ui dialogs/TagGuessOptions.ui dialogs/OrganizeCollectionOptions.ui dialogs/OrganizeCollectionDialogBase.ui playlist/layouts/PlaylistLayoutEditDialog.ui core-impl/podcasts/sql/PodcastSettingsBase.ui core-impl/podcasts/sql/SqlPodcastProviderSettingsWidget.ui core-impl/podcasts/sql/PodcastFilenameLayoutConfigWidget.ui browsers/playlistbrowser/PodcastCategoryBase.ui ) add_library(amaroklib SHARED ${amaroklib_LIB_SRCS} ${AMAROK_MPRIS1_ADAPTOR_SRCS}) target_link_libraries(amaroklib Phonon::phonon4qt5 KF5::Archive KF5::CoreAddons KF5::Declarative KF5::GlobalAccel KF5::GuiAddons KF5::I18n KF5::IconThemes KF5::KCMUtils KF5::KIOCore KF5::KIOFileWidgets KF5::KIOWidgets KF5::KIONTLM KF5::NewStuff KF5::Notifications KF5::NotifyConfig KF5::Package KF5::TextEditor KF5::ThreadWeaver KF5::WindowSystem ${QT_QTSCRIPTTOOLS_LIBRARY} Qt5::Gui Qt5::Quick Qt5::QuickWidgets Qt5::Script Qt5::ScriptTools Qt5::Sql Qt5::Svg ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBS} amarokpud amarokcore amarokocsclient amarok-transcoding amarokshared ) include_directories(${TAGLIB_INCLUDES}) add_definitions(${TAGLIB_CFLAGS}) target_link_libraries(amaroklib ${TAGLIB_LIBRARIES}) if( TAGLIB-EXTRAS_FOUND ) include_directories(${TAGLIB-EXTRAS_INCLUDES}) add_definitions(${TAGLIB-EXTRAS_CFLAGS}) target_link_libraries(amaroklib ${TAGLIB-EXTRAS_LIBRARIES}) endif() if(WIN32) target_link_libraries(amaroklib Qt5::WebKitWidgets) endif() if(APPLE) target_link_libraries(amaroklib "/System/Library/Frameworks/Foundation.framework") set_target_properties(amaroklib PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") endif() set_target_properties(amaroklib PROPERTIES VERSION 1.0.0 SOVERSION 1 ) install(TARGETS amaroklib ${INSTALL_TARGETS_DEFAULT_ARGS} ) ##################################################################### # AMAROK ##################################################################### set( amarok_SRCS main.cpp ) file(GLOB ICONS_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../images/*-apps-amarok.png) ecm_add_app_icon(amarok_SRCS ICONS ${ICONS_SRCS}) add_executable(amarok ${amarok_SRCS}) if(APPLE) set_target_properties(amarok PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") set(MACOSX_BUNDLE_BUNDLE_NAME "Amarok 2") set(MACOSX_BUNDLE_BUNDLE_VERSION "2.8.0-git") set(MACOSX_BUNDLE_COPYRIGHT "Amarok Team") set_target_properties(amarok PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${MAC_FILES_DIR}/Info.plist.template) set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/amarok.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) endif() target_link_libraries(amarok KF5::CoreAddons KF5::DBusAddons KF5::I18n amarokcore amaroklib ${X11_LIBRARIES} ) install(TARGETS amarok ${INSTALL_TARGETS_DEFAULT_ARGS}) ########### install files ############### install(PROGRAMS org.kde.amarok.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) install(PROGRAMS org.kde.amarok_containers.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) install(FILES org.kde.amarok.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install(FILES amarok-plugin.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR}) install(FILES amarok-contextapplet.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR}) install(FILES amarok_codecinstall.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR}) install(FILES amarok_append.desktop DESTINATION ${SERVICES_INSTALL_DIR}/ServiceMenus) install(FILES amarok-play-audiocd.desktop DESTINATION ${DATA_INSTALL_DIR}/solid/actions) install(FILES amarok.knsrc DESTINATION ${CONFIG_INSTALL_DIR}) # protocol handlers install(FILES amarokurls/amarok.protocol DESTINATION ${SERVICES_INSTALL_DIR}) install(FILES amarokitpc.protocol DESTINATION ${SERVICES_INSTALL_DIR}) #install(FILES amarokpcast.protocol DESTINATION ${SERVICES_INSTALL_DIR}) install(FILES amarokconfig.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) install(FILES dbus/mpris1/org.freedesktop.MediaPlayer.root.xml dbus/mpris1/org.freedesktop.MediaPlayer.player.xml dbus/mpris1/org.freedesktop.MediaPlayer.tracklist.xml dbus/org.kde.amarok.App.xml dbus/org.kde.amarok.Collection.xml dbus/mpris1/org.kde.amarok.Mpris1Extensions.Player.xml dbus/mpris2/org.kde.amarok.Mpris2Extensions.Player.xml DESTINATION ${DBUS_INTERFACES_INSTALL_DIR}) install(FILES services/InfoParserLoading.html browsers/hover_info_template.html DESTINATION ${DATA_INSTALL_DIR}/amarok/data) ecm_install_icons(ICONS DESTINATION ${ICON_INSTALL_DIR} THEME hicolor ) diff --git a/src/core/podcasts/PodcastImageFetcher.cpp b/src/core/podcasts/PodcastImageFetcher.cpp index eb097dbef1..8415e636c2 100644 --- a/src/core/podcasts/PodcastImageFetcher.cpp +++ b/src/core/podcasts/PodcastImageFetcher.cpp @@ -1,154 +1,166 @@ /**************************************************************************************** * Copyright (c) 2009 Bart Cerneels * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "core/podcasts/PodcastImageFetcher.h" #include "core/support/Debug.h" #include #include #include PodcastImageFetcher::PodcastImageFetcher() { } void PodcastImageFetcher::addChannel( Podcasts::PodcastChannelPtr channel ) { DEBUG_BLOCK if( channel->imageUrl().isEmpty() ) { debug() << channel->title() << " does not have an imageUrl"; return; } if( hasCachedImage( channel ) ) { debug() << "using cached image for " << channel->title(); QString imagePath = cachedImagePath( channel ).toLocalFile(); QImage image( imagePath ); if( image.isNull() ) error() << "could not load pixmap from " << imagePath; else channel->setImage( image ); return; } + if( m_channels.contains( channel ) ) + { + debug() << "Channel already queued:" << channel->title(); + return; + } + + if( m_jobChannelMap.values().contains( channel ) ) + { + debug() << "Copy job already running for channel:" << channel->title(); + return; + } + debug() << "Adding " << channel->title() << " to fetch queue"; m_channels.append( channel ); } void PodcastImageFetcher::addEpisode( Podcasts::PodcastEpisodePtr episode ) { Q_UNUSED( episode ); } QUrl PodcastImageFetcher::cachedImagePath( Podcasts::PodcastChannelPtr channel ) { return cachedImagePath( channel.data() ); } QUrl PodcastImageFetcher::cachedImagePath( Podcasts::PodcastChannel *channel ) { QUrl imagePath = channel->saveLocation(); if( imagePath.isEmpty() || !imagePath.isLocalFile() ) imagePath = QUrl::fromLocalFile( Amarok::saveLocation( "podcasts" ) ); QCryptographicHash md5( QCryptographicHash::Md5 ); md5.addData( channel->url().url().toLocal8Bit() ); QString extension = Amarok::extension( channel->imageUrl().fileName() ); imagePath = imagePath.adjusted( QUrl::StripTrailingSlash ); imagePath.setPath( imagePath.path() + '/' + ( md5.result().toHex() + '.' + extension ) ); return imagePath; } bool PodcastImageFetcher::hasCachedImage( Podcasts::PodcastChannelPtr channel ) { DEBUG_BLOCK return QFile( PodcastImageFetcher::cachedImagePath( Podcasts::PodcastChannelPtr::dynamicCast( channel ) ).toLocalFile() ).exists(); } void PodcastImageFetcher::run() { if( m_channels.isEmpty() && m_episodes.isEmpty() && m_jobChannelMap.isEmpty() && m_jobEpisodeMap.isEmpty() ) { //nothing to do emit( done( this ) ); return; } QNetworkConfigurationManager mgr; if( !mgr.isOnline() ) { debug() << "QNetworkConfigurationManager reports we are not online, canceling podcast image download"; emit( done( this ) ); //TODO: schedule another run after Solid reports we are online again return; } foreach( Podcasts::PodcastChannelPtr channel, m_channels ) { QUrl cachedPath = cachedImagePath( channel ); KIO::mkdir( cachedPath.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash) ); KIO::FileCopyJob *job = KIO::file_copy( channel->imageUrl(), cachedPath, -1, KIO::HideProgressInfo | KIO::Overwrite ); //remove channel from the todo list m_channels.removeAll( channel ); m_jobChannelMap.insert( job, channel ); connect( job, &KIO::FileCopyJob::finished, this, &PodcastImageFetcher::slotDownloadFinished ); } //TODO: episodes } void PodcastImageFetcher::slotDownloadFinished( KJob *job ) { DEBUG_BLOCK //QMap::take() also removes the entry from the map. Podcasts::PodcastChannelPtr channel = m_jobChannelMap.take( job ); if( channel.isNull() ) { error() << "got null PodcastChannelPtr " << __FILE__ << ":" << __LINE__; return; } if( job->error() ) { error() << "downloading podcast image " << job->errorString(); } else { QString imagePath = cachedImagePath( channel ).toLocalFile(); QImage image( imagePath ); if( image.isNull() ) error() << "could not load pixmap from " << imagePath; else channel->setImage( image ); } //call run again to start the next batch of transfers. run(); } diff --git a/src/services/CMakeLists.txt b/src/services/CMakeLists.txt index a588da33d6..516b26a337 100644 --- a/src/services/CMakeLists.txt +++ b/src/services/CMakeLists.txt @@ -1,17 +1,23 @@ -include_directories( - ${Amarok_SOURCE_DIR}/src - ) +include_directories( ${Amarok_SOURCE_DIR}/src ) add_subdirectory( magnatune ) -add_subdirectory( jamendo ) -add_subdirectory( mp3tunes ) -if (QCA2_FOUND) - add_subdirectory( ampache ) -endif () -if (LIBMYGPO_QT_FOUND ) + +# add_subdirectory( jamendo ) // Needs complete rewrite because of API change + +if(OPENSSL_FOUND OR LIBGCRYPT_FOUND) + if(LIBXML2_FOUND AND CURL_FOUND AND LOUDMOUTH_FOUND AND GLIB2_FOUND AND GOBJECT_FOUND AND QT5_GLIB_SUPPORT) +# add_subdirectory( mp3tunes ) // Needs more work + endif() +endif() + +add_subdirectory( ampache ) + +if ( Mygpo-qt5_FOUND ) add_subdirectory( gpodder ) endif() + if( LIBLASTFM_FOUND ) - add_subdirectory( lastfm ) +# add_subdirectory( lastfm ) // Needs more work endif() + add_subdirectory( opmldirectory ) diff --git a/src/services/ampache/AddServerDialog.cpp b/src/services/ampache/AddServerDialog.cpp index 545f87d520..52ee9495b7 100644 --- a/src/services/ampache/AddServerDialog.cpp +++ b/src/services/ampache/AddServerDialog.cpp @@ -1,108 +1,118 @@ /**************************************************************************************** * Copyright (c) 2010 Ian Monroe * * * This program 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 2 of the License, or (at your option) version 3 or * * any later version accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of * * version 3 of the license. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "AddServerDialog.h" #include "AmpacheAccountLogin.h" #include "ui_NewServerWidget.h" +#include +#include + AddServerDialog::AddServerDialog() - : KDialog() + : QDialog() , m_widgets( new Ui::NewServerWidget ) { QWidget* widget = new QWidget(); m_widgets->setupUi(widget); - setMainWidget(widget); + + setLayout(new QVBoxLayout); + layout()->addWidget(widget); + + auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + layout()->addWidget(buttonBox); m_widgets->verifyButton->setEnabled(false); - setCaption(i18n("Add new Ampache server")); - enableButtonOk(false); + setWindowTitle(i18n("Add new Ampache server")); - connect( m_widgets->verifyButton, SIGNAL(released()), this, SLOT(verifyData())); - QList inputs; + connect( m_widgets->verifyButton, &QPushButton::released, this, &AddServerDialog::verifyData); + QList inputs; inputs << m_widgets->nameLineEdit << m_widgets->serverAddressLineEdit << m_widgets->userNameLineEdit << m_widgets-> passwordLineEdit; - foreach(QObject* line, inputs) - connect( line, SIGNAL(textEdited(QString)), this, SLOT(anyTextEdited())); + foreach(QLineEdit* line, inputs) + connect( line, &QLineEdit::textEdited, this, &AddServerDialog::anyTextEdited); } AddServerDialog::~AddServerDialog() { delete m_widgets; } void AddServerDialog::anyTextEdited() { bool minimumData = (!(name().isEmpty() || url().isEmpty() || password().isEmpty() || username().isEmpty() )); - enableButtonOk(minimumData); + findChild()->button(QDialogButtonBox::Ok)->setEnabled(minimumData); m_widgets->verifyButton->setEnabled(minimumData); } void AddServerDialog::verifyData() { m_widgets->verifyButton->setEnabled(false); delete m_login; //should always be null at this point. m_login = new AmpacheAccountLogin( url(), username(), password(), this ); - connect(m_login, SIGNAL(finished()), this, SLOT(loginResult())); + connect(m_login, &AmpacheAccountLogin::finished, this, &AddServerDialog::loginResult); } void AddServerDialog::loginResult() { QLabel* label = m_widgets->verifyLabel; QPalette pal = label->palette(); if( m_login->authenticated() ) { label->setText( i18n("Successfully connected") ); pal.setColor( QPalette::WindowText, Qt::darkGreen ); } else { label->setText( i18n("Connection failure") ); pal.setColor( QPalette::WindowText, Qt::red ); } label->setPalette(pal); delete m_login; m_widgets->verifyButton->setEnabled(true); } QString AddServerDialog::name() { return m_widgets->nameLineEdit->text(); } -QString +QUrl AddServerDialog::url() { - return m_widgets->serverAddressLineEdit->text(); + return QUrl::fromUserInput( m_widgets->serverAddressLineEdit->text() ); } QString AddServerDialog::password() { return m_widgets->passwordLineEdit->text(); } QString AddServerDialog::username() { return m_widgets->userNameLineEdit->text(); } diff --git a/src/services/ampache/AddServerDialog.h b/src/services/ampache/AddServerDialog.h index 8303b2e9b6..3dde04f506 100644 --- a/src/services/ampache/AddServerDialog.h +++ b/src/services/ampache/AddServerDialog.h @@ -1,51 +1,51 @@ /**************************************************************************************** * Copyright (c) 2010 Ian Monroe * * * This program 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 2 of the License, or (at your option) version 3 or * * any later version accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of * * version 3 of the license. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef ADDSERVERDIALOG_H #define ADDSERVERDIALOG_H -#include +#include #include namespace Ui { class NewServerWidget; } class AmpacheAccountLogin; -class AddServerDialog : public KDialog +class AddServerDialog : public QDialog { Q_OBJECT public: AddServerDialog(); ~AddServerDialog(); QString name(); QString password(); QString username(); - QString url(); + QUrl url(); private Q_SLOTS: void anyTextEdited(); void verifyData(); void loginResult(); private: Ui::NewServerWidget* m_widgets; QPointer m_login; }; #endif // ADDSERVERDIALOG_H diff --git a/src/services/ampache/AmpacheAccountLogin.cpp b/src/services/ampache/AmpacheAccountLogin.cpp index 489e35556a..8b4b05cb98 100644 --- a/src/services/ampache/AmpacheAccountLogin.cpp +++ b/src/services/ampache/AmpacheAccountLogin.cpp @@ -1,279 +1,283 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * (c) 2010 Ian Monroe * * (c) 2013 Ralf Engels * * * * This program 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 2 of the License, or (at your option) version 3 or * * any later version accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of * * version 3 of the license. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "AmpacheAccountLogin.h" #include "core/support/Amarok.h" #include "core/support/Components.h" #include "core/support/Debug.h" +#include #include #include -#include +#include -#include +#include #include #include -AmpacheAccountLogin::AmpacheAccountLogin( const QString& url, const QString& username, const QString& password, QWidget* parent ) +AmpacheAccountLogin::AmpacheAccountLogin( const QUrl& url, const QString& username, const QString& password, QWidget* parent ) : QObject(parent) , m_authenticated( false ) , m_server( url ) , m_username( username ) , m_password( password ) - , m_sessionId( QString() ) - , m_lastRequest( 0 ) + , m_authRequest( Q_NULLPTR ) + , m_pingRequest( Q_NULLPTR ) { reauthenticate(); - } AmpacheAccountLogin::~AmpacheAccountLogin() { - } void AmpacheAccountLogin::reauthenticate() { DEBUG_BLOCK // We need to check the version of Ampache we are attempting to authenticate against, as this changes how we deal with it QUrl url = getRequestUrl( "ping" ); debug() << "Verifying Ampache Version Using: " << url.url(); - m_lastRequest = The::networkAccessManager()->getData( url, this, + m_pingRequest = The::networkAccessManager()->getData( url, this, SLOT(authenticate(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); - if( !m_lastRequest ) + if( !m_pingRequest ) emit finished(); } void AmpacheAccountLogin::authenticate( const QUrl &requestUrl, QByteArray data, NetworkAccessManagerProxy::Error e ) { - if( !m_lastRequest ) + if( !m_pingRequest ) return; - QCA::Initializer init; - DEBUG_BLOCK Q_UNUSED( requestUrl ); QDomDocument doc; doc.setContent( data ); - if( !generalVerify( doc, e ) ) + if( !generalVerify( m_pingRequest, doc, e ) ) return; // so lets figure out what we got here: debug() << "Version reply: " << data; int version = getVersion( doc ); QUrl url = getRequestUrl( "handshake" ); - QString timestamp = QString::number( QDateTime::currentDateTime().toTime_t() ); + QUrlQuery query( url ); + QString timestamp = QString::number( QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000 ); QString passPhrase; // We need to use different authentication strings depending on the version of ampache if( version > 350000 ) { - - - debug() << "New Password Scheme " << version; - url.addQueryItem( "version", "350001" ); + query.addQueryItem( "version", "350001" ); - QCA::Hash sha256Hash( "sha256" ); - sha256Hash.update( m_password.toUtf8() ); - QString hashedPassword = QCA::arrayToHex( sha256Hash.final().toByteArray() ); + QCryptographicHash sha256Hash( QCryptographicHash::Sha256 ); + sha256Hash.addData( m_password.toUtf8() ); + QString hashedPassword = sha256Hash.result().toHex(); QString rawHandshake = timestamp + hashedPassword; - sha256Hash.clear(); - sha256Hash.update( rawHandshake.toUtf8() ); + sha256Hash.reset(); + sha256Hash.addData( rawHandshake.toUtf8() ); - passPhrase = QCA::arrayToHex( sha256Hash.final().toByteArray() ); + passPhrase = sha256Hash.result().toHex(); } else { debug() << "Version Older than 35001 Generated MD5 Auth " << version; QString rawHandshake = timestamp + m_password; - QCA::Hash md5Hash( "md5" ); + QCryptographicHash md5Hash( QCryptographicHash::Md5 ); - md5Hash.update( rawHandshake.toUtf8() ); - passPhrase = QCA::arrayToHex( md5Hash.final().toByteArray() ); + md5Hash.addData( rawHandshake.toUtf8() ); + passPhrase = md5Hash.result().toHex(); } - url.addQueryItem( "timestamp", timestamp ); - url.addQueryItem( "auth", passPhrase ); + query.addQueryItem( "timestamp", timestamp ); + query.addQueryItem( "auth", passPhrase ); + url.setQuery( query ); debug() << "Authenticating with string: " << url.url() << passPhrase; // TODO: Amarok::Components::logger()->newProgressOperation( m_xmlDownloadJob, i18n( "Authenticating with Ampache" ) ); - m_lastRequest = The::networkAccessManager()->getData( url, this, + m_authRequest = The::networkAccessManager()->getData( url, this, SLOT(authenticationComplete(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); - if( !m_lastRequest ) + if( !m_authRequest ) emit finished(); } void AmpacheAccountLogin::authenticationComplete( const QUrl &requestUrl, QByteArray data, NetworkAccessManagerProxy::Error e ) { - if( !m_lastRequest ) + Q_UNUSED( requestUrl ); + + if( !m_authRequest ) return; DEBUG_BLOCK - Q_UNUSED( requestUrl ); QDomDocument doc; doc.setContent( data ); - if( !generalVerify( doc, e ) ) + if( !generalVerify( m_authRequest, doc, e ) ) return; // so lets figure out what we got here: debug() << "Authentication reply: " << data; QDomElement root = doc.firstChildElement("root"); //find status code: QDomElement element = root.firstChildElement("auth"); if( element.isNull() ) { // Default the Version down if it didn't work debug() << "authenticationComplete failed"; KMessageBox::error( qobject_cast(parent()), i18n( "Authentication failed." ), i18n( "Authentication Error" ) ); emit finished(); return; } m_sessionId = element.text(); m_authenticated = true; emit loginSuccessful(); emit finished(); } int AmpacheAccountLogin::getVersion( const QDomDocument& doc ) const { DEBUG_BLOCK QDomElement root = doc.firstChildElement("root"); //is this an error? QDomElement error = root.firstChildElement("error"); //find status code: QDomElement version = root.firstChildElement("version"); // It's OK if we get a null response from the version, that just means we're dealing with an older version if( !error.isNull() ) { // Default the Version down if it didn't work debug() << "getVersion error: " << error.text(); return 100000; } else if( !version.isNull() ) { debug() << "getVersion returned: " << version.text(); return version.text().toInt(); } else { debug() << "getVersion no version"; return 0; } } bool -AmpacheAccountLogin::generalVerify( const QDomDocument& doc, NetworkAccessManagerProxy::Error e ) +AmpacheAccountLogin::generalVerify( QNetworkReply *reply, const QDomDocument& doc, NetworkAccessManagerProxy::Error e ) { - Q_ASSERT( m_lastRequest ); + Q_ASSERT( reply ); - if( m_lastRequest->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() != 200 ) + if( reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() != 200 ) { debug() << "server response code:" << - m_lastRequest->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() << - m_lastRequest->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString(); - // KMessageBox::error( qobject_cast(parent()), domError.text(), i18n( "Authentication Error" ) ); + reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt() << + reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString(); + emit finished(); return false; } if( e.code != QNetworkReply::NoError ) { debug() << "authenticate Error:" << e.description; emit finished(); return false; } QDomElement root = doc.firstChildElement("root"); QDomElement error = root.firstChildElement("error"); if( !error.isNull() ) { // Default the Version down if it didn't work debug() << "generalVerify error: " << error.text(); KMessageBox::error( qobject_cast(parent()), error.text(), i18n( "Authentication Error" ) ); emit finished(); return false; } return true; } QUrl AmpacheAccountLogin::getRequestUrl( const QString &action ) const { //lets keep this around for now if we want to allow people to add a service that prompts for stuff /* But comment it out since the AmpacheQueryMaker does not do this if ( m_server.isEmpty() || m_password.isEmpty() ) { KPasswordDialog dlg( 0 , KPasswordDialog::ShowUsernameLine ); dlg.setPrompt( i18n( "Enter the server name and a password" ) ); if( !dlg.exec() ) return QUrl(); //the user canceled m_server = QUrl( dlg.username() ).url(); m_password = dlg.password(); } */ - QString path = m_server + "/server/xml.server.php"; + QUrl url = m_server; + url.setPath( m_server.path() + "/server/xml.server.php" ); + QString scheme = m_server.scheme(); - if( !path.startsWith("http://") && !path.startsWith("https://") ) - path = "http://" + path; + if( scheme != "http" && scheme != "https" ) + url.setScheme( "http" ); - QUrl url( path ); + QUrlQuery query( m_server ); if( !action.isEmpty() ) - url.addQueryItem( "action", action ); + query.addQueryItem( "action", action ); + + if( !m_username.isEmpty() && action != "ping" ) + query.addQueryItem( "user", m_username ); + + if( !m_sessionId.isEmpty() && action == "ping" ) + query.addQueryItem( "auth", m_sessionId ); - if( !m_username.isEmpty() ) - url.addQueryItem( "user", m_username ); + url.setQuery( query ); return url; } diff --git a/src/services/ampache/AmpacheAccountLogin.h b/src/services/ampache/AmpacheAccountLogin.h index bf6b9f0aa9..553b06c43e 100644 --- a/src/services/ampache/AmpacheAccountLogin.h +++ b/src/services/ampache/AmpacheAccountLogin.h @@ -1,81 +1,81 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * (c) 2010 Ian Monroe * * (c) 2013 Ralf Engels * * * * This program 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 2 of the License, or (at your option) version 3 or * * any later version accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of * * version 3 of the license. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef AMPACHEACCOUNTLOGIN_H #define AMPACHEACCOUNTLOGIN_H #include "NetworkAccessManagerProxy.h" +#include +#include #include -#include -#include -#include class QNetworkReply; #ifdef MAKE_AMPACHE_ACCOUNT_LOGIN_LIB -#define AMPACHE_ACCOUNT_EXPORT KDE_EXPORT +#define AMPACHE_ACCOUNT_EXPORT Q_DECL_EXPORT #else -#define AMPACHE_ACCOUNT_EXPORT KDE_IMPORT +#define AMPACHE_ACCOUNT_EXPORT Q_DECL_IMPORT #endif class AMPACHE_ACCOUNT_EXPORT AmpacheAccountLogin : public QObject { Q_OBJECT public: - AmpacheAccountLogin ( const QString& url, const QString& username, const QString& password, QWidget* parent = 0 ); + AmpacheAccountLogin ( const QUrl& url, const QString& username, const QString& password, QWidget* parent = 0 ); ~AmpacheAccountLogin(); - QString server() const { return m_server; } + QUrl server() const { return m_server; } QString sessionId() const { return m_sessionId; } bool authenticated() const { return m_authenticated; } void reauthenticate(); Q_SIGNALS: void loginSuccessful(); //!authentication was successful void finished(); //!authentication was or was not successful private Q_SLOTS: void authenticate( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ); void authenticationComplete( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ); private: int getVersion( const QDomDocument& doc ) const; /** Does general response verification. Emits finished if something is fishy. @returns true if the check was successful. */ - bool generalVerify( const QDomDocument& doc, NetworkAccessManagerProxy::Error e ); + bool generalVerify( QNetworkReply *reply, const QDomDocument& doc, NetworkAccessManagerProxy::Error e ); /** Returns the base url. You would need to add query items to use it. */ QUrl getRequestUrl( const QString &action = QString() ) const; bool m_authenticated; - QString m_server; + QUrl m_server; QString m_username; QString m_password; QString m_sessionId; - QNetworkReply* m_lastRequest; + QNetworkReply *m_authRequest; + QNetworkReply *m_pingRequest; }; #endif // AMPACHEACCOUNTLOGIN_H diff --git a/src/services/ampache/AmpacheConfig.cpp b/src/services/ampache/AmpacheConfig.cpp index 9d7379a548..c02e20238a 100644 --- a/src/services/ampache/AmpacheConfig.cpp +++ b/src/services/ampache/AmpacheConfig.cpp @@ -1,122 +1,118 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "AmpacheConfig.h" -#include -#include -#include -#include +#include "core/support/Amarok.h" + AmpacheConfig::AmpacheConfig() { load(); } void AmpacheConfig::load() { - KConfigGroup config = KGlobal::config()->group( "Service_Ampache" ); + KConfigGroup config = Amarok::config( "Service_Ampache" ); int serverIndex = 0; QString serverEntry = "server" + QString::number( serverIndex ); while ( config.hasKey( serverEntry ) ) { QStringList list = config.readEntry(serverEntry, QStringList() ); if ( list.isEmpty() ) continue; AmpacheServerEntry entry; entry.name = list.takeFirst(); - entry.url = list.takeFirst(); + entry.url = QUrl( list.takeFirst() ); entry.username = list.takeFirst(); entry.password = list.takeFirst(); entry.addToCollection = false; //FIXME m_servers.append( entry ); serverIndex++; serverEntry = "server" + QString::number( serverIndex ); } } void AmpacheConfig::save() { //delete all entries to make sure the indexes are correct - KConfigGroup config = KGlobal::config()->group( "Service_Ampache" ); - - kDebug( 14310 ) << "saving to config file " << KGlobal::config()->name() ; + KConfigGroup config = Amarok::config( "Service_Ampache" ); int serverIndex = 0; QString serverEntry = "server" + QString::number( serverIndex ); while ( config.hasKey ( serverEntry ) ) { - kDebug( 14310 ) << "deleting " << serverEntry; +// kDebug( 14310 ) << "deleting " << serverEntry; config.deleteEntry( serverEntry ); serverIndex++; serverEntry = "server" + QString::number( serverIndex ); } for( int i = 0; i < m_servers.size(); i++ ) { AmpacheServerEntry entry = m_servers.at( i ); QStringList list; list << entry.name; - list << entry.url; + list << entry.url.url(); list << entry.username; list << entry.password; serverEntry = "server" + QString::number( i ); - kDebug( 14310 ) << "adding " << serverEntry; +// kDebug( 14310 ) << "adding " << serverEntry; config.writeEntry( serverEntry, list ); } } int AmpacheConfig::serverCount() { return m_servers.size(); } AmpacheServerList AmpacheConfig::servers() { return m_servers; } void AmpacheConfig::addServer( const AmpacheServerEntry &server ) { m_servers.append( server ); } void AmpacheConfig::removeServer( int index ) { m_servers.removeAt( index ); } void AmpacheConfig::updateServer( int index, const AmpacheServerEntry & server ) { m_servers.removeAt( index ); m_servers.insert( index, server ); } diff --git a/src/services/ampache/AmpacheConfig.h b/src/services/ampache/AmpacheConfig.h index b2e8d34332..32dda5dbf6 100644 --- a/src/services/ampache/AmpacheConfig.h +++ b/src/services/ampache/AmpacheConfig.h @@ -1,65 +1,66 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef AMPACHECONFIG_H #define AMPACHECONFIG_H #include #include +#include class AmpacheServerEntry { public: QString name; - QString url; + QUrl url; QString username; QString password; bool addToCollection; }; typedef QList< AmpacheServerEntry > AmpacheServerList; /** A class for accessing the Ampache plugin configuration @author */ class AmpacheConfig{ public: AmpacheConfig(); void load(); void save(); int serverCount(); AmpacheServerList servers(); void addServer( const AmpacheServerEntry &server ); void removeServer( int index); void updateServer( int index, const AmpacheServerEntry &server ); private: bool m_hasChanged; AmpacheServerList m_servers; // Disable copy constructor and assignment AmpacheConfig( const AmpacheConfig& ); AmpacheConfig& operator=( const AmpacheConfig& ); }; #endif diff --git a/src/services/ampache/AmpacheMeta.h b/src/services/ampache/AmpacheMeta.h index 99c20eb829..62ed83b676 100644 --- a/src/services/ampache/AmpacheMeta.h +++ b/src/services/ampache/AmpacheMeta.h @@ -1,138 +1,137 @@ /**************************************************************************************** * Copyright (c) 2007 Casey Link * * Copyright (c) 2007 Nikolaj Hald Nielsen * * (c) 2013 Ralf Engels * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef AMPACHEMETA_H #define AMPACHEMETA_H #include "ServiceBase.h" #include "ServiceMetaBase.h" #include "ServiceAlbumCoverDownloader.h" -#include - #include #include #include +#include #include #include namespace Meta { class AmpacheTrack : public ServiceTrack { public: explicit AmpacheTrack( const QString& title, ServiceBase * service = 0 ) : ServiceTrack( title ) , m_service( service ) , m_discNumber( 0 ) { Q_UNUSED(m_service); // might be used again later } virtual QString sourceName() { return "Ampache"; } virtual QString sourceDescription() { return "The Ampache music server project: http://Ampache.org"; } - virtual QPixmap emblem() { return QPixmap( KStandardDirs::locate( "data", "amarok/images/emblem-ampache.png" ) ); } - virtual QString scalableEmblem() { return KStandardDirs::locate( "data", "amarok/images/emblem-ampache-scalable.svgz" ); } + virtual QPixmap emblem() { return QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-ampache.png" ) ); } + virtual QString scalableEmblem() { return QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-ampache-scalable.svgz" ); } virtual QString notPlayableReason() const; virtual int discNumber() const { return m_discNumber; } virtual void setDiscNumber( int newDiscNumber ) { m_discNumber = newDiscNumber; } private: ServiceBase * m_service; int m_discNumber; }; class AmpacheAlbum : public ServiceAlbumWithCover { private: QString m_coverURL; public: AmpacheAlbum( const QString &name ); AmpacheAlbum( const QStringList &resultRow ); ~AmpacheAlbum(); virtual QString downloadPrefix() const { return "ampache"; } virtual void setCoverUrl( const QString &coverURL ); virtual QString coverUrl() const; bool operator==( const Meta::Album &other ) const { return name() == other.name(); } QList ids() const { return m_ampacheAlbums.keys(); } struct AmpacheAlbumInfo { int id; int discNumber; int year; }; /** Add an ampache album to this Amarok album. Warning: The album will not be automatically registered with the new id, same as with setId */ void addInfo( const AmpacheAlbumInfo &info ); /** Get's an album info for a specific ID */ AmpacheAlbumInfo getInfo( int id ) const; private: // the unique album key of ampache contains discNumber and year // the Amarok key only name and artist // so this AmpacheAlbum object represents a number of ampache albums. QHash m_ampacheAlbums; }; class AmpacheArtist : public ServiceArtist { private: QString m_coverURL; public: AmpacheArtist( const QString &name, ServiceBase * service ) : ServiceArtist( name ) , m_service( service ) {} virtual bool isBookmarkable() const { return true; } virtual QString collectionName() const { return m_service->name(); } virtual bool simpleFiltering() const { return true; } bool operator==( const Meta::Artist &other ) const { return name() == other.name(); } private: ServiceBase * m_service; }; } #endif // End include guard diff --git a/src/services/ampache/AmpacheService.cpp b/src/services/ampache/AmpacheService.cpp index a0ca207d91..822fd72b27 100644 --- a/src/services/ampache/AmpacheService.cpp +++ b/src/services/ampache/AmpacheService.cpp @@ -1,138 +1,136 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "AmpacheService" #include "AmpacheService.h" #include "AmpacheConfig.h" #include "AmpacheAccountLogin.h" #include "core/support/Amarok.h" #include "core/support/Components.h" #include "core/interfaces/Logger.h" #include "browsers/SingleCollectionTreeItemModel.h" #include "core-impl/collections/support/CollectionManager.h" #include #include "core/support/Debug.h" #ifdef HAVE_LIBLASTFM #include "LastfmInfoParser.h" #endif -#include +#include AmpacheServiceFactory::AmpacheServiceFactory() : ServiceFactory() {} void AmpacheServiceFactory::init() { //read config and create the needed number of services AmpacheConfig config; AmpacheServerList servers = config.servers(); m_initialized = true; for( int i = 0; i < servers.size(); i++ ) { AmpacheServerEntry server = servers.at( i ); ServiceBase* service = new AmpacheService( this, "Ampache (" + server.name + ')', server.url, server. username, server.password ); emit newService( service ); } } QString AmpacheServiceFactory::name() { return "Ampache"; } KConfigGroup AmpacheServiceFactory::config() { return Amarok::config( "Service_Ampache" ); } bool AmpacheServiceFactory::possiblyContainsTrack(const QUrl &url) const { AmpacheConfig config; foreach( const AmpacheServerEntry &server, config.servers() ) { - if ( url.url().contains( server.url, Qt::CaseInsensitive ) ) + if ( server.url.isParentOf( url ) ) return true; } return false; } - -AmpacheService::AmpacheService( AmpacheServiceFactory* parent, const QString & name, const QString &url, const QString &username, const QString &password ) +AmpacheService::AmpacheService( AmpacheServiceFactory* parent, const QString & name, const QUrl &url, const QString &username, const QString &password ) : ServiceBase( name, parent ) , m_infoParser( 0 ) , m_collection( 0 ) , m_ampacheLogin( new AmpacheAccountLogin( url, username, password, this ) ) { DEBUG_BLOCK connect( m_ampacheLogin, &AmpacheAccountLogin::loginSuccessful, this, &AmpacheService::onLoginSuccessful ); setShortDescription( i18n( "Amarok frontend for your Ampache server" ) ); setIcon( QIcon::fromTheme( "view-services-ampache-amarok" ) ); setLongDescription( i18n( "Use Amarok as a seamless frontend to your Ampache server. This lets you browse and play all the Ampache contents from within Amarok." ) ); - setImagePath( KStandardDirs::locate( "data", "amarok/images/hover_info_ampache.png" ) ); + setImagePath( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/hover_info_ampache.png" ) ); #ifdef HAVE_LIBLASTFM m_infoParser = new LastfmInfoParser(); #endif } AmpacheService::~AmpacheService() { CollectionManager::instance()->removeTrackProvider( m_collection ); delete m_collection; m_ampacheLogin->deleteLater(); } void AmpacheService::polish() { m_bottomPanel->hide(); setInfoParser( m_infoParser ); /*if ( !m_authenticated ) authenticate( );*/ } void AmpacheService::reauthenticate() { m_ampacheLogin->reauthenticate(); // it would make sense here to clean the complete cache // information from a server might get outdated. } - void AmpacheService::onLoginSuccessful() { m_collection = new Collections::AmpacheServiceCollection( this, m_ampacheLogin->server(), m_ampacheLogin->sessionId() ); // connect( m_collection, SIGNAL(authenticationNeeded()), SLOT(authenticate()) ); CollectionManager::instance()->addTrackProvider( m_collection ); QList levels; levels << CategoryId::Artist << CategoryId::Album; setModel( new SingleCollectionTreeItemModel( m_collection, levels ) ); setServiceReady( true ); } diff --git a/src/services/ampache/AmpacheService.h b/src/services/ampache/AmpacheService.h index dbb115c60a..37c07278ef 100644 --- a/src/services/ampache/AmpacheService.h +++ b/src/services/ampache/AmpacheService.h @@ -1,80 +1,80 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef AMPACHESERVICE_H #define AMPACHESERVICE_H #include "AmpacheServiceCollection.h" #include "ServiceBase.h" #include class AmpacheAccountLogin; class AmpacheServiceFactory: public ServiceFactory { Q_PLUGIN_METADATA(IID AmarokPluginFactory_iid FILE "amarok_service_ampache.json") Q_INTERFACES(Plugins::PluginFactory) Q_OBJECT public: AmpacheServiceFactory(); virtual ~AmpacheServiceFactory() {} virtual bool possiblyContainsTrack( const QUrl &url ) const; virtual void init(); virtual QString name(); virtual KConfigGroup config(); }; /** A service for displaying, previewing and downloading music from Ampache music servers @author */ class AmpacheService : public ServiceBase { Q_OBJECT public: explicit AmpacheService( AmpacheServiceFactory* parent, const QString &name, - const QString &url = QString(), const QString &username = QString(), + const QUrl &url = QUrl(), const QString &username = QString(), const QString &password = QString() ); ~AmpacheService(); void polish(); void reauthenticate(); virtual Collections::Collection * collection() { return m_collection; } private Q_SLOTS: void onLoginSuccessful(); private: InfoParserBase *m_infoParser; Collections::AmpacheServiceCollection * m_collection; QPointer m_ampacheLogin; // Disable copy constructor and assignment AmpacheService( const AmpacheService& ); AmpacheService& operator=( const AmpacheService& ); }; #endif diff --git a/src/services/ampache/AmpacheServiceCollection.cpp b/src/services/ampache/AmpacheServiceCollection.cpp index 43eef7ec51..e1728cfa28 100644 --- a/src/services/ampache/AmpacheServiceCollection.cpp +++ b/src/services/ampache/AmpacheServiceCollection.cpp @@ -1,187 +1,196 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "AmpacheServiceCollection.h" #include "AmpacheServiceQueryMaker.h" #include "NetworkAccessManagerProxy.h" -#include +#include #include #include #include #include +#include using namespace Collections; AmpacheServiceCollection::AmpacheServiceCollection( ServiceBase *service, - const QString &server, + const QUrl &server, const QString &sessionId ) : ServiceCollection( service, "AmpacheCollection", "AmpacheCollection" ) , m_server( server ) , m_sessionId( sessionId ) { m_trackForUrlWorker = 0; } AmpacheServiceCollection::~AmpacheServiceCollection() { } QueryMaker * AmpacheServiceCollection::queryMaker() { return new AmpacheServiceQueryMaker( this, m_server, m_sessionId ); } QString AmpacheServiceCollection::collectionId() const { - return "Ampache: " + m_server; + return "Ampache: " + m_server.url(); } QString AmpacheServiceCollection::prettyName() const { - return i18n( "Ampache Server %1", m_server ); + return i18n( "Ampache Server %1", m_server.url() ); } bool AmpacheServiceCollection::possiblyContainsTrack( const QUrl &url ) const { - return url.url().contains( m_server ); + return m_server.isParentOf( url ); } void AmpacheServiceCollection::slotAuthenticationNeeded() { emit authenticationNeeded(); } Meta::TrackPtr AmpacheServiceCollection::trackForUrl( const QUrl &url ) { - MetaProxy::TrackPtr trackptr( new MetaProxy::Track( url.url(), MetaProxy::Track::ManualLookup ) ); + MetaProxy::TrackPtr trackptr( new MetaProxy::Track( url, MetaProxy::Track::ManualLookup ) ); AmpacheTrackForUrlWorker *worker = new AmpacheTrackForUrlWorker( url, trackptr, m_server, m_sessionId, service() ); - connect( worker, SIGNAL(authenticationNeeded()), SLOT(slotAuthenticationNeeded()) ); + connect( worker, &AmpacheTrackForUrlWorker::authenticationNeeded, + this, &AmpacheServiceCollection::slotAuthenticationNeeded ); ThreadWeaver::Queue::instance()->enqueue( QSharedPointer(worker) ); return Meta::TrackPtr::staticCast( trackptr ); } void AmpacheServiceCollection::slotLookupComplete( const Meta::TrackPtr& ) { } void AmpacheTrackForUrlWorker::parseTrack( const QString &xml ) { //so lets figure out what we got here: QDomDocument doc( "reply" ); doc.setContent( xml ); QDomElement root = doc.firstChildElement( "root" ); QDomElement song = root.firstChildElement( "song" ); m_urlTrackId = song.attribute( "id", "0" ).toInt(); QDomElement element = song.firstChildElement( "title" ); QString title = element.text(); if ( title.isEmpty() ) title = "Unknown"; element = song.firstChildElement( "url" ); m_urlTrack = new Meta::AmpacheTrack( title, m_service ); Meta::TrackPtr trackPtr( m_urlTrack ); m_urlTrack->setUidUrl( element.text() ); m_urlTrack->setId( m_urlTrackId ); element = song.firstChildElement( "time" ); m_urlTrack->setLength( element.text().toInt() * 1000 ); element = song.firstChildElement( "track" ); m_urlTrack->setTrackNumber( element.text().toInt() ); QDomElement albumElement = song.firstChildElement( "album" ); m_urlAlbumId = albumElement.attribute( "id", "0" ).toInt(); Meta::AmpacheAlbum *album = new Meta::AmpacheAlbum( albumElement.text() ); QDomElement artElement = song.firstChildElement( "art" ); album->setCoverUrl( artElement.text() ); album->addTrack( trackPtr ); m_urlTrack->setAlbumPtr( Meta::AlbumPtr( album ) ); QDomElement artistElement = song.firstChildElement( "artist" ); Meta::ServiceArtist *artist = new Meta::ServiceArtist( artistElement.text() ); Meta::ArtistPtr artistPtr( artist ); m_urlTrack->setArtist( artistPtr ); album->setAlbumArtist( artistPtr ); } AmpacheTrackForUrlWorker::AmpacheTrackForUrlWorker( const QUrl &url, MetaProxy::TrackPtr track, - const QString &server, + const QUrl &server, const QString &sessionId, ServiceBase *service ) : Amarok::TrackForUrlWorker( url ) , m_proxy( track ) , m_server( server ) , m_sessionId( sessionId ) , m_service( service ) { } AmpacheTrackForUrlWorker::~AmpacheTrackForUrlWorker() {} void -AmpacheTrackForUrlWorker::run() +AmpacheTrackForUrlWorker::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) { + Q_UNUSED( self ) + Q_UNUSED( thread ) + m_urlTrack = 0; m_urlAlbum = 0; m_urlArtist = 0; m_urlTrackId = 0; m_urlAlbumId = 0; m_urlArtistId = 0; //send url_to_song to Ampache - QString requestUrl = - QString( "%1/server/xml.server.php?action=url_to_song&auth=%2&url=%3" ) - .arg( m_server, m_sessionId, QUrl::toPercentEncoding( m_url.url() ) ); + QUrl requestUrl = m_server; + requestUrl.setPath( m_server.path() + "/server/xml.server.php" ); + QUrlQuery query; + query.addQueryItem( "action", "url_to_song" ); + query.addQueryItem( "auth", m_sessionId ); + query.addQueryItem( "url", m_url.toEncoded() ); + requestUrl.setQuery( query ); QNetworkRequest req( requestUrl ); QNetworkReply *reply = The::networkAccessManager()->get( req ); if( reply->waitForReadyRead(-1) ) { if( reply->error() == QNetworkReply::ContentAccessDenied ) { debug() << "Trying to re-authenticate Ampache.."; emit authenticationNeeded(); } } parseTrack( reply->readAll() ); m_track = Meta::TrackPtr( m_urlTrack ); m_proxy->updateTrack( m_track ); reply->deleteLater(); } diff --git a/src/services/ampache/AmpacheServiceCollection.h b/src/services/ampache/AmpacheServiceCollection.h index 4b18f9335f..062fb672e6 100644 --- a/src/services/ampache/AmpacheServiceCollection.h +++ b/src/services/ampache/AmpacheServiceCollection.h @@ -1,93 +1,93 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef AMPACHESERVICECOLLECTION_H #define AMPACHESERVICECOLLECTION_H #include #include "AmpacheMeta.h" #include "core/collections/support/TrackForUrlWorker.h" class AmpacheTrackForUrlWorker : public Amarok::TrackForUrlWorker { Q_OBJECT public: AmpacheTrackForUrlWorker( const QUrl &url, MetaProxy::TrackPtr track, - const QString &server, const QString &sessionId, + const QUrl &server, const QString &sessionId, ServiceBase *service); ~AmpacheTrackForUrlWorker(); - virtual void run(); + virtual void run(ThreadWeaver::JobPointer self = QSharedPointer(), ThreadWeaver::Thread *thread = 0); void parseTrack( const QString &xml ); Q_SIGNALS: void authenticationNeeded(); private: MetaProxy::TrackPtr m_proxy; int m_urlTrackId; int m_urlAlbumId; int m_urlArtistId; Meta::AmpacheTrack *m_urlTrack; Meta::AmpacheAlbum *m_urlAlbum; Meta::ServiceArtist *m_urlArtist; - QString m_server; + QUrl m_server; QString m_sessionId; ServiceBase *m_service; }; namespace Collections { /** A collection that dynamically fetches data from a remote location as needed @author */ class AmpacheServiceCollection : public ServiceCollection { Q_OBJECT public: - AmpacheServiceCollection( ServiceBase *service, const QString &server, + AmpacheServiceCollection( ServiceBase *service, const QUrl &server, const QString &sessionId ); virtual ~AmpacheServiceCollection(); virtual QueryMaker *queryMaker(); virtual QString collectionId() const; virtual QString prettyName() const; virtual Meta::TrackPtr trackForUrl( const QUrl &url ); virtual bool possiblyContainsTrack( const QUrl &url ) const; Q_SIGNALS: void authenticationNeeded(); public Q_SLOTS: void slotAuthenticationNeeded(); void slotLookupComplete( const Meta::TrackPtr & ); private: - QString m_server; + QUrl m_server; QString m_sessionId; AmpacheTrackForUrlWorker *m_trackForUrlWorker; }; } //namespace Collections #endif diff --git a/src/services/ampache/AmpacheServiceQueryMaker.cpp b/src/services/ampache/AmpacheServiceQueryMaker.cpp index a1e271adcb..265877ba84 100644 --- a/src/services/ampache/AmpacheServiceQueryMaker.cpp +++ b/src/services/ampache/AmpacheServiceQueryMaker.cpp @@ -1,739 +1,757 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * Copyright (c) 2007 Adam Pigg * * Copyright (c) 2007 Casey Link * * (c) 2013 Ralf Engels * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "AmpacheServiceQueryMaker" #include "AmpacheServiceQueryMaker.h" #include "AmpacheMeta.h" #include "core/meta/Statistics.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include "core/meta/support/MetaConstants.h" #include "core-impl/collections/support/MemoryMatcher.h" -#include #include #include +#include +#include using namespace Collections; struct AmpacheServiceQueryMaker::Private { AmpacheServiceCollection* collection; QueryMaker::QueryType type; int maxsize; QAtomicInt expectedReplies; - QString server; + QUrl server; QString sessionId; QList parentTrackIds; QList parentAlbumIds; QList parentArtistIds; uint dateFilter; QString artistFilter; QString albumFilter; /** We are collecting the results of the queries and submit them in one block to ensure that we don't report albums twice and because the CollectionTreeItemModelBase does not handle multiple results correctly (which it should). */ Meta::AlbumList albumResults; Meta::ArtistList artistResults; Meta::TrackList trackResults; }; -AmpacheServiceQueryMaker::AmpacheServiceQueryMaker( AmpacheServiceCollection * collection, const QString &server, const QString &sessionId ) +AmpacheServiceQueryMaker::AmpacheServiceQueryMaker( AmpacheServiceCollection * collection, const QUrl &server, const QString &sessionId ) : DynamicServiceQueryMaker() , d( new Private ) { d->collection = collection; d->type = QueryMaker::None; d->maxsize = 0; d->server = server; d->sessionId = sessionId; d->dateFilter = 0; } AmpacheServiceQueryMaker::~AmpacheServiceQueryMaker() { delete d; } void AmpacheServiceQueryMaker::run() { DEBUG_BLOCK if( d->expectedReplies ) // still running an old query return; //naive implementation, fix this //note: we are not handling filtering yet d->collection->acquireReadLock(); if ( d->type == QueryMaker::Artist ) fetchArtists(); else if( d->type == QueryMaker::Album ) fetchAlbums(); else if( d->type == QueryMaker::Track ) fetchTracks(); else warning() << "Requested unhandled query type"; //TODO error handling d->collection->releaseLock(); } void AmpacheServiceQueryMaker::abortQuery() { } QueryMaker * AmpacheServiceQueryMaker::setQueryType( QueryType type ) { d->type = type; return this; } QueryMaker* AmpacheServiceQueryMaker::addMatch( const Meta::TrackPtr &track ) { DEBUG_BLOCK const Meta::AmpacheTrack* serviceTrack = dynamic_cast< const Meta::AmpacheTrack * >( track.data() ); if( serviceTrack ) { d->parentTrackIds << serviceTrack->id(); debug() << "parent id set to: " << d->parentTrackIds; } else { // searching for something from another collection //hmm, not sure what to do now } return this; } QueryMaker* AmpacheServiceQueryMaker::addMatch( const Meta::ArtistPtr &artist, ArtistMatchBehaviour behaviour ) { Q_UNUSED( behaviour ) // TODO DEBUG_BLOCK if( d->parentAlbumIds.isEmpty() ) { const Meta::AmpacheArtist* serviceArtist = dynamic_cast< const Meta::AmpacheArtist * >( artist.data() ); if( serviceArtist ) { d->parentArtistIds << serviceArtist->id(); } else { // searching for something from another collection if( d->collection->artistMap().contains( artist->name() ) ) { serviceArtist = static_cast< const Meta::AmpacheArtist* >( d->collection->artistMap().value( artist->name() ).data() ); d->parentArtistIds << serviceArtist->id(); } else { //hmm, not sure what to do now } } } return this; } QueryMaker * AmpacheServiceQueryMaker::addMatch( const Meta::AlbumPtr & album ) { DEBUG_BLOCK const Meta::AmpacheAlbum* serviceAlbum = dynamic_cast< const Meta::AmpacheAlbum * >( album.data() ); if( serviceAlbum ) { d->parentAlbumIds << serviceAlbum->ids(); debug() << "parent id set to: " << d->parentAlbumIds; d->parentArtistIds.clear(); } else { // searching for something from another collection if( d->collection->albumMap().contains( album ) ) // compares albums by value { serviceAlbum = static_cast< const Meta::AmpacheAlbum* >( d->collection->albumMap().value( album ).data() ); d->parentAlbumIds << serviceAlbum->ids(); d->parentArtistIds.clear(); } else { //hmm, not sure what to do now } } return this; } void AmpacheServiceQueryMaker::fetchArtists() { DEBUG_BLOCK Meta::ArtistList artists; // first try the cache if( !d->parentArtistIds.isEmpty() ) { foreach( int artistId, d->parentArtistIds ) artists << d->collection->artistById( artistId ); } if( !artists.isEmpty() ) { debug() << "got" << artists.count() << "artists from the memory collection"; - emit newResultReady( artists ); + emit newArtistsReady( artists ); emit queryDone(); return; } QUrl request = getRequestUrl( "artists" ); + QUrlQuery query( request ); if ( !d->artistFilter.isEmpty() ) - request.addQueryItem( "filter", d->artistFilter ); + { + query.addQueryItem( "filter", d->artistFilter ); + request.setQuery( query ); + } d->expectedReplies.ref(); The::networkAccessManager()->getData( request, this, SLOT(artistDownloadComplete(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); } void AmpacheServiceQueryMaker::fetchAlbums() { DEBUG_BLOCK Meta::AlbumList albums; // first try the cache if( !d->parentArtistIds.isEmpty() ) { foreach( int artistId, d->parentArtistIds ) albums << matchAlbums( d->collection, d->collection->artistById( artistId ) ); } if( !albums.isEmpty() ) { debug() << "got" << albums.count() << "albums from the memory collection"; - emit newResultReady( albums ); + emit newAlbumsReady( albums ); emit queryDone(); return; } if( !d->parentArtistIds.isEmpty() ) { foreach( int id, d->parentArtistIds ) { QUrl request = getRequestUrl( "artist_albums" ); - request.addQueryItem( "filter", QString::number( id ) ); + QUrlQuery query( request ); + query.addQueryItem( "filter", QString::number( id ) ); + request.setQuery( query ); d->expectedReplies.ref(); The::networkAccessManager()->getData( request, this, SLOT(albumDownloadComplete(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); } } else { QUrl request = getRequestUrl( "albums" ); + QUrlQuery query( request ); if ( !d->albumFilter.isEmpty() ) - request.addQueryItem( "filter", d->albumFilter ); + { + query.addQueryItem( "filter", d->albumFilter ); + request.setQuery( query ); + } d->expectedReplies.ref(); The::networkAccessManager()->getData( request, this, SLOT(albumDownloadComplete(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); } } void AmpacheServiceQueryMaker::fetchTracks() { DEBUG_BLOCK Meta::TrackList tracks; //debug() << "parent album id: " << d->parentAlbumId; // first try the cache // TODO: this is fishy as we cannot be sure that the cache contains // everything // we should cache database query results instead if( !d->parentTrackIds.isEmpty() ) { foreach( int trackId, d->parentTrackIds ) { tracks << d->collection->trackById( trackId ); } } else if( !d->parentAlbumIds.isEmpty() ) { foreach( int albumId, d->parentAlbumIds ) { AlbumMatcher albumMatcher( d->collection->albumById( albumId ) ); tracks << albumMatcher.match( d->collection->trackMap().values() ); } } else if( d->parentArtistIds.isEmpty() ) { foreach( int artistId, d->parentArtistIds ) { ArtistMatcher artistMatcher( d->collection->artistById( artistId ) ); tracks << artistMatcher.match( d->collection->trackMap().values() ); } } if( !tracks.isEmpty() ) { debug() << "got" << tracks.count() << "tracks from the memory collection"; - emit newResultReady( tracks ); + emit newTracksReady( tracks ); emit queryDone(); return; } QUrl request = getRequestUrl(); if( !d->parentAlbumIds.isEmpty() ) { foreach( int id, d->parentAlbumIds ) { QUrl request = getRequestUrl( "album_songs" ); - request.addQueryItem( "filter", QString::number( id ) ); + QUrlQuery query( request ); + query.addQueryItem( "filter", QString::number( id ) ); + request.setQuery( query ); d->expectedReplies.ref(); The::networkAccessManager()->getData( request, this, SLOT(trackDownloadComplete(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); } } else if( !d->parentArtistIds.isEmpty() ) { foreach( int id, d->parentArtistIds ) { QUrl request = getRequestUrl( "artist_songs" ); - request.addQueryItem( "filter", QString::number( id ) ); + QUrlQuery query( request ); + query.addQueryItem( "filter", QString::number( id ) ); + request.setQuery( query ); d->expectedReplies.ref(); The::networkAccessManager()->getData( request, this, SLOT(trackDownloadComplete(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); } } else { QUrl request = getRequestUrl( "songs" ); d->expectedReplies.ref(); The::networkAccessManager()->getData( request, this, SLOT(trackDownloadComplete(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); } } void AmpacheServiceQueryMaker::artistDownloadComplete( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ) { Q_UNUSED( url ); if( e.code != QNetworkReply::NoError ) { warning() << "Artist download error:" << e.description; if( !d->expectedReplies.deref() ) emit queryDone(); return; } // DEBUG_BLOCK // so lets figure out what we got here: QDomDocument doc( "reply" ); doc.setContent( data ); QDomElement root = doc.firstChildElement( "root" ); // Is this an error, if so we need to 'un-ready' the service and re-authenticate before contiuning QDomElement domError = root.firstChildElement( "error" ); if ( !domError.isNull() ) { - warning() << "Error getting Artist List" << domError.text(); + warning() << "Error getting Artist List" << domError.text() << "Code:" << domError.attribute("code"); AmpacheService *parentService = dynamic_cast< AmpacheService * >( d->collection->service() ); if( !parentService ) return; else parentService->reauthenticate(); } for( QDomNode n = root.firstChild(); !n.isNull(); n = n.nextSibling() ) { QDomElement e = n.toElement(); // try to convert the node to an element. QDomElement element = n.firstChildElement( "name" ); int artistId = e.attribute( "id", "0").toInt(); // check if we have the artist already Meta::ArtistPtr artistPtr = d->collection->artistById( artistId ); if( !artistPtr ) { // new artist Meta::ServiceArtist* artist = new Meta::AmpacheArtist( element.text(), d->collection->service() ); artist->setId( artistId ); // debug() << "Adding artist: " << element.text() << " with id: " << artistId; artistPtr = artist; d->collection->acquireWriteLock(); d->collection->addArtist( artistPtr ); d->collection->releaseLock(); } if( !d->artistResults.contains( artistPtr ) ) d->artistResults.push_back( artistPtr ); } if( !d->expectedReplies.deref() ) { - emit newResultReady( d->artistResults ); + emit newArtistsReady( d->artistResults ); emit queryDone(); d->artistResults.clear(); } } void AmpacheServiceQueryMaker::albumDownloadComplete( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ) { Q_UNUSED( url ); if( e.code != QNetworkReply::NoError ) { warning() << "Album download error:" << e.description; if( !d->expectedReplies.deref() ) emit queryDone(); return; } // DEBUG_BLOCK //so lets figure out what we got here: QDomDocument doc( "reply" ); doc.setContent( data ); QDomElement root = doc.firstChildElement( "root" ); // Is this an error, if so we need to 'un-ready' the service and re-authenticate before contiuning QDomElement domError = root.firstChildElement( "error" ); if( !domError.isNull() ) { - warning() << "Error getting Album List" << domError.text(); + warning() << "Error getting Album List" << domError.text() << "Code:" << domError.attribute("code"); AmpacheService *parentService = dynamic_cast< AmpacheService * >(d->collection->service()); if( parentService == 0 ) return; else parentService->reauthenticate(); } for( QDomNode n = root.firstChild(); !n.isNull(); n = n.nextSibling() ) { QDomElement e = n.toElement(); // try to convert the node to an element. // --- the album artist Meta::ArtistPtr artistPtr; QDomElement artistElement = n.firstChildElement( "artist" ); if( !artistElement.isNull() ) { int artistId = artistElement.attribute( "id", "0").toInt(); // check if we already know the artist artistPtr = d->collection->artistById( artistId ); if( !artistPtr.data() ) { // new artist. Meta::ServiceArtist* artist = new Meta::AmpacheArtist( artistElement.text(), d->collection->service() ); artistPtr = artist; artist->setId( artistId ); // debug() << "Adding artist: " << artistElement.text() << " with id: " << artistId; d->collection->acquireWriteLock(); d->collection->addArtist( artistPtr ); d->collection->releaseLock(); } } QDomElement element = n.firstChildElement( "name" ); QString title = element.text(); Meta::AmpacheAlbum::AmpacheAlbumInfo info; info.id = e.attribute( "id", "0" ).toInt(); element = n.firstChildElement( "disk" ); info.discNumber = element.text().toInt(); element = n.firstChildElement( "year" ); info.year = element.text().toInt(); // check if we have the album already Meta::AlbumPtr albumPtr = d->collection->albumById( info.id ); if( !albumPtr ) { // check if we at least have an album with the same title and artist Meta::AmpacheAlbum* album = static_cast( const_cast( d->collection->albumMap().value( title, artistPtr ? artistPtr->name() : QString() ).data() ) ); if( !album ) { // new album album = new Meta::AmpacheAlbum( title ); album->setAlbumArtist( artistPtr ); // -- cover element = n.firstChildElement( "art" ); QString coverUrl = element.text(); album->setCoverUrl( coverUrl ); } album->addInfo( info ); // debug() << "Adding album" << title << "with id:" << info.id; albumPtr = album; // register a new id with the ServiceCollection album->setId( info.id ); d->collection->acquireWriteLock(); d->collection->addAlbum( albumPtr ); d->collection->releaseLock(); } if( !d->albumResults.contains( albumPtr ) ) d->albumResults.push_back( albumPtr ); } if( !d->expectedReplies.deref() ) { - emit newResultReady( d->albumResults ); + emit newAlbumsReady( d->albumResults ); emit queryDone(); d->albumResults.clear(); } } void AmpacheServiceQueryMaker::trackDownloadComplete( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ) { Q_UNUSED( url ); if( e.code != QNetworkReply::NoError ) { warning() << "Track download error:" << e.description; if( !d->expectedReplies.deref() ) emit queryDone(); return; } // DEBUG_BLOCK //so lets figure out what we got here: QDomDocument doc( "reply" ); doc.setContent( data ); QDomElement root = doc.firstChildElement( "root" ); // Is this an error, if so we need to 'un-ready' the service and re-authenticate before contiuning QDomElement domError = root.firstChildElement( "error" ); if( !domError.isNull() ) { - warning() << "Error getting Track Download " << domError.text(); + warning() << "Error getting Track Download " << domError.text() << "Code:" << domError.attribute("code"); AmpacheService *parentService = dynamic_cast< AmpacheService * >( d->collection->service() ); if( parentService == 0 ) return; else parentService->reauthenticate(); } for( QDomNode n = root.firstChild(); !n.isNull(); n = n.nextSibling() ) { QDomElement e = n.toElement(); // try to convert the node to an element. int trackId = e.attribute( "id", "0" ).toInt(); Meta::TrackPtr trackPtr = d->collection->trackById( trackId ); if( !trackPtr ) { // new track QDomElement element = n.firstChildElement( "title" ); QString title = element.text(); Meta::AmpacheTrack * track = new Meta::AmpacheTrack( title, d->collection->service() ); trackPtr = track; track->setId( trackId ); element = n.firstChildElement( "url" ); track->setUidUrl( element.text() ); element = n.firstChildElement( "time" ); track->setLength( element.text().toInt() * 1000 ); element = n.firstChildElement( "track" ); track->setTrackNumber( element.text().toInt() ); element = n.firstChildElement( "rating" ); track->statistics()->setRating( element.text().toDouble() * 2.0 ); QDomElement albumElement = n.firstChildElement( "album" ); int albumId = albumElement.attribute( "id", "0").toInt(); QDomElement artistElement = n.firstChildElement( "artist" ); int artistId = artistElement.attribute( "id", "0").toInt(); Meta::ArtistPtr artistPtr = d->collection->artistById( artistId ); // TODO: this assumes that we query all artist before tracks if( artistPtr ) { // debug() << "Found parent artist " << artistPtr->name(); Meta::ServiceArtist *artist = dynamic_cast< Meta::ServiceArtist * > ( artistPtr.data() ); track->setArtist( artistPtr ); artist->addTrack( trackPtr ); } Meta::AlbumPtr albumPtr = d->collection->albumById( albumId ); // TODO: this assumes that we query all albums before tracks if( albumPtr ) { // debug() << "Found parent album " << albumPtr->name() << albumId; Meta::AmpacheAlbum *album = dynamic_cast< Meta::AmpacheAlbum * > ( albumPtr.data() ); track->setDiscNumber( album->getInfo( albumId ).discNumber ); track->setYear( album->getInfo( albumId ).year ); track->setAlbumPtr( albumPtr ); // debug() << " parent album with"<discNumber()<year(); album->addTrack( trackPtr ); } // debug() << "Adding track: " << title << " with id: " << trackId; d->collection->acquireWriteLock(); d->collection->addTrack( trackPtr ); d->collection->releaseLock(); } if( !d->trackResults.contains( trackPtr ) ) d->trackResults.push_back( trackPtr ); } if( !d->expectedReplies.deref() ) { - emit newResultReady( d->trackResults ); + emit newTracksReady( d->trackResults ); emit queryDone(); d->trackResults.clear(); } } QueryMaker * AmpacheServiceQueryMaker::addFilter( qint64 value, const QString & filter, bool matchBegin, bool matchEnd ) { Q_UNUSED( matchBegin ) Q_UNUSED( matchEnd ) //for now, only accept artist filters // TODO: What about albumArtist? if( value == Meta::valArtist ) { d->artistFilter = filter; } else if( value == Meta::valAlbum ) { d->albumFilter = filter; } else { warning() << "unsupported filter" << Meta::nameForField( value ); } return this; } QueryMaker* AmpacheServiceQueryMaker::addNumberFilter( qint64 value, qint64 filter, QueryMaker::NumberComparison compare ) { if( value == Meta::valCreateDate && compare == QueryMaker::GreaterThan ) { debug() << "asking to filter based on added date"; d->dateFilter = filter; debug() << "setting dateFilter to:" << d->dateFilter; } else { warning() << "unsupported filter" << Meta::nameForField( value ); } return this; } int AmpacheServiceQueryMaker::validFilterMask() { //we only supprt artist and album filters for now... return ArtistFilter | AlbumFilter; } QueryMaker * AmpacheServiceQueryMaker::limitMaxResultSize( int size ) { d->maxsize = size; return this; } QUrl AmpacheServiceQueryMaker::getRequestUrl( const QString &action ) const { - QString path = d->server; + QUrl url = d->server; + QString scheme = url.scheme(); + + if( scheme != "http" && scheme != "https" ) + url.setScheme( "http" ); - if( !path.startsWith("http://") && !path.startsWith("https://") ) - path = "http://" + path; + QUrlQuery query( url ); - QUrl url( path ); + url = url.adjusted( QUrl::StripTrailingSlash ); + url.setPath( url.path() + "/server/xml.server.php" ); - url = url.adjusted(QUrl::StripTrailingSlash); - url.setPath(url.path() + '/' + ( "/server/xml.server.php" )); - url.addQueryItem( "auth", d->sessionId ); + query.addQueryItem( "auth", d->sessionId ); if( !action.isEmpty() ) - url.addQueryItem( "action", action ); + query.addQueryItem( "action", action ); if( d->dateFilter > 0 ) { QDateTime from; from.setTime_t( d->dateFilter ); - url.addQueryItem( "add", from.toString( Qt::ISODate ) ); + query.addQueryItem( "add", from.toString( Qt::ISODate ) ); } - url.addQueryItem( "limit", QString::number( d->maxsize ) ); + query.addQueryItem( "limit", QString::number( d->maxsize ) ); + url.setQuery( query ); return url; } diff --git a/src/services/ampache/AmpacheServiceQueryMaker.h b/src/services/ampache/AmpacheServiceQueryMaker.h index adea5d9e95..1a4d5d69c9 100644 --- a/src/services/ampache/AmpacheServiceQueryMaker.h +++ b/src/services/ampache/AmpacheServiceQueryMaker.h @@ -1,92 +1,92 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef AMPACHESERVICEQUERYMAKER_H #define AMPACHESERVICEQUERYMAKER_H #include "DynamicServiceQueryMaker.h" #include "AmpacheServiceCollection.h" #include "AmpacheService.h" #include "NetworkAccessManagerProxy.h" #include namespace Collections { /** QueryMaker for Ampache Since Ampache only supports a very limited set of searches this query maker is very restricted. E.g. it does not support searches with AND and OR. TODO: support tags TODO: think about only using the MemoryQueryMaker */ class AmpacheServiceQueryMaker : public DynamicServiceQueryMaker { Q_OBJECT public: - AmpacheServiceQueryMaker( AmpacheServiceCollection * collection, const QString &server, const QString &sessionId ); + AmpacheServiceQueryMaker( AmpacheServiceCollection * collection, const QUrl &server, const QString &sessionId ); ~AmpacheServiceQueryMaker(); virtual void run(); virtual void abortQuery(); virtual QueryMaker* setQueryType( QueryType type ); using DynamicServiceQueryMaker::addMatch; virtual QueryMaker* addMatch( const Meta::TrackPtr &track ); virtual QueryMaker* addMatch( const Meta::ArtistPtr &artist, ArtistMatchBehaviour behaviour = TrackArtists ); virtual QueryMaker* addMatch( const Meta::AlbumPtr &album ); virtual QueryMaker* addFilter( qint64 value, const QString &filter, bool matchBegin = false, bool matchEnd = false ); virtual QueryMaker* addNumberFilter( qint64 value, qint64 filter, QueryMaker::NumberComparison compare ); virtual int validFilterMask(); virtual QueryMaker* limitMaxResultSize( int size ); void fetchArtists(); void fetchAlbums(); void fetchTracks(); protected: struct Private; Private * const d; public Q_SLOTS: void artistDownloadComplete( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ); void albumDownloadComplete( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ); void trackDownloadComplete( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ); private: // Disable copy constructor and assignment AmpacheServiceQueryMaker( const AmpacheServiceQueryMaker& ); AmpacheServiceQueryMaker& operator= ( const AmpacheServiceQueryMaker& ); /** Gets the url for the ampache requests. Already adds query items for the dateFilter and the limit. */ QUrl getRequestUrl( const QString &action = QString() ) const; /* template void emitProperResult(const ListType& list); */ }; } //namespace Collections #endif diff --git a/src/services/ampache/AmpacheSettings.cpp b/src/services/ampache/AmpacheSettings.cpp index d66bda9cde..89db74f0b8 100644 --- a/src/services/ampache/AmpacheSettings.cpp +++ b/src/services/ampache/AmpacheSettings.cpp @@ -1,171 +1,174 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "AmpacheSettings.h" #include "AddServerDialog.h" #include "ui_AmpacheConfigWidget.h" #include #include +#include #include K_PLUGIN_FACTORY_WITH_JSON( ampachesettings, "amarok_service_ampache_config.json", registerPlugin(); ) -AmpacheSettings::AmpacheSettings(QWidget * parent, const QVariantList & args) - : KCModule( AmpacheSettingsFactory::componentData(), parent, args ) +AmpacheSettings::AmpacheSettings( QWidget *parent, const QVariantList &args ) + : KCModule( parent, args ) + , m_configDialog(new Ui::AmpacheConfigWidget) , m_lastRowEdited(-1) , m_lastColumnEdited(-1) { - m_configDialog = new Ui::AmpacheConfigWidget; m_configDialog->setupUi( this ); m_configDialog->serverList->setMinimumWidth(700); m_configDialog->serverList->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); m_configDialog->serverList->verticalHeader()->hide(); - connect ( m_configDialog->serverList, SIGNAL(cellDoubleClicked(int,int)), this, SLOT(onCellDoubleClicked(int,int))); - connect ( m_configDialog->serverList, SIGNAL(cellChanged(int,int)), this, SLOT(saveCellEdit(int,int))); - connect ( m_configDialog->addButton, SIGNAL(clicked()), this, SLOT(add()) ); - connect ( m_configDialog->removeButton, SIGNAL(clicked()), this, SLOT(remove()) ); + connect ( m_configDialog->serverList, &QTableWidget::cellDoubleClicked, this, &AmpacheSettings::onCellDoubleClicked ); + connect ( m_configDialog->serverList, &QTableWidget::cellChanged, this, &AmpacheSettings::saveCellEdit ); + connect ( m_configDialog->addButton, &QPushButton::clicked, this, &AmpacheSettings::add ); + connect ( m_configDialog->removeButton, &QPushButton::clicked, this, &AmpacheSettings::remove ); } AmpacheSettings::~AmpacheSettings() { delete m_configDialog; } void AmpacheSettings::serverNameChanged(const QString & text) { m_configDialog->addButton->setEnabled( !text.isEmpty() ); } void AmpacheSettings::save() { m_config.save(); KCModule::save(); } void AmpacheSettings::load() { loadList(); KCModule::load(); } void AmpacheSettings::loadList() { QTableWidget* serverList = m_configDialog->serverList; serverList->setRowCount(m_config.servers().size()); for( int i = 0; i < m_config.servers().size(); i++ ) { AmpacheServerEntry entry = m_config.servers().at( i ); serverList->setItem(i, 0, new QTableWidgetItem(entry.name)); - serverList->setItem(i, 1, new QTableWidgetItem(entry.url)); + serverList->setItem(i, 1, new QTableWidgetItem(entry.url.url())); serverList->setItem(i, 2, new QTableWidgetItem(entry.username)); QString starPassword = entry.password; starPassword.fill('*'); QTableWidgetItem* password = new QTableWidgetItem(starPassword); password->setData(0xf00, entry.password); serverList->setItem(i, 3, password); } serverList->resizeColumnsToContents(); int columnWidth = serverList->columnWidth(3) + serverList->columnViewportPosition(3); serverList->setMinimumWidth( qBound( 200, columnWidth, 700) ); } void AmpacheSettings::defaults() { } void AmpacheSettings::add() { AddServerDialog dialog; if(dialog.exec() == QDialog::Accepted) { AmpacheServerEntry server; server.name = dialog.name(); server.url = dialog.url(); server.username = dialog.username(); - server.password =dialog.password(); + server.password = dialog.password(); + server.addToCollection = false; if( server.name.isEmpty()) return; m_config.addServer( server ); } loadList(); emit changed( true ); } void AmpacheSettings::remove() { int index = m_configDialog->serverList->currentRow(); m_configDialog->serverList->removeRow( index ); m_config.removeServer( index ); emit changed( true ); } void AmpacheSettings::onCellDoubleClicked(int row, int column) { QTableWidgetItem* item = m_configDialog->serverList->item(row, column); m_configDialog->serverList->editItem(item); m_lastRowEdited = row; m_lastColumnEdited = column; } void AmpacheSettings::saveCellEdit(int row, int column) { if(m_lastRowEdited != row || m_lastColumnEdited != column) //only worry about user edits return; - kDebug( 14310 ) << Q_FUNC_INFO << row << column; +// kDebug( 14310 ) << Q_FUNC_INFO << row << column; QString newValue = m_configDialog->serverList->item(row, column)->text(); AmpacheServerEntry server = m_config.servers().at(row); switch(column) { case 0: server.name = newValue; break; case 1: - server.url = newValue; + server.url = QUrl( newValue ); break; case 2: server.username = newValue; break; case 3: server.password = newValue; break; default: qWarning() << Q_FUNC_INFO << "invalid column"; } m_config.updateServer(row, server); m_configDialog->serverList->resizeColumnToContents(column); emit changed( true ); } +#include "moc_AmpacheSettings.cpp" #include "AmpacheSettings.moc" diff --git a/src/services/ampache/AmpacheSettings.h b/src/services/ampache/AmpacheSettings.h index ac80848f94..e49dc36a11 100644 --- a/src/services/ampache/AmpacheSettings.h +++ b/src/services/ampache/AmpacheSettings.h @@ -1,59 +1,59 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef AMPACHESETTINGS_H #define AMPACHESETTINGS_H #include "AmpacheConfig.h" -#include +#include namespace Ui { class AmpacheConfigWidget; } /** Class for handling settings for Ampache services @author */ class AmpacheSettings : public KCModule { Q_OBJECT public: - explicit AmpacheSettings( QWidget *parent = 0, const QVariantList &args = QVariantList() ); + explicit AmpacheSettings( QWidget *parent, const QVariantList &args ); - ~AmpacheSettings(); + virtual ~AmpacheSettings(); - virtual void save(); - virtual void load(); - virtual void defaults(); + virtual void save() Q_DECL_OVERRIDE; + virtual void load() Q_DECL_OVERRIDE; + virtual void defaults() Q_DECL_OVERRIDE; private: AmpacheConfig m_config; Ui::AmpacheConfigWidget * m_configDialog; void loadList(); int m_lastRowEdited; int m_lastColumnEdited; private Q_SLOTS: void add(); void remove(); void serverNameChanged(const QString & text); void onCellDoubleClicked(int row, int column); void saveCellEdit(int row, int column); }; #endif diff --git a/src/services/ampache/CMakeLists.txt b/src/services/ampache/CMakeLists.txt index f149e65178..6e337a599d 100644 --- a/src/services/ampache/CMakeLists.txt +++ b/src/services/ampache/CMakeLists.txt @@ -1,94 +1,89 @@ - include_directories( - ../ - ${Amarok_SOURCE_DIR}/src/ - ${Amarok_SOURCE_DIR}/src/core-impl/collections - ${Amarok_SOURCE_DIR}/src/network - ${Amarok_SOURCE_DIR}/src/statusbar - ${CMAKE_CURRENT_BINARY_DIR}/../../.. - - ${QCA2_INCLUDE_DIR} - - ) +include_directories( + ../ + ${Amarok_SOURCE_DIR}/src/ + ${Amarok_SOURCE_DIR}/src/core-impl/collections + ${Amarok_SOURCE_DIR}/src/network + ${Amarok_SOURCE_DIR}/src/statusbar + ${CMAKE_CURRENT_BINARY_DIR}/../../.. +) if( LIBLASTFM_FOUND ) include_directories( ${LIBLASTFM_INCLUDE_DIR} ) endif() add_subdirectory( images ) ########### next target ############### set(libampache_account_login_SRCS AmpacheAccountLogin.cpp) + add_library(ampache_account_login SHARED ${libampache_account_login_SRCS}) + target_link_libraries(ampache_account_login amaroklib amarokcore - KF5::KDELibs4Support KF5::KIOCore - ${QCA2_LIBRARIES} ) install(TARGETS ampache_account_login DESTINATION ${INSTALL_TARGETS_DEFAULT_ARGS}) ########### next target ############### - set(amarok_service_ampache_PART_SRCS - AmpacheService.cpp - AmpacheServiceCollection.cpp - AmpacheServiceQueryMaker.cpp - AmpacheMeta.cpp - AmpacheConfig.cpp - ) - if( LIBLASTFM_FOUND ) - set(amarok_service_ampache_PART_SRCS - ${amarok_service_ampache_PART_SRCS} - LastfmInfoParser.cpp ) - endif() - - - - add_library(amarok_service_ampache MODULE ${amarok_service_ampache_PART_SRCS}) - target_link_libraries(amarok_service_ampache - ampache_account_login - amarokcore - amaroklib - KF5::KDELibs4Support - - KF5::KIOCore - KF5::ThreadWeaver - Qt5::Xml - - ) - if( LIBLASTFM_FOUND ) - target_link_libraries(amarok_service_ampache - ${LIBLASTFM_LIBRARY} - ) - endif() - - - install(TARGETS amarok_service_ampache DESTINATION ${PLUGIN_INSTALL_DIR} ) + +set(amarok_service_ampache_PART_SRCS + AmpacheService.cpp + AmpacheServiceCollection.cpp + AmpacheServiceQueryMaker.cpp + AmpacheMeta.cpp + AmpacheConfig.cpp +) +if( LIBLASTFM_FOUND ) + set(amarok_service_ampache_PART_SRCS + ${amarok_service_ampache_PART_SRCS} + LastfmInfoParser.cpp ) +endif() -########### next target ############### +add_library(amarok_service_ampache MODULE ${amarok_service_ampache_PART_SRCS}) +target_link_libraries(amarok_service_ampache + ampache_account_login + amarokcore + amaroklib - set(kcm_amarok_service_ampache_PART_SRCSS AddServerDialog.cpp AmpacheSettings.cpp AmpacheConfig.cpp ) + KF5::KIOCore + KF5::ThreadWeaver + Qt5::Xml +) +if( LIBLASTFM_FOUND ) + target_link_libraries(amarok_service_ampache + ${LIBLASTFM_LIBRARY} + ) +endif() - ki18n_wrap_ui( kcm_amarok_service_ampache_PART_SRCSS AmpacheConfigWidget.ui NewServerWidget.ui ) +install(TARGETS amarok_service_ampache DESTINATION ${PLUGIN_INSTALL_DIR} ) +install(FILES amarok_service_ampache.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) - add_library(kcm_amarok_service_ampache MODULE ${kcm_amarok_service_ampache_PART_SRCSS} ) +kcoreaddons_desktop_to_json(amarok_service_ampache amarok_service_ampache.desktop SERVICE_TYPES ${CMAKE_SOURCE_DIR}/src/amarok-plugin.desktop) - target_link_libraries( kcm_amarok_service_ampache - ampache_account_login Qt5::Core - Qt5::Gui ) +########### next target ############### + +set(kcm_amarok_service_ampache_PART_SRCSS AddServerDialog.cpp AmpacheSettings.cpp AmpacheConfig.cpp ) + +ki18n_wrap_ui( kcm_amarok_service_ampache_PART_SRCSS AmpacheConfigWidget.ui NewServerWidget.ui ) - install(TARGETS kcm_amarok_service_ampache DESTINATION ${PLUGIN_INSTALL_DIR}) +add_library(kcm_amarok_service_ampache MODULE ${kcm_amarok_service_ampache_PART_SRCSS} ) + +target_link_libraries( kcm_amarok_service_ampache + ampache_account_login + KF5::ConfigWidgets +) -########### install files ############### +install(TARGETS kcm_amarok_service_ampache DESTINATION ${PLUGIN_INSTALL_DIR}) +install(FILES amarok_service_ampache_config.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) - install( FILES amarok_service_ampache.desktop DESTINATION ${SERVICES_INSTALL_DIR}) - install( FILES amarok_service_ampache_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}) +kcoreaddons_desktop_to_json(kcm_amarok_service_ampache amarok_service_ampache_config.desktop SERVICE_TYPES kcmodule.desktop) diff --git a/src/services/ampache/LastfmInfoParser.cpp b/src/services/ampache/LastfmInfoParser.cpp index ed59cb5eeb..1cc15fcd49 100644 --- a/src/services/ampache/LastfmInfoParser.cpp +++ b/src/services/ampache/LastfmInfoParser.cpp @@ -1,185 +1,185 @@ /*************************************************************************************** * Copyright (c) 2009 Dan Meltzer * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "LastfmInfoParser.h" #include "core/meta/Meta.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" -#include +#include #include #include void LastfmInfoParser::getInfo(Meta::TrackPtr track) { DEBUG_BLOCK QMap query; query[ "method" ] = "track.getInfo"; query[ "track" ] = track->name(); query[ "album" ] = track->album() ? track->album()->name() : QString(); query[ "artist" ] = track->artist() ? track->artist()->name() : QString(); query[ "apikey" ] = Amarok::lastfmApiKey(); m_jobs[ "getTrackInfo" ] = lastfm::ws::post( query ); - connect( m_jobs[ "getTrackInfo" ], SIGNAL(finished()), SLOT(onGetTrackInfo()) ); + connect( m_jobs[ "getTrackInfo" ], &QNetworkReply::finished, this, &LastfmInfoParser::onGetTrackInfo ); } void LastfmInfoParser::onGetTrackInfo() { DEBUG_BLOCK if( !m_jobs[ "getTrackInfo" ] ) { debug() << "WARNING: GOT RESULT but no object"; return; } switch ( m_jobs[ "getTrackInfo" ]->error() ) { case QNetworkReply::NoError: { lastfm::XmlQuery lfm; lfm.parse( m_jobs[ "getTrackInfo" ]->readAll() ); lastfm::XmlQuery wiki = lfm["track"]["wiki"]; const QString contentText = wiki["content"].text(); const QString publishedDate = wiki["published"].text(); QString html; if( !contentText.isEmpty() ) html = QString("

%1

Updated: %2

").arg( contentText, publishedDate ); else html = i18n( "

No information found for this track.

" ); emit info( html ); break; } default: break; } m_jobs["getTrackInfo"]->deleteLater(); m_jobs["getTrackInfo"] = 0; } void LastfmInfoParser::getInfo(Meta::AlbumPtr album) { DEBUG_BLOCK QMap query; query[ "method" ] = "album.getInfo"; query[ "album" ] = album->name(); query[ "artist" ] = album->albumArtist() ? album->albumArtist()->name() : QString(); query[ "apikey" ] = Amarok::lastfmApiKey(); m_jobs[ "getAlbumInfo" ] = lastfm::ws::post( query ); - connect( m_jobs[ "getAlbumInfo" ], SIGNAL(finished()), SLOT(onGetAlbumInfo()) ); + connect( m_jobs[ "getAlbumInfo" ], &QNetworkReply::finished, this, &LastfmInfoParser::onGetAlbumInfo ); } void LastfmInfoParser::onGetAlbumInfo() { DEBUG_BLOCK if( !m_jobs[ "getAlbumInfo" ] ) { debug() << "WARNING: GOT RESULT but no object"; return; } switch ( m_jobs[ "getAlbumInfo" ]->error() ) { case QNetworkReply::NoError: { lastfm::XmlQuery lfm; lfm.parse( m_jobs[ "getAlbumInfo" ]->readAll() ); lastfm::XmlQuery wiki = lfm["album"]["wiki"]; const QString summaryText = wiki["summary"].text(); const QString contentText = wiki["content"].text(); const QString publishedDate = wiki["published"].text(); const QString albumUrl = lfm["image size=large"].text(); QString html; if( !contentText.isEmpty() ) html = QString("

%2

Updated: %3

").arg( albumUrl, contentText, publishedDate ); else html = i18n( "

No information found for this album.

" ); emit info( html ); break; } default: break; } m_jobs["getAlbumInfo"]->deleteLater(); m_jobs["getAlbumInfo"] = 0; } void LastfmInfoParser::getInfo(Meta::ArtistPtr artist) { QMap query; query[ "method" ] = "artist.getInfo"; query[ "artist" ] = artist->name(); debug() << "api key is: " << Amarok::lastfmApiKey(); query[ "apikey" ] = Amarok::lastfmApiKey(); m_jobs[ "getArtistInfo" ] = lastfm::ws::post( query ); - connect( m_jobs[ "getArtistInfo" ], SIGNAL(finished()), SLOT(onGetArtistInfo()) ); + connect( m_jobs[ "getArtistInfo" ], &QNetworkReply::finished, this, &LastfmInfoParser::onGetArtistInfo ); } void LastfmInfoParser::onGetArtistInfo() { DEBUG_BLOCK if( !m_jobs[ "getArtistInfo" ] ) { debug() << "WARNING: GOT RESULT but no object"; return; } switch ( m_jobs[ "getArtistInfo" ]->error() ) { case QNetworkReply::NoError: { lastfm::XmlQuery lfm; lfm.parse( m_jobs[ "getArtistInfo" ]->readAll() ); debug() << lfm.text(); lastfm::XmlQuery bio = lfm["artist"]["bio"]; const QString summaryText = bio["summary"].text(); const QString contentText = bio["content"].text(); const QString publishedDate = bio["published"].text(); const QString imageUrl = lfm["image size=large"].text(); QString html; if( !contentText.isEmpty() ) html = QString("

%2

Updated: %3

").arg( imageUrl, contentText, publishedDate ); else html = i18n( "

No information found for this artist.

" ); emit info( html ); break; } default: break; } m_jobs["getArtistInfo"]->deleteLater(); m_jobs["getArtistInfo"] = 0; } diff --git a/src/services/ampache/NewServerWidget.ui b/src/services/ampache/NewServerWidget.ui index f336a12678..bc7af89fdd 100644 --- a/src/services/ampache/NewServerWidget.ui +++ b/src/services/ampache/NewServerWidget.ui @@ -1,107 +1,107 @@ NewServerWidget 0 0 345 184 Name - + Server Address - + 0 0 200 0 Username - + Password - + QLineEdit::Password - + Check Connection 0 0 - KPushButton + QPushButton QPushButton -
kpushbutton.h
+
qpushbutton.h
- KLineEdit + QLineEdit QLineEdit -
klineedit.h
+
qlineedit.h
diff --git a/src/services/ampache/amarok_service_ampache.desktop b/src/services/ampache/amarok_service_ampache.desktop index 79b769cf98..066f759944 100644 --- a/src/services/ampache/amarok_service_ampache.desktop +++ b/src/services/ampache/amarok_service_ampache.desktop @@ -1,128 +1,127 @@ [Desktop Entry] Type=Service -ServiceTypes=KPluginInfo Icon=view-services-ampache-amarok Name=Ampache Name[bg]=Ampache Name[bs]=Ampache Name[ca]=Ampache Name[ca@valencia]=Ampache Name[cs]=Ampache Name[da]=Ampache Name[de]=Ampache Name[el]=Ampache Name[en_GB]=Ampache Name[eo]=Ampache Name[es]=Ampache Name[et]=Ampache Name[eu]=Ampache Name[fi]=Ampache Name[fr]=Ampache Name[ga]=Ampache Name[gl]=Ampache Name[hu]=Ampache Name[id]=Ampache Name[is]=Ampache Name[it]=Ampache Name[ja]=Ampache Name[km]=Ampache Name[ko]=Ampache Name[lt]=Ampache Name[lv]=Ampache Name[nb]=Ampache Name[nds]=Ampache Name[nl]=Ampache Name[nn]=Ampache Name[pa]=ਅਮਪਾਂਚੇ Name[pl]=Ampache Name[pt]=Ampache Name[pt_BR]=Ampache Name[ro]=Ampache Name[ru]=Ampache Name[sk]=Ampache Name[sl]=Ampache Name[sq]=Ampache Name[sr]=Ампач Name[sr@ijekavian]=Ампач Name[sr@ijekavianlatin]=Ampache Name[sr@latin]=Ampache Name[sv]=Ampache Name[th]=บริการ Ampache Name[tr]=Ampache Name[uk]=Ampache Name[wa]=Ampache Name[x-test]=xxAmpachexx Name[zh_CN]=Ampache Name[zh_TW]=Ampache Comment=Listen to music from an Ampache server Comment[bg]=Слушане на музика от Ampache сървър Comment[bs]=Slušajte muziku sa Ampačevog servera Comment[ca]=Escolta música des d'un servidor Ampache Comment[ca@valencia]=Escolta música des d'un servidor Ampache Comment[cs]=Poslouchejte hudbu ze serveru Ampache Comment[da]=Lyt til musik fra en Ampache-server Comment[de]=Musik von einem Ampache-Server wiedergeben Comment[el]=Ακρόαση μουσικής από έναν εξυπηρετητή Ampache Comment[en_GB]=Listen to music from an Ampache server Comment[es]=Escuchar música desde el servidor Ampache Comment[et]=Ampache serveri muusika kuulamine Comment[eu]=Entzun musika Ampache zerbitzari batetik Comment[fi]=Kuuntele musiikkia Ampache-palvelusta Comment[fr]=Écouter de la musique depuis un serveur « Ampache » Comment[ga]=Éist le ceol ó fhreastalaí Ampache Comment[gl]=Escoite música dun servizo Ampache Comment[he]=האזנה למוזיקה משרת Ampache Comment[hu]=Zenehallgatás egy Ampache-kiszolgálóról Comment[id]=Mendengarkan musik dari server Ampache Comment[is]=Hlusta á tónlist frá Ampache þjóni Comment[it]=Ascolta musica da un server Ampache Comment[ja]=Ampache サーバから音楽を聴けます Comment[km]=ស្ដាប់​តន្ត្រី​ពី​ម៉ាស៊ីន​បម្រើ Ampache Comment[ko]=Ampache 서버의 음악 듣기 Comment[ku]=Ji pêşkêşkera Ampache guhdarî ya muzîkê bike Comment[lt]=Klausyti muzikos iš Ampache serverio Comment[lv]=Klausieties mūziku no Ampache servera Comment[nb]=Lytt til musikk fra en Ampache-tjener Comment[nds]=Musik vun en Ampache-Server tohören Comment[nl]=Luister naar muziek vanaf een Ampache-server Comment[nn]=Høyr på musikk frå ein Ampache-tenar Comment[pl]=Słuchaj muzyki z serwera Ampache Comment[pt]=Ouvir música de um servidor Ampache Comment[pt_BR]=Ouça músicas de um servidor Ampache Comment[ro]=Ascultă muzică de pe un server Ampache Comment[ru]=Прослушивание музыки с сервера Ampache Comment[sk]=Počúvať hudbu z Ampache servra Comment[sl]=Poslušajte glasbo s strežnika Ampache Comment[sr]=Слушајте музику са Ампачевог сервера Comment[sr@ijekavian]=Слушајте музику са Ампачевог сервера Comment[sr@ijekavianlatin]=Slušajte muziku sa Ampachejevog servera Comment[sr@latin]=Slušajte muziku sa Ampachejevog servera Comment[sv]=Lyssna på musik från en Ampache-server Comment[th]=ฟังดนตรีจากบริการ Ampache Comment[tr]=Bir Ampache sunucudan müzik dinle Comment[uk]=Прослуховування музики з сервера Ampache Comment[wa]=Schoûter del muzike d' on sierveu Ampache Comment[x-test]=xxListen to music from an Ampache serverxx Comment[zh_CN]=聆听来自 Ampache 服务器的音乐 Comment[zh_TW]=從 Ampache 伺服器上聽音樂 ServiceTypes=Amarok/Plugin X-KDE-Amarok-authors=Nikolaj Hald Nielsen X-KDE-Amarok-email=nhnFreespirit@gmail.com X-KDE-Amarok-framework-version=73 X-KDE-Amarok-name=AmpacheService X-KDE-Amarok-rank=100 X-KDE-Amarok-version=1 X-KDE-PluginInfo-Author=Nikolaj Hald Nielsen X-KDE-PluginInfo-Email=nhnFreespirit@gmail.com X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Category=Service X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=false X-KDE-Library=amarok_service_ampache X-KDE-PluginInfo-Name=amarok_service_ampache diff --git a/src/services/gpodder/CMakeLists.txt b/src/services/gpodder/CMakeLists.txt index 190490caac..c2fce1eb8f 100644 --- a/src/services/gpodder/CMakeLists.txt +++ b/src/services/gpodder/CMakeLists.txt @@ -1,72 +1,88 @@ include_directories( - ../ - ../../ - ../../core-impl/collections - ../../statusbar - ../../widgets - ../../context - ../../network - ../../dynamic # for CustomBias.h - ../../browsers/playlistbrowser - ${CMAKE_CURRENT_BINARY_DIR}/../.. #for amarokconfig.h - - - ${LIBMYGPO_QT_INCLUDE_DIRS} - ${LIBMYGPO_QT_INCLUDE_DIRS}/../ - ) - - add_subdirectory( images ) - - set(amarok_service_gpodder_PART_SRCS - GpodderService.cpp - GpodderServiceConfig.cpp - GpodderServiceModel.cpp - GpodderServiceView.cpp - GpodderProvider.cpp - GpodderPodcastMeta.cpp - GpodderTreeItem.cpp - GpodderPodcastTreeItem.cpp - GpodderTagTreeItem.cpp - GpodderPodcastRequestHandler.cpp - GpodderSortFilterProxyModel.cpp - ) - - add_library(amarok_service_gpodder MODULE ${amarok_service_gpodder_PART_SRCS}) - - target_link_libraries(amarok_service_gpodder - amarokcore - amaroklib - amarokpud - ${LIBMYGPO_QT_LIBRARIES} - KF5::KDELibs4Support - - KF5::KIOCore - ${KDE4_SOLID_LIBRARY} - KF5::ThreadWeaver - Qt5::Network - ) - - install( TARGETS amarok_service_gpodder DESTINATION ${PLUGIN_INSTALL_DIR} ) - - set(kcm_amarok_service_gpodder_PART_SRCS - GpodderServiceSettings.cpp - GpodderServiceConfig.cpp - ) - - ki18n_wrap_ui( kcm_amarok_service_gpodder_PART_SRCS GpodderConfigWidget.ui ) - - add_library(kcm_amarok_service_gpodder MODULE ${kcm_amarok_service_gpodder_PART_SRCS} ) - - target_link_libraries( kcm_amarok_service_gpodder - amarokcore - amaroklib - ${LIBMYGPO_QT_LIBRARIES} - - - KF5::KIOCore - Qt5::Network ) - - install(TARGETS kcm_amarok_service_gpodder DESTINATION ${PLUGIN_INSTALL_DIR}) - - install( FILES amarok_service_gpodder.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) - install( FILES amarok_service_gpodder_config.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + ../ + ../../ + ../../core-impl/collections + ../../statusbar + ../../widgets + ../../context + ../../network + ../../dynamic # for CustomBias.h + ../../browsers/playlistbrowser + ${CMAKE_CURRENT_BINARY_DIR}/../.. #for amarokconfig.h + + + ${LIBMYGPO_QT5_INCLUDE_DIRS} + ${LIBMYGPO_QT5_INCLUDE_DIRS}/../ +) + +add_subdirectory( images ) + +find_package( KF5 COMPONENTS Wallet REQUIRED ) + +set(libgpodder_service_config_SRCS + GpodderServiceConfig.cpp +) + +add_library(gpodder_service_config SHARED ${libgpodder_service_config_SRCS}) + +target_link_libraries(gpodder_service_config + amaroklib + amarokcore + KF5::Wallet +) + +install(TARGETS gpodder_service_config DESTINATION ${INSTALL_TARGETS_DEFAULT_ARGS}) + +set(amarok_service_gpodder_PART_SRCS + GpodderService.cpp + GpodderServiceModel.cpp + GpodderServiceView.cpp + GpodderProvider.cpp + GpodderPodcastMeta.cpp + GpodderTreeItem.cpp + GpodderPodcastTreeItem.cpp + GpodderTagTreeItem.cpp + GpodderPodcastRequestHandler.cpp + GpodderSortFilterProxyModel.cpp +) + +add_library(amarok_service_gpodder MODULE ${amarok_service_gpodder_PART_SRCS}) + +target_link_libraries(amarok_service_gpodder + amarokcore + amaroklib + amarokpud + gpodder_service_config + ${LIBMYGPO_QT_LIBRARIES} + KF5::KIOCore + KF5::ThreadWeaver + Qt5::Network +) + +install( TARGETS amarok_service_gpodder DESTINATION ${PLUGIN_INSTALL_DIR} ) + +kcoreaddons_desktop_to_json(amarok_service_gpodder amarok_service_gpodder.desktop SERVICE_TYPES ${CMAKE_SOURCE_DIR}/src/amarok-plugin.desktop) + + +set(kcm_amarok_service_gpodder_PART_SRCS + GpodderServiceSettings.cpp +) + +ki18n_wrap_ui( kcm_amarok_service_gpodder_PART_SRCS GpodderConfigWidget.ui ) + +add_library(kcm_amarok_service_gpodder MODULE ${kcm_amarok_service_gpodder_PART_SRCS} ) + +target_link_libraries( kcm_amarok_service_gpodder + amarokcore + amaroklib + gpodder_service_config + ${LIBMYGPO_QT_LIBRARIES} + KF5::ConfigWidgets + KF5::KIOCore + Qt5::Network +) + +install(TARGETS kcm_amarok_service_gpodder DESTINATION ${PLUGIN_INSTALL_DIR}) +install(FILES amarok_service_gpodder_config.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) + +kcoreaddons_desktop_to_json(kcm_amarok_service_gpodder amarok_service_gpodder_config.desktop SERVICE_TYPES kcmodule.desktop) diff --git a/src/services/gpodder/GpodderConfigWidget.ui b/src/services/gpodder/GpodderConfigWidget.ui index 76d60677d5..46e3108105 100644 --- a/src/services/gpodder/GpodderConfigWidget.ui +++ b/src/services/gpodder/GpodderConfigWidget.ui @@ -1,125 +1,125 @@ GpodderConfigWidget 0 0 341 162 1 1 0 gpodder.net Profile &Username: false kcfg_GpodderUsername &Password: false kcfg_GpodderPassword - + QLineEdit::Password - + 0 0 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Ubuntu'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="http://www.gpodder.net/register/"><span style=" text-decoration: underline; color:#0057ae;">Sign up to gpodder.net</span></a></p></body></html> true true &Test Login Qt::Horizontal - KLineEdit + QLineEdit QLineEdit -
klineedit.h
+
qlineedit.h
kcfg_GpodderUsername kcfg_GpodderPassword testLogin - klineedit.h + qlineedit.h
diff --git a/src/services/gpodder/GpodderPodcastMeta.cpp b/src/services/gpodder/GpodderPodcastMeta.cpp index d0ba859a4c..27a7e04cde 100644 --- a/src/services/gpodder/GpodderPodcastMeta.cpp +++ b/src/services/gpodder/GpodderPodcastMeta.cpp @@ -1,64 +1,66 @@ /**************************************************************************************** * Copyright (c) 2011 Stefan Derkits * * Copyright (c) 2011 Christian Wagner * * Copyright (c) 2011 Felix Winter * * Copyright (c) 2011 Lucas Lira Gomes * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "GpodderPodcastMeta" #include "GpodderPodcastMeta.h" #include "GpodderProvider.h" using namespace Podcasts; Podcasts::GpodderPodcastChannel::GpodderPodcastChannel( GpodderProvider *provider ) : PodcastChannel() , m_provider( provider ) { } Podcasts::GpodderPodcastChannel::GpodderPodcastChannel( GpodderProvider *provider, PodcastChannelPtr channel ) : PodcastChannel( channel ) , m_provider( provider ) { } Podcasts::GpodderPodcastChannel::GpodderPodcastChannel( GpodderProvider *provider, mygpo::PodcastPtr channel ) : PodcastChannel() , m_provider( provider ) { setUrl( channel->url() ); setWebLink( channel->website() ); setImageUrl( channel->logoUrl() ); setDescription( channel->description() ); setTitle( channel->title() ); } Playlists::PlaylistProvider * Podcasts::GpodderPodcastChannel::provider() const { return dynamic_cast( m_provider ); } QUrl Podcasts::GpodderPodcastChannel::uidUrl() const { - return QString( "amarok-gpodder://%1" ).arg( url().url() ); + QUrl u = url(); + u.setScheme( QStringLiteral( "amarok-gpodder" ) ); + return u; } diff --git a/src/services/gpodder/GpodderPodcastMeta.h b/src/services/gpodder/GpodderPodcastMeta.h index 48f415ee76..5739221f02 100644 --- a/src/services/gpodder/GpodderPodcastMeta.h +++ b/src/services/gpodder/GpodderPodcastMeta.h @@ -1,61 +1,61 @@ /**************************************************************************************** * Copyright (c) 2011 Stefan Derkits * * Copyright (c) 2011 Christian Wagner * * Copyright (c) 2011 Felix Winter * * Copyright (c) 2011 Lucas Lira Gomes * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef GPODDERPODCASTMETA_H #define GPODDERPODCASTMETA_H #include "core/playlists/PlaylistProvider.h" #include "core/podcasts/PodcastMeta.h" -#include +#include namespace Podcasts { class GpodderPodcastChannel; class GpodderProvider; -typedef KSharedPtr GpodderPodcastChannelPtr; +typedef AmarokSharedPointer GpodderPodcastChannelPtr; typedef QList GpodderPodcastChannelList; class GpodderPodcastChannel : public PodcastChannel { public: GpodderPodcastChannel( GpodderProvider *provider ); //Copy a PodcastChannel GpodderPodcastChannel( GpodderProvider *provider, PodcastChannelPtr channel ); //Create a channel from a mygpo podcast channel GpodderPodcastChannel( GpodderProvider *provider, mygpo::PodcastPtr channel ); //PodcastChannel Methods virtual Playlists::PlaylistProvider *provider() const; //Playlist virtual methods virtual QUrl uidUrl() const; private: GpodderProvider *m_provider; }; } Q_DECLARE_METATYPE( Podcasts::GpodderPodcastChannelPtr ) Q_DECLARE_METATYPE( Podcasts::GpodderPodcastChannelList ) #endif diff --git a/src/services/gpodder/GpodderPodcastRequestHandler.cpp b/src/services/gpodder/GpodderPodcastRequestHandler.cpp index bef9ffe0e6..83b5927c4c 100644 --- a/src/services/gpodder/GpodderPodcastRequestHandler.cpp +++ b/src/services/gpodder/GpodderPodcastRequestHandler.cpp @@ -1,49 +1,50 @@ /**************************************************************************************** * Copyright (c) 2011 Stefan Derkits * * Copyright (c) 2011 Christian Wagner * * Copyright (c) 2011 Felix Winter * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "GpodderPodcastRequestHandler.h" #include "core/support/Debug.h" GpodderPodcastRequestHandler::GpodderPodcastRequestHandler( mygpo::PodcastListPtr podcasts, QModelIndex parentItem, GpodderServiceModel *model ) : QObject( model ) , m_podcasts( podcasts ) , m_parentItem( parentItem ) , m_model( model ) { } GpodderPodcastRequestHandler::~GpodderPodcastRequestHandler() { } void GpodderPodcastRequestHandler::finished() { m_model->insertPodcastList( m_podcasts, m_parentItem ); } void GpodderPodcastRequestHandler::requestError( QNetworkReply::NetworkError error ) { debug() << "Error in Podcast request: " << error; } + void GpodderPodcastRequestHandler::parseError() { debug() << "Error while parsing gpodder.net Podcasts"; } diff --git a/src/services/gpodder/GpodderPodcastRequestHandler.h b/src/services/gpodder/GpodderPodcastRequestHandler.h index 9bde6126f4..20b4dd28f1 100644 --- a/src/services/gpodder/GpodderPodcastRequestHandler.h +++ b/src/services/gpodder/GpodderPodcastRequestHandler.h @@ -1,45 +1,45 @@ /**************************************************************************************** * Copyright (c) 2011 Stefan Derkits * * Copyright (c) 2011 Christian Wagner * * Copyright (c) 2011 Felix Winter * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef GPODDERPODCASTREQUESTHANDLER_H_ #define GPODDERPODCASTREQUESTHANDLER_H_ #include "GpodderServiceModel.h" -#include +#include #include class GpodderPodcastRequestHandler : public QObject { Q_OBJECT public: GpodderPodcastRequestHandler( mygpo::PodcastListPtr podcasts, QModelIndex parentItem, GpodderServiceModel *model ); virtual ~GpodderPodcastRequestHandler(); public Q_SLOTS: void finished(); void requestError( QNetworkReply::NetworkError ); void parseError(); private: mygpo::PodcastListPtr m_podcasts; QModelIndex m_parentItem; GpodderServiceModel *m_model; }; #endif /* GPODDERPODCASTREQUESTHANDLER_H_ */ diff --git a/src/services/gpodder/GpodderPodcastTreeItem.h b/src/services/gpodder/GpodderPodcastTreeItem.h index 62517a5733..218618f264 100644 --- a/src/services/gpodder/GpodderPodcastTreeItem.h +++ b/src/services/gpodder/GpodderPodcastTreeItem.h @@ -1,38 +1,38 @@ /**************************************************************************************** * Copyright (c) 2011 Stefan Derkits * * Copyright (c) 2011 Christian Wagner * * Copyright (c) 2011 Felix Winter * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef GPODDERPODCASTTREEITEM_H_ #define GPODDERPODCASTTREEITEM_H_ #include "GpodderTreeItem.h" -#include +#include class GpodderPodcastTreeItem: public GpodderTreeItem { Q_OBJECT public: GpodderPodcastTreeItem( mygpo::PodcastPtr podcast, GpodderTreeItem *parent = 0 ); virtual ~GpodderPodcastTreeItem(); virtual QVariant displayData() const; mygpo::PodcastPtr podcast() const; private: mygpo::PodcastPtr m_podcast; }; #endif /* GPODDERPODCASTTREEITEM_H_ */ diff --git a/src/services/gpodder/GpodderProvider.cpp b/src/services/gpodder/GpodderProvider.cpp index 51a6e7e3c9..3cfdad9e5b 100644 --- a/src/services/gpodder/GpodderProvider.cpp +++ b/src/services/gpodder/GpodderProvider.cpp @@ -1,1307 +1,1297 @@ /**************************************************************************************** * Copyright (c) 2011 Stefan Derkits * * Copyright (c) 2011 Christian Wagner * * Copyright (c) 2011 Felix Winter * * Copyright (c) 2011 Lucas Lira Gomes * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "GpodderProvider" #include "GpodderProvider.h" #include "core-impl/capabilities/timecode/TimecodeWriteCapability.h" #include "core-impl/podcasts/sql/SqlPodcastProvider.h" #include "core/interfaces/Logger.h" #include "core/support/Amarok.h" #include "core/support/Components.h" #include "core/support/Debug.h" #include "EngineController.h" #include "gpodder/GpodderServiceConfig.h" #include "NetworkAccessManagerProxy.h" #include "PodcastModel.h" -#include -#include -#include -#include - #include #include +#include #include +#include + + using namespace Podcasts; GpodderProvider::GpodderProvider( const QString& username, const QString& devicename, ApiRequest *apiRequest ) : m_apiRequest( apiRequest ) , m_username( username ) , m_deviceName( devicename ) , m_channels() , m_addRemoveResult() , m_deviceUpdatesResult() , m_episodeActionListResult() , m_timestampStatus( 0 ) , m_timestampSubscription( subscriptionTimestamp() ) , m_removeAction( 0 ) , m_addList() , m_removeList() , m_timerGeneratePlayAction( new QTimer( this ) ) , m_timerSynchronizeStatus( new QTimer( this ) ) , m_timerSynchronizeSubscriptions( new QTimer( this ) ) { //We have to load episode actions and podcasts subscriptions changes //that weren't uploaded before the time we closed amarok loadCachedEpisodeActions(); loadCachedPodcastsChanges(); //Request all channels and episodes from m_devicename device and after it //request episode actions too requestDeviceUpdates(); //Connect default podcasts signals to make possible to ask the user if he wants //to upload a new local podcast to gpodder.net - connect( The::playlistManager()->defaultPodcasts(), - SIGNAL(playlistAdded(Playlists::PlaylistPtr)), - SLOT(slotSyncPlaylistAdded(Playlists::PlaylistPtr)) ); - connect( The::playlistManager()->defaultPodcasts(), - SIGNAL(playlistRemoved(Playlists::PlaylistPtr)), - SLOT(slotSyncPlaylistRemoved(Playlists::PlaylistPtr)) ); + connect( The::playlistManager()->defaultPodcasts(), &PodcastProvider::playlistAdded, + this, &GpodderProvider::slotSyncPlaylistAdded ); + connect( The::playlistManager()->defaultPodcasts(), &PodcastProvider::playlistRemoved, + this, &GpodderProvider::slotSyncPlaylistRemoved ); Podcasts::SqlPodcastProvider *sqlPodcastProvider; - sqlPodcastProvider = - dynamic_cast - ( The::playlistManager()->defaultPodcasts() ); + sqlPodcastProvider = dynamic_cast + ( The::playlistManager()->defaultPodcasts() ); - connect( The::podcastModel(), - SIGNAL(episodeMarkedAsNew(Podcasts::PodcastEpisodePtr)), - SLOT(slotEpisodeMarkedAsNew(Podcasts::PodcastEpisodePtr)) ); + connect( The::podcastModel(), &PlaylistBrowserNS::PodcastModel::episodeMarkedAsNew, + this, &GpodderProvider::slotEpisodeMarkedAsNew ); if( sqlPodcastProvider ) { - connect( sqlPodcastProvider, - SIGNAL(episodeDeleted(Podcasts::PodcastEpisodePtr)), - SLOT(slotEpisodeDeleted(Podcasts::PodcastEpisodePtr)) ); - connect( sqlPodcastProvider, - SIGNAL(episodeDownloaded(Podcasts::PodcastEpisodePtr)), - SLOT(slotEpisodeDownloaded(Podcasts::PodcastEpisodePtr)) ); + connect( sqlPodcastProvider, &SqlPodcastProvider::episodeDeleted, + this, &GpodderProvider::slotEpisodeDeleted ); + connect( sqlPodcastProvider,&SqlPodcastProvider::episodeDownloaded, + this, &GpodderProvider::slotEpisodeDownloaded ); } //Connect engine controller signals to make possible to synchronize podcast status - connect( The::engineController(), SIGNAL(trackChanged(Meta::TrackPtr)), - SLOT(slotTrackChanged(Meta::TrackPtr)) ); - connect( The::engineController(), SIGNAL(trackPositionChanged(qint64,bool)), - SLOT(slotTrackPositionChanged(qint64,bool)) ); - connect( The::engineController(), SIGNAL(paused()), - SLOT(slotPaused()) ); + connect( The::engineController(), &EngineController::trackChanged, + this, &GpodderProvider::slotTrackChanged ); + connect( The::engineController(), &EngineController::trackPositionChanged, + this, &GpodderProvider::slotTrackPositionChanged ); + connect( The::engineController(), &EngineController::paused, + this, &GpodderProvider::slotPaused ); //These timers will periodically synchronize data between local podcasts and gpodder.net - connect( m_timerSynchronizeStatus, SIGNAL(timeout()), - SLOT(timerSynchronizeStatus()) ); - connect( m_timerSynchronizeSubscriptions, SIGNAL(timeout()), - SLOT(timerSynchronizeSubscriptions()) ); - connect( m_timerGeneratePlayAction, SIGNAL(timeout()), - SLOT(timerGenerateEpisodeAction()) ); + connect( m_timerSynchronizeStatus, &QTimer::timeout, + this, &GpodderProvider::timerSynchronizeStatus ); + connect( m_timerSynchronizeSubscriptions, &QTimer::timeout, + this, &GpodderProvider::timerSynchronizeSubscriptions ); + connect( m_timerGeneratePlayAction, &QTimer::timeout, + this, &GpodderProvider::timerGenerateEpisodeAction ); m_timerGeneratePlayAction->stop(); m_timerSynchronizeStatus->stop(); m_timerSynchronizeSubscriptions->stop(); } GpodderProvider::~GpodderProvider() { delete m_timerGeneratePlayAction; delete m_timerSynchronizeStatus; delete m_timerSynchronizeSubscriptions; //Save cached episode actions and Podcast changes, in order to //upload them to gpodder.net in the next saveCachedEpisodeActions(); saveCachedPodcastsChanges(); m_uploadEpisodeStatusMap.clear(); m_episodeStatusMap.clear(); m_redirectionUrlMap.clear(); m_channels.clear(); } bool GpodderProvider::possiblyContainsTrack( const QUrl &url ) const { DEBUG_BLOCK foreach( PodcastChannelPtr ptr, m_channels ) { foreach( PodcastEpisodePtr episode, ptr->episodes() ) { if( episode->uidUrl() == url.url() ) return true; } } return false; } Meta::TrackPtr GpodderProvider::trackForUrl( const QUrl &url ) { DEBUG_BLOCK if( url.isEmpty() ) return Meta::TrackPtr(); foreach( PodcastChannelPtr podcast, m_channels ) { foreach( PodcastEpisodePtr episode, podcast->episodes() ) { if( episode->uidUrl() == url.url() ) { return Meta::TrackPtr::dynamicCast( episode ); } } } return Meta::TrackPtr(); } PodcastEpisodePtr GpodderProvider::episodeForGuid( const QString &guid ) { foreach( PodcastChannelPtr ptr, m_channels ) { foreach( PodcastEpisodePtr episode, ptr->episodes() ) { if( episode->guid() == guid ) return episode; } } return PodcastEpisodePtr(); } void GpodderProvider::addPodcast( const QUrl &url ) { Q_UNUSED( url ) } Playlists::PlaylistPtr GpodderProvider::addPlaylist( Playlists::PlaylistPtr playlist ) { DEBUG_BLOCK PodcastChannelPtr channel = PodcastChannelPtr::dynamicCast( playlist ); if( channel.isNull() ) return Playlists::PlaylistPtr(); //This function is executed every time a new channel is found on gpodder.net PodcastChannelPtr master; PodcastChannelPtr slave; foreach( PodcastChannelPtr tempChannel, The::playlistManager()->defaultPodcasts()->channels() ) if( tempChannel->url() == channel->url() ) master = tempChannel; foreach( PodcastChannelPtr tempChannel, this->channels() ) if( tempChannel->url() == channel->url() ) slave = tempChannel; if( !master ) master = The::playlistManager()->defaultPodcasts()->addChannel( channel ); if( !slave ) { slave = this->addChannel( master ); //If playlist is not a GpodderPodcastChannelPtr then we must subscribe //it in gpodder.net if( !GpodderPodcastChannelPtr::dynamicCast( playlist ) ) { //The service will try to subscribe this podcast in gpodder.net in //the next synchronization QUrl url = QUrl( slave->url().url() ); m_removeList.removeAll( url ); m_addList << url; } } //Create a playlist synchronization between master and slave The::playlistManager()->setupSync( Playlists::PlaylistPtr::dynamicCast( master ), Playlists::PlaylistPtr::dynamicCast( slave ) ); return Playlists::PlaylistPtr::dynamicCast( slave ); } PodcastChannelPtr GpodderProvider::addChannel( PodcastChannelPtr channel ) { DEBUG_BLOCK GpodderPodcastChannelPtr gpodderChannel( new GpodderPodcastChannel( this, channel ) ); m_channels << PodcastChannelPtr::dynamicCast( gpodderChannel );; emit playlistAdded( Playlists::PlaylistPtr::dynamicCast( gpodderChannel ) ); return PodcastChannelPtr::dynamicCast( gpodderChannel ); } PodcastEpisodePtr GpodderProvider::addEpisode( PodcastEpisodePtr episode ) { if( episode.isNull() ) return PodcastEpisodePtr(); if( episode->channel().isNull() ) { debug() << "channel is null"; return PodcastEpisodePtr(); } return episode; } PodcastChannelList GpodderProvider::channels() { DEBUG_BLOCK PodcastChannelList list; foreach( PodcastChannelPtr channel, m_channels ) list << PodcastChannelPtr::dynamicCast( channel ); return list; } QString GpodderProvider::prettyName() const { return i18n( "Gpodder Podcasts" ); } QIcon GpodderProvider::icon() const { return QIcon::fromTheme( "view-services-gpodder-amarok" ); } Playlists::PlaylistList GpodderProvider::playlists() { Playlists::PlaylistList playlists; foreach( PodcastChannelPtr channel, m_channels ) playlists << Playlists::PlaylistPtr::staticCast( channel ); return playlists; } void GpodderProvider::completePodcastDownloads() { } void GpodderProvider::removeChannel( const QUrl &url ) { for( int i = 0; i < m_channels.size(); i++ ) { if( m_channels.at( i )->url() == url ) { PodcastChannelPtr channel = m_channels.at( i ); QUrl url = QUrl( channel->url().url() ); m_channels.removeAll( channel ); m_episodeStatusMap.remove( url ); m_uploadEpisodeStatusMap.remove( url ); m_addList.removeAll( url ); emit playlistRemoved( Playlists::PlaylistPtr::dynamicCast( channel ) ); return; } } } QActionList GpodderProvider::channelActions( PodcastChannelList channels ) { QActionList actions; if( channels.isEmpty() ) return actions; if( m_removeAction == 0 ) { m_removeAction = new QAction( QIcon::fromTheme( "edit-delete" ), i18n( "&Delete Channel and Episodes" ), this ); m_removeAction->setProperty( "popupdropper_svg_id", "delete" ); connect( m_removeAction, SIGNAL(triggered()), SLOT(slotRemoveChannels()) ); } //Set the episode list as data that we'll retrieve in the slot m_removeAction->setData( QVariant::fromValue( channels ) ); actions << m_removeAction; return actions; } QActionList GpodderProvider::playlistActions( const Playlists::PlaylistList &playlists ) { PodcastChannelList channels; foreach( const Playlists::PlaylistPtr &playlist, playlists ) { PodcastChannelPtr channel = PodcastChannelPtr::dynamicCast( playlist ); if( channel ) channels << channel; } return channelActions( channels ); } void GpodderProvider::slotRemoveChannels() { DEBUG_BLOCK QAction *action = qobject_cast( QObject::sender() ); if( action == 0 ) return; PodcastChannelList channels = action->data().value(); action->setData( QVariant() ); //Clear data foreach( PodcastChannelPtr channel, channels ) { - removeChannel( channel->url().url() ); + removeChannel( channel->url() ); //The service will try to unsubscribe this podcast from gpodder.net //in the next synchronization m_removeList << channel->url(); } } void GpodderProvider::slotSyncPlaylistAdded( Playlists::PlaylistPtr playlist ) { PodcastChannelPtr channel = Podcasts::PodcastChannelPtr::dynamicCast( playlist ); //If the new channel already exist in gpodder channels, then //we don't have to add it to gpodder.net again foreach( PodcastChannelPtr tempChannel, m_channels ) if( channel->url() == tempChannel->url() ) return; addPlaylist( playlist ); m_timerSynchronizeSubscriptions->start( 60000 ); } void GpodderProvider::slotSyncPlaylistRemoved( Playlists::PlaylistPtr playlist ) { Podcasts::PodcastChannelPtr channel = Podcasts::PodcastChannelPtr::dynamicCast( playlist ); //If gpodder channels doesn't contais the removed channel from default //podcast provider, then we don't have to remove it from gpodder.net foreach( PodcastChannelPtr tempChannel, m_channels ) if( channel->url() == tempChannel->url() ) { - removeChannel( tempChannel->url().url() ); + removeChannel( tempChannel->url() ); //The service will try to unsubscribe this podcast from gpodder.net //in the next synchronization m_removeList << tempChannel->url(); m_timerSynchronizeSubscriptions->start( 60000 ); return; } } qulonglong GpodderProvider::subscriptionTimestamp() { - KConfigGroup config = KGlobal::config()->group( GpodderServiceConfig::configSectionName() ); + KConfigGroup config = Amarok::config( GpodderServiceConfig::configSectionName() ); return config.readEntry( "subscriptionTimestamp", 0 ); } void GpodderProvider::setSubscriptionTimestamp( qulonglong newTimestamp ) { - KConfigGroup config = KGlobal::config()->group( GpodderServiceConfig::configSectionName() ); + KConfigGroup config = Amarok::config( GpodderServiceConfig::configSectionName() ); config.writeEntry( "subscriptionTimestamp", newTimestamp ); } void GpodderProvider::synchronizeStatus() { DEBUG_BLOCK debug() << "new episodes status: " << m_uploadEpisodeStatusMap.size(); - if( Solid::Networking::status() == Solid::Networking::Unconnected ) + if( !QNetworkConfigurationManager().isOnline() ) return; if( !m_uploadEpisodeStatusMap.isEmpty() ) { m_episodeActionsResult = m_apiRequest->uploadEpisodeActions( m_username, m_uploadEpisodeStatusMap.values() ); //Only clear m_episodeStatusList if the synchronization with gpodder.net really worked connect( m_episodeActionsResult.data(), SIGNAL(finished()), SLOT(slotSuccessfulStatusSynchronisation()) ); connect( m_episodeActionsResult.data(), SIGNAL(requestError(QNetworkReply::NetworkError)), SLOT(synchronizeStatusRequestError(QNetworkReply::NetworkError)) ); connect( m_episodeActionsResult.data(), SIGNAL(parseError()), SLOT(synchronizeStatusParseError()) ); Amarok::Components::logger()->shortMessage( i18n( "Trying to synchronize statuses with gpodder.net" ) ); } else m_timerSynchronizeStatus->stop(); } void GpodderProvider::slotSuccessfulStatusSynchronisation() { DEBUG_BLOCK m_timestampStatus = QDateTime::currentMSecsSinceEpoch(); m_uploadEpisodeStatusMap.clear(); //In addition, the server MUST send any URLs that have been rewritten (sanitized, see bug:747) //as a list of tuples with the key "update_urls". The client SHOULD parse this list and update //the local subscription list accordingly (the server only sanitizes the URL, so the semantic //"content" should stay the same and therefore the client can simply update the URL value //locally and use it for future updates updateLocalPodcasts( m_episodeActionsResult->updateUrlsList() ); } void GpodderProvider::synchronizeStatusParseError() { DEBUG_BLOCK QTimer::singleShot( 20000, this, SLOT(timerSynchronizeStatus()) ); debug() << "synchronizeStatus [Status Synchronization] - Parse error"; } void GpodderProvider::synchronizeStatusRequestError(QNetworkReply::NetworkError error) { DEBUG_BLOCK QTimer::singleShot( 20000, this, SLOT(timerSynchronizeStatus()) ); debug() << "synchronizeStatus [Status Synchronization] - Request error nr.: " << error; } void GpodderProvider::synchronizeSubscriptions() { DEBUG_BLOCK debug() << "add: " << m_addList.size(); debug() << "remove: " << m_removeList.size(); - if( Solid::Networking::status() == Solid::Networking::Unconnected ) + if( !QNetworkConfigurationManager().isOnline() ) return; if( !m_removeList.isEmpty() || !m_addList.isEmpty() ) { m_addRemoveResult = m_apiRequest->addRemoveSubscriptions( m_username, m_deviceName, m_addList, m_removeList ); //Only clear m_addList and m_removeList if the synchronization with gpodder.net really worked connect( m_addRemoveResult.data(), SIGNAL(finished()), this, SLOT(slotSuccessfulSubscriptionSynchronisation()) ); Amarok::Components::logger()->shortMessage( i18n( "Trying to synchronize subscriptions with gpodder.net" ) ); } else m_timerSynchronizeSubscriptions->stop(); } void GpodderProvider::slotSuccessfulSubscriptionSynchronisation() { DEBUG_BLOCK m_timestampSubscription = QDateTime::currentMSecsSinceEpoch(); setSubscriptionTimestamp( m_timestampSubscription ); m_addList.clear(); m_removeList.clear(); //In addition, the server MUST send any URLs that have been rewritten (sanitized, see bug:747) //as a list of tuples with the key "update_urls". The client SHOULD parse this list and update //the local subscription list accordingly (the server only sanitizes the URL, so the semantic //"content" should stay the same and therefore the client can simply update the URL value //locally and use it for future updates updateLocalPodcasts( m_addRemoveResult->updateUrlsList() ); } void GpodderProvider::slotTrackChanged( Meta::TrackPtr track ) { m_trackToSyncStatus = NULL; if( track != Meta::TrackPtr( 0 ) ) { //If the episode is from one of the gpodder subscribed podcasts, then we must keep looking it - if( ( this->possiblyContainsTrack( track->uidUrl() ) ) || - ( this->possiblyContainsTrack( track->uidUrl() ) && - The::playlistManager()->defaultPodcasts()->possiblyContainsTrack( track->uidUrl() ) - ) ) + if( ( this->possiblyContainsTrack( QUrl( track->uidUrl() ) ) ) ) { m_trackToSyncStatus = track; QTimer::singleShot( 10000, this, SLOT(timerPrepareToSyncPodcastStatus()) ); //A bookmark will be created if we have a play status available, //for current track, at m_episodeStatusMap createPlayStatusBookmark(); return; } } m_timerGeneratePlayAction->stop(); //EpisodeActions should be sent when the user clicks //stops and doesn't resume listening in e.g. 1 minute //Or when the user is not listening a podcast in e.g. 1 minute m_timerSynchronizeStatus->start( 60000 ); } void GpodderProvider::slotTrackPositionChanged( qint64 position, bool userSeek ) { Q_UNUSED( position ) //If the current track is in one of the subscribed gpodder channels and it's position //is not at the beginning of the track, then we probably should sync it status. if( m_trackToSyncStatus ) { if( userSeek ) { //Test if this track still playing after 10 seconds to avoid accidentally user changes QTimer::singleShot( 10000, this, SLOT(timerPrepareToSyncPodcastStatus()) ); } } } void GpodderProvider::slotPaused() { m_timerGeneratePlayAction->stop(); //EpisodeActions should be sent when the user clicks pause //or stop and doesn't resume listening in e.g. 1 minute m_timerSynchronizeStatus->start( 60000 ); } void GpodderProvider::timerSynchronizeSubscriptions() { synchronizeSubscriptions(); } void GpodderProvider::timerSynchronizeStatus() { synchronizeStatus(); } void GpodderProvider::timerPrepareToSyncPodcastStatus() { if( The::engineController()->currentTrack() == m_trackToSyncStatus ) { EpisodeActionPtr tempEpisodeAction; PodcastEpisodePtr tempEpisode = PodcastEpisodePtr::dynamicCast( m_trackToSyncStatus ); if( tempEpisode ) { qulonglong positionSeconds = The::engineController()->trackPosition(); qulonglong lengthSeconds = The::engineController()->trackLength() / 1000; QString podcastUrl = resolvedPodcastUrl( tempEpisode ).url(); tempEpisodeAction = EpisodeActionPtr( new EpisodeAction( QUrl( podcastUrl ), QUrl( tempEpisode->uidUrl() ), m_deviceName, EpisodeAction::Play, QDateTime::currentMSecsSinceEpoch(), 1, positionSeconds + 1, lengthSeconds ) ); //Any previous episodeAction, from the same podcast, will be replaced - m_uploadEpisodeStatusMap.insert( tempEpisode->uidUrl(), tempEpisodeAction ); + m_uploadEpisodeStatusMap.insert( QUrl( tempEpisode->uidUrl() ), tempEpisodeAction ); } //Starts to generate EpisodeActions m_timerGeneratePlayAction->start( 30000 ); } } void GpodderProvider::timerGenerateEpisodeAction() { //Create and update episode actions if( The::engineController()->currentTrack() == m_trackToSyncStatus ) { EpisodeActionPtr tempEpisodeAction; PodcastEpisodePtr tempEpisode = PodcastEpisodePtr::dynamicCast( m_trackToSyncStatus ); if( tempEpisode ) { qulonglong positionSeconds = The::engineController()->trackPosition(); qulonglong lengthSeconds = The::engineController()->trackLength() / 1000; QString podcastUrl = resolvedPodcastUrl( tempEpisode ).url(); tempEpisodeAction = EpisodeActionPtr( new EpisodeAction( QUrl( podcastUrl ), QUrl( tempEpisode->uidUrl() ), m_deviceName, EpisodeAction::Play, QDateTime::currentMSecsSinceEpoch(), 1, positionSeconds + 1, lengthSeconds ) ); //Any previous episodeAction, from the same podcast, will be replaced - m_uploadEpisodeStatusMap.insert( tempEpisode->uidUrl(), tempEpisodeAction ); + m_uploadEpisodeStatusMap.insert( QUrl( tempEpisode->uidUrl() ), tempEpisodeAction ); //Make local podcasts aware of new episodeActions - m_episodeStatusMap.insert( tempEpisode->uidUrl(), tempEpisodeAction ); + m_episodeStatusMap.insert( QUrl( tempEpisode->uidUrl() ), tempEpisodeAction ); } } } void GpodderProvider::requestDeviceUpdates() { DEBUG_BLOCK - if( Solid::Networking::status() == Solid::Networking::Unconnected ) + if( !QNetworkConfigurationManager().isOnline() ) { QTimer::singleShot( 10000, this, SLOT(requestDeviceUpdates()) ); return; } m_deviceUpdatesResult = m_apiRequest->deviceUpdates( m_username, m_deviceName, 0 ); connect( m_deviceUpdatesResult.data(), SIGNAL(finished()), SLOT(deviceUpdatesFinished()) ); connect( m_deviceUpdatesResult.data(), SIGNAL(requestError(QNetworkReply::NetworkError)), SLOT(deviceUpdatesRequestError(QNetworkReply::NetworkError)) ); connect( m_deviceUpdatesResult.data(), SIGNAL(parseError()), SLOT(deviceUpdatesParseError()) ); } void GpodderProvider::deviceUpdatesFinished() { DEBUG_BLOCK debug() << "DeviceUpdate timestamp: " << m_deviceUpdatesResult->timestamp(); //Channels to subscribe locally foreach( mygpo::PodcastPtr podcast, m_deviceUpdatesResult->addList() ) { debug() << "Subscribing GPO channel: " << podcast->title() << ": " << podcast->url(); GpodderPodcastChannelPtr channel = GpodderPodcastChannelPtr( new GpodderPodcastChannel( this, podcast ) ); //First we need to resolve redirection url's if there is any requestUrlResolve( channel ); } //Request the last episode status for every episode in gpodder.net //subscribed podcasts QTimer::singleShot( 1000, this, SLOT(requestEpisodeActionsInCascade()) ); //Only after all subscription changes are committed should we save the timestamp m_timestampSubscription = m_deviceUpdatesResult->timestamp(); setSubscriptionTimestamp( m_timestampSubscription ); } void GpodderProvider::continueDeviceUpdatesFinished() { foreach( GpodderPodcastChannelPtr channel, m_resolvedChannelsToBeAdded ) { m_channelsToRequestActions.enqueue( channel->url() ); PodcastChannelPtr master; PodcastChannelPtr slave; slave = this->addChannel( PodcastChannelPtr::dynamicCast( channel ) ); foreach( PodcastChannelPtr tempChannel, The::playlistManager()->defaultPodcasts()->channels() ) if( tempChannel->url() == channel->url() ) master = tempChannel; if( !master ) master = The::playlistManager()->defaultPodcasts()->addChannel( slave ); //Create a playlist synchronization between master and slave The::playlistManager()->setupSync( Playlists::PlaylistPtr::dynamicCast( master ), Playlists::PlaylistPtr::dynamicCast( slave ) ); } m_resolvedChannelsToBeAdded.clear(); } void GpodderProvider::deviceUpdatesParseError() { DEBUG_BLOCK QTimer::singleShot( 10000, this, SLOT(requestDeviceUpdates()) ); debug() << "deviceUpdates [Subscription Synchronization] - Parse error"; Amarok::Components::logger()->shortMessage( i18n( "GPodder Service failed to get data from the server. Will retry in 10 seconds..." ) ); } void GpodderProvider::deviceUpdatesRequestError( QNetworkReply::NetworkError error ) { DEBUG_BLOCK QTimer::singleShot( 10000, this, SLOT(requestDeviceUpdates()) ); debug() << "deviceUpdates [Subscription Synchronization] - Request error nr.: " << error; Amarok::Components::logger()->shortMessage( i18n( "GPodder Service failed to get data from the server. Will retry in 10 seconds..." ) ); } void GpodderProvider::requestEpisodeActionsInCascade() { DEBUG_BLOCK - if( Solid::Networking::status() == Solid::Networking::Unconnected ) + if( !QNetworkConfigurationManager().isOnline() ) { QTimer::singleShot( 10000, this, SLOT(requestEpisodeActionsInCascade()) ); return; } //This function will download all episode actions for //every podcast contained in m_channelsToRequestActions if( !m_channelsToRequestActions.isEmpty() ) { QUrl url = m_channelsToRequestActions.head(); m_episodeActionListResult = m_apiRequest->episodeActionsByPodcast( m_username, url.toString(), true ); debug() << "Requesting actions for " << url.toString(); connect( m_episodeActionListResult.data(), SIGNAL(finished()), SLOT(episodeActionsInCascadeFinished()) ); connect( m_episodeActionListResult.data(), SIGNAL(requestError(QNetworkReply::NetworkError)), SLOT(episodeActionsInCascadeRequestError(QNetworkReply::NetworkError)) ); connect( m_episodeActionListResult.data(), SIGNAL(parseError()), SLOT(episodeActionsInCascadeParseError()) ); } else { //We should try to upload cached EpisodeActions to gpodder.net synchronizeStatus(); } } void GpodderProvider::episodeActionsInCascadeFinished() { DEBUG_BLOCK m_timestampStatus = m_episodeActionListResult->timestamp(); foreach( EpisodeActionPtr tempEpisodeAction, m_episodeActionListResult->list() ) { if( tempEpisodeAction->action() == EpisodeAction::Play ) { debug() << QString( "Adding a new play status to episode: %1" ) .arg( tempEpisodeAction->episodeUrl().toString() ); m_episodeStatusMap.insert( tempEpisodeAction->episodeUrl(), tempEpisodeAction ); //A bookmark will be created if we have a play status available, //for current track, at m_episodeStatusMap createPlayStatusBookmark(); } else { PodcastChannelPtr channel; PodcastEpisodePtr episode; foreach( PodcastChannelPtr tempChannel, m_channels ) if( tempChannel->url() == tempEpisodeAction->podcastUrl() ) { channel = tempChannel; foreach( PodcastEpisodePtr tempEpisode, channel->episodes() ) if( tempEpisode->uidUrl() == tempEpisodeAction->episodeUrl().toString() ) episode = tempEpisode; } if( channel && episode ) { if( tempEpisodeAction->action() == EpisodeAction::New ) { if( !episode ) { debug() << QString( "New episode to be added found: %1" ) .arg( tempEpisodeAction->episodeUrl().toString() ); PodcastEpisodePtr tempEpisode; tempEpisode = PodcastEpisodePtr( new PodcastEpisode() ); tempEpisode->setUidUrl( tempEpisodeAction->episodeUrl() ); tempEpisode->setChannel( PodcastChannelPtr::dynamicCast( channel ) ); channel->addEpisode( tempEpisode ); } else { debug() << QString( "Marking an existent episode as new: %1" ) .arg( tempEpisodeAction->episodeUrl().toString() ); episode->setNew( true ); } } else if( tempEpisodeAction->action() == EpisodeAction::Download ) { debug() << QString( "Adding a new download status to episode: %1" ) .arg( tempEpisodeAction->episodeUrl().toString() ); } else if( tempEpisodeAction->action() == EpisodeAction::Delete ) { debug() << QString( "Adding a new delete status to episode: %1" ) .arg( tempEpisodeAction->episodeUrl().toString() ); } m_episodeStatusMap.insert( tempEpisodeAction->episodeUrl(), tempEpisodeAction ); } else { //For some reason the podcast and/or episode for this action //wasn't found debug() << QString( "Episode and/or channel not found" );; } } } //We must remove this podcast url and continue with the others m_channelsToRequestActions.dequeue(); QTimer::singleShot( 100, this, SLOT(requestEpisodeActionsInCascade()) ); } void GpodderProvider::episodeActionsInCascadeParseError() { DEBUG_BLOCK QTimer::singleShot( 10000, this, SLOT(requestEpisodeActionsInCascade()) ); //If we fail to get EpisodeActions for this channel then we must put it //at the end of the list. In order to be synced later on. m_channelsToRequestActions.enqueue( m_channelsToRequestActions.dequeue() ); debug() << "episodeActionsInCascade [Status Synchronization] - Parse Error"; } void GpodderProvider::episodeActionsInCascadeRequestError( QNetworkReply::NetworkError error ) { DEBUG_BLOCK QTimer::singleShot( 10000, this, SLOT(requestEpisodeActionsInCascade()) ); //If we fail to get EpisodeActions for this channel then we must put it //at the end of the list. In order to be synced later on. m_channelsToRequestActions.enqueue( m_channelsToRequestActions.dequeue() ); debug() << "episodeActionsInCascade [Status Synchronization] - Request error nr.: " << error; } void GpodderProvider::updateLocalPodcasts( const QList > updatedUrls ) { QList< QPair >::const_iterator tempUpdatedUrl = updatedUrls.begin(); for(; tempUpdatedUrl != updatedUrls.end(); ++tempUpdatedUrl ) { foreach( PodcastChannelPtr tempChannel, The::playlistManager()->defaultPodcasts()->channels() ) { if( tempChannel->url() == (*tempUpdatedUrl).first ) tempChannel->setUrl( (*tempUpdatedUrl).second ); } foreach( PodcastChannelPtr tempGpodderChannel, m_channels ) { if( tempGpodderChannel->url() == (*tempUpdatedUrl).first ) tempGpodderChannel->setUrl( (*tempUpdatedUrl).second ); } } } void GpodderProvider::createPlayStatusBookmark() { Meta::TrackPtr track = The::engineController()->currentTrack(); if( track ) { - EpisodeActionPtr tempEpisodeAction = m_episodeStatusMap.value( track->uidUrl() ); + EpisodeActionPtr tempEpisodeAction = m_episodeStatusMap.value( QUrl( track->uidUrl() ) ); //Create an AutoTimecode at the last position position, so the user always know where he stopped to listen if( tempEpisodeAction && ( tempEpisodeAction->action() == EpisodeAction::Play ) ) { if( track && track->has() ) { QScopedPointer tcw( track->create() ); qint64 positionMiliSeconds = tempEpisodeAction->position() * 1000; tcw->writeAutoTimecode( positionMiliSeconds ); } } } } void GpodderProvider::requestUrlResolve( Podcasts::GpodderPodcastChannelPtr channel ) { if( !channel ) return; m_resolveUrlJob = KIO::get( channel->url(), KIO::Reload, KIO::HideProgressInfo ); - connect( m_resolveUrlJob, SIGNAL(result(KJob*)), - SLOT(urlResolveFinished(KJob*)) ); + connect( m_resolveUrlJob, &KJob::result, + this, &GpodderProvider::urlResolveFinished ); connect( m_resolveUrlJob, - SIGNAL(permanentRedirection(KIO::Job*,QUrl,QUrl)), - SLOT(urlResolvePermanentRedirection(KIO::Job*,QUrl,QUrl)) ); + &KIO::TransferJob::permanentRedirection, + this, &GpodderProvider::urlResolvePermanentRedirection ); m_resolvedPodcasts.insert( m_resolveUrlJob, channel ); } void GpodderProvider::urlResolvePermanentRedirection( KIO::Job *job, const QUrl &fromUrl, const QUrl &toUrl ) { DEBUG_BLOCK KIO::TransferJob *transferJob = dynamic_cast( job ); GpodderPodcastChannelPtr channel = m_resolvedPodcasts.value( transferJob ); m_redirectionUrlMap.insert( toUrl, channel->url() ); channel->setUrl( toUrl ); debug() << fromUrl.url() << " was redirected to " << toUrl.url(); requestUrlResolve( channel ); } void GpodderProvider::urlResolveFinished( KJob * job ) { KIO::TransferJob *transferJob = dynamic_cast( job ); if( transferJob && ( !( transferJob->isErrorPage() || job->error() ) ) ) { m_resolvedChannelsToBeAdded.push_back( m_resolvedPodcasts.value( transferJob ) ); m_resolvedPodcasts.remove( transferJob ); } else requestUrlResolve( m_resolvedPodcasts.value( transferJob ) ); if( m_resolvedPodcasts.empty() ) continueDeviceUpdatesFinished(); m_resolveUrlJob = 0; } void GpodderProvider::slotEpisodeDownloaded( PodcastEpisodePtr episode ) { EpisodeActionPtr tempEpisodeAction; QString podcastUrl = resolvedPodcastUrl( episode ).url(); tempEpisodeAction = EpisodeActionPtr( new EpisodeAction( QUrl( podcastUrl ), QUrl( episode->uidUrl() ), m_deviceName, EpisodeAction::Download, QDateTime::currentMSecsSinceEpoch(), 0, 0, 0 ) ); //Any previous episodeAction, from the same podcast, will be replaced - m_uploadEpisodeStatusMap.insert( episode->uidUrl(), tempEpisodeAction ); + m_uploadEpisodeStatusMap.insert( QUrl( episode->uidUrl() ), tempEpisodeAction ); m_timerSynchronizeStatus->start( 60000 ); } void GpodderProvider::slotEpisodeDeleted( PodcastEpisodePtr episode ) { EpisodeActionPtr tempEpisodeAction; QString podcastUrl = resolvedPodcastUrl( episode ).url(); tempEpisodeAction = EpisodeActionPtr( new EpisodeAction( QUrl( podcastUrl ), QUrl( episode->uidUrl() ), m_deviceName, EpisodeAction::Delete, QDateTime::currentMSecsSinceEpoch(), 0, 0, 0 ) ); //Any previous episodeAction, from the same podcast, will be replaced - m_uploadEpisodeStatusMap.insert( episode->uidUrl(), tempEpisodeAction ); + m_uploadEpisodeStatusMap.insert( QUrl( episode->uidUrl() ), tempEpisodeAction ); m_timerSynchronizeStatus->start( 60000 ); } void GpodderProvider::slotEpisodeMarkedAsNew( PodcastEpisodePtr episode ) { EpisodeActionPtr tempEpisodeAction; QString podcastUrl = resolvedPodcastUrl( episode ).url(); tempEpisodeAction = EpisodeActionPtr( new EpisodeAction( QUrl( podcastUrl ), QUrl( episode->uidUrl() ), m_deviceName, EpisodeAction::New, QDateTime::currentMSecsSinceEpoch(), 0, 0, 0 ) ); //Any previous episodeAction, from the same podcast, will be replaced - m_uploadEpisodeStatusMap.insert( episode->uidUrl(), tempEpisodeAction ); + m_uploadEpisodeStatusMap.insert( QUrl( episode->uidUrl() ), tempEpisodeAction ); m_timerSynchronizeStatus->start( 60000 ); } inline KConfigGroup GpodderProvider::gpodderActionsConfig() const { return Amarok::config( "GPodder Cached Episode Actions" ); } void GpodderProvider::loadCachedEpisodeActions() { DEBUG_BLOCK if( !gpodderActionsConfig().exists() ) return; int action; bool validActionType; bool actionTypeConversion; qulonglong timestamp = 0; qulonglong started = 0; qulonglong position = 0; qulonglong total = 0; QStringList actionsDetails; EpisodeAction::ActionType actionType; foreach( QString episodeUrl, gpodderActionsConfig().keyList() ) { actionsDetails.clear(); actionsDetails = gpodderActionsConfig().readEntry( episodeUrl ).split( ',' ); if( actionsDetails.count() != 6 ) debug() << "There are less/more fields than expected."; else { action = actionsDetails[1].toInt( &actionTypeConversion ); if( !actionTypeConversion ) debug() << "Failed to convert actionType field to int."; else { validActionType = true; timestamp = actionsDetails[2].toULongLong(); started = actionsDetails[3].toULongLong(); position = actionsDetails[4].toULongLong(); total = actionsDetails[5].toULongLong(); switch( action ) { case 0: actionType = EpisodeAction::Download; break; case 1: actionType = EpisodeAction::Play; break; case 2: actionType = EpisodeAction::Delete; break; case 3: actionType = EpisodeAction::New; break; default: validActionType = false; break; } //We can't create a EpisodeAction if action isn't a valid alternative if( !validActionType ) debug() << "Action isn't a valid alternative."; else { debug() << QString( "Loaded %1 action." ).arg( episodeUrl ); EpisodeActionPtr tempEpisodeAction = EpisodeActionPtr( new EpisodeAction( QUrl( actionsDetails[0] ), QUrl( episodeUrl ), m_deviceName, actionType, timestamp, started, position, total ) ); //Any previous episodeAction, from the same podcast, will be replaced m_uploadEpisodeStatusMap.insert( tempEpisodeAction->episodeUrl(), tempEpisodeAction ); m_episodeStatusMap.insert( tempEpisodeAction->episodeUrl(), tempEpisodeAction ); } } } } //We should delete cached EpisodeActions, since we already loaded them gpodderActionsConfig().deleteGroup(); synchronizeStatus(); } void GpodderProvider::saveCachedEpisodeActions() { DEBUG_BLOCK if( m_uploadEpisodeStatusMap.isEmpty() ) return; int actionType; QList actionsDetails; foreach( EpisodeActionPtr action, m_uploadEpisodeStatusMap.values() ) { actionsDetails.clear(); actionsDetails.append( action->podcastUrl().toString() ); switch( action->action() ) { case EpisodeAction::Download: actionType = 0; break; case EpisodeAction::Play: actionType = 1; break; case EpisodeAction::Delete: actionType = 2; break; case EpisodeAction::New: actionType = 3; break; default: actionType = -1; break; } actionsDetails.append( QString::number( actionType ) ); actionsDetails.append( QString::number( action->timestamp() ) ); actionsDetails.append( QString::number( action->started() ) ); actionsDetails.append( QString::number( action->position() ) ); actionsDetails.append( QString::number( action->total() ) ); gpodderActionsConfig().writeEntry( action->episodeUrl().toString(), actionsDetails ); } } inline KConfigGroup GpodderProvider::gpodderPodcastsConfig() const { return Amarok::config( "GPodder Cached Podcast Changes" ); } void GpodderProvider::loadCachedPodcastsChanges() { DEBUG_BLOCK if( !gpodderPodcastsConfig().exists() ) return; QStringList podcastsUrlsToAdd; QStringList podcastsUrlsToRemove; podcastsUrlsToAdd = gpodderPodcastsConfig().readEntry( "addList" ).split( ',' ); podcastsUrlsToRemove = gpodderPodcastsConfig().readEntry( "removeList" ).split( ',' ); foreach( QString podcastUrl, podcastsUrlsToAdd ) { debug() << QString( "New channel to subscribe: %1" ).arg( podcastUrl ); m_addList.append( QUrl( podcastUrl ) ); } foreach( QString podcastUrl, podcastsUrlsToRemove ) { debug() << QString( "New channel to unsubscribe: %1 action." ).arg( podcastUrl ); m_removeList.append( QUrl( podcastUrl ) ); } //We should delete cached podcasts changes, since we already loaded them gpodderPodcastsConfig().deleteGroup(); synchronizeSubscriptions(); } void GpodderProvider::saveCachedPodcastsChanges() { DEBUG_BLOCK if( !m_addList.isEmpty() ) { QStringList podcastUrlsToAdd; foreach( QUrl podcastUrl, m_addList ) podcastUrlsToAdd.append( podcastUrl.toString() ); gpodderPodcastsConfig().writeEntry( "addList", podcastUrlsToAdd ); } if( !m_removeList.isEmpty() ) { QStringList podcastsUrlsToRemove; foreach( QUrl podcastUrl, m_removeList ) podcastsUrlsToRemove.append( podcastUrl.toString() ); gpodderPodcastsConfig().writeEntry( "removeList", podcastsUrlsToRemove ); } } QUrl GpodderProvider::resolvedPodcastUrl( const PodcastEpisodePtr episode ) { QUrl podcastUrl = episode->channel()->url(); if( m_redirectionUrlMap.contains( podcastUrl ) ) podcastUrl = m_redirectionUrlMap.value( podcastUrl ); return podcastUrl; } diff --git a/src/services/gpodder/GpodderProvider.h b/src/services/gpodder/GpodderProvider.h index 99bc9f379e..39ba891c56 100644 --- a/src/services/gpodder/GpodderProvider.h +++ b/src/services/gpodder/GpodderProvider.h @@ -1,183 +1,183 @@ /**************************************************************************************** * Copyright (c) 2011 Stefan Derkits * * Copyright (c) 2011 Christian Wagner * * Copyright (c) 2011 Felix Winter * * Copyright (c) 2011 Lucas Lira Gomes * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef GPODDERPODCASTPROVIDER_H #define GPODDERPODCASTPROVIDER_H #include "core/podcasts/PodcastProvider.h" #include "core/podcasts/PodcastReader.h" #include "GpodderPodcastMeta.h" -#include -#include +#include +#include #include "playlistmanager/file/KConfigSyncRelStore.h" #include "playlistmanager/PlaylistManager.h" -#include -#include +#include +#include #include #include #include using namespace mygpo; class QAction; namespace Podcasts { class GpodderProvider : public PodcastProvider { Q_OBJECT public: GpodderProvider( const QString& username, const QString& devicename, ApiRequest *apiRequest ); ~GpodderProvider(); //TrackProvider methods bool possiblyContainsTrack( const QUrl &url ) const; Meta::TrackPtr trackForUrl( const QUrl &url ); //PodcastProvider methods /** Special function to get an episode for a given guid. * * note: this functions is required because QUrl does not preserve every possible guids. * This means we can not use trackForUrl(). * Problematic guids contain non-latin characters, percent encoded parts, capitals, etc. */ virtual PodcastEpisodePtr episodeForGuid( const QString &guid ); virtual void addPodcast( const QUrl &url ); virtual Podcasts::PodcastChannelPtr addChannel( Podcasts::PodcastChannelPtr channel ); virtual Podcasts::PodcastEpisodePtr addEpisode( Podcasts::PodcastEpisodePtr episode ); virtual Podcasts::PodcastChannelList channels(); // PlaylistProvider methods virtual QString prettyName() const; virtual QIcon icon() const; virtual Playlists::PlaylistList playlists(); virtual void completePodcastDownloads(); /** Copy a playlist to the provider. */ virtual Playlists::PlaylistPtr addPlaylist( Playlists::PlaylistPtr playlist ); virtual QActionList playlistActions( const Playlists::PlaylistList &playlists ); private Q_SLOTS: void requestDeviceUpdates(); void deviceUpdatesFinished(); void continueDeviceUpdatesFinished(); void deviceUpdatesParseError(); void deviceUpdatesRequestError( QNetworkReply::NetworkError error ); void requestEpisodeActionsInCascade(); void episodeActionsInCascadeFinished(); void episodeActionsInCascadeParseError(); void episodeActionsInCascadeRequestError( QNetworkReply::NetworkError error ); void timerGenerateEpisodeAction(); void timerSynchronizeStatus(); void timerSynchronizeSubscriptions(); void timerPrepareToSyncPodcastStatus(); void slotRemoveChannels(); void synchronizeStatusParseError(); void synchronizeStatusRequestError( QNetworkReply::NetworkError error ); void slotSuccessfulStatusSynchronisation(); void slotSuccessfulSubscriptionSynchronisation(); void slotSyncPlaylistAdded( Playlists::PlaylistPtr playlist ); void slotSyncPlaylistRemoved( Playlists::PlaylistPtr playlist ); void slotPaused(); void slotTrackChanged( Meta::TrackPtr track ); void slotTrackPositionChanged( qint64 position, bool userSeek ); void requestUrlResolve( GpodderPodcastChannelPtr channel ); void urlResolvePermanentRedirection ( KIO::Job *job, const QUrl &fromUrl, const QUrl &toUrl ); void urlResolveFinished( KJob * ); void slotEpisodeDownloaded( Podcasts::PodcastEpisodePtr episode ); void slotEpisodeDeleted( Podcasts::PodcastEpisodePtr episode ); void slotEpisodeMarkedAsNew( Podcasts::PodcastEpisodePtr episode ); private: QActionList channelActions( PodcastChannelList episodes ); ApiRequest *m_apiRequest; const QString m_username; const QString m_deviceName; PodcastChannelList m_channels; KIO::TransferJob *m_resolveUrlJob; AddRemoveResultPtr m_addRemoveResult; DeviceUpdatesPtr m_deviceUpdatesResult; AddRemoveResultPtr m_episodeActionsResult; EpisodeActionListPtr m_episodeActionListResult; qulonglong m_timestampStatus; qulonglong m_timestampSubscription; qulonglong subscriptionTimestamp(); void setSubscriptionTimestamp( qulonglong newTimestamp ); void removeChannel( const QUrl &url ); void createPlayStatusBookmark(); void synchronizeStatus(); void synchronizeSubscriptions(); void updateLocalPodcasts( const QList< QPair > updatedUrls ); KConfigGroup gpodderActionsConfig() const; void loadCachedEpisodeActions(); void saveCachedEpisodeActions(); KConfigGroup gpodderPodcastsConfig() const; void loadCachedPodcastsChanges(); void saveCachedPodcastsChanges(); QAction *m_removeAction; //Lists of podcasts to be added or removed from gpodder.net QList m_addList; QList m_removeList; QUrl resolvedPodcastUrl( const PodcastEpisodePtr episode ); QMap m_redirectionUrlMap; QQueue m_channelsToRequestActions; QMap m_resolvedPodcasts; //Used as a temporary container for podcasts with already urls resolved //before adding them to m_channels QQueue m_resolvedChannelsToBeAdded; QMap m_episodeStatusMap; QMap m_uploadEpisodeStatusMap; QTimer *m_timerGeneratePlayAction; QTimer *m_timerSynchronizeStatus; QTimer *m_timerSynchronizeSubscriptions; Meta::TrackPtr m_trackToSyncStatus; }; } #endif diff --git a/src/services/gpodder/GpodderService.cpp b/src/services/gpodder/GpodderService.cpp index 7746a8f1e6..359fa445b5 100644 --- a/src/services/gpodder/GpodderService.cpp +++ b/src/services/gpodder/GpodderService.cpp @@ -1,285 +1,274 @@ /**************************************************************************************** * Copyright (c) 2010 - 2011 Stefan Derkits * * Copyright (c) 2010 - 2011 Christian Wagner * * Copyright (c) 2010 - 2011 Felix Winter * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "GpodderService" #include "GpodderService.h" #include "core/podcasts/PodcastProvider.h" #include "core/support/Debug.h" #include "GpodderPodcastTreeItem.h" #include "GpodderServiceConfig.h" #include "GpodderServiceModel.h" #include "GpodderServiceView.h" #include "GpodderSortFilterProxyModel.h" -#include -#include +#include +#include #include "playlistmanager/PlaylistManager.h" #include "widgets/SearchWidget.h" -#include -#include -#include -#include #include +#include +#include -GpodderServiceFactory::GpodderServiceFactory( QObject *parent, const QVariantList &args ) - : ServiceFactory( parent, args ) -{ - KPluginInfo pluginInfo( "amarok_service_gpodder.desktop" ); - pluginInfo.setConfig( config() ); - m_info = pluginInfo; -} +GpodderServiceFactory::GpodderServiceFactory() + : ServiceFactory() +{} + +GpodderServiceFactory::~GpodderServiceFactory() +{} void GpodderServiceFactory::init() { ServiceBase *service = createGpodderService(); if( service ) { m_initialized = true; emit newService( service ); } } QString GpodderServiceFactory::name() { return "gpodder.net"; } -KPluginInfo -GpodderServiceFactory::info() -{ - KPluginInfo pluginInfo( "amarok_service_gpodder.desktop", "services" ); - pluginInfo.setConfig( config() ); - return pluginInfo; -} - KConfigGroup GpodderServiceFactory::config() { return Amarok::config( GpodderServiceConfig::configSectionName() ); } void GpodderServiceFactory::slotCreateGpodderService() { //Until we can remove a service when networking gets disabled, only create it the first time. if( !m_initialized ) { ServiceBase *service = createGpodderService(); if( service ) { m_initialized = true; emit newService( service ); } } } void GpodderServiceFactory::slotRemoveGpodderService() { if( activeServices().isEmpty() ) return; m_initialized = false; emit removeService( activeServices().first() ); } ServiceBase * GpodderServiceFactory::createGpodderService() { ServiceBase *service = new GpodderService( this, QLatin1String( "gpodder" ) ); return service; } GpodderService::GpodderService( GpodderServiceFactory *parent, const QString &name ) : ServiceBase( name, parent, false ) , m_inited( false ) , m_apiRequest( 0 ) , m_podcastProvider( 0 ) , m_proxyModel( 0 ) , m_subscribeButton( 0 ) , m_selectionModel( 0 ) { DEBUG_BLOCK setShortDescription( i18n( "gpodder.net: Podcast Directory Service" ) ); setIcon( QIcon::fromTheme( "view-services-gpodder-amarok" ) ); setLongDescription( i18n( "gpodder.net is an online Podcast Directory & Synchonisation Service." ) ); - setImagePath( KStandardDirs::locate( "data", "amarok/images/mygpo.png" ) ); + setImagePath( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/mygpo.png" ) ); init(); } GpodderService::~GpodderService() { DEBUG_BLOCK if( m_podcastProvider ) { //Remove the provider The::playlistManager()->removeProvider( m_podcastProvider ); delete m_podcastProvider; } if ( m_apiRequest ) delete m_apiRequest; } //This Method should only contain the most necessary things for initilazing the Service void GpodderService::init() { DEBUG_BLOCK GpodderServiceConfig config; const QString &username = config.username(); const QString &password = config.password(); if ( m_apiRequest ) delete m_apiRequest; //We have to check this here too, since KWallet::openWallet() doesn't //guarantee that it will always return a wallet. //Notice that LastFm service does the same verification. if ( !config.isDataLoaded() ) { debug() << "Failed to read gpodder credentials."; m_apiRequest = new mygpo::ApiRequest( The::networkAccessManager() ); } else { if( config.enableProvider() ) { m_apiRequest = new mygpo::ApiRequest( username, password, The::networkAccessManager() ); if( m_podcastProvider ) delete m_podcastProvider; enableGpodderProvider( username ); } else m_apiRequest = new mygpo::ApiRequest( The::networkAccessManager() ); } setServiceReady( true ); m_inited = true; } //This Method should contain the rest of the Service Initialization (not soo necessary things, that //can be done after the Object was created) void GpodderService::polish() { DEBUG_BLOCK generateWidgetInfo(); if( m_polished ) return; //do not allow this content to get added to the playlist. At least not for now setPlayableTracks( false ); GpodderServiceView *view = new GpodderServiceView( this ); view->setHeaderHidden( true ); view->setFrameShape( QFrame::NoFrame ); // Was set true in OpmlDirectoryService, but I think we won't need this on true view->setDragEnabled( false ); view->setItemsExpandable( true ); view->setSortingEnabled( false ); view->setEditTriggers( QAbstractItemView::NoEditTriggers ); view->setDragDropMode( QAbstractItemView::NoDragDrop ); setView( view ); GpodderServiceModel *sourceModel = new GpodderServiceModel( m_apiRequest, this ); m_proxyModel = new GpodderSortFilterProxyModel( this ); m_proxyModel->setDynamicSortFilter( true ); m_proxyModel->setFilterCaseSensitivity( Qt::CaseInsensitive ); m_proxyModel->setSourceModel( sourceModel ); setModel( m_proxyModel ); m_selectionModel = view->selectionModel(); m_subscribeButton = new QPushButton(); m_subscribeButton->setParent( m_bottomPanel ); m_subscribeButton->setText( i18n( "Subscribe" ) ); m_subscribeButton->setObjectName( "subscribeButton" ); m_subscribeButton->setIcon( QIcon::fromTheme( "get-hot-new-stuff-amarok" ) ); m_subscribeButton->setEnabled( true ); - connect( m_subscribeButton, SIGNAL(clicked()), this, SLOT(subscribe()) ); + connect( m_subscribeButton, &QPushButton::clicked, this, &GpodderService::subscribe ); - connect( m_searchWidget, SIGNAL(filterChanged(QString)), - m_proxyModel, SLOT(setFilterWildcard(QString)) ); + connect( m_searchWidget, &SearchWidget::filterChanged, + m_proxyModel, &QSortFilterProxyModel::setFilterWildcard ); m_polished = true; } void GpodderService::itemSelected( CollectionTreeItem * selectedItem ) { Q_UNUSED( selectedItem ) DEBUG_BLOCK return; } void GpodderService::subscribe() { QModelIndex index = m_proxyModel->mapToSource( m_selectionModel->currentIndex() ); GpodderTreeItem *treeItem = static_cast( index.internalPointer() ); if( GpodderPodcastTreeItem *podcastTreeItem = qobject_cast( treeItem ) ) { Podcasts::PodcastProvider *podcastProvider = The::playlistManager()->defaultPodcasts(); QUrl kUrl( podcastTreeItem->podcast()->url() ); podcastProvider->addPodcast( kUrl ); } } void GpodderService::enableGpodderProvider( const QString &username ) { DEBUG_BLOCK QString deviceName = QLatin1String( "amarok-" ) % QHostInfo::localHostName(); debug() << QString( "Enabling GpodderProvider( Username: %1 - Device: %1 )" ) .arg( username ) .arg( deviceName ); m_podcastProvider = new Podcasts::GpodderProvider( username, deviceName, m_apiRequest ); //Add the gpodder's provider to the playlist manager The::playlistManager()->addProvider( m_podcastProvider, PlaylistManager::PodcastChannel ); } diff --git a/src/services/gpodder/GpodderService.h b/src/services/gpodder/GpodderService.h index 6c94fe6976..e08fc97d04 100644 --- a/src/services/gpodder/GpodderService.h +++ b/src/services/gpodder/GpodderService.h @@ -1,88 +1,87 @@ /**************************************************************************************** * Copyright (c) 2010 - 2011 Stefan Derkits * * Copyright (c) 2010 - 2011 Christian Wagner * * Copyright (c) 2010 - 2011 Felix Winter * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef GPODDERSERVICE_H #define GPODDERSERVICE_H #include "core/support/Amarok.h" #include "GpodderProvider.h" #include "services/ServiceBase.h" #include #include class GpodderService; namespace The { GpodderService *gpodderService(); } class GpodderServiceFactory : public ServiceFactory { Q_PLUGIN_METADATA(IID AmarokPluginFactory_iid FILE "amarok_service_gpodder.json") Q_INTERFACES(Plugins::PluginFactory) Q_OBJECT public: - GpodderServiceFactory( QObject *parent, const QVariantList &args ); - virtual ~GpodderServiceFactory() {} + GpodderServiceFactory(); + virtual ~GpodderServiceFactory(); virtual void init(); virtual QString name(); - virtual KPluginInfo info(); virtual KConfigGroup config(); private Q_SLOTS: void slotCreateGpodderService(); void slotRemoveGpodderService(); private: ServiceBase *createGpodderService(); }; class GpodderService : public ServiceBase { Q_OBJECT public: GpodderService( GpodderServiceFactory *parent, const QString &name ); virtual ~GpodderService(); private Q_SLOTS: void subscribe(); void itemSelected( CollectionTreeItem *selectedItem ); private: void init(); void polish(); void enableGpodderProvider( const QString &username ); virtual Collections::Collection *collection() { return 0; } bool m_inited; mygpo::ApiRequest *m_apiRequest; Podcasts::GpodderProvider *m_podcastProvider; QSortFilterProxyModel *m_proxyModel; QPushButton *m_subscribeButton; QItemSelectionModel *m_selectionModel; }; #endif // GPODDERSERVICE_H diff --git a/src/services/gpodder/GpodderServiceConfig.cpp b/src/services/gpodder/GpodderServiceConfig.cpp index f559060fc3..abcaab58bf 100644 --- a/src/services/gpodder/GpodderServiceConfig.cpp +++ b/src/services/gpodder/GpodderServiceConfig.cpp @@ -1,235 +1,239 @@ /**************************************************************************************** * Copyright (c) 2007 Shane King * * Copyright (c) 2009 Leo Franchi * * Copyright (c) 2010 Stefan Derkits * * Copyright (c) 2010 Christian Wagner * * Copyright (c) 2010 Felix Winter * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "GPodderConfig" #include "GpodderServiceConfig.h" #include "App.h" +#include "core/support/Amarok.h" #include "core/support/Debug.h" -#include -#include - #include +#include + +#include +#include +#include + GpodderServiceConfig::GpodderServiceConfig() : m_username( "" ) , m_password( "" ) , m_enableProvider( false ) , m_ignoreWallet( false ) , m_isDataLoaded( false ) , m_askDiag( 0 ) , m_wallet( 0 ) { DEBUG_BLOCK load(); } GpodderServiceConfig::~GpodderServiceConfig() { DEBUG_BLOCK if( m_askDiag ) m_askDiag->deleteLater(); if( m_wallet ) m_wallet->deleteLater(); } void GpodderServiceConfig::load() { DEBUG_BLOCK debug() << "Load config"; - KConfigGroup config = KGlobal::config()->group( configSectionName() ); + KConfigGroup config = Amarok::config( configSectionName() ); m_enableProvider = config.readEntry( "enableProvider", false ); m_ignoreWallet = config.readEntry( "ignoreWallet", false ); //We only want to load the wallet if the user has enabled features that require a user/pass tryToOpenWallet(); if( m_wallet ) { if( !m_wallet->hasFolder( "Amarok" ) ) m_wallet->createFolder( "Amarok" ); // do a one-time transfer // can remove at some point in the future, post-2.2 m_wallet->setFolder( "Amarok" ); if( m_wallet->readPassword( "gpodder_password", m_password ) != 0 ) debug() << "Failed to read gpodder.net password from kwallet!"; else { QByteArray rawUsername; if( m_wallet->readEntry( "gpodder_username", rawUsername ) != 0 ) debug() << "Failed to read gpodder.net username from kwallet.. :("; else m_username = QString::fromUtf8( rawUsername ); } } else if( m_ignoreWallet ) { m_username = config.readEntry( "username", QString() ); m_password = config.readEntry( "password", QString() ); } else debug() << "Failed to load the data."; m_isDataLoaded = !( m_username.isEmpty() || m_password.isEmpty() ); } void GpodderServiceConfig::save() { DEBUG_BLOCK debug() << "Save config"; - KConfigGroup config = KGlobal::config()->group( configSectionName() ); + KConfigGroup config = Amarok::config( configSectionName() ); config.writeEntry( "enableProvider", m_enableProvider ); config.writeEntry( "ignoreWallet", m_ignoreWallet ); //Whenever this function is called, we'll assume the user wants to //change something, so blow away the subscription timestamp key config.writeEntry( "subscriptionTimestamp", 0 ); //Maybe the wallet had already closed or m_enableProvider and m_ignoreWallet //could had changed also. So we try to reopen the wallet if it's not open. tryToOpenWallet(); if( m_wallet ) { m_wallet->setFolder( "Amarok" ); if( m_wallet->writeEntry( "gpodder_username", m_username.toUtf8() ) != 0 ) debug() << "Failed to save gpodder.net username to kwallet!"; if( m_wallet->writePassword( "gpodder_password", m_password ) != 0 ) debug() << "Failed to save gpodder.net pw to kwallet!"; } else { if( m_enableProvider ) { debug() << "Couldnt access the wallet to save the gpodder.net credentials"; askAboutMissingKWallet(); } else debug() << "There isn't valid credentials to be saved"; } config.sync(); } void GpodderServiceConfig::askAboutMissingKWallet() { if ( !m_askDiag ) { - m_askDiag = new KDialog( 0 ); + m_askDiag = new QMessageBox( Q_NULLPTR ); - m_askDiag->setCaption( i18n( "gpodder.net credentials" ) ); - m_askDiag->setMainWidget( new QLabel( i18n( "No running KWallet found. Would you like Amarok to save your gpodder.net credentials in plaintext?" ), m_askDiag ) ); - m_askDiag->setButtons( KDialog::Yes | KDialog::No ); + m_askDiag->setWindowTitle( i18n( "gpodder.net credentials" ) ); + m_askDiag->setText( i18n( "No running KWallet found. Would you like Amarok to save your gpodder.net credentials in plaintext?" ) ); + m_askDiag->setStandardButtons( QMessageBox::Yes | QMessageBox::No ); m_askDiag->setModal( true ); - connect( m_askDiag, SIGNAL(yesClicked()), this, SLOT(textDialogYes()) ); - connect( m_askDiag, SIGNAL(noClicked()), this, SLOT(textDialogNo()) ); + connect( m_askDiag, &QMessageBox::accepted, this, &GpodderServiceConfig::textDialogYes ); + connect( m_askDiag, &QMessageBox::rejected, this, &GpodderServiceConfig::textDialogNo ); } m_askDiag->exec(); } void GpodderServiceConfig::tryToOpenWallet() { DEBUG_BLOCK //We only want to load the wallet if the user has enabled features //that require a user/pass if( ( m_enableProvider ) && ( !m_ignoreWallet ) ) { debug() << "Opening wallet"; //Open wallet unless explicitly told not to m_wallet = KWallet::Wallet::openWallet( KWallet::Wallet::NetworkWallet(), - 0, KWallet::Wallet::Synchronous ); + 0 ); } else { debug() << "The wallet was ignored or is not needed."; m_wallet = 0; } } void GpodderServiceConfig::reset() { debug() << "Reset config"; m_username = ""; m_password = ""; m_enableProvider = false; m_ignoreWallet = false; } void GpodderServiceConfig::textDialogYes() //SLOT { DEBUG_BLOCK if ( !m_ignoreWallet ) { - KConfigGroup config = KGlobal::config()->group( configSectionName() ); + KConfigGroup config = Amarok::config( configSectionName() ); m_ignoreWallet = true; config.writeEntry( "ignoreWallet", m_ignoreWallet ); config.writeEntry( "username", m_username ); config.writeEntry( "password", m_password ); config.sync(); } } void GpodderServiceConfig::textDialogNo() //SLOT { DEBUG_BLOCK if ( m_ignoreWallet ) { - KConfigGroup config = KGlobal::config()->group( configSectionName() ); + KConfigGroup config = Amarok::config( configSectionName() ); m_ignoreWallet = false; config.writeEntry( "ignoreWallet", m_ignoreWallet ); config.writeEntry( "username", QString() ); config.writeEntry( "password", QString() ); config.sync(); } } diff --git a/src/services/gpodder/GpodderServiceConfig.h b/src/services/gpodder/GpodderServiceConfig.h index 2cc46be6c7..7ac06dfff0 100644 --- a/src/services/gpodder/GpodderServiceConfig.h +++ b/src/services/gpodder/GpodderServiceConfig.h @@ -1,77 +1,83 @@ /**************************************************************************************** * Copyright (c) 2007 Shane King * * Copyright (c) 2009 Leo Franchi * * Copyright (c) 2010 Stefan Derkits * * Copyright (c) 2010 Christian Wagner * * Copyright (c) 2010 Felix Winter * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef GPODDERSERVICECONFIG_H #define GPODDERSERVICECONFIG_H #include #include namespace KWallet { class Wallet; } -class KDialog; +class QMessageBox; -class GpodderServiceConfig : public QObject +#ifdef MAKE_GPODDER_SERVICE_CONFIG_LIB +#define GPODDER_CONFIG_EXPORT Q_DECL_EXPORT +#else +#define GPODDER_CONFIG_EXPORT Q_DECL_IMPORT +#endif + +class GPODDER_CONFIG_EXPORT GpodderServiceConfig : public QObject { Q_OBJECT public: static const char *configSectionName() { return "Service_gpodder"; } GpodderServiceConfig(); ~GpodderServiceConfig(); void load(); void save(); void reset(); const QString &username() { return m_username; } void setUsername( const QString &username ) { m_username = username; } const QString &password() { return m_password; } void setPassword( const QString &password ) { m_password = password; } bool enableProvider() { return m_enableProvider; } void setEnableProvider( bool enableProvider ) { m_enableProvider = enableProvider; } bool ignoreWallet() { return m_ignoreWallet; } void setIgnoreWallet( bool ignoreWallet ) { m_ignoreWallet = ignoreWallet; } bool isDataLoaded() { return m_isDataLoaded; } private Q_SLOTS: void textDialogYes(); void textDialogNo(); private: void askAboutMissingKWallet(); void tryToOpenWallet(); QString m_username; QString m_password; bool m_enableProvider; //Enables PodcastProvider if correct LoginData given bool m_ignoreWallet; bool m_isDataLoaded; - KDialog *m_askDiag; + QMessageBox *m_askDiag; KWallet::Wallet *m_wallet; }; #endif // GPODDERSERVICECONFIG_H diff --git a/src/services/gpodder/GpodderServiceModel.cpp b/src/services/gpodder/GpodderServiceModel.cpp index f09f001ccf..b7db19cf92 100644 --- a/src/services/gpodder/GpodderServiceModel.cpp +++ b/src/services/gpodder/GpodderServiceModel.cpp @@ -1,397 +1,396 @@ /**************************************************************************************** * Copyright (c) 2011 Stefan Derkits * * Copyright (c) 2011 Christian Wagner * * Copyright (c) 2011 Felix Winter * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "GpodderServiceModel.h" #include "core/support/Debug.h" #include "GpodderPodcastRequestHandler.h" #include "GpodderPodcastTreeItem.h" #include "GpodderServiceSettings.h" #include "GpodderTagTreeItem.h" -#include - #include #include +#include #include static const int s_numberItemsToLoad = 100; using namespace mygpo; GpodderServiceModel::GpodderServiceModel( ApiRequest *request, QObject *parent ) : QAbstractItemModel( parent ) , m_rootItem( 0 ) , m_topTagsItem( 0 ) , m_topPodcastsItem( 0 ) , m_suggestedPodcastsItem( 0 ) , m_topTags( 0 ) , m_apiRequest( request ) { GpodderServiceConfig config; m_rootItem = new GpodderTreeItem( ); m_topTagsItem = new GpodderTreeItem( m_rootItem, "Top Tags" ); m_rootItem->appendChild( m_topTagsItem ); m_topPodcastsItem = new GpodderTreeItem( m_rootItem, "Top Podcasts" ); m_rootItem->appendChild( m_topPodcastsItem ); if ( config.isDataLoaded() && config.enableProvider() ) { m_suggestedPodcastsItem = new GpodderTreeItem( m_rootItem, "Suggested Podcasts" ); m_rootItem->appendChild( m_suggestedPodcastsItem ); } } GpodderServiceModel::~GpodderServiceModel() { delete m_rootItem; } QModelIndex GpodderServiceModel::index( int row, int column, const QModelIndex &parent ) const { if( !hasIndex( row, column, parent ) ) return QModelIndex(); GpodderTreeItem *parentItem; if( !parent.isValid() ) parentItem = m_rootItem; else parentItem = static_cast( parent.internalPointer() ); if( parentItem == 0 ) return QModelIndex(); GpodderTreeItem *childItem = parentItem->child( row ); if( childItem ) return createIndex( row, column, childItem ); else return QModelIndex(); } QModelIndex GpodderServiceModel::parent( const QModelIndex &index ) const { if( !index.isValid() ) return QModelIndex(); GpodderTreeItem *childItem = static_cast( index.internalPointer() ); if( childItem == 0 || childItem->isRoot() ) return QModelIndex(); GpodderTreeItem *parentItem = childItem->parent(); if( parentItem == 0 ) return QModelIndex(); int childIndex; if( parentItem->isRoot() ) return QModelIndex(); else childIndex = parentItem->parent()->children().indexOf( parentItem ); return createIndex( childIndex, 0, parentItem ); } int GpodderServiceModel::rowCount( const QModelIndex &parent ) const { GpodderTreeItem *parentItem; if( !parent.isValid() ) { return m_rootItem->childCount(); } parentItem = static_cast( parent.internalPointer() ); if( parentItem == 0 ) return 0; return parentItem->childCount(); } int GpodderServiceModel::columnCount( const QModelIndex &parent ) const { Q_UNUSED( parent ) return 1; } QVariant GpodderServiceModel::data( const QModelIndex &index, int role ) const { if( !index.isValid() ) return QVariant(); if( role != Qt::DisplayRole ) return QVariant(); GpodderTreeItem *item = static_cast( index.internalPointer() ); if( item == 0 ) { return QVariant(); } return item->displayData(); } void GpodderServiceModel::insertTagList() { if( m_rootItem != 0 ) { beginInsertRows( createIndex( 0,0, m_topTagsItem), 0, m_topTags->list().count() - 1 ); m_topTagsItem->appendTags( m_topTags ); endInsertRows(); } } void GpodderServiceModel::topTagsRequestError( QNetworkReply::NetworkError error ) { DEBUG_BLOCK debug() << "Error in TopTags request: " << error; - QTimer::singleShot( 20000, this, SLOT(requestTopTags()) ); + QTimer::singleShot( 20000, this, &GpodderServiceModel::requestTopTags ); } void GpodderServiceModel::topTagsParseError() { DEBUG_BLOCK debug() << "Error while parsing TopTags"; - QTimer::singleShot( 20000, this, SLOT(requestTopTags()) ); + QTimer::singleShot( 20000, this, &GpodderServiceModel::requestTopTags ); } void GpodderServiceModel::topPodcastsRequestError( QNetworkReply::NetworkError error ) { DEBUG_BLOCK debug() << "Error in TopPodcasts request: " << error; - QTimer::singleShot( 20000, this, SLOT(requestTopPodcasts()) ); + QTimer::singleShot( 20000, this, &GpodderServiceModel::requestTopPodcasts ); } void GpodderServiceModel::topPodcastsParseError() { DEBUG_BLOCK debug() << "Error while parsing TopPodcasts"; - QTimer::singleShot( 20000, this, SLOT(requestTopPodcasts()) ); + QTimer::singleShot( 20000, this, &GpodderServiceModel::requestTopPodcasts ); } void GpodderServiceModel::suggestedPodcastsRequestError( QNetworkReply::NetworkError error ) { DEBUG_BLOCK debug() << "Error in suggestedPodcasts request: " << error; - QTimer::singleShot( 20000, this, SLOT(requestSuggestedPodcasts()) ); + QTimer::singleShot( 20000, this, &GpodderServiceModel::requestSuggestedPodcasts ); } void GpodderServiceModel::suggestedPodcastsParseError() { DEBUG_BLOCK debug() << "Error while parsing suggestedPodcasts"; - QTimer::singleShot( 20000, this, SLOT(requestSuggestedPodcasts()) ); + QTimer::singleShot( 20000, this, &GpodderServiceModel::requestSuggestedPodcasts ); } void GpodderServiceModel::insertPodcastList( mygpo::PodcastListPtr podcasts, const QModelIndex &parentItem ) { DEBUG_BLOCK emit layoutAboutToBeChanged(); beginInsertRows( parentItem, 0, podcasts->list().count() - 1 ); GpodderTreeItem *item = static_cast( parentItem.internalPointer() ); if( item != 0 ) { debug() << "Appending Podcasts..."; item->appendPodcasts( podcasts ); } endInsertRows(); emit layoutChanged(); } bool GpodderServiceModel::hasChildren( const QModelIndex &parent ) const { if( !parent.isValid() ) return true; GpodderTreeItem *treeItem = static_cast( parent.internalPointer() ); if( treeItem == 0 ) return false; if( treeItem->childCount() > 0 ) return true; if( !qobject_cast( treeItem ) ) { return true; } else { return false; } } bool GpodderServiceModel::canFetchMore( const QModelIndex &parent ) const { // root item if( !parent.isValid() ) { return !m_rootItem->hasChildren(); } // already fetched or just started? GpodderTreeItem *treeItem = static_cast( parent.internalPointer() ); if( treeItem == 0 || treeItem->hasChildren() /* || m_currentFetchingMap.values().contains( parent ) */ ) { return false; } // TagTreeItem if( qobject_cast( treeItem ) ) { - if( Solid::Networking::status() == Solid::Networking::Unconnected ) + if( !QNetworkConfigurationManager().isOnline() ) return false; return true; } return false; } void GpodderServiceModel::fetchMore( const QModelIndex &parent ) { // root item if( !parent.isValid() ) { requestTopTags(); requestTopPodcasts(); if ( m_suggestedPodcastsItem != 0 ) requestSuggestedPodcasts(); } GpodderTreeItem *treeItem = static_cast( parent.internalPointer() ); // TagTreeItem if( GpodderTagTreeItem *tagTreeItem = qobject_cast( treeItem ) ) { m_rootItem->setHasChildren( true ); tagTreeItem->setHasChildren( true ); mygpo::PodcastListPtr podcasts = m_apiRequest->podcastsOfTag( s_numberItemsToLoad, tagTreeItem->tag()->tag() ); GpodderPodcastRequestHandler *podcastRequestHandler = new GpodderPodcastRequestHandler( podcasts, parent, this ); connect( podcasts.data(), SIGNAL(finished()), podcastRequestHandler, SLOT(finished()) ); connect( podcasts.data(), SIGNAL(requestError(QNetworkReply::NetworkError)), podcastRequestHandler, SLOT(requestError(QNetworkReply::NetworkError)) ); connect( podcasts.data(), SIGNAL(parseError()), podcastRequestHandler, SLOT(parseError()) ); } } void GpodderServiceModel::requestTopTags() { - if( Solid::Networking::status() == Solid::Networking::Unconnected ) + if( !QNetworkConfigurationManager().isOnline() ) { QTimer::singleShot( 10000, this, SLOT(requestTopTags()) ); return; } m_rootItem->setHasChildren( true ); m_topTags = m_apiRequest->topTags( s_numberItemsToLoad ); connect( m_topTags.data(), SIGNAL(finished()), this, SLOT(insertTagList()) ); connect( m_topTags.data(), SIGNAL(requestError(QNetworkReply::NetworkError)), SLOT(topTagsRequestError(QNetworkReply::NetworkError)) ); connect( m_topTags.data(), SIGNAL(parseError()), SLOT(topTagsParseError()) ); } void GpodderServiceModel::requestTopPodcasts() { - if( Solid::Networking::status() == Solid::Networking::Unconnected ) + if( !QNetworkConfigurationManager().isOnline() ) { QTimer::singleShot( 10000, this, SLOT(requestTopPodcasts()) ); return; } m_rootItem->setHasChildren( true ); mygpo::PodcastListPtr topPodcasts = m_apiRequest->toplist( s_numberItemsToLoad ); GpodderPodcastRequestHandler *podcastRequestHandler = new GpodderPodcastRequestHandler( topPodcasts, createIndex( 0,0, m_topPodcastsItem ), this ); connect( topPodcasts.data(), SIGNAL(finished()), podcastRequestHandler, SLOT(finished()) ); connect( topPodcasts.data(), SIGNAL(requestError(QNetworkReply::NetworkError)), SLOT(topPodcastsRequestError(QNetworkReply::NetworkError)) ); connect( topPodcasts.data(), SIGNAL(parseError()), SLOT(topPodcastsParseError()) ); } void GpodderServiceModel::requestSuggestedPodcasts() { - if( Solid::Networking::status() == Solid::Networking::Unconnected ) + if( !QNetworkConfigurationManager().isOnline() ) { QTimer::singleShot( 10000, this, SLOT(requestSuggestedPodcasts()) ); return; } m_rootItem->setHasChildren( true ); mygpo::PodcastListPtr topSuggestions = m_apiRequest->suggestions( s_numberItemsToLoad ); GpodderPodcastRequestHandler *podcastRequestHandler = new GpodderPodcastRequestHandler( topSuggestions, createIndex( 0,0, m_suggestedPodcastsItem ), this ); connect( topSuggestions.data(), SIGNAL(finished()), podcastRequestHandler, SLOT(finished()) ); connect( topSuggestions.data(), SIGNAL(requestError(QNetworkReply::NetworkError)), SLOT(suggestedPodcastsRequestError(QNetworkReply::NetworkError)) ); connect( topSuggestions.data(), SIGNAL(parseError()), SLOT(suggestedPodcastsParseError()) ); } diff --git a/src/services/gpodder/GpodderServiceModel.h b/src/services/gpodder/GpodderServiceModel.h index 6de40084cb..3916523af8 100644 --- a/src/services/gpodder/GpodderServiceModel.h +++ b/src/services/gpodder/GpodderServiceModel.h @@ -1,78 +1,78 @@ /**************************************************************************************** * Copyright (c) 2011 Stefan Derkits * * Copyright (c) 2011 Christian Wagner * * Copyright (c) 2011 Felix Winter * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef GPODDERSERVICEMODEL_H_ #define GPODDERSERVICEMODEL_H_ #include "GpodderTreeItem.h" -#include -#include +#include +#include #include "NetworkAccessManagerProxy.h" #include #include class GpodderTreeItem; class GpodderServiceModel: public QAbstractItemModel { Q_OBJECT public: explicit GpodderServiceModel( mygpo::ApiRequest *request, QObject *parent = 0 ); virtual ~GpodderServiceModel(); // QAbstractItemModel methods virtual QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const; virtual QModelIndex parent( const QModelIndex &index ) const; virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const; virtual int columnCount( const QModelIndex &parent = QModelIndex() ) const; virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const; virtual bool hasChildren( const QModelIndex &parent = QModelIndex() ) const; void insertPodcastList( mygpo::PodcastListPtr podcasts, const QModelIndex & parentItem ); private Q_SLOTS: void topTagsRequestError( QNetworkReply::NetworkError error ); void topTagsParseError(); void insertTagList(); void topPodcastsRequestError( QNetworkReply::NetworkError error ); void topPodcastsParseError(); void suggestedPodcastsRequestError( QNetworkReply::NetworkError error ); void suggestedPodcastsParseError(); void requestTopTags(); void requestTopPodcasts(); void requestSuggestedPodcasts(); protected: virtual bool canFetchMore( const QModelIndex &parent ) const; virtual void fetchMore( const QModelIndex &parent ); private: GpodderTreeItem *m_rootItem; GpodderTreeItem *m_topTagsItem; GpodderTreeItem *m_topPodcastsItem; GpodderTreeItem *m_suggestedPodcastsItem; //The gpodder.net topTags mygpo::TagListPtr m_topTags; mygpo::ApiRequest *m_apiRequest; }; #endif /* GPODDERSERVICEMODEL_H_ */ diff --git a/src/services/gpodder/GpodderServiceSettings.cpp b/src/services/gpodder/GpodderServiceSettings.cpp index 34280c786d..7ff5a4c60d 100644 --- a/src/services/gpodder/GpodderServiceSettings.cpp +++ b/src/services/gpodder/GpodderServiceSettings.cpp @@ -1,257 +1,256 @@ /**************************************************************************************** * Copyright (c) 2007 Shane King * * Copyright (c) 2010 Stefan Derkits * * Copyright (c) 2010 Christian Wagner * * Copyright (c) 2010 Felix Winter * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "GpodderServiceSettings" #include "GpodderServiceSettings.h" #include "core/podcasts/PodcastProvider.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include "NetworkAccessManagerProxy.h" #include "playlistmanager/PlaylistManager.h" #include "ui_GpodderConfigWidget.h" -#include #include #include #include #include #include #include -#include -K_PLUGIN_FACTORY( GpodderServiceSettingsFactory, registerPlugin(); ) -K_EXPORT_PLUGIN( GpodderServiceSettingsFactory( "kcm_amarok_gpodder" ) ) + +K_PLUGIN_FACTORY_WITH_JSON( GpodderServiceSettingsFactory, "amarok_service_gpodder_config.json", registerPlugin(); ) GpodderServiceSettings::GpodderServiceSettings( QWidget *parent, const QVariantList &args ) - : KCModule( GpodderServiceSettingsFactory::componentData(), parent, args ) + : KCModule( parent, args ) , m_enableProvider( false ) , m_createDevice( 0 ) { debug() << "Creating gpodder.net config object"; m_configDialog = new Ui::GpodderConfigWidget; m_configDialog->setupUi( this ); - connect( m_configDialog->kcfg_GpodderUsername, - SIGNAL(textChanged(QString)), this, - SLOT(settingsChanged()) ); - connect( m_configDialog->kcfg_GpodderPassword, - SIGNAL(textChanged(QString)), this, - SLOT(settingsChanged()) ); - connect( m_configDialog->testLogin, SIGNAL(clicked()), this, - SLOT(testLogin()) ); + connect( m_configDialog->kcfg_GpodderUsername, &QLineEdit::textChanged, + this, &GpodderServiceSettings::settingsChanged ); + connect( m_configDialog->kcfg_GpodderPassword, &QLineEdit::textChanged, + this, &GpodderServiceSettings::settingsChanged ); + connect( m_configDialog->testLogin, &QPushButton::clicked, + this, &GpodderServiceSettings::testLogin ); load(); } GpodderServiceSettings::~GpodderServiceSettings() { if( m_createDevice ) m_createDevice->deleteLater(); if( m_devices ) m_devices->deleteLater(); delete m_configDialog; } void GpodderServiceSettings::save() { m_config.setUsername( m_configDialog->kcfg_GpodderUsername->text() ); m_config.setPassword( m_configDialog->kcfg_GpodderPassword->text() ); m_config.setEnableProvider( m_enableProvider ); m_config.setIgnoreWallet( false ); m_config.save(); KCModule::save(); } void GpodderServiceSettings::testLogin() { DEBUG_BLOCK if ( ( !m_configDialog->kcfg_GpodderUsername->text().isEmpty() ) && ( !m_configDialog->kcfg_GpodderPassword->text().isEmpty() ) ) { m_configDialog->testLogin->setEnabled( false ); m_configDialog->testLogin->setText( i18n( "Testing..." ) ); mygpo::ApiRequest api( m_configDialog->kcfg_GpodderUsername->text(), m_configDialog->kcfg_GpodderPassword->text(), The::networkAccessManager() ); m_devices = api.listDevices( m_configDialog->kcfg_GpodderUsername->text() ); - connect( m_devices.data(), SIGNAL(finished()), SLOT(finished()) ); - connect( m_devices.data(), - SIGNAL(requestError(QNetworkReply::NetworkError)), - SLOT(onError(QNetworkReply::NetworkError)) ); - connect( m_devices.data(), SIGNAL(parseError()), SLOT(onParseError()) ); + connect( m_devices.data(), &mygpo::DeviceList::finished, + this, &GpodderServiceSettings::finished ); + connect( m_devices.data(), &mygpo::DeviceList::requestError, + this, &GpodderServiceSettings::onError ); + connect( m_devices.data(), &mygpo::DeviceList::parseError, + this, &GpodderServiceSettings::onParseError ); } else { KMessageBox::error( this, i18n( "Either the username or the password is empty, please correct and try again." ), i18n( "Failed" ) ); } } void GpodderServiceSettings::finished() { DEBUG_BLOCK debug() << "Authentication worked, got List of Devices, searching for Amarok Device"; m_configDialog->testLogin->setText( i18nc( "The operation completed as expected", "Success" ) ); m_configDialog->testLogin->setEnabled( false ); bool deviceExists = false; QList ptrList = m_devices->devicesList(); mygpo::DevicePtr devPtr; QString hostname = QHostInfo::localHostName(); QString deviceID = QLatin1String( "amarok-" ) % hostname; foreach( devPtr, ptrList ) { if( devPtr->id().compare( deviceID ) == 0 ) { deviceExists = true; break; } } if( !deviceExists ) { debug() << "Create new device " % deviceID; mygpo::ApiRequest api( m_configDialog->kcfg_GpodderUsername->text(), m_configDialog->kcfg_GpodderPassword->text(), The::networkAccessManager() ); m_createDevice = api.renameDevice( m_configDialog->kcfg_GpodderUsername->text(), deviceID, QLatin1String( "Amarok on " ) % hostname, mygpo::Device::OTHER ); - connect( m_createDevice, SIGNAL(finished()), - SLOT(deviceCreationFinished()) ); - connect( m_createDevice, SIGNAL(error(QNetworkReply::NetworkError)), - SLOT(deviceCreationError(QNetworkReply::NetworkError)) ); + connect( m_createDevice, &QNetworkReply::finished, + this, &GpodderServiceSettings::deviceCreationFinished ); + connect( m_createDevice, QOverload::of(&QNetworkReply::error), + this, &GpodderServiceSettings::deviceCreationError ); } else { debug() << "Amarok device was found and everything looks perfect"; } } void GpodderServiceSettings::onError( QNetworkReply::NetworkError code ) { DEBUG_BLOCK debug() << code; if( code == QNetworkReply::NoError ) debug() << "No Error was found, but onError was called - should not happen"; else if( code == QNetworkReply::AuthenticationRequiredError ) { debug() << "Authentication failed"; KMessageBox::error( this, i18n( "Either the username or the password is incorrect, please correct and try again" ), i18n( "Failed" ) ); m_configDialog->testLogin->setText( i18n( "&Test Login" ) ); m_configDialog->testLogin->setEnabled( true ); } else { KMessageBox::error( this, i18n( "Unable to connect to gpodder.net service or other error occurred." ), i18n( "Failed" ) ); m_configDialog->testLogin->setText( i18n( "&Test Login" ) ); m_configDialog->testLogin->setEnabled( true ); } } void GpodderServiceSettings::onParseError() { debug() << "Couldn't parse DeviceList, should not happen if gpodder.net is working correctly"; m_configDialog->testLogin->setText( i18n( "&Test Login" ) ); m_configDialog->testLogin->setEnabled( true ); KMessageBox::error( this, i18n( "Error parsing the Reply, check if gpodder.net is working correctly and report a bug" ), i18n( "Failed" ) ); } void GpodderServiceSettings::deviceCreationFinished() { debug() << "Creation of Amarok Device finished"; } void GpodderServiceSettings::deviceCreationError( QNetworkReply::NetworkError code ) { debug() << "Error creating Amarok Device"; debug() << code; m_configDialog->testLogin->setText( i18n( "&Test Login" ) ); m_configDialog->testLogin->setEnabled( true ); } void GpodderServiceSettings::load() { m_config.load(); m_configDialog->kcfg_GpodderUsername->setText( m_config.username() ); m_configDialog->kcfg_GpodderPassword->setText( m_config.password() ); m_enableProvider = m_config.enableProvider(); KCModule::load(); } void GpodderServiceSettings::defaults() { m_config.reset(); m_configDialog->kcfg_GpodderUsername->setText( "" ); m_configDialog->kcfg_GpodderPassword->setText( "" ); m_enableProvider = false; } void GpodderServiceSettings::settingsChanged() { m_configDialog->testLogin->setText( i18n( "&Test Login" ) ); m_configDialog->testLogin->setEnabled( true ); m_enableProvider = true; emit changed( true ); } + +#include "GpodderServiceSettings.moc" diff --git a/src/services/gpodder/GpodderServiceSettings.h b/src/services/gpodder/GpodderServiceSettings.h index 16bdb84fcb..005de04cc1 100644 --- a/src/services/gpodder/GpodderServiceSettings.h +++ b/src/services/gpodder/GpodderServiceSettings.h @@ -1,70 +1,66 @@ /**************************************************************************************** * Copyright (c) 2007 Shane King * * Copyright (c) 2010 Stefan Derkits * * Copyright (c) 2010 Christian Wagner * * Copyright (c) 2010 Felix Winter * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef GPODDERSERVICESETTINGS_H #define GPODDERSERVICESETTINGS_H -#include "core/podcasts/PodcastMeta.h" #include "GpodderServiceConfig.h" -#include +#include -#include +#include #include namespace Ui { class GpodderConfigWidget; } -class QListWidgetItem; - class GpodderServiceSettings : public KCModule { Q_OBJECT public: - explicit GpodderServiceSettings( QWidget *parent = 0, - const QVariantList &args = QVariantList() ); + GpodderServiceSettings( QWidget *parent, const QVariantList &args ); virtual ~GpodderServiceSettings(); - virtual void save(); - virtual void load(); - virtual void defaults(); + void save() Q_DECL_OVERRIDE; + void load() Q_DECL_OVERRIDE; + void defaults() Q_DECL_OVERRIDE; private Q_SLOTS: void testLogin(); void finished(); void onError( QNetworkReply::NetworkError code ); void onParseError( ); void deviceCreationFinished(); void deviceCreationError( QNetworkReply::NetworkError code ); void settingsChanged(); private: Ui::GpodderConfigWidget *m_configDialog; GpodderServiceConfig m_config; mygpo::DeviceListPtr m_devices; mygpo::AddRemoveResultPtr m_result; bool m_enableProvider; QNetworkReply *m_createDevice; }; #endif // GPODDERSERVICESETTINGS_H diff --git a/src/services/gpodder/GpodderTagTreeItem.h b/src/services/gpodder/GpodderTagTreeItem.h index 59226d87d8..b6d91d58b0 100644 --- a/src/services/gpodder/GpodderTagTreeItem.h +++ b/src/services/gpodder/GpodderTagTreeItem.h @@ -1,40 +1,40 @@ /**************************************************************************************** * Copyright (c) 2011 Stefan Derkits * * Copyright (c) 2011 Christian Wagner * * Copyright (c) 2011 Felix Winter * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef GPODDERTAGTREEITEM_H_ #define GPODDERTAGTREEITEM_H_ #include "GpodderTreeItem.h" -#include +#include #include class GpodderTagTreeItem: public GpodderTreeItem { Q_OBJECT public: GpodderTagTreeItem( mygpo::TagPtr tag, GpodderTreeItem *parent = 0 ); virtual ~GpodderTagTreeItem(); virtual QVariant displayData() const; mygpo::TagPtr tag() const; private: mygpo::TagPtr m_tag; }; #endif /* GPODDERTAGTREEITEM_H_ */ diff --git a/src/services/gpodder/GpodderTreeItem.cpp b/src/services/gpodder/GpodderTreeItem.cpp index d6f212b7de..bd4f65c229 100644 --- a/src/services/gpodder/GpodderTreeItem.cpp +++ b/src/services/gpodder/GpodderTreeItem.cpp @@ -1,103 +1,103 @@ /**************************************************************************************** * Copyright (c) 2011 Stefan Derkits * * Copyright (c) 2011 Christian Wagner * * Copyright (c) 2011 Felix Winter * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "GpodderTreeItem.h" #include "GpodderPodcastTreeItem.h" #include "GpodderServiceModel.h" #include "GpodderTagTreeItem.h" GpodderTreeItem::GpodderTreeItem( GpodderTreeItem *parent, QString name ) : QObject( parent ) , m_parentItem( parent ) , m_name( name ) , m_hasChildren( false ) { } GpodderTreeItem::~GpodderTreeItem() { qDeleteAll( m_childItems ); } void GpodderTreeItem::appendChild( GpodderTreeItem *item ) { m_childItems.append( item ); } GpodderTreeItem * GpodderTreeItem::child( int row ) { return m_childItems.value( row ); } bool GpodderTreeItem::hasChildren() const { return m_hasChildren; } void GpodderTreeItem::setHasChildren( bool hasChildren ) { m_hasChildren = hasChildren; } int GpodderTreeItem::childCount() const { return m_childItems.count(); } GpodderTreeItem * GpodderTreeItem::parent() const { return m_parentItem; } QVariant GpodderTreeItem::displayData() const { return m_name; } bool GpodderTreeItem::isRoot() const { return ( m_parentItem == 0 ); } void GpodderTreeItem::appendTags( mygpo::TagListPtr tags ) { - foreach( mygpo::TagPtr tag, tags->list() ) + for( const auto &tag : tags->list() ) { GpodderTagTreeItem *treeItem = new GpodderTagTreeItem( tag, this ); appendChild( treeItem ); } } void GpodderTreeItem::appendPodcasts( mygpo::PodcastListPtr podcasts ) { - foreach( mygpo::PodcastPtr podcast, podcasts->list() ) + for( const auto &podcast : podcasts->list() ) { appendChild( new GpodderPodcastTreeItem( podcast, this ) ); } } diff --git a/src/services/gpodder/GpodderTreeItem.h b/src/services/gpodder/GpodderTreeItem.h index f5697fef70..a3c528b705 100644 --- a/src/services/gpodder/GpodderTreeItem.h +++ b/src/services/gpodder/GpodderTreeItem.h @@ -1,60 +1,60 @@ /**************************************************************************************** * Copyright (c) 2011 Stefan Derkits * * Copyright (c) 2011 Christian Wagner * * Copyright (c) 2011 Felix Winter * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef GPODDERTREEITEM_H_ #define GPODDERTREEITEM_H_ -#include -#include +#include +#include #include #include #include class GpodderServiceModel; class GpodderTreeItem : public QObject { Q_OBJECT public: GpodderTreeItem( GpodderTreeItem *parent = 0, QString name = "" ); virtual ~GpodderTreeItem(); void appendChild( GpodderTreeItem *child ); GpodderTreeItem *child( int row ); int childCount() const; void setHasChildren( bool hasChildren ); bool hasChildren() const; GpodderTreeItem *parent() const; bool isRoot() const; virtual QVariant displayData() const; virtual void appendTags( mygpo::TagListPtr tags ); virtual void appendPodcasts( mygpo::PodcastListPtr podcasts ); private: QList m_childItems; GpodderTreeItem *m_parentItem; QString m_name; bool m_hasChildren; }; #endif /* GPODDERTREEITEM_H_ */ diff --git a/src/services/gpodder/amarok_service_gpodder.desktop b/src/services/gpodder/amarok_service_gpodder.desktop index fa357e43e3..fa8e4ec8c6 100644 --- a/src/services/gpodder/amarok_service_gpodder.desktop +++ b/src/services/gpodder/amarok_service_gpodder.desktop @@ -1,112 +1,111 @@ [Desktop Entry] Type=Service -ServiceTypes=KPluginInfo Icon=view-services-gpodder-amarok Name=gpodder.net Name[bg]=gpodder.net Name[bs]=gpodder.net Name[ca]=gpodder.net Name[ca@valencia]=gpodder.net Name[cs]=gpodder.net Name[da]=gpodder.net Name[de]=gpodder.net Name[el]=gpodder.net Name[en_GB]=gpodder.net Name[es]=gpodder.net Name[et]=gpodder.net Name[eu]=gpodder.net Name[fi]=gpodder.net Name[fr]=gpodder.net Name[ga]=gpodder.net Name[gl]=gpodder.net Name[hu]=gpodder.net Name[id]=gpodder.net Name[it]=gpodder.net Name[ja]=gpodder.net Name[km]=gpodder.net Name[lt]=gpodder.net Name[lv]=gpodder.net Name[nb]=gpodder.net Name[nds]=gpodder.net Name[nl]=gpodder.net Name[pa]=gpodder.net Name[pl]=gpodder.net Name[pt]=gpodder.net Name[pt_BR]=gpodder.net Name[ro]=gpodder.net Name[ru]=gpodder.net Name[sk]=gpodder.net Name[sl]=gpodder.net Name[sr]=Гподер.нет Name[sr@ijekavian]=Гподер.нет Name[sr@ijekavianlatin]=gpodder.net Name[sr@latin]=gpodder.net Name[sv]=gpodder.net Name[tr]=gpodder.net Name[uk]=gpodder.net Name[x-test]=xxgpodder.netxx Name[zh_CN]=gpodder.net Name[zh_TW]=gpodder.net Comment=Podcast Service Comment[bg]=Услуга за подкастове Comment[bs]=Podcast usluga Comment[ca]=Servei de podcasts Comment[ca@valencia]=Servei de podcasts Comment[cs]=Služba podcastů Comment[da]=Podcast-tjeneste Comment[de]=Podcast-Dienst Comment[el]=Υπηρεσία Podcast Comment[en_GB]=Podcast Service Comment[es]=Servicio de podcast Comment[et]=Podcastide teenus Comment[eu]=Podcast-en zerbitzua Comment[fi]=Podcast-palvelu Comment[fr]=Service de podcasts Comment[ga]=Seirbhís Phodchraolta Comment[gl]=Servizo de podcast Comment[hu]=Podcast szolgáltatás Comment[id]=Layanan Podcast Comment[it]=Servizio Podcast Comment[ja]=ポッドキャストサービス Comment[km]=សេវា​ផតខាស់ Comment[lt]=Garso prenumeratų tarnyba Comment[lv]=Podraižu serviss Comment[nb]=Podkast-tjeneste Comment[nds]=Podcast-Deenst Comment[nl]=Podcast-service Comment[pa]=ਪੋਡਕਾਸਟ ਸਰਵਿਸ Comment[pl]=Usługa podcast Comment[pt]=Serviço de 'Podcasts' Comment[pt_BR]=Serviço de podcasts Comment[ro]=Serviciu Podcast Comment[ru]=Каталог подкастов Comment[sk]=Podcast služba Comment[sl]=Storitev za podcaste Comment[sr]=Сервис подемисија Comment[sr@ijekavian]=Сервис подемисија Comment[sr@ijekavianlatin]=Servis podemisija Comment[sr@latin]=Servis podemisija Comment[sv]=Podsändningstjänst Comment[tr]=Podcast Servisi Comment[uk]=Служба трансляцій Comment[x-test]=xxPodcast Servicexx Comment[zh_CN]=播客服务 Comment[zh_TW]=Podcast 服務 ServiceTypes=Amarok/Plugin X-KDE-Amarok-authors=Stefan Derkits, Christian Wagner, Felix Winter, Lucas Lira Gomes X-KDE-Amarok-email=stefan@derkits.at, christian.wagner86@gmx.at, ixos01@gmail.com, x8lucas8x@gmail.com X-KDE-Amarok-framework-version=73 X-KDE-Amarok-name=gpodderService X-KDE-Amarok-rank=100 X-KDE-Amarok-version=1 X-KDE-PluginInfo-Author=Stefan Derkits, Christian Wagner, Felix Winter, Lucas Lira Gomes X-KDE-PluginInfo-Email=stefan@derkits.at, christian.wagner86@gmx.at, ixos01@gmail.com, x8lucas8x@gmail.com X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Category=Service X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=false X-KDE-Library=amarok_service_gpodder X-KDE-PluginInfo-Name=amarok_service_gpodder diff --git a/src/services/jamendo/CMakeLists.txt b/src/services/jamendo/CMakeLists.txt index 7c9a86e2a8..365afac824 100644 --- a/src/services/jamendo/CMakeLists.txt +++ b/src/services/jamendo/CMakeLists.txt @@ -1,39 +1,34 @@ include_directories( ../ ../../ ../../core-impl/collections ../../statusbar ${CMAKE_CURRENT_BINARY_DIR}/../.. ) add_subdirectory( images ) ########### next target ############### set(amarok_service_jamendo_PART_SRCS JamendoService.cpp JamendoMeta.cpp JamendoDatabaseHandler.cpp JamendoXmlParser.cpp JamendoInfoParser.cpp ) add_library(amarok_service_jamendo MODULE ${amarok_service_jamendo_PART_SRCS}) target_link_libraries(amarok_service_jamendo amarokcore amaroklib amarokpud - KF5::KDELibs4Support - KF5::KIOCore KF5::ThreadWeaver ) install(TARGETS amarok_service_jamendo DESTINATION ${PLUGIN_INSTALL_DIR} ) - -########### install files ############### - - install( FILES amarok_service_jamendo.desktop DESTINATION ${SERVICES_INSTALL_DIR}) + kcoreaddons_desktop_to_json(amarok_service_jamendo amarok_service_jamendo.desktop SERVICE_TYPES ${CMAKE_SOURCE_DIR}/src/amarok-plugin.desktop) diff --git a/src/services/jamendo/JamendoDatabaseHandler.cpp b/src/services/jamendo/JamendoDatabaseHandler.cpp index fe8d111980..8597d9ed07 100644 --- a/src/services/jamendo/JamendoDatabaseHandler.cpp +++ b/src/services/jamendo/JamendoDatabaseHandler.cpp @@ -1,279 +1,279 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "JamendoDatabaseHandler.h" #include "core-impl/storage/StorageManager.h" #include "core/support/Debug.h" #include using namespace Meta; JamendoDatabaseHandler::JamendoDatabaseHandler() { } JamendoDatabaseHandler::~JamendoDatabaseHandler() { } void JamendoDatabaseHandler::createDatabase( ) { //Get database instance auto db = StorageManager::instance()->sqlStorage(); QString autoIncrement = "AUTO_INCREMENT"; // create table containing tracks QString queryString = "CREATE TABLE jamendo_tracks (" - "id INTEGER PRIMARY KEY " + autoIncrement + ',' + + "id INTEGER PRIMARY KEY " + autoIncrement + ',' + "name " + db->textColumnType() + ',' + "track_number INTEGER," "length INTEGER," "preview_url " + db->exactTextColumnType() + ',' + "album_id INTEGER," "artist_id INTEGER ) ENGINE = MyISAM;"; debug() << "Creating jamendo_tracks: " << queryString; QStringList result = db->query( queryString ); db->query( "CREATE INDEX jamendo_tracks_id ON jamendo_tracks(id);" ); db->query( "CREATE INDEX jamendo_tracks_album_id ON jamendo_tracks(album_id);" ); db->query( "CREATE INDEX jamendo_tracks_artist_id ON jamendo_tracks(artist_id);" ); //Create album table queryString = "CREATE TABLE jamendo_albums (" "id INTEGER PRIMARY KEY " + autoIncrement + ',' + "name " + db->textColumnType() + ',' + "description " + db->exactTextColumnType() + ',' + "popularity FLOAT, " + "cover_url " + db->exactTextColumnType() + ',' + "launch_year Integer, " "genre " + db->exactTextColumnType() + ',' + "artist_id INTEGER, " "mp3_torrent_url " + db->exactTextColumnType() + ',' + "ogg_torrent_url " + db->exactTextColumnType() + " ) ENGINE = MyISAM;"; debug() << "Creating jamendo_albums: " << queryString; result = db->query( queryString ); db->query( "CREATE INDEX jamendo_albums_id ON jamendo_albums(id);" ); db->query( "CREATE INDEX jamendo_albums_name ON jamendo_albums(name);" ); db->query( "CREATE INDEX jamendo_albums_artist_id ON jamendo_albums(artist_id);" ); //Create artist table queryString = "CREATE TABLE jamendo_artists (" "id INTEGER PRIMARY KEY " + autoIncrement + ',' + "name " + db->textColumnType() + ',' + "description " + db->textColumnType() + ',' + "country " + db->textColumnType() + ',' + "photo_url " + db->textColumnType() + ',' + "jamendo_url " + db->textColumnType() + ',' + "home_url " + db->textColumnType() + ") ENGINE = MyISAM;"; debug() << "Creating jamendo_artists: " << queryString; result = db->query( queryString ); db->query( "CREATE INDEX jamendo_artists_id ON jamendo_artists(id);" ); db->query( "CREATE INDEX jamendo_artists_name ON jamendo_artists(name);" ); //create genre table queryString = "CREATE TABLE jamendo_genre (" "id INTEGER PRIMARY KEY " + autoIncrement + ',' + "name " + db->textColumnType() + ',' + "album_id INTEGER" + ") ENGINE = MyISAM;"; debug() << "Creating jamendo_genres: " << queryString; result = db->query( queryString ); db->query( "CREATE INDEX jamendo_genre_id ON jamendo_genre(id);" ); db->query( "CREATE INDEX jamendo_genre_name ON jamendo_genre(name);" ); db->query( "CREATE INDEX jamendo_genre_album_id ON jamendo_genre(album_id);" ); } void JamendoDatabaseHandler::destroyDatabase( ) { debug() << "Destroy Jamendo database "; auto db = StorageManager::instance()->sqlStorage(); QStringList result = db->query( "DROP INDEX jamendo_tracks_id ON jamendo_tracks;"); result = db->query( "DROP INDEX jamendo_tracks_artist_id ON jamendo_tracks;"); result = db->query( "DROP INDEX jamendo_tracks_album_id ON jamendo_tracks;"); result = db->query( "DROP INDEX jamendo_albums_id ON jamendo_albums;"); result = db->query( "DROP INDEX jamendo_albums_name ON jamendo_albums;"); result = db->query( "DROP INDEX jamendo_albums_artist_id ON jamendo_albums;"); result = db->query( "DROP INDEX jamendo_artists_id ON jamendo_artists;"); result = db->query( "DROP INDEX jamendo_artists_name ON jamendo_artists;"); result = db->query( "DROP INDEX jamendo_genre_id ON jamendo_genre;"); result = db->query( "DROP INDEX jamendo_genre_album_id ON jamendo_genre;"); result = db->query( "DROP INDEX jamendo_genre_name ON jamendo_genre;"); result = db->query( "DROP TABLE IF EXISTS jamendo_tracks;" ); result = db->query( "DROP TABLE IF EXISTS jamendo_albums;" ); result = db->query( "DROP TABLE IF EXISTS jamendo_artists;" ); result = db->query( "DROP TABLE IF EXISTS jamendo_genre;" ); //FIXME: We only support sqlite currently. DbConnection no longer exists. } int JamendoDatabaseHandler::insertTrack( ServiceTrack *track ) { JamendoTrack * jTrack = static_cast ( track ); QString numberString; auto db = StorageManager::instance()->sqlStorage(); QString queryString = "INSERT INTO jamendo_tracks ( id, name, track_number, length, " "album_id, artist_id, preview_url ) VALUES ( " + QString::number( jTrack->id() ) + ", '" + db->escape( jTrack->name() ) + "', " + QString::number( jTrack->trackNumber() ) + ", " + QString::number( jTrack->length() ) + ", " + QString::number( jTrack->albumId() ) + ", " + QString::number( jTrack->artistId() ) + ", '" + db->escape( jTrack->uidUrl() ) + "' );"; // debug() << "Adding Jamendo track " << queryString; int trackId = db->insert( queryString, NULL ); // Process moods: /* QStringList moods = track->getMoods(); foreach( QString mood, moods ) { queryString = "INSERT INTO jamendo_moods ( track_id, mood ) VALUES ( " + QString::number( trackId ) + ", '" + db->escape( mood ) + "' );"; //debug() << "Adding Jamendo mood: " << queryString; db->insert( queryString, NULL ); } */ return trackId; } int JamendoDatabaseHandler::insertAlbum( ServiceAlbum *album ) { JamendoAlbum * jAlbum = static_cast ( album ); QString queryString, popularity; auto sqlDb = StorageManager::instance()->sqlStorage(); popularity = QString::number( jAlbum->popularity() ); if( popularity == "nan" ) // sometimes this seems to happen, I don't know why popularity = '0'; queryString = "INSERT INTO jamendo_albums ( id, name, description, " "popularity, cover_url, launch_year, genre, " "artist_id, mp3_torrent_url, ogg_torrent_url ) VALUES ( " + QString::number( jAlbum->id() ) + ", '" + sqlDb->escape( jAlbum->name() ) + "', '" + sqlDb->escape( jAlbum->description() )+ "', " + popularity + ", '" + sqlDb->escape( jAlbum->coverUrl() )+ "', " + QString::number( jAlbum->launchYear() ) + ", '" + sqlDb->escape( jAlbum->genre() )+ "', " + QString::number( jAlbum->artistId() ) + ", '" + sqlDb->escape( QString() ) + "', '" // Deprecated + sqlDb->escape( QString() ) + "' );"; // Deprecated //debug() << "Adding Jamendo album " << queryString; return sqlDb->insert( queryString, QString() ); } int JamendoDatabaseHandler::insertArtist( ServiceArtist *artist ) { JamendoArtist * jArtist = static_cast ( artist ); QString queryString; auto sqlDb = StorageManager::instance()->sqlStorage(); queryString = "INSERT INTO jamendo_artists ( id, name, description, " "country, photo_url, jamendo_url, home_url " ") VALUES ( " + QString::number( jArtist->id() ) + ", '" + sqlDb->escape( jArtist->name() ) + "', '" + sqlDb->escape( jArtist->description() ) + "', '" + sqlDb->escape( jArtist->country() ) + "', '" + sqlDb->escape( jArtist->photoURL() ) + "', '" + sqlDb->escape( jArtist->jamendoURL() ) + "', '" + sqlDb->escape( jArtist->homeURL() ) + "' );"; //debug() << "Adding Jamendo artist " << queryString; return sqlDb->insert( queryString, QString() ); /* QString m_country; QString m_photoURL; QString m_jamendoURL; QString m_homeURL;*/ } int JamendoDatabaseHandler::insertGenre(ServiceGenre * genre) { QString queryString; auto sqlDb = StorageManager::instance()->sqlStorage(); queryString = "INSERT INTO jamendo_genre ( album_id, name " ") VALUES ( " + QString::number ( genre->albumId() ) + ", '" + sqlDb->escape( genre->name() ) + "' );"; //debug() << "Adding Jamendo genre " << queryString; return sqlDb->insert( queryString, 0 ); } void JamendoDatabaseHandler::begin( ) { StorageManager *mgr = StorageManager::instance(); QString queryString = "BEGIN;"; mgr->sqlStorage()->query( queryString ); } void JamendoDatabaseHandler::commit( ) { StorageManager *mgr = StorageManager::instance(); QString queryString = "COMMIT;"; mgr->sqlStorage()->query( queryString ); } void JamendoDatabaseHandler::trimGenres( int minCount ) { QString query = QString("delete from jamendo_genre where name IN ( SELECT name from jamendo_genre GROUP BY jamendo_genre.name HAVING COUNT ( jamendo_genre.name ) < %1 );").arg( minCount ); auto sqlDb = StorageManager::instance()->sqlStorage(); sqlDb->query( query ); //also trim genre names that have only 1 or 2 chars query = QString ("delete from jamendo_genre where name REGEXP '^.{1,2}$';" ); sqlDb->query( query ); } diff --git a/src/services/jamendo/JamendoInfoParser.cpp b/src/services/jamendo/JamendoInfoParser.cpp index 632fb4e476..18834c0c9c 100644 --- a/src/services/jamendo/JamendoInfoParser.cpp +++ b/src/services/jamendo/JamendoInfoParser.cpp @@ -1,119 +1,119 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "JamendoInfoParser.h" #include "core/support/Debug.h" #include "JamendoMeta.h" -#include +#include using namespace Meta; JamendoInfoParser::JamendoInfoParser() : InfoParserBase() { } JamendoInfoParser::~JamendoInfoParser() { } void JamendoInfoParser::getInfo(ArtistPtr artist) { DEBUG_BLOCK JamendoArtist * jamendoArtist = dynamic_cast ( artist.data() ); if ( jamendoArtist == 0) return; QString description = jamendoArtist->description(); if ( description.isEmpty() ) description = i18n( "No description available..." ); QString infoHtml = ""; infoHtml += "
"; infoHtml += i18n( "Artist" ) + "

"; infoHtml += ""; infoHtml += jamendoArtist->prettyName(); infoHtml += "

"; if ( !jamendoArtist->photoURL().isEmpty() ) infoHtml += "photoURL() + "\" align=\"middle\" border=\"1\">

"; infoHtml += description; infoHtml += "

" + i18n( "From Jamendo.com" ) + "
"; infoHtml += ""; emit( info( infoHtml ) ); } void JamendoInfoParser::getInfo(AlbumPtr album) { DEBUG_BLOCK JamendoAlbum * jamendoAlbum = dynamic_cast ( album.data() ); if ( jamendoAlbum == 0) return; QString description = jamendoAlbum->description(); if ( description.isEmpty() ) description = i18n( "No description available..." ); QString infoHtml = ""; infoHtml += "
"; infoHtml += i18n( "Album" ) + "

"; infoHtml += ""; infoHtml += jamendoAlbum->prettyName(); infoHtml += "

"; if ( !jamendoAlbum->coverUrl().isEmpty() ) infoHtml += "coverUrl() + "\" align=\"middle\" border=\"1\">

"; infoHtml += description; infoHtml += "

" + i18n( "From Jamendo.com" ) + "
"; infoHtml += ""; emit( info( infoHtml ) ); } void JamendoInfoParser::getInfo(TrackPtr track) { DEBUG_BLOCK JamendoTrack * jamendoTrack = dynamic_cast ( track.data() ); if ( jamendoTrack == 0) return; QString infoHtml = ""; infoHtml += "
"; infoHtml += i18n( "Track" ) + "

"; infoHtml += ""; infoHtml += jamendoTrack->prettyName(); infoHtml += "

"; infoHtml += "

" + i18n( "From Jamendo.com" ) + "
"; infoHtml += ""; emit( info( infoHtml ) ); } diff --git a/src/services/jamendo/JamendoMeta.cpp b/src/services/jamendo/JamendoMeta.cpp index a2ba0f7a8b..cb208141c6 100644 --- a/src/services/jamendo/JamendoMeta.cpp +++ b/src/services/jamendo/JamendoMeta.cpp @@ -1,310 +1,310 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "JamendoMeta.h" #include "JamendoService.h" #include "SvgHandler.h" #include "core/support/Debug.h" -#include +#include #include using namespace Meta; JamendoMetaFactory::JamendoMetaFactory( const QString & dbPrefix, JamendoService * service ) : ServiceMetaFactory( dbPrefix ) , m_service( service ) { } TrackPtr JamendoMetaFactory::createTrack( const QStringList & rows ) { JamendoTrack * track = new JamendoTrack( rows ); track->setService( m_service ); return TrackPtr( track ); } int JamendoMetaFactory::getAlbumSqlRowCount() { return ServiceMetaFactory::getAlbumSqlRowCount() + 6; } QString JamendoMetaFactory::getAlbumSqlRows() { QString sqlRows = ServiceMetaFactory::getAlbumSqlRows(); sqlRows += ", "; sqlRows += tablePrefix() + "_albums.popularity, "; sqlRows += tablePrefix() + "_albums.cover_url, "; sqlRows += tablePrefix() + "_albums.launch_year, "; sqlRows += tablePrefix() + "_albums.genre, "; sqlRows += tablePrefix() + "_albums.mp3_torrent_url, "; // Deprecated sqlRows += tablePrefix() + "_albums.ogg_torrent_url "; // Deprecated return sqlRows; } AlbumPtr JamendoMetaFactory::createAlbum( const QStringList & rows ) { JamendoAlbum * album = new JamendoAlbum( rows ); album->setService( m_service ); album->setSourceName( "Jamendo.com" ); return AlbumPtr( album ); } int JamendoMetaFactory::getArtistSqlRowCount() { return ServiceMetaFactory::getArtistSqlRowCount() + 4; } QString JamendoMetaFactory::getArtistSqlRows() { QString sqlRows = ServiceMetaFactory::getArtistSqlRows(); sqlRows += ", "; sqlRows += tablePrefix() + "_artists.country, "; sqlRows += tablePrefix() + "_artists.photo_url, "; sqlRows += tablePrefix() + "_artists.jamendo_url, "; sqlRows += tablePrefix() + "_artists.home_url "; return sqlRows; } ArtistPtr JamendoMetaFactory::createArtist( const QStringList & rows ) { JamendoArtist * artist = new JamendoArtist( rows ); artist->setSourceName( "Jamendo.com" ); return ArtistPtr( artist ); } GenrePtr JamendoMetaFactory::createGenre( const QStringList & rows ) { JamendoGenre * genre = new JamendoGenre( rows ); genre->setSourceName( "Jamendo.com" ); return GenrePtr( genre ); } //// JamendoTrack //// JamendoTrack::JamendoTrack( const QString &name ) : ServiceTrack( name ) , m_service ( 0 ) { } JamendoTrack::JamendoTrack( const QStringList & resultRow ) : ServiceTrack( resultRow ) , m_service ( 0 ) { } QString Meta::JamendoTrack::sourceName() { return "Jamendo.com"; } QString Meta::JamendoTrack::sourceDescription() { return i18n( "A site where artists can freely share their music" ); } QPixmap Meta::JamendoTrack::emblem() { - return QPixmap( KStandardDirs::locate( "data", "amarok/images/emblem-jamendo.png" ) ); + return QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-jamendo.png" ) ); } QString JamendoTrack::scalableEmblem() { - return KStandardDirs::locate( "data", "amarok/images/emblem-jamendo-scalable.svgz" ); + return QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-jamendo-scalable.svgz" ); } void Meta::JamendoTrack::setService(JamendoService * service) { m_service = service; } QString JamendoTrack::type() const { return "mp3"; } //// JamendoArtist //// JamendoArtist::JamendoArtist( const QString &name ) : ServiceArtist( name ) { } JamendoArtist::JamendoArtist( const QStringList & resultRow ) : ServiceArtist( resultRow ) { m_country = resultRow[3]; m_photoURL = resultRow[4]; m_jamendoURL = resultRow[5]; m_homeURL = resultRow[6]; } void JamendoArtist::setCountry( const QString & country ) { m_country = country; } QString JamendoArtist::country() const { return m_country; } void JamendoArtist::setPhotoURL( const QString &photoURL ) { m_photoURL = photoURL; } QString JamendoArtist::photoURL( ) const { return m_photoURL; } void JamendoArtist::setHomeURL( const QString &homeURL ) { m_homeURL = homeURL; } QString JamendoArtist::homeURL( ) const { return m_homeURL; } void JamendoArtist::setJamendoURL( const QString & jamendoURL ) { m_jamendoURL = jamendoURL; } QString JamendoArtist::jamendoURL() const { return m_jamendoURL; } //// JamendoAlbum //// JamendoAlbum::JamendoAlbum( const QString &name ) : ServiceAlbumWithCover( name ) { } JamendoAlbum::JamendoAlbum( const QStringList & resultRow ) : ServiceAlbumWithCover( resultRow ) { m_popularity = resultRow[4].toFloat(); m_coverURL = resultRow[5]; m_launchYear = resultRow[6].toInt(); m_genre = resultRow[7]; } void JamendoAlbum::setCoverUrl( const QString &coverURL ) { m_coverURL = coverURL; } QString JamendoAlbum::coverUrl( ) const { return m_coverURL; } void JamendoAlbum::setLaunchYear( int launchYear ) { m_launchYear = launchYear; } int JamendoAlbum::launchYear( ) const { return m_launchYear; } void JamendoAlbum::setGenre( const QString&genre ) { m_genre = genre; } QString JamendoAlbum::genre( ) const { return m_genre; } void JamendoAlbum::setPopularity( float popularity ) { m_popularity = popularity; } float JamendoAlbum::popularity() const { return m_popularity; } void Meta::JamendoAlbum::setService( JamendoService * service ) { m_service = service; } JamendoService * Meta::JamendoAlbum::service() { return m_service; } JamendoGenre::JamendoGenre( const QString & name ) : ServiceGenre( name ) { } JamendoGenre::JamendoGenre( const QStringList & resultRow ) : ServiceGenre( resultRow ) { } diff --git a/src/services/jamendo/JamendoService.cpp b/src/services/jamendo/JamendoService.cpp index c90276259f..acd41dd013 100644 --- a/src/services/jamendo/JamendoService.cpp +++ b/src/services/jamendo/JamendoService.cpp @@ -1,294 +1,288 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "JamendoService.h" #include "browsers/CollectionTreeItem.h" #include "browsers/CollectionTreeView.h" #include "browsers/SingleCollectionTreeItemModel.h" #include "core-impl/collections/support/CollectionManager.h" #include "core/support/Debug.h" #include "core/support/Components.h" #include "core/interfaces/Logger.h" #include "EngineController.h" #include "JamendoInfoParser.h" #include "ServiceSqlRegistry.h" #include "widgets/SearchWidget.h" #include -#include -#include -#include -#include -#include -#include -#include - #include +#include +#include +#include +#include #include #include +#include #include #include using namespace Meta; JamendoServiceFactory::JamendoServiceFactory() : ServiceFactory() { - KPluginInfo pluginInfo( "amarok_service_jamendo.desktop" ); - pluginInfo.setConfig( config() ); - m_info = pluginInfo; } void JamendoServiceFactory::init() { ServiceBase* service = new JamendoService( this, "Jamendo.com" ); m_initialized = true; emit newService( service ); } QString JamendoServiceFactory::name() { return "Jamendo.com"; } KConfigGroup JamendoServiceFactory::config() { return Amarok::config( "Service_Jamendo" ); } JamendoService::JamendoService( JamendoServiceFactory* parent, const QString & name) : ServiceBase( name, parent ) , m_currentAlbum( 0 ) , m_xmlParser( 0 ) { - setShortDescription( i18n( "A archive of free, Creative Commons licensed music" ) ); + setShortDescription( i18n( "An archive of free, Creative Commons licensed music" ) ); setIcon( QIcon::fromTheme( "view-services-jamendo-amarok" ) ); setLongDescription( i18n( "Jamendo.com puts artists and music lovers in touch with each other. The site allows artists to upload their own albums to share them with the world and users to download all of them for free. Listen to and download all Jamendo.com contents from within Amarok." ) ); -setImagePath( KStandardDirs::locate( "data", "amarok/images/hover_info_jamendo.png" ) ); + setImagePath( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/hover_info_jamendo.png" ) ); ServiceMetaFactory * metaFactory = new JamendoMetaFactory( "jamendo", this ); ServiceSqlRegistry * registry = new ServiceSqlRegistry( metaFactory ); m_collection = new Collections::ServiceSqlCollection( "jamendo", "Jamendo.com", metaFactory, registry ); CollectionManager::instance()->addTrackProvider( m_collection ); setServiceReady( true ); } JamendoService::~JamendoService() { DEBUG_BLOCK if( m_collection ) { CollectionManager::instance()->removeTrackProvider( m_collection ); m_collection->deleteLater(); m_collection = 0; } //if currently running, stop it or we will get crashes if( m_xmlParser ) { m_xmlParser->requestAbort(); delete m_xmlParser; m_xmlParser = 0; } } void JamendoService::polish() { generateWidgetInfo(); if ( m_polished ) return; - KHBox * bottomPanelLayout = new KHBox; - bottomPanelLayout->setParent( m_bottomPanel ); + BoxWidget *bottomPanelLayout = new BoxWidget( false, m_bottomPanel ); m_updateListButton = new QPushButton; m_updateListButton->setParent( bottomPanelLayout ); m_updateListButton->setText( i18nc( "Fetch new information from the website", "Update" ) ); m_updateListButton->setObjectName( "updateButton" ); m_updateListButton->setIcon( QIcon::fromTheme( "view-refresh-amarok" ) ); m_downloadButton = new QPushButton; m_downloadButton->setParent( bottomPanelLayout ); m_downloadButton->setText( i18n( "Download" ) ); m_downloadButton->setObjectName( "downloadButton" ); m_downloadButton->setIcon( QIcon::fromTheme( "download-amarok" ) ); m_downloadButton->setEnabled( false ); - connect( m_updateListButton, SIGNAL(clicked()), this, SLOT(updateButtonClicked()) ); - connect( m_downloadButton, SIGNAL(clicked()), this, SLOT(download()) ); + connect( m_updateListButton, &QPushButton::clicked, this, &JamendoService::updateButtonClicked ); + connect( m_downloadButton, &QPushButton::clicked, this, &JamendoService::download ); setInfoParser( new JamendoInfoParser() ); QList levels; levels << CategoryId::Genre << CategoryId::Artist << CategoryId::Album; setModel( new SingleCollectionTreeItemModel( m_collection, levels ) ); - connect( m_contentView, SIGNAL(itemSelected(CollectionTreeItem*)), this, SLOT(itemSelected(CollectionTreeItem*)) ); + connect( static_cast( m_contentView ), &ServiceCollectionTreeView::itemSelected, + this, &JamendoService::itemSelected ); QMenu *filterMenu = new QMenu( 0 ); // QAction *action = filterMenu->addAction( i18n("Artist") ); // connect( action, SIGNAL(triggered(bool)), SLOT(sortByArtist()) ); // // action = filterMenu->addAction( i18n( "Artist / Album" ) ); // connect( action, SIGNAL(triggered(bool)), SLOT(sortByArtistAlbum()) ); // // action = filterMenu->addAction( i18n( "Album" ) ); // connect( action, SIGNAL(triggered(bool)), SLOT(sortByAlbum()) ); QAction *action = filterMenu->addAction( i18n( "Genre / Artist" ) ); - connect( action, SIGNAL(triggered(bool)), SLOT(sortByGenreArtist()) ); + connect( action, &QAction::triggered, this, &JamendoService::sortByGenreArtist ); action = filterMenu->addAction( i18n( "Genre / Artist / Album" ) ); - connect( action, SIGNAL(triggered(bool)), SLOT(sortByGenreArtistAlbum()) ); + connect( action, &QAction::triggered, this, &JamendoService::sortByGenreArtistAlbum ); QAction *filterMenuAction = new QAction( QIcon::fromTheme( "preferences-other" ), i18n( "Sort Options" ), this ); filterMenuAction->setMenu( filterMenu ); m_searchWidget->toolBar()->addSeparator(); m_searchWidget->toolBar()->addAction( filterMenuAction ); QToolButton *tbutton = qobject_cast< QToolButton* >( m_searchWidget->toolBar()->widgetForAction( filterMenuAction ) ); if( tbutton ) tbutton->setPopupMode( QToolButton::InstantPopup ); // m_menubar->show(); m_polished = true; } void JamendoService::updateButtonClicked() { m_updateListButton->setEnabled( false ); debug() << "JamendoService: start downloading xml file"; - KTemporaryFile tempFile; - tempFile.setSuffix( ".gz" ); + QTemporaryFile tempFile; +// tempFile.setSuffix( ".gz" ); tempFile.setAutoRemove( false ); //file will be removed in JamendoXmlParser if( !tempFile.open() ) return; //error m_tempFileName = tempFile.fileName(); m_listDownloadJob = KIO::file_copy( /* Deprecated */ QUrl("http://imgjam.com/data/dbdump_artistalbumtrack.xml.gz"), - QUrl( m_tempFileName ), 0700 , KIO::HideProgressInfo | KIO::Overwrite ); + QUrl::fromLocalFile( m_tempFileName ), 0700 , KIO::HideProgressInfo | KIO::Overwrite ); Amarok::Components::logger()->newProgressOperation( m_listDownloadJob, i18n( "Downloading Jamendo.com database..." ), this, SLOT(listDownloadCancelled()) ); - connect( m_listDownloadJob, SIGNAL(result(KJob*)), - this, SLOT(listDownloadComplete(KJob*)) ); + connect( m_listDownloadJob, &KJob::result, + this, &JamendoService::listDownloadComplete ); } void JamendoService::listDownloadComplete(KJob * downloadJob) { if( downloadJob != m_listDownloadJob ) return ; //not the right job, so let's ignore it debug() << "JamendoService: xml file download complete"; m_listDownloadJob = 0; //testing if ( downloadJob->error() != 0 ) { //TODO: error handling here m_updateListButton->setEnabled( true ); // otherwise button will remain inactive in case of error return; } Amarok::Components::logger()->shortMessage( i18n( "Updating the local Jamendo database." ) ); debug() << "JamendoService: create xml parser"; if( m_xmlParser == 0 ) m_xmlParser = new JamendoXmlParser( m_tempFileName ); - connect( m_xmlParser, SIGNAL(doneParsing()), SLOT(doneParsing()) ); + connect( m_xmlParser, &JamendoXmlParser::doneParsing, this, &JamendoService::doneParsing ); ThreadWeaver::Queue::instance()->enqueue( QSharedPointer(m_xmlParser) ); downloadJob->deleteLater(); } void JamendoService::listDownloadCancelled() { m_listDownloadJob->kill(); m_listDownloadJob = 0; debug() << "Aborted xml download"; m_updateListButton->setEnabled( true ); } void JamendoService::doneParsing() { debug() << "JamendoService: done parsing"; m_updateListButton->setEnabled( true ); // model->setGenre("All"); //delete sender sender()->deleteLater(); m_xmlParser = 0; m_collection->emitUpdated(); } void JamendoService::itemSelected( CollectionTreeItem * selectedItem ) { DEBUG_BLOCK //we only enable the download button if there is only one item selected and it happens to //be an album or a track DataPtr dataPtr = selectedItem->data(); if ( typeid( *dataPtr.data() ) == typeid( JamendoTrack ) ) { debug() << "is right type (track)"; JamendoTrack * track = static_cast ( dataPtr.data() ); m_currentAlbum = static_cast ( track->album().data() ); m_downloadButton->setEnabled( true ); } else if ( typeid( * dataPtr.data() ) == typeid( JamendoAlbum ) ) { m_currentAlbum = static_cast ( dataPtr.data() ); debug() << "is right type (album) named " << m_currentAlbum->name(); m_downloadButton->setEnabled( true ); } else { debug() << "is wrong type"; m_downloadButton->setEnabled( false ); } return; } void JamendoService::download() // SLOT { DEBUG_BLOCK if ( !m_polished ) polish(); CollectionTreeView *treeView = static_cast( view() ); treeView->copySelectedToLocalCollection(); } diff --git a/src/services/jamendo/JamendoXmlParser.cpp b/src/services/jamendo/JamendoXmlParser.cpp index 4a6342c3a2..2bd3a9ca37 100644 --- a/src/services/jamendo/JamendoXmlParser.cpp +++ b/src/services/jamendo/JamendoXmlParser.cpp @@ -1,443 +1,443 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "JamendoXmlParser.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include "core/support/Components.h" #include "core/interfaces/Logger.h" #include #include -#include +#include using namespace Meta; static const QString COVERURL_BASE = "http://api.jamendo.com/get2/image/album/redirect/?id=%1&imagesize=100"; JamendoXmlParser::JamendoXmlParser( const QString &filename ) : QObject() , ThreadWeaver::Job() + , m_sFileName( filename ) , n_numberOfTransactions ( 0 ) , n_maxNumberOfTransactions ( 5000 ) , m_aborted( false ) { DEBUG_BLOCK // From: http://www.linuxselfhelp.com/HOWTO/MP3-HOWTO-13.html#ss13.3 m_id3GenreHash.insert( 0, "Blues" ); m_id3GenreHash.insert( 1, "Classic Rock" ); m_id3GenreHash.insert( 2, "Country" ); m_id3GenreHash.insert( 3, "Dance" ); m_id3GenreHash.insert( 4, "Disco" ); m_id3GenreHash.insert( 5, "Funk" ); m_id3GenreHash.insert( 6, "Grunge" ); m_id3GenreHash.insert( 7, "Hip-Hop" ); m_id3GenreHash.insert( 8, "Jazz" ); m_id3GenreHash.insert( 9, "Metal" ); m_id3GenreHash.insert( 10, "New Age" ); m_id3GenreHash.insert( 11, "Oldies" ); m_id3GenreHash.insert( 12, "Other" ); m_id3GenreHash.insert( 13, "Pop" ); m_id3GenreHash.insert( 14, "R&B" ); m_id3GenreHash.insert( 15, "Rap" ); m_id3GenreHash.insert( 16, "Reggae" ); m_id3GenreHash.insert( 17, "Rock" ); m_id3GenreHash.insert( 18, "Techno" ); m_id3GenreHash.insert( 19, "Industrial" ); m_id3GenreHash.insert( 20, "Alternative" ); m_id3GenreHash.insert( 21, "Ska" ); m_id3GenreHash.insert( 22, "Death Metal" ); m_id3GenreHash.insert( 23, "Pranks" ); m_id3GenreHash.insert( 24, "Soundtrack" ); m_id3GenreHash.insert( 25, "Euro-Techno" ); m_id3GenreHash.insert( 26, "Ambient" ); m_id3GenreHash.insert( 27, "Trip-Hop" ); m_id3GenreHash.insert( 28, "Vocal" ); m_id3GenreHash.insert( 29, "Jazz+Funk" ); m_id3GenreHash.insert( 30, "Fusion" ); m_id3GenreHash.insert( 31, "Trance" ); m_id3GenreHash.insert( 32, "Classical" ); m_id3GenreHash.insert( 33, "Instrumental" ); m_id3GenreHash.insert( 34, "Acid" ); m_id3GenreHash.insert( 35, "House" ); m_id3GenreHash.insert( 36, "Game" ); m_id3GenreHash.insert( 37, "Sound Clip" ); m_id3GenreHash.insert( 38, "Gospel" ); m_id3GenreHash.insert( 39, "Noise" ); m_id3GenreHash.insert( 40, "AlternRock" ); m_id3GenreHash.insert( 41, "Bass" ); m_id3GenreHash.insert( 42, "Soul" ); m_id3GenreHash.insert( 43, "Punk" ); m_id3GenreHash.insert( 44, "Space" ); m_id3GenreHash.insert( 45, "Meditative" ); m_id3GenreHash.insert( 46, "Instrumental Pop" ); m_id3GenreHash.insert( 47, "Instrumental Rock" ); m_id3GenreHash.insert( 48, "Ethnic" ); m_id3GenreHash.insert( 49, "Gothic" ); m_id3GenreHash.insert( 50, "Darkwave" ); m_id3GenreHash.insert( 51, "Techno-Industrial" ); m_id3GenreHash.insert( 52, "Electronic" ); m_id3GenreHash.insert( 53, "Pop-Folk" ); m_id3GenreHash.insert( 54, "Eurodance" ); m_id3GenreHash.insert( 55, "Dream" ); m_id3GenreHash.insert( 56, "Southern Rock" ); m_id3GenreHash.insert( 57, "Comedy" ); m_id3GenreHash.insert( 58, "Cult" ); m_id3GenreHash.insert( 59, "Gangsta" ); m_id3GenreHash.insert( 60, "Top 40" ); m_id3GenreHash.insert( 61, "Christian Rap" ); m_id3GenreHash.insert( 62, "Pop/Funk" ); m_id3GenreHash.insert( 63, "Jungle" ); m_id3GenreHash.insert( 64, "Native American" ); m_id3GenreHash.insert( 65, "Cabaret" ); m_id3GenreHash.insert( 66, "New Wave" ); m_id3GenreHash.insert( 67, "Psychedelic" ); m_id3GenreHash.insert( 68, "Rave" ); m_id3GenreHash.insert( 69, "Showtunes" ); m_id3GenreHash.insert( 70, "Trailer" ); m_id3GenreHash.insert( 71, "Lo-Fi" ); m_id3GenreHash.insert( 72, "Tribal" ); m_id3GenreHash.insert( 73, "Acid Punk" ); m_id3GenreHash.insert( 74, "Acid Jazz" ); m_id3GenreHash.insert( 75, "Polka" ); m_id3GenreHash.insert( 76, "Retro" ); m_id3GenreHash.insert( 77, "Musical" ); m_id3GenreHash.insert( 78, "Rock & Roll" ); m_id3GenreHash.insert( 79, "Hard Rock" ); - m_sFileName = filename; albumTags.clear(); m_dbHandler = new JamendoDatabaseHandler(); - connect( this, SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(completeJob()) ); + connect( this, &JamendoXmlParser::done, this, &JamendoXmlParser::completeJob ); } JamendoXmlParser::~JamendoXmlParser() { DEBUG_BLOCK m_reader.clear(); delete m_dbHandler; } void JamendoXmlParser::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) { Q_UNUSED(self); Q_UNUSED(thread); if( m_aborted ) return; readConfigFile( m_sFileName ); } void JamendoXmlParser::defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { Q_EMIT started(self); ThreadWeaver::Job::defaultBegin(self, thread); } void JamendoXmlParser::defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { ThreadWeaver::Job::defaultEnd(self, thread); if (!self->success()) { Q_EMIT failed(self); } Q_EMIT done(self); } void JamendoXmlParser::completeJob() { if( m_aborted ) return; Amarok::Components::logger()->longMessage( i18ncp( "First part of: Jamendo.com database update complete. Added 3 tracks on 4 albums from 5 artists.", "Jamendo.com database update complete. Added 1 track on ", "Jamendo.com database update complete. Added %1 tracks on ", m_nNumberOfTracks) + i18ncp( "Middle part of: Jamendo.com database update complete. Added 3 tracks on 4 albums from 5 artists.", "1 album from ", "%1 albums from ", m_nNumberOfAlbums) + i18ncp( "Last part of: Jamendo.com database update complete. Added 3 tracks on 4 albums from 5 artists.", "1 artist.", "%1 artists.", m_nNumberOfArtists ) , Amarok::Logger::Information ); debug() << "JamendoXmlParser: total number of artists: " << m_nNumberOfArtists; debug() << "JamendoXmlParser: total number of albums: " << m_nNumberOfAlbums; debug() << "JamendoXmlParser: total number of tracks: " << m_nNumberOfTracks; emit doneParsing(); deleteLater(); } void JamendoXmlParser::readConfigFile( const QString &filename ) { if( m_aborted ) return; m_nNumberOfTracks = 0; m_nNumberOfAlbums = 0; m_nNumberOfArtists = 0; if( !QFile::exists( filename ) ) { debug() << "jamendo xml file does not exist"; return; } - QIODevice *file = KFilterDev::deviceForFile( filename, "application/x-gzip", true ); + KFilterDev *file = new KFilterDev( filename ); - if( !file || !file->open( QIODevice::ReadOnly ) ) + if( !file->open( QIODevice::ReadOnly ) ) { debug() << "JamendoXmlParser::readConfigFile error reading file"; return; } m_reader.setDevice( file ); m_dbHandler->destroyDatabase(); m_dbHandler->createDatabase(); m_dbHandler->begin(); //start transaction (MAJOR speedup!!) while( !m_reader.atEnd() ) { m_reader.readNext(); if( m_reader.isStartElement() ) { QStringRef localname = m_reader.name(); if( localname == "artist" ) { readArtist(); } } } m_dbHandler->commit(); //complete transaction //as genres are just user tags, remove any that are not applied to at least 10 albums to weed out the worst crap //perhaps make this a config option m_dbHandler->trimGenres( 10 ); file->close(); delete file; QFile::remove( filename ); } void JamendoXmlParser::readArtist() { if( m_aborted ) return; Q_ASSERT( m_reader.isStartElement() && m_reader.name() == "artist" ); // debug() << "Found artist: "; m_nNumberOfArtists++; QString name; QString description; QString imageUrl; QString jamendoUrl; while( !m_reader.atEnd() ) { m_reader.readNext(); if( m_reader.isEndElement() && m_reader.name() == "artist" ) break; if( m_reader.isStartElement() ) { QStringRef localname = m_reader.name(); if( localname == "id" ) m_currentArtistId = m_reader.readElementText().toInt(); else if ( localname == "name" ) name = m_reader.readElementText(); else if( localname == "url" ) jamendoUrl = m_reader.readElementText(); else if( localname == "image" ) imageUrl = m_reader.readElementText(); else if( localname == "album" ) readAlbum(); } } JamendoArtist currentArtist( name ); currentArtist.setDescription( description ); currentArtist.setId( m_currentArtistId ); currentArtist.setPhotoURL( imageUrl ); currentArtist.setJamendoURL( jamendoUrl ); m_dbHandler->insertArtist( ¤tArtist ); countTransaction(); // debug() << " Name: " << currentArtist.name(); // debug() << " Id: " << currentArtist.id(); // debug() << " Photo: " << currentArtist.photoURL(); // debug() << " J_url: " << currentArtist.jamendoURL(); // debug() << " H_url: " << currentArtist.homeURL(); // debug() << " Decription: " << currentArtist.description(); } void JamendoXmlParser::readAlbum() { if( m_aborted ) return; Q_ASSERT( m_reader.isStartElement() && m_reader.name() == "album" ); //debug() << "Found album: "; QString name; QString genre; QString description; QStringList tags; QString coverUrl; QString releaseDate; while( !m_reader.atEnd() ) { m_reader.readNext(); if( m_reader.isEndElement() && m_reader.name() == "album" ) break; if( m_reader.isStartElement() ) { QStringRef localname = m_reader.name(); if( localname == "id" ) m_currentAlbumId = m_reader.readElementText().toInt(); else if ( localname == "name" ) name = m_reader.readElementText(); else if( localname == "id3genre" ) genre = m_id3GenreHash.value( m_reader.readElementText().toInt() ); else if( localname == "releasedate" ) releaseDate = m_reader.readElementText(); else if( localname == "track" ) readTrack(); // else if ( currentChildElement.tagName() == "description" ) // description = currentChildElement.text(); //we use tags instad of genres for creating genres in the database, as the //Jamendo.com genres are messy at best // else if ( currentChildElement.tagName() == "tags" ) // tags = currentChildElement.text().split(' ', QString::SkipEmptyParts); // n = n.nextSibling(); } } //We really do not like albums with no genres, makes the service freeze, so simply ignore this. if( !genre.isEmpty() && genre != "Unknown" ) { m_nNumberOfAlbums++; JamendoAlbum currentAlbum( name ); currentAlbum.setGenre( genre ); currentAlbum.setDescription( description ); currentAlbum.setId( m_currentAlbumId ); currentAlbum.setArtistId( m_currentArtistId ); currentAlbum.setLaunchYear( releaseDate.left( 4 ).toInt() ); currentAlbum.setCoverUrl( COVERURL_BASE.arg( m_currentAlbumId ) ); m_albumArtistMap.insert( currentAlbum.id(), currentAlbum.artistId() ); int newId = m_dbHandler->insertAlbum( ¤tAlbum ); countTransaction(); //debug() << "inserting genre with album_id = " << newId << " and name = " << genreName; ServiceGenre currentGenre( genre ); currentGenre.setAlbumId( newId ); m_dbHandler->insertGenre( ¤tGenre ); countTransaction(); } } void JamendoXmlParser::readTrack() { if( m_aborted ) return; Q_ASSERT( m_reader.isStartElement() && m_reader.name() == "track" ); //debug() << "Found track: "; m_nNumberOfTracks++; QString name; QString id; qint64 length = 0LL; QString trackNumber; QString genre; while( !m_reader.atEnd() ) { m_reader.readNext(); if( m_reader.isEndElement() && m_reader.name() == "track" ) break; if( m_reader.isStartElement() ) { QStringRef localname = m_reader.name(); if( localname == "name" ) name = m_reader.readElementText(); else if( localname == "id" ) id = m_reader.readElementText(); else if( localname == "duration" ) length = m_reader.readElementText().toFloat() * 1000; else if ( localname == "numalbum" ) trackNumber = m_reader.readElementText(); else if ( localname == "id3genre" ) genre = m_id3GenreHash.value( m_reader.readElementText().toInt() ); } } static const QString previewUrl = "http://api.jamendo.com/get2/stream/track/redirect/?id=%1&streamencoding=mp32"; JamendoTrack currentTrack( name ); currentTrack.setId( id.toInt() ); currentTrack.setUidUrl( previewUrl.arg( id ) ); currentTrack.setAlbumId( m_currentAlbumId ); currentTrack.setArtistId( m_currentArtistId ); currentTrack.setLength( length ); currentTrack.setTrackNumber( trackNumber.toInt() ); currentTrack.setGenre( genre ); if( m_albumArtistMap.contains( currentTrack.albumId() ) ) currentTrack.setArtistId( m_albumArtistMap.value( currentTrack.albumId() ) ); // debug() << "inserting track with artist id: " << currentTrack.artistId(); m_dbHandler->insertTrack( ¤tTrack ); countTransaction(); } void JamendoXmlParser::countTransaction() { n_numberOfTransactions++; if ( n_numberOfTransactions >= n_maxNumberOfTransactions ) { m_dbHandler->commit(); m_dbHandler->begin(); n_numberOfTransactions = 0; } } void JamendoXmlParser::requestAbort() { m_aborted = true; } diff --git a/src/services/jamendo/JamendoXmlParser.h b/src/services/jamendo/JamendoXmlParser.h index 8e93b2b4af..371be18e6b 100644 --- a/src/services/jamendo/JamendoXmlParser.h +++ b/src/services/jamendo/JamendoXmlParser.h @@ -1,131 +1,128 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef JAMENDOXMLPARSER_H #define JAMENDOXMLPARSER_H #include "JamendoDatabaseHandler.h" #include #include #include #include #include #include /** * Parser for the XML file from http://imgjam.com/data/dbdump_artistalbumtrack.xml.gz * * @author Nikolaj Hald Nielsen */ class JamendoXmlParser : public QObject, public ThreadWeaver::Job { Q_OBJECT public: /** * Constructor * @param fileName The file to parse - * @return Pointer to new object */ JamendoXmlParser( const QString &fileName ); /** * The function that starts the actual work. Inherited from ThreadWeaver::Job * Note the work is performed in a separate thread - * @return Returns true on success and false on failure */ void run(ThreadWeaver::JobPointer self = QSharedPointer(), ThreadWeaver::Thread *thread = 0) Q_DECL_OVERRIDE; /** * Destructor - * @return none */ ~JamendoXmlParser(); /** * Reads, and starts parsing, file. Should not be used directly. * @param filename The file to read */ void readConfigFile( const QString &filename ); virtual void requestAbort (); Q_SIGNALS: /** This signal is emitted when this job is being processed by a thread. */ void started(ThreadWeaver::JobPointer); /** This signal is emitted when the job has been finished (no matter if it succeeded or not). */ void done(ThreadWeaver::JobPointer); /** This job has failed. * This signal is emitted when success() returns false after the job is executed. */ void failed(ThreadWeaver::JobPointer); /** * Signal emitted when parsing is complete. */ void doneParsing(); private Q_SLOTS: /** * Called when the job has completed. Is executed in the GUI thread */ void completeJob(); private: JamendoDatabaseHandler * m_dbHandler; QXmlStreamReader m_reader; QString m_sFileName; QMap albumTags; //used for applying genres to individual tracks int m_nNumberOfTracks; int m_nNumberOfAlbums; int m_nNumberOfArtists; /** * Read a DOM element representing an artist */ void readArtist(); /** * Read a DOM element representing an album */ void readAlbum(); /** * Read a DOM element representing a track */ void readTrack(); void countTransaction(); int m_currentArtistId; int m_currentAlbumId; int n_numberOfTransactions; int n_maxNumberOfTransactions; QHash< int, QString > m_id3GenreHash; QMap m_albumArtistMap; bool m_aborted; protected: void defaultBegin(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; void defaultEnd(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; }; #endif diff --git a/src/services/jamendo/amarok_service_jamendo.desktop b/src/services/jamendo/amarok_service_jamendo.desktop index d2ce510c8f..ded4673a38 100644 --- a/src/services/jamendo/amarok_service_jamendo.desktop +++ b/src/services/jamendo/amarok_service_jamendo.desktop @@ -1,130 +1,129 @@ [Desktop Entry] Type=Service -ServiceTypes=KPluginInfo Icon=view-services-jamendo-amarok Name=Jamendo Name[bg]=Jamendo Name[bs]=Jamendo Name[ca]=Jamendo Name[ca@valencia]=Jamendo Name[cs]=Jamendo Name[csb]=Jamendo Name[da]=Jamendo Name[de]=Jamendo Name[el]=Jamendo Name[en_GB]=Jamendo Name[eo]=Jamendo Name[es]=Jamendo Name[et]=Jamendo Name[eu]=Jamendo Name[fi]=Jamendo Name[fr]=Jamendo Name[ga]=Jamendo Name[gl]=Jamendo Name[hu]=Jamendo Name[ia]=Jamendo Name[id]=Jamendo Name[is]=Jamendo Name[it]=Jamendo Name[ja]=Jamendo Name[km]=Jamendo Name[ko]=Jamendo Name[lt]=Jamendo Name[lv]=Jamendo Name[mr]=जामेंदो Name[nb]=Jamendo Name[nds]=Jamendo Name[nl]=Jamendo Name[nn]=Jamendo Name[pa]=ਜਾਮੀਂਡੋ Name[pl]=Jamendo Name[pt]=Jamendo Name[pt_BR]=Jamendo Name[ro]=Jamendo Name[ru]=Jamendo Name[sk]=Jamendo Name[sl]=Jamendo Name[sq]=Jamendo Name[sr]=Џамендо Name[sr@ijekavian]=Џамендо Name[sr@ijekavianlatin]=Jamendo Name[sr@latin]=Jamendo Name[sv]=Jamendo Name[th]=บริการ Jamendo Name[tr]=Jamendo Name[ug]=Jamendo Name[uk]=Jamendo Name[wa]=Jamendo Name[x-test]=xxJamendoxx Name[zh_CN]=Jamendo Name[zh_TW]=Jamendo Comment=Listen to and download music uploaded by independent artists Comment[bg]=Слушане и сваляне на музика от независими изпълнители Comment[bs]=Slušajte i preuzimajte muziku nezavisnih izvođača Comment[ca]=Escolta i baixa música pujada per artistes independents Comment[ca@valencia]=Escolta i baixa música pujada per artistes independents Comment[cs]=Poslouchejte a stahujte hudbu nahranou nezávislými umělci Comment[da]=Lyt til og download musik som er uploadet af uafhængige kunstnere Comment[de]=Musik unabhängiger Interpreten hören und herunterladen Comment[el]=Ακούστε και κατεβάστε μουσική ανεξάρτητων καλλιτεχνών Comment[en_GB]=Listen to and download music uploaded by independent artists Comment[es]=Escuchar y descargar música enviada por artistas independientes Comment[et]=Sõltumatute esitajate muusika kuulamine ja allalaadimine Comment[eu]=Entzun eta deskargatu musikari independenteek igotako musika Comment[fi]=Kuuntele ja lataa itsenäisten artistien lähettämää musiikkia Comment[fr]=Écouter et télécharger de la musique mise à disposition par des artistes indépendants Comment[ga]=Éist le ceol uasluchtaithe ag ealaíontóirí neamhspleácha agus íosluchtaigh é freisin Comment[gl]=Escoite e descargue música enviada por artistas independentes Comment[he]=האזנה והורדת מוזיקה שהועלתה על ידי יוצרים עצמאיים Comment[hu]=Független művészek által feltöltött zene hallgatása és letöltése Comment[id]=Mendengar dan juga mengunduh musik yang diunggah oleh artis sendiri Comment[is]=Hlusta á og hala niður tónlist frá sjálfstæðum óhaðum listamönnum Comment[it]=Ascoltare e scaricare musica inviata da artisti indipendenti Comment[ja]=無所属のアーティストによってアップロードされた音楽を聴いたりダウンロードしたりできます Comment[km]=ស្ដាប់ និង​ទាញ​យក​តន្ត្រី​ដែល​បាន​ផ្ទុក​ឡើង​ដោយ​សិល្បករ​ឯករាជ្យ Comment[ko]=독립된 가수가 만든 음악을 듣고 내려받기 Comment[ku]=Muzîkên, alî yê hunermendê azad ve hatine bar kirin, guhdarî bike û bi daxe Comment[lt]=Klausyti ir atsisiųsti muziką, atsiųstą nepriklausomų atlikėjų Comment[lv]=Klausieties un lejupielādējiet mūziku, kuru augšupielādē neatkarīgie mākslinieki Comment[nb]=Lytt til og last ned musikk som er lastet opp av uavhengige artister Comment[nds]=Musik anhören un daalladen, de nich afhangig Künstlers hoochlaadt hebbt Comment[nl]=Luister naar en download muziek die door onafhankelijke artiesten is vrijgegeven Comment[nn]=Høyr på og last ned musikk lasta opp av uavhengige artistar Comment[pl]=Słuchaj i pobieraj muzykę udostępnioną przez niezależnych wykonawców Comment[pt]=Escutar e transferir as músicas enviadas por artistas independentes Comment[pt_BR]=Ouça e baixe músicas enviadas por artistas independentes Comment[ro]=Ascultă și descarcă muzică încărcată de artiști independenți Comment[ru]=Прослушивание и загрузка музыки, предоставленной независимыми исполнителями Comment[sk]=Počúvať a sťahovať hudbu nahranú nezávislými interpretmi Comment[sl]=Poslušajte in prejmite glasbo, ki so jo ustvarili neodvisni glasbeniki Comment[sr]=Слушајте и преузимајте музику независних извођача Comment[sr@ijekavian]=Слушајте и преузимајте музику независних извођача Comment[sr@ijekavianlatin]=Slušajte i preuzimajte muziku nezavisnih izvođača Comment[sr@latin]=Slušajte i preuzimajte muziku nezavisnih izvođača Comment[sv]=Lyssna på och ladda ner musik uppladdad av oberoende artister Comment[th]=ฟังและดาวน์โหลดดนตรีที่ถูกอัปโหลดจากศิลปินอิสระต่าง ๆ Comment[tr]=Bağımsız sanatçılar tarafından yüklenmiş müzikleri dinleyin ve indirin Comment[uk]=Прослухайте і отримайте музику, записану незалежними виконавцями Comment[wa]=Schoûter eyet aberweter del muzike eberwetêye pa des årtisses dislaxhîs Comment[x-test]=xxListen to and download music uploaded by independent artistsxx Comment[zh_CN]=收听并下载由独立艺术家上传的音乐 Comment[zh_TW]=聆聽並下載由獨立藝人所上傳的音樂 ServiceTypes=Amarok/Plugin X-KDE-Amarok-authors=Nikolaj Hald Nielsen X-KDE-Amarok-email=nhnFreespirit@gmail.com X-KDE-Amarok-framework-version=73 X-KDE-Amarok-name=JamendoService X-KDE-Amarok-rank=100 X-KDE-Amarok-version=1 X-KDE-PluginInfo-Author=Nikolaj Hald Nielsen X-KDE-PluginInfo-Email=nhnFreespirit@gmail.com X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Category=Service X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-KDE-Library=amarok_service_jamendo X-KDE-PluginInfo-Name=amarok_service_jamendo diff --git a/src/services/lastfm/AvatarDownloader.h b/src/services/lastfm/AvatarDownloader.h index 32aa9359c3..1d4d518adb 100644 --- a/src/services/lastfm/AvatarDownloader.h +++ b/src/services/lastfm/AvatarDownloader.h @@ -1,59 +1,61 @@ /**************************************************************************************** * Copyright (c) 2008 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef AVATAR_DOWNLOADER_H #define AVATAR_DOWNLOADER_H #include "network/NetworkAccessManagerProxy.h" #include #include +#include class AvatarDownloader : public QObject { Q_OBJECT public: /** * Constructor. */ AvatarDownloader(); /** * Destructor. */ ~AvatarDownloader(); /** * Start the download * @param url The url that should be downloaded. */ void downloadAvatar( const QString& username, const QUrl &url ); Q_SIGNALS: void avatarDownloaded( const QString &username, QPixmap avatar ); private Q_SLOTS: /** * Slot called when the network access manager finished a request */ void downloaded( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ); private: QHash m_userAvatarUrls; }; + #endif diff --git a/src/services/lastfm/CMakeLists.txt b/src/services/lastfm/CMakeLists.txt index 22c249775a..ece5f206f2 100644 --- a/src/services/lastfm/CMakeLists.txt +++ b/src/services/lastfm/CMakeLists.txt @@ -1,80 +1,93 @@ include_directories( ${LIBLASTFM_INCLUDE_DIR} ) add_subdirectory( images ) ########### next target ############### -set( amarok_service_lastfm_shared_SRCS +find_package( KF5 COMPONENTS Wallet REQUIRED ) + +set( amarok_service_lastfm_config_SRCS LastFmServiceConfig.cpp ) -add_library( amarok_service_lastfm_shared SHARED ${amarok_service_lastfm_shared_SRCS} ) -target_link_libraries( amarok_service_lastfm_shared + +add_library( amarok_service_lastfm_config SHARED ${amarok_service_lastfm_config_SRCS} ) + +target_link_libraries( amarok_service_lastfm_config amarokcore - KF5::KDELibs4Support - + KF5::Wallet ) -install( TARGETS amarok_service_lastfm_shared ${INSTALL_TARGETS_DEFAULT_ARGS} ) + +install( TARGETS amarok_service_lastfm_config ${INSTALL_TARGETS_DEFAULT_ARGS} ) ########### next target ############### set( amarok_service_lastfm_PART_SRCS LastFmService.cpp LastFmServiceCollection.cpp ScrobblerAdapter.cpp SynchronizationAdapter.cpp SynchronizationTrack.cpp LastFmTreeModel.cpp LastFmTreeView.cpp AvatarDownloader.cpp meta/LastFmMeta.cpp meta/LastFmMultiPlayableCapability.cpp meta/LastFmStreamInfoCapability.cpp biases/LastFmBias.cpp biases/WeeklyTopBias.cpp SimilarArtistsAction.cpp LoveTrackAction.cpp ) + add_library(amarok_service_lastfm MODULE ${amarok_service_lastfm_PART_SRCS} ) + target_link_libraries( amarok_service_lastfm - amarok_service_lastfm_shared + amarok_service_lastfm_config amarokcore amaroklib amarokpud ${LIBLASTFM_LIBRARY} - KF5::KDELibs4Support - KF5::KIOCore - ${KDE4_SOLID_LIBRARY} KF5::ThreadWeaver + KF5::Wallet Qt5::Network ) + install( TARGETS amarok_service_lastfm DESTINATION ${PLUGIN_INSTALL_DIR} ) +kcoreaddons_desktop_to_json( amarok_service_lastfm amarok_service_lastfm.desktop SERVICE_TYPES ${CMAKE_SOURCE_DIR}/src/amarok-plugin.desktop ) + ########### next target ############### set(kcm_amarok_service_lastfm_PART_SRCS LastFmServiceSettings.cpp ) + ki18n_wrap_ui( kcm_amarok_service_lastfm_PART_SRCS LastFmConfigWidget.ui ) + add_library(kcm_amarok_service_lastfm MODULE ${kcm_amarok_service_lastfm_PART_SRCS} ) + target_link_libraries( kcm_amarok_service_lastfm - amarok_service_lastfm_shared + amarok_service_lastfm_config amarokcore amaroklib ${LIBLASTFM_LIBRARY} - KF5::KIOCore - Qt5::Network ) + Qt5::Network +) + install( TARGETS kcm_amarok_service_lastfm DESTINATION ${PLUGIN_INSTALL_DIR} ) +kcoreaddons_desktop_to_json( kcm_amarok_service_lastfm amarok_service_lastfm_config.desktop SERVICE_TYPES kcmodule.desktop ) + ########### install files ############### -install( FILES amarok_service_lastfm.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) -install( FILES amarok_service_lastfm_config.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) -install( FILES amaroklastfm.protocol DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES amarok_service_lastfm.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) +install( FILES amarok_service_lastfm_config.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) +install( FILES amaroklastfm.protocol DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) diff --git a/src/services/lastfm/LastFmConfigWidget.ui b/src/services/lastfm/LastFmConfigWidget.ui index 4eda6867b0..3a942e5b18 100644 --- a/src/services/lastfm/LastFmConfigWidget.ui +++ b/src/services/lastfm/LastFmConfigWidget.ui @@ -1,232 +1,232 @@ LastFmConfigWidget 0 0 450 460 1 1 0 Last.fm Profile &Username: false kcfg_ScrobblerUsername &Password: false kcfg_ScrobblerPassword - + QLineEdit::Password - + 0 0 <a href="http://www.last.fm:80/signup.php">Sign up to Last.fm</a> true true &Test Login Why not join the <a href="http://www.last.fm:80/group/Amarok+Users">Amarok Last.fm group</a> and share your musical tastes with other Amarok users? true true Qt::Vertical Last.fm Services false &Submit tracks true &Retrieve similar artists false &Use composer data if available in Last.fm as artist false Use Last.fm tags like <b>7 of 10 stars</b> to represent your Amarok ratings during statistics synchronization Use fancy tags to represent ratings <html>When scrobbling tracks to Last.fm, it autocorrects common errors in track tags like artist, album and title. Check this to be notified when this happens and what the autocorrection was</html> Announce correction suggestions for track tags <html>Check if you want certain tracks neither to be scrobbled nor to be updated to Last.fm now playing</html> Do not scrobble tracks with label: <html>Select a preferred label (or write a new one). Tracks with this label will not be scrobbled</html> true QComboBox::InsertAlphabetically Qt::Horizontal - KLineEdit + QLineEdit QLineEdit -
klineedit.h
+
qlineedit.h
kcfg_ScrobblerUsername kcfg_ScrobblerPassword testLogin kcfg_SubmitPlayedSongs kcfg_RetrieveSimilarArtists - klineedit.h + qlineedit.h
diff --git a/src/services/lastfm/LastFmService.cpp b/src/services/lastfm/LastFmService.cpp index 10bfb30f15..78219d0dd0 100644 --- a/src/services/lastfm/LastFmService.cpp +++ b/src/services/lastfm/LastFmService.cpp @@ -1,533 +1,533 @@ /**************************************************************************************** * Copyright (c) 2007 Shane King * * Copyright (c) 2008 Leo Franchi * * Copyright (c) 2009 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "LastFmService" #include "core/support/Debug.h" #include "LastFmService.h" #include "AvatarDownloader.h" #include "EngineController.h" #include "biases/LastFmBias.h" #include "biases/WeeklyTopBias.h" #include "LastFmServiceCollection.h" #include "LastFmServiceConfig.h" #include "LoveTrackAction.h" #include "SimilarArtistsAction.h" #include "LastFmTreeModel.h" #include "LastFmTreeView.h" #include "ScrobblerAdapter.h" #include "GlobalCurrentTrackActions.h" #include "core/support/Components.h" #include "core/interfaces/Logger.h" #include "meta/LastFmMeta.h" #include "SynchronizationAdapter.h" #include "statsyncing/Controller.h" #include "widgets/SearchWidget.h" -#include -#include +#include +#include #include #include #include #include -#include //Qt::escape +#include +#include #include #include QString md5( const QByteArray& src ) { QByteArray const digest = QCryptographicHash::hash( src, QCryptographicHash::Md5 ); return QString::fromLatin1( digest.toHex() ).rightJustified( 32, '0' ); } LastFmServiceFactory::LastFmServiceFactory() : ServiceFactory() {} void LastFmServiceFactory::init() { ServiceBase *service = new LastFmService( this, "Last.fm" ); m_initialized = true; emit newService( service ); } QString LastFmServiceFactory::name() { return "Last.fm"; } KConfigGroup LastFmServiceFactory::config() { return Amarok::config( LastFmServiceConfig::configSectionName() ); } bool LastFmServiceFactory::possiblyContainsTrack( const QUrl &url ) const { return url.scheme() == "lastfm"; } LastFmService::LastFmService( LastFmServiceFactory *parent, const QString &name ) : ServiceBase( name, parent, false ) , m_collection( 0 ) , m_polished( false ) , m_avatarLabel( 0 ) , m_profile( 0 ) , m_userinfo( 0 ) , m_subscriber( false ) , m_authenticateReply( 0 ) , m_config( LastFmServiceConfig::instance() ) { DEBUG_BLOCK setShortDescription( i18n( "Last.fm: The social music revolution" ) ); setIcon( QIcon::fromTheme( "view-services-lastfm-amarok" ) ); setLongDescription( i18n( "Last.fm is a popular online service that provides personal radio stations and music recommendations. A personal listening station is tailored based on your listening habits and provides you with recommendations for new music. It is also possible to play stations with music that is similar to a particular artist as well as listen to streams from people you have added as friends or that Last.fm considers your musical \"neighbors\"" ) ); - setImagePath( KStandardDirs::locate( "data", "amarok/images/hover_info_lastfm.png" ) ); + setImagePath( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/hover_info_lastfm.png" ) ); //We have no use for searching currently.. m_searchWidget->setVisible( false ); // set the global static Lastfm::Ws stuff lastfm::ws::ApiKey = Amarok::lastfmApiKey(); lastfm::ws::SharedSecret = Amarok::lastfmApiSharedSecret(); // set the nam TWICE. Yes. It prevents liblastfm from deleting it, see their code lastfm::setNetworkAccessManager( The::networkAccessManager() ); lastfm::setNetworkAccessManager( The::networkAccessManager() ); // enable custom bias m_biasFactories << new Dynamic::LastFmBiasFactory(); Dynamic::BiasFactory::instance()->registerNewBiasFactory( m_biasFactories.last() ); m_biasFactories << new Dynamic::WeeklyTopBiasFactory(); Dynamic::BiasFactory::instance()->registerNewBiasFactory( m_biasFactories.last() ); // add the "play similar artists" action to all artist The::globalCollectionActions()->addArtistAction( new SimilarArtistsAction( this ) ); The::globalCollectionActions()->addTrackAction( new LoveTrackAction( this ) ); QAction *loveAction = new QAction( QIcon::fromTheme( "love-amarok" ), i18n( "Last.fm: Love" ), this ); - connect( loveAction, SIGNAL(triggered()), this, SLOT(love()) ); + connect( loveAction, &QAction::triggered, this, &LastFmService::loveCurrentTrack ); loveAction->setShortcut( i18n( "Ctrl+L" ) ); The::globalCurrentTrackActions()->addAction( loveAction ); - connect( m_config.data(), SIGNAL(updated()), this, SLOT(slotReconfigure()) ); - QTimer::singleShot(0, this, SLOT(slotReconfigure())); // call reconfigure but only after constructor is finished (because it might call virtual methods) + connect( m_config.data(), &LastFmServiceConfig::updated, this, &LastFmService::slotReconfigure ); + QTimer::singleShot(0, this, &LastFmService::slotReconfigure); // call reconfigure but only after constructor is finished (because it might call virtual methods) } LastFmService::~LastFmService() { DEBUG_BLOCK using namespace Dynamic; QMutableListIterator it( m_biasFactories ); while( it.hasNext() ) { AbstractBiasFactory *factory = it.next(); it.remove(); BiasFactory::instance()->removeBiasFactory( factory ); delete factory; } if( m_collection ) { CollectionManager::instance()->removeTrackProvider( m_collection ); m_collection->deleteLater(); m_collection = 0; } StatSyncing::Controller *controller = Amarok::Components::statSyncingController(); if( m_scrobbler && controller ) controller->unregisterScrobblingService( StatSyncing::ScrobblingServicePtr( m_scrobbler.data() ) ); if( m_synchronizationAdapter && controller ) controller->unregisterProvider( m_synchronizationAdapter ); } void LastFmService::slotReconfigure() { lastfm::ws::Username = m_config->username(); bool ready = !m_config->username().isEmpty(); // core features require just username /* create ServiceCollection only once the username is known (remember, getting * username from KWallet is async! */ if( !m_collection && ready ) { m_collection = new Collections::LastFmServiceCollection( m_config->username() ); CollectionManager::instance()->addTrackProvider( m_collection ); } // create Model once the username is known, it depends on it implicitly if( !model() && ready ) { setModel( new LastFmTreeModel( this ) ); } setServiceReady( ready ); // emits ready(), which needs to be done *after* creating collection // now authenticate w/ last.fm and get our session key if we don't have one if( !m_config->sessionKey().isEmpty() ) { debug() << __PRETTY_FUNCTION__ << "using saved session key for last.fm"; continueReconfiguring(); } else if( !m_config->username().isEmpty() && !m_config->password().isEmpty() ) { debug() << __PRETTY_FUNCTION__ << "got no saved session key, authenticating with last.fm"; // discard any possible ongoing auth connections if( m_authenticateReply ) { - disconnect( m_authenticateReply, SIGNAL(finished()), this, SLOT(onAuthenticated()) ); + disconnect( m_authenticateReply, &QNetworkReply::finished, this, &LastFmService::onAuthenticated ); m_authenticateReply->abort(); m_authenticateReply->deleteLater(); m_authenticateReply = 0; } const QString authToken = md5( QString( "%1%2" ).arg( m_config->username(), md5( m_config->password().toUtf8() ) ).toUtf8() ); QMap query; query[ "method" ] = "auth.getMobileSession"; query[ "username" ] = m_config->username(); query[ "authToken" ] = authToken; m_authenticateReply = lastfm::ws::post( query ); - connect( m_authenticateReply, SIGNAL(finished()), this, SLOT(onAuthenticated()) ); // calls continueReconfiguring() + connect( m_authenticateReply, &QNetworkReply::finished, this, &LastFmService::onAuthenticated ); // calls continueReconfiguring() } else { debug() << __PRETTY_FUNCTION__ << "either last.fm username or password is empty"; continueReconfiguring(); } } void LastFmService::continueReconfiguring() { StatSyncing::Controller *controller = Amarok::Components::statSyncingController(); Q_ASSERT( controller ); lastfm::ws::SessionKey = m_config->sessionKey(); // we also check username, KWallet may deliver it really late, but we need it bool authenticated = serviceReady() && !m_config->sessionKey().isEmpty(); if( m_scrobbler && (!authenticated || !m_config->scrobble()) ) { debug() << __PRETTY_FUNCTION__ << "unregistering and destorying ScrobblerAdapter"; controller->unregisterScrobblingService( StatSyncing::ScrobblingServicePtr( m_scrobbler.data() ) ); m_scrobbler.clear(); } else if( !m_scrobbler && authenticated && m_config->scrobble() ) { debug() << __PRETTY_FUNCTION__ << "creating and registering ScrobblerAdapter"; m_scrobbler = QSharedPointer( new ScrobblerAdapter( "Amarok", m_config ) ); controller->registerScrobblingService( StatSyncing::ScrobblingServicePtr( m_scrobbler.data() ) ); } if( m_synchronizationAdapter && !authenticated ) { debug() << __PRETTY_FUNCTION__ << "unregistering and destorying SynchronizationAdapter"; controller->unregisterProvider( m_synchronizationAdapter ); m_synchronizationAdapter = 0; } else if( !m_synchronizationAdapter && authenticated ) { debug() << __PRETTY_FUNCTION__ << "creating and registering SynchronizationAdapter"; m_synchronizationAdapter = StatSyncing::ProviderPtr( new SynchronizationAdapter( m_config ) ); controller->registerProvider( m_synchronizationAdapter ); } // update possibly changed user info QNetworkReply *reply = lastfm::User::getInfo(); - connect( reply, SIGNAL(finished()), SLOT(onGetUserInfo()) ); + connect( reply, &QNetworkReply::finished, this, &LastFmService::onGetUserInfo ); } void LastFmService::onAuthenticated() { if( !m_authenticateReply ) warning() << __PRETTY_FUNCTION__ << "null reply!"; else m_authenticateReply->deleteLater(); /* temporarily disconnect form config updates to prevent calling * slotReconfigure() for the second time. */ - disconnect( m_config.data(), SIGNAL(updated()), this, SLOT(slotReconfigure()) ); + disconnect( m_config.data(), &LastFmServiceConfig::updated, this, &LastFmService::slotReconfigure ); switch( m_authenticateReply ? m_authenticateReply->error() : QNetworkReply::UnknownNetworkError ) { case QNetworkReply::NoError: { lastfm::XmlQuery lfm; if( !lfm.parse( m_authenticateReply->readAll() ) || lfm.children( "error" ).size() > 0 ) { debug() << "error from authenticating with last.fm service:" << lfm.text(); m_config->setSessionKey( QString() ); m_config->save(); break; } m_config->setSessionKey( lfm[ "session" ][ "key" ].text() ); m_config->save(); break; } case QNetworkReply::AuthenticationRequiredError: Amarok::Components::logger()->longMessage( i18nc("Last.fm: errorMessage", "Either the username was not recognized, or the password was incorrect." ) ); break; default: Amarok::Components::logger()->longMessage( i18nc("Last.fm: errorMessage", "There was a problem communicating with the Last.fm services. Please try again later." ) ); break; } m_authenticateReply = 0; // connect back to config updates - connect( m_config.data(), SIGNAL(updated()), this, SLOT(slotReconfigure()) ); + connect( m_config.data(), &LastFmServiceConfig::updated, this, &LastFmService::slotReconfigure ); continueReconfiguring(); } void LastFmService::onGetUserInfo() { QNetworkReply *reply = qobject_cast( sender() ); if( !reply ) warning() << __PRETTY_FUNCTION__ << "null reply!"; else reply->deleteLater(); switch( reply ? reply->error() : QNetworkReply::UnknownNetworkError ) { case QNetworkReply::NoError: { lastfm::XmlQuery lfm; if( lfm.parse( reply->readAll() ) ) { m_country = lfm["user"]["country"].text(); m_age = lfm["user"]["age"].text(); m_gender = lfm["user"]["gender"].text(); m_playcount = lfm["user"]["playcount"].text(); m_subscriber = lfm["user"]["subscriber"].text() == "1"; debug() << "profile info " << m_country << " " << m_age << " " << m_gender << " " << m_playcount << " " << m_subscriber; if( !lfm["user"][ "image" ].text().isEmpty() ) { debug() << "profile avatar: " <downloadAvatar( m_config->username(), url); - connect( downloader, SIGNAL(avatarDownloaded(QString,QPixmap)), - SLOT(onAvatarDownloaded(QString,QPixmap)) ); + connect( downloader, &AvatarDownloader::avatarDownloaded, + this, &LastFmService::onAvatarDownloaded ); } updateProfileInfo(); } else debug() << "Got exception in parsing from last.fm:" << lfm.parseError().message(); break; } case QNetworkReply::AuthenticationRequiredError: debug() << "Last.fm: errorMessage: Sorry, we don't recognise that username, or you typed the password incorrectly."; break; default: debug() << "Last.fm: errorMessage: There was a problem communicating with the Last.fm services. Please try again later."; break; } } void LastFmService::onAvatarDownloaded( const QString &username, QPixmap avatar ) { DEBUG_BLOCK sender()->deleteLater(); if( username == m_config->username() && !avatar.isNull() ) { LastFmTreeModel* lfm = dynamic_cast( model() ); if( !lfm ) return; int m = lfm->avatarSize(); avatar = avatar.scaled( m, m, Qt::KeepAspectRatio, Qt::SmoothTransformation ); lfm->prepareAvatar( avatar, m ); m_avatar = avatar; if( m_avatarLabel ) m_avatarLabel->setPixmap( m_avatar ); } } void LastFmService::updateEditHint( int index ) { if( !m_customStationEdit ) return; QString hint; switch ( index ) { case 0: hint = i18n( "Enter an artist name" ); break; case 1: hint = i18n( "Enter a tag" ); break; case 2: hint = i18n( "Enter a Last.fm user name" ); break; default: return; } - m_customStationEdit->setClickMessage( hint ); + m_customStationEdit->setPlaceholderText( hint ); } void LastFmService::updateProfileInfo() { if( m_userinfo ) { - m_userinfo->setText( i18n( "Username: %1", Qt::escape( m_config->username() ) ) ); + m_userinfo->setText( i18n( "Username: %1", m_config->username().toHtmlEscaped() ) ); } if( m_profile && !m_playcount.isEmpty() ) { m_profile->setText( i18np( "Play Count: %1 play", "Play Count: %1 plays", m_playcount.toInt() ) ); } } void LastFmService::polish() { if( !m_polished ) { LastFmTreeView* view = new LastFmTreeView( this ); view->setFrameShape( QFrame::NoFrame ); view->setDragEnabled ( true ); view->setSortingEnabled( false ); view->setDragDropMode ( QAbstractItemView::DragOnly ); setView( view ); //m_bottomPanel->setMaximumHeight( 300 ); m_bottomPanel->hide(); m_topPanel->setMaximumHeight( 300 ); - KHBox * outerProfilebox = new KHBox( m_topPanel ); - outerProfilebox->setSpacing(1); - outerProfilebox->setMargin(0); + BoxWidget * outerProfilebox = new BoxWidget( false, m_topPanel ); + outerProfilebox->layout()->setSpacing(1); m_avatarLabel = new QLabel(outerProfilebox); if( !m_avatar ) { int m = LastFmTreeModel::avatarSize(); m_avatarLabel->setPixmap( QIcon::fromTheme( "filename-artist-amarok" ).pixmap(m, m) ); m_avatarLabel->setFixedSize( m, m ); } else { m_avatarLabel->setPixmap( m_avatar ); m_avatarLabel->setFixedSize( m_avatar.width(), m_avatar.height() ); m_avatarLabel->setMargin( 5 ); } - KVBox * innerProfilebox = new KVBox( outerProfilebox ); - innerProfilebox->setSpacing(0); + BoxWidget * innerProfilebox = new BoxWidget( true, outerProfilebox ); innerProfilebox->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ); m_userinfo = new QLabel(innerProfilebox); m_userinfo->setText( m_config->username() ); m_profile = new QLabel(innerProfilebox); m_profile->setText(QString()); updateProfileInfo(); QGroupBox *customStation = new QGroupBox( i18n( "Create a Custom Last.fm Station" ), m_topPanel ); m_customStationCombo = new QComboBox; QStringList choices; choices << i18n( "Artist" ) << i18n( "Tag" ) << i18n( "User" ); m_customStationCombo->insertItems(0, choices); - m_customStationEdit = new KLineEdit; - m_customStationEdit->setClearButtonShown( true ); + m_customStationEdit = new QLineEdit; + m_customStationEdit->setClearButtonEnabled( true ); updateEditHint( m_customStationCombo->currentIndex() ); m_customStationButton = new QPushButton; m_customStationButton->setObjectName( "customButton" ); m_customStationButton->setIcon( QIcon::fromTheme( "media-playback-start-amarok" ) ); QHBoxLayout *hbox = new QHBoxLayout(); hbox->addWidget(m_customStationCombo); hbox->addWidget(m_customStationEdit); hbox->addWidget(m_customStationButton); customStation->setLayout(hbox); - connect( m_customStationEdit, SIGNAL(returnPressed()), this, SLOT(playCustomStation()) ); - connect( m_customStationButton, SIGNAL(clicked()), this, SLOT(playCustomStation()) ); - connect( m_customStationCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateEditHint(int))); + connect( m_customStationEdit, &QLineEdit::returnPressed, this, &LastFmService::playCustomStation ); + connect( m_customStationButton, &QPushButton::clicked, this, &LastFmService::playCustomStation ); + connect( m_customStationCombo, QOverload::of(&QComboBox::currentIndexChanged), + this, &LastFmService::updateEditHint); QList levels; levels << CategoryId::Genre << CategoryId::Album; m_polished = true; } } void -LastFmService::love() +LastFmService::loveCurrentTrack() { love( The::engineController()->currentTrack() ); } void LastFmService::love( Meta::TrackPtr track ) { if( m_scrobbler ) m_scrobbler->loveTrack( track ); } void LastFmService::playCustomStation() { DEBUG_BLOCK QString text = m_customStationEdit->text(); QString station; debug() << "Selected combo " <currentIndex(); switch ( m_customStationCombo->currentIndex() ) { case 0: station = "lastfm://artist/" + text + "/similarartists"; break; case 1: station = "lastfm://globaltags/" + text; break; case 2: station = "lastfm://user/" + text + "/personal"; break; default: return; } if ( !station.isEmpty() ) { - playLastFmStation( station ); + playLastFmStation( QUrl( station ) ); } } void LastFmService::playLastFmStation( const QUrl &url ) { Meta::TrackPtr track = CollectionManager::instance()->trackForUrl( url ); The::playlistController()->insertOptioned( track, Playlist::OnPlayMediaAction ); } Collections::Collection * LastFmService::collection() { return m_collection; } diff --git a/src/services/lastfm/LastFmService.h b/src/services/lastfm/LastFmService.h index 97fce17e8e..e96a76d8f5 100644 --- a/src/services/lastfm/LastFmService.h +++ b/src/services/lastfm/LastFmService.h @@ -1,111 +1,113 @@ /**************************************************************************************** * Copyright (c) 2007 Shane King * * Copyright (c) 2008 Leo Franchi * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef LASTFMSERVICE_H #define LASTFMSERVICE_H #include "services/ServiceBase.h" #include "services/lastfm/LastFmServiceConfig.h" #include "statsyncing/Provider.h" +#include namespace Collections { class LastFmServiceCollection; } namespace Dynamic { class AbstractBiasFactory; } class ScrobblerAdapter; -class KLineEdit; +class QLineEdit; class QComboBox; class QLabel; class QNetworkReply; +class QPixmap; class LastFmServiceFactory : public ServiceFactory { Q_PLUGIN_METADATA(IID AmarokPluginFactory_iid FILE "amarok_service_lastfm.json") Q_INTERFACES(Plugins::PluginFactory) Q_OBJECT public: LastFmServiceFactory(); virtual void init(); virtual QString name(); virtual KConfigGroup config(); virtual bool possiblyContainsTrack( const QUrl &url ) const; }; class LastFmService : public ServiceBase { Q_OBJECT public: LastFmService( LastFmServiceFactory* parent, const QString &name ); virtual ~LastFmService(); virtual void polish(); virtual Collections::Collection * collection(); void love( Meta::TrackPtr track ); private Q_SLOTS: - void love(); + void loveCurrentTrack(); void playCustomStation(); void updateEditHint( int index ); void slotReconfigure(); void onAuthenticated(); void onGetUserInfo(); void onAvatarDownloaded( const QString& username, QPixmap avatar ); private: void continueReconfiguring(); void playLastFmStation( const QUrl &url ); void updateProfileInfo(); QSharedPointer m_scrobbler; StatSyncing::ProviderPtr m_synchronizationAdapter; Collections::LastFmServiceCollection *m_collection; QList m_biasFactories; bool m_polished; QWidget *m_profileBox; QLabel *m_avatarLabel; QLabel *m_profile; QLabel *m_userinfo; QComboBox *m_globalComboBox; - KLineEdit *m_customStationEdit; + QLineEdit *m_customStationEdit; QPushButton *m_customStationButton; QComboBox *m_customStationCombo; QString m_station; QString m_age; QString m_gender; QString m_country; QString m_playcount; QPixmap m_avatar; bool m_subscriber; QNetworkReply *m_authenticateReply; LastFmServiceConfigPtr m_config; }; #endif // LASTFMSERVICE_H diff --git a/src/services/lastfm/LastFmServiceCollection.cpp b/src/services/lastfm/LastFmServiceCollection.cpp index a3ae8d1497..7e1deadc2a 100644 --- a/src/services/lastfm/LastFmServiceCollection.cpp +++ b/src/services/lastfm/LastFmServiceCollection.cpp @@ -1,325 +1,325 @@ /**************************************************************************************** * Copyright (c) 2008 Shane King * * Copyright (c) 2008 Nikolaj Hald Nielsen * * Copyright (c) 2008 Leo Franchi * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "lastfm" #include "LastFmServiceCollection.h" #include "meta/LastFmMeta.h" #include using namespace Collections; LastFmServiceCollection::LastFmServiceCollection( const QString &userName ) : ServiceCollection( 0, "last.fm", "last.fm" ) { DEBUG_BLOCK Meta::ServiceGenre * userStreams = new Meta::ServiceGenre( i18n( "%1's Streams", userName ) ); Meta::GenrePtr userStreamsPtr( userStreams ); addGenre( userStreamsPtr ); Meta::ServiceGenre * globalTags = new Meta::ServiceGenre( i18n( "Global Tags" ) ); Meta::GenrePtr globalTagsPtr( globalTags ); addGenre( globalTagsPtr ); m_neighborsLoved = new Meta::ServiceGenre( i18n( "Neighbors' Loved Radio" ) ); Meta::GenrePtr neighborsLovedPtr( m_neighborsLoved ); addGenre( neighborsLovedPtr ); m_neighborsPersonal = new Meta::ServiceGenre( i18n( "Neighbors' Personal Radio" ) ); Meta::GenrePtr neighborsPersonalPtr( m_neighborsPersonal ); addGenre( neighborsPersonalPtr ); m_friendsLoved = new Meta::ServiceGenre( i18n( "Friends' Loved Radio" ) ); Meta::GenrePtr friendsLovedPtr( m_friendsLoved ); addGenre( friendsLovedPtr ); m_friendsPersonal = new Meta::ServiceGenre( i18n( "Friends' Personal Radio" ) ); Meta::GenrePtr friendsPersonalPtr( m_friendsPersonal ); addGenre( friendsPersonalPtr ); // Only show these if the user is a subscriber. QStringList lastfmPersonal; lastfmPersonal << "personal" << "recommended" << "loved" << "neighbours"; foreach( const QString &station, lastfmPersonal ) { LastFm::Track * track = new LastFm::Track( "lastfm://user/" + userName + "/" + station ); Meta::TrackPtr trackPtr( track ); userStreams->addTrack( trackPtr ); addTrack( trackPtr ); } QStringList lastfmGenres; lastfmGenres << "Alternative" << "Ambient" << "Chill Out" << "Classical"<< "Dance" << "Electronica" << "Favorites" << "Gospel" << "Heavy Metal" << "Hip Hop" << "Indie Rock" << "Industrial" << "Japanese" << "Pop" << "Psytrance" << "Rap" << "Rock" << "Soundtrack" << "Techno" << "Trance"; foreach( const QString &genre, lastfmGenres ) { LastFm::Track * track = new LastFm::Track( "lastfm://globaltags/" + genre ); Meta::TrackPtr trackPtr( track ); globalTags->addTrack( trackPtr ); addTrack( trackPtr ); } QMap< QString, QString > params; params[ "method" ] = "user.getNeighbours"; params[ "user" ] = userName; m_jobs[ "user.getNeighbours" ] = lastfm::ws::post( params ); - connect( m_jobs[ "user.getNeighbours" ], SIGNAL(finished()), this, SLOT(slotAddNeighboursLoved()) ); - //connect( m_jobs[ "user.getNeighbours" ], SIGNAL(finished()), this, SLOT(slotAddNeighboursPersonal()) ); + connect( m_jobs[ "user.getNeighbours" ], &QNetworkReply::finished, this, &LastFmServiceCollection::slotAddNeighboursLoved ); + //connect( m_jobs[ "user.getNeighbours" ], &QNetworkReply::finished, this, &LastFmServiceCollection::slotAddNeighboursPersonal ); // TODO TMP HACK why do i get exceptions there...!? params[ "method" ] = "user.getFriends"; m_jobs[ "user.getFriends" ] = lastfm::ws::post( params ); - connect( m_jobs[ "user.getFriends" ], SIGNAL(finished()), this, SLOT(slotAddFriendsLoved()) ); - //connect( m_jobs[ "user.getFriends" ], SIGNAL(finished()), this, SLOT(slotAddFriendsPersonal()) ); + connect( m_jobs[ "user.getFriends" ], &QNetworkReply::finished, this, &LastFmServiceCollection::slotAddFriendsLoved ); + //connect( m_jobs[ "user.getFriends" ], &QNetworkReply::finished, this, &LastFmServiceCollection::slotAddFriendsPersonal ); //TODO Automatically add simmilar artist streams for the users favorite artists. } LastFmServiceCollection::~LastFmServiceCollection() { DEBUG_BLOCK } bool LastFmServiceCollection::possiblyContainsTrack( const QUrl &url ) const { return url.scheme() == "lastfm"; } Meta::TrackPtr LastFmServiceCollection::trackForUrl( const QUrl &url ) { return Meta::TrackPtr( new LastFm::Track( url.url() ) ); } QString LastFmServiceCollection::collectionId() const { return QLatin1String( "last.fm" ); } QString LastFmServiceCollection::prettyName() const { return i18n( "Last.fm" ); } void LastFmServiceCollection::slotAddNeighboursLoved() { DEBUG_BLOCK if( !m_jobs[ "user.getNeighbours" ] ) { debug() << "BAD! got no result object"; return; } switch (m_jobs[ "user.getNeighbours" ]->error()) { case QNetworkReply::NoError: { // iterate through each neighbour lastfm::XmlQuery lfm; if( lfm.parse( m_jobs[ "user.getNeighbours" ]->readAll() ) ) { foreach( const lastfm::XmlQuery &e, lfm[ "neighbours" ].children( "user" ) ) { const QString name = e[ "name" ].text(); //debug() << "got neighbour!!! - " << name; LastFm::Track *track = new LastFm::Track( "lastfm://user/" + name + "/loved" ); Meta::TrackPtr trackPtr( track ); m_neighborsLoved->addTrack( trackPtr ); addTrack( trackPtr ); } } else { debug() << "Got exception in parsing from last.fm:" << lfm.parseError().message(); } break; } case QNetworkReply::AuthenticationRequiredError: debug() << "Last.fm: errorMessage: Sorry, we don't recognise that username, or you typed the password incorrectly."; break; default: debug() << "Last.fm: errorMessage: There was a problem communicating with the Last.fm services. Please try again later."; break; } m_jobs[ "user.getNeighbours" ]->deleteLater(); } void LastFmServiceCollection::slotAddNeighboursPersonal() { DEBUG_BLOCK switch (m_jobs[ "user.getNeighbours" ]->error()) { case QNetworkReply::NoError: { // iterate through each neighbour if( !m_jobs[ "user.getNeighbours" ] ) { debug() << "BAD! got no result object"; return; } lastfm::XmlQuery lfm; if( lfm.parse( m_jobs[ "user.getNeighbours" ]->readAll() ) ) { // iterate through each neighbour foreach( const lastfm::XmlQuery &e, lfm[ "neighbours" ].children( "user" ) ) { const QString name = e[ "name" ].text(); debug() << "got neighbour!!! - " << name; LastFm::Track *track = new LastFm::Track( "lastfm://user/" + name + "/personal" ); Meta::TrackPtr trackPtr( track ); m_neighborsPersonal->addTrack( trackPtr ); addTrack( trackPtr ); } // should be safe, as both slots SHOULD get called before we return to the event loop... m_jobs[ "user.getNeighbours" ]->deleteLater(); } else { debug() << "Got exception in parsing from last.fm:" << lfm.parseError().message(); } break; } case QNetworkReply::AuthenticationRequiredError: debug() << "Last.fm: errorMessage: Sorry, we don't recognise that username, or you typed the password incorrectly."; break; default: debug() << "Last.fm: errorMessage: There was a problem communicating with the Last.fm services. Please try again later."; break; } } void LastFmServiceCollection::slotAddFriendsLoved() { DEBUG_BLOCK if( !m_jobs[ "user.getFriends" ] ) { debug() << "BAD! got no result object"; return; } switch (m_jobs[ "user.getFriends" ]->error()) { case QNetworkReply::NoError: { lastfm::XmlQuery lfm; if( lfm.parse( m_jobs[ "user.getFriends" ]->readAll() ) ) { foreach( const lastfm::XmlQuery &e, lfm[ "friends" ].children( "user" ) ) { const QString name = e[ "name" ].text(); LastFm::Track *track = new LastFm::Track( "lastfm://user/" + name + "/loved" ); Meta::TrackPtr trackPtr( track ); m_friendsLoved->addTrack( trackPtr ); addTrack( trackPtr ); } } else { debug() << "Got exception in parsing from last.fm:" << lfm.parseError().message(); } break; } case QNetworkReply::AuthenticationRequiredError: debug() << "Last.fm: errorMessage: Sorry, we don't recognise that username, or you typed the password incorrectly."; break; default: debug() << "Last.fm: errorMessage: There was a problem communicating with the Last.fm services. Please try again later."; break; } m_jobs[ "user.getFriends" ]->deleteLater(); } void LastFmServiceCollection::slotAddFriendsPersonal() { DEBUG_BLOCK if( !m_jobs[ "user.getFriends" ] ) { debug() << "BAD! got no result object"; return; } switch (m_jobs[ "user.getFriends" ]->error()) { case QNetworkReply::NoError: { lastfm::XmlQuery lfm; if( lfm.parse( m_jobs[ "user.getFriends" ]->readAll() ) ) { foreach( const lastfm::XmlQuery &e, lfm[ "friends" ].children( "user" ) ) { const QString name = e[ "name" ].text(); LastFm::Track *track = new LastFm::Track( "lastfm://user/" + name + "/personal" ); Meta::TrackPtr trackPtr( track ); m_friendsPersonal->addTrack( trackPtr ); addTrack( trackPtr ); } } else { debug() << "Got exception in parsing from last.fm:" << lfm.parseError().message(); } break; } case QNetworkReply::AuthenticationRequiredError: debug() << "Last.fm: errorMessage: Sorry, we don't recognise that username, or you typed the password incorrectly."; break; default: debug() << "Last.fm: errorMessage: There was a problem communicating with the Last.fm services. Please try again later."; break; } m_jobs[ "user.getFriends" ]->deleteLater(); } QueryMaker* LastFmServiceCollection::queryMaker() { // TODO //return new LastFmServiceQueryMaker( this ); return ServiceCollection::queryMaker(); } diff --git a/src/services/lastfm/LastFmServiceConfig.cpp b/src/services/lastfm/LastFmServiceConfig.cpp index ec2ee042a9..5e371de8d2 100644 --- a/src/services/lastfm/LastFmServiceConfig.cpp +++ b/src/services/lastfm/LastFmServiceConfig.cpp @@ -1,286 +1,288 @@ /**************************************************************************************** * Copyright (c) 2007 Shane King * * Copyright (c) 2009 Leo Franchi * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "lastfm" #include "LastFmServiceConfig.h" #include "App.h" +#include "core/interfaces/Logger.h" +#include "core/support/Amarok.h" #include "core/support/Components.h" #include "core/support/Debug.h" -#include #include -#include +#include +#include #include #include QWeakPointer LastFmServiceConfig::s_instance; LastFmServiceConfigPtr LastFmServiceConfig::instance() { Q_ASSERT( QThread::currentThread() == QCoreApplication::instance()->thread() ); LastFmServiceConfigPtr strongRef = s_instance.toStrongRef(); if( strongRef ) return strongRef; LastFmServiceConfigPtr newStrongRef( new LastFmServiceConfig() ); s_instance = newStrongRef; return newStrongRef; } LastFmServiceConfig::LastFmServiceConfig() : m_askDiag( 0 ) , m_wallet( 0 ) { DEBUG_BLOCK - KConfigGroup config = KGlobal::config()->group( configSectionName() ); + KConfigGroup config = Amarok::config( configSectionName() ); m_sessionKey = config.readEntry( "sessionKey", QString() ); m_scrobble = config.readEntry( "scrobble", defaultScrobble() ); m_fetchSimilar = config.readEntry( "fetchSimilar", defaultFetchSimilar() ); m_scrobbleComposer = config.readEntry( "scrobbleComposer", defaultScrobbleComposer() ); m_useFancyRatingTags = config.readEntry( "useFancyRatingTags", defaultUseFancyRatingTags() ); m_announceCorrections = config.readEntry( "announceCorrections", defaultAnnounceCorrections() ); m_filterByLabel = config.readEntry( "filterByLabel", defaultFilterByLabel() ); m_filteredLabel = config.readEntry( "filteredLabel", defaultFilteredLabel() ); if( config.hasKey( "kWalletUsage" ) ) m_kWalletUsage = KWalletUsage( config.readEntry( "kWalletUsage", int( NoPasswordEnteredYet ) ) ); else { // migrate from the old config that used "ignoreWallet" key set to yes/no if( config.readEntry( "ignoreWallet", "" ) == "yes" ) m_kWalletUsage = PasswordInAscii; else if( config.hasKey( "scrobble" ) ) // assume password was saved in KWallet if the config was once written m_kWalletUsage = PasswodInKWallet; else m_kWalletUsage = NoPasswordEnteredYet; // config not yet saved, assume unused } switch( m_kWalletUsage ) { case NoPasswordEnteredYet: break; case PasswodInKWallet: openWalletToRead(); break; case PasswordInAscii: m_username = config.readEntry( "username", QString() ); m_password = config.readEntry( "password", QString() ); break; } } LastFmServiceConfig::~LastFmServiceConfig() { DEBUG_BLOCK if( m_askDiag ) m_askDiag->deleteLater(); if( m_wallet ) m_wallet->deleteLater(); } void LastFmServiceConfig::save() { - KConfigGroup config = KGlobal::config()->group( configSectionName() ); + KConfigGroup config = Amarok::config( configSectionName() ); // if username and password is empty, reset to NoPasswordEnteredYet; this enables // going from PasswordInAscii to PasswodInKWallet if( m_username.isEmpty() && m_password.isEmpty() ) { m_kWalletUsage = NoPasswordEnteredYet; config.deleteEntry( "username" ); // prevent possible stray credentials config.deleteEntry( "password" ); } config.writeEntry( "sessionKey", m_sessionKey ); config.writeEntry( "scrobble", m_scrobble ); config.writeEntry( "fetchSimilar", m_fetchSimilar ); config.writeEntry( "scrobbleComposer", m_scrobbleComposer ); config.writeEntry( "useFancyRatingTags", m_useFancyRatingTags ); config.writeEntry( "announceCorrections", m_announceCorrections ); config.writeEntry( "kWalletUsage", int( m_kWalletUsage ) ); config.writeEntry( "filterByLabel", m_filterByLabel ); config.writeEntry( "filteredLabel", m_filteredLabel ); config.deleteEntry( "ignoreWallet" ); // remove old settings switch( m_kWalletUsage ) { case NoPasswordEnteredYet: if( m_username.isEmpty() && m_password.isEmpty() ) break; // stay in this state /* Falls through. */ case PasswodInKWallet: openWalletToWrite(); config.deleteEntry( "username" ); // prevent possible stray credentials config.deleteEntry( "password" ); break; case PasswordInAscii: config.writeEntry( "username", m_username ); config.writeEntry( "password", m_password ); break; } config.sync(); emit updated(); } void LastFmServiceConfig::openWalletToRead() { if( m_wallet && m_wallet->isOpen() ) { slotWalletOpenedToRead( true ); return; } if( m_wallet ) disconnect( m_wallet, 0, this, 0 ); else { openWalletAsync(); if( !m_wallet ) // can happen, see bug 322964 { slotWalletOpenedToRead( false ); return; } } - connect( m_wallet, SIGNAL(walletOpened(bool)), SLOT(slotWalletOpenedToRead(bool)) ); + connect( m_wallet, &KWallet::Wallet::walletOpened, this, &LastFmServiceConfig::slotWalletOpenedToRead ); } void LastFmServiceConfig::openWalletToWrite() { if( m_wallet && m_wallet->isOpen() ) { slotWalletOpenedToWrite( true ); return; } if( m_wallet ) disconnect( m_wallet, 0, this, 0 ); else { openWalletAsync(); if( !m_wallet ) // can happen, see bug 322964 { slotWalletOpenedToWrite( false ); return; } } - connect( m_wallet, SIGNAL(walletOpened(bool)), SLOT(slotWalletOpenedToWrite(bool)) ); + connect( m_wallet, &KWallet::Wallet::walletOpened, this, &LastFmServiceConfig::slotWalletOpenedToWrite ); } void LastFmServiceConfig::openWalletAsync() { Q_ASSERT( !m_wallet ); using namespace KWallet; m_wallet = Wallet::openWallet( Wallet::NetworkWallet(), 0, Wallet::Asynchronous ); } void LastFmServiceConfig::prepareOpenedWallet() { if( !m_wallet->hasFolder( "Amarok" ) ) m_wallet->createFolder( "Amarok" ); m_wallet->setFolder( "Amarok" ); } void LastFmServiceConfig::slotWalletOpenedToRead( bool success ) { if( !success ) { warning() << __PRETTY_FUNCTION__ << "failed to open wallet"; QString message = i18n( "Failed to open KDE Wallet to read Last.fm credentials" ); Amarok::Components::logger()->longMessage( message, Amarok::Logger::Warning ); if( m_wallet ) m_wallet->deleteLater(); // no point in having invalid wallet around m_wallet = 0; return; } Q_ASSERT( m_wallet ); prepareOpenedWallet(); if( m_wallet->readPassword( "lastfm_password", m_password ) > 0 ) warning() << "Failed to read lastfm password from kwallet"; QByteArray rawUsername; if( m_wallet->readEntry( "lastfm_username", rawUsername ) > 0 ) warning() << "Failed to read last.fm username from kwallet"; else m_username = QString::fromUtf8( rawUsername ); emit updated(); } void LastFmServiceConfig::slotWalletOpenedToWrite( bool success ) { if( !success ) { askAboutMissingKWallet(); if( m_wallet ) m_wallet->deleteLater(); // no point in having invalid wallet around m_wallet = 0; return; } Q_ASSERT( m_wallet ); prepareOpenedWallet(); if( m_wallet->writePassword( "lastfm_password", m_password ) > 0 ) warning() << "Failed to save last.fm password to kwallet"; if( m_wallet->writeEntry( "lastfm_username", m_username.toUtf8() ) > 0 ) warning() << "Failed to save last.fm username to kwallet"; m_kWalletUsage = PasswodInKWallet; - KConfigGroup config = KGlobal::config()->group( configSectionName() ); + KConfigGroup config = Amarok::config( configSectionName() ); config.writeEntry( "kWalletUsage", int( m_kWalletUsage ) ); config.sync(); } void LastFmServiceConfig::askAboutMissingKWallet() { if ( !m_askDiag ) { - m_askDiag = new KDialog( 0 ); - m_askDiag->setCaption( i18n( "Last.fm credentials" ) ); - m_askDiag->setMainWidget( new QLabel( i18n( "No running KWallet found. Would you like Amarok to save your Last.fm credentials in plaintext?" ) ) ); - m_askDiag->setButtons( KDialog::Yes | KDialog::No ); + m_askDiag = new QMessageBox; + m_askDiag->setText( i18n( "No running KWallet found." ) ); + m_askDiag->setInformativeText( i18n( "Would you like Amarok to save your Last.fm credentials in plaintext?" ) ); + m_askDiag->setStandardButtons( QMessageBox::Yes | QMessageBox::No ); - connect( m_askDiag, SIGNAL(yesClicked()), this, SLOT(slotStoreCredentialsInAscii()) ); + connect( m_askDiag, &QDialog::accepted, this, &LastFmServiceConfig::slotStoreCredentialsInAscii ); // maybe connect SIGNAL(noClicked()) to a message informing the user the password will // be forgotten on Amarok restart } m_askDiag->show(); } void LastFmServiceConfig::slotStoreCredentialsInAscii() //SLOT { DEBUG_BLOCK m_kWalletUsage = PasswordInAscii; save(); } diff --git a/src/services/lastfm/LastFmServiceConfig.h b/src/services/lastfm/LastFmServiceConfig.h index 3e707dc533..551747a212 100644 --- a/src/services/lastfm/LastFmServiceConfig.h +++ b/src/services/lastfm/LastFmServiceConfig.h @@ -1,143 +1,143 @@ /**************************************************************************************** * Copyright (c) 2007 Shane King * * Copyright (c) 2009 Leo Franchi * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef LASTFMSERVICECONFIG_H #define LASTFMSERVICECONFIG_H #include "services/lastfm/amarok_lastfm_shared_export.h" #include #include #include namespace KWallet { class Wallet; } -class KDialog; +class QMessageBox; class LastFmServiceConfig; typedef QSharedPointer LastFmServiceConfigPtr; /** * Configuration of the Last.fm plugin. Because some operations are async, you should * connect to the updated() signal and listen to changes, especially ones to username, * password or sessionKey. */ class AMAROK_LASTFM_SHARED_EXPORT LastFmServiceConfig : public QObject { Q_OBJECT public: static const char *configSectionName() { return "Service_LastFm"; } /** * Singleton pattern accessor. Not thread safe - must be called from the main * thread. */ static LastFmServiceConfigPtr instance(); ~LastFmServiceConfig(); /** * Saves the configuration back to the storage and notifies other users about * the change. This method must be called after calling any of the set* methods. */ void save(); QString username() const { return m_username; } void setUsername( const QString &username ) { m_username = username; } QString password() const { return m_password; } void setPassword( const QString &password ) { m_password = password; } QString sessionKey() const { return m_sessionKey; } void setSessionKey( const QString &sessionKey ) { m_sessionKey = sessionKey; } bool scrobble() const { return m_scrobble; } static bool defaultScrobble() { return true; } void setScrobble( bool scrobble ) { m_scrobble = scrobble; } bool fetchSimilar() const { return m_fetchSimilar; } static bool defaultFetchSimilar() { return true; } void setFetchSimilar( bool fetchSimilar ) { m_fetchSimilar = fetchSimilar; } bool scrobbleComposer() const { return m_scrobbleComposer; } static bool defaultScrobbleComposer() { return false; } void setScrobbleComposer( bool scrobbleComposer ) { m_scrobbleComposer = scrobbleComposer; } bool useFancyRatingTags() const { return m_useFancyRatingTags; } static bool defaultUseFancyRatingTags() { return true; } void setUseFancyRatingTags( bool useFancyRatingTags ) { m_useFancyRatingTags = useFancyRatingTags; } bool announceCorrections() const { return m_announceCorrections; } static bool defaultAnnounceCorrections() { return true; } void setAnnounceCorrections( bool announceCorrections ) { m_announceCorrections = announceCorrections; } bool filterByLabel() const { return m_filterByLabel; } static bool defaultFilterByLabel() { return false; } void setFilterByLabel( bool filterByLabel ) { m_filterByLabel = filterByLabel; } QString filteredLabel() const { return m_filteredLabel; } static QString defaultFilteredLabel() { return QString(); } void setFilteredLabel( const QString &filteredLabel ) { m_filteredLabel = filteredLabel; } Q_SIGNALS: /** * Emitted when settings are changed. (after save() is called) */ void updated(); private Q_SLOTS: void slotWalletOpenedToRead( bool success ); void slotWalletOpenedToWrite( bool success ); void slotStoreCredentialsInAscii(); private: Q_DISABLE_COPY( LastFmServiceConfig ) LastFmServiceConfig(); void openWalletToRead(); void openWalletToWrite(); void openWalletAsync(); void prepareOpenedWallet(); void askAboutMissingKWallet(); // don't remove or reorder entires, would break saved config enum KWalletUsage { NoPasswordEnteredYet, PasswodInKWallet, PasswordInAscii }; QString m_username; QString m_password; QString m_sessionKey; bool m_scrobble; bool m_fetchSimilar; bool m_scrobbleComposer; bool m_useFancyRatingTags; bool m_announceCorrections; bool m_filterByLabel; QString m_filteredLabel; KWalletUsage m_kWalletUsage; - KDialog* m_askDiag; + QMessageBox* m_askDiag; KWallet::Wallet* m_wallet; static QWeakPointer s_instance; }; #endif // LASTFMSERVICECONFIG_H diff --git a/src/services/lastfm/LastFmServiceSettings.cpp b/src/services/lastfm/LastFmServiceSettings.cpp index 4a79ce61d6..fec35d0fd1 100644 --- a/src/services/lastfm/LastFmServiceSettings.cpp +++ b/src/services/lastfm/LastFmServiceSettings.cpp @@ -1,258 +1,260 @@ /**************************************************************************************** * Copyright (c) 2007 Shane King * * Copyright (c) 2013 Vedant Agarwala * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "LastFmServiceSettings" #include "LastFmServiceSettings.h" #include "core/collections/QueryMaker.h" #include "core/meta/Meta.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include "core-impl/collections/support/CollectionManager.h" #include "network/NetworkAccessManagerProxy.h" #include "ui_LastFmConfigWidget.h" #include #include #include #include #include -K_PLUGIN_FACTORY( LastFmServiceSettingsFactory, registerPlugin(); ) -K_EXPORT_PLUGIN( LastFmServiceSettingsFactory( "kcm_amarok_lastfm" ) ) +K_PLUGIN_FACTORY_WITH_JSON( LastFmServiceSettingsFactory, "amarok_service_lastfm_config.json", registerPlugin(); ) QString md5( const QByteArray& src ) { QByteArray const digest = QCryptographicHash::hash( src, QCryptographicHash::Md5 ); return QString::fromLatin1( digest.toHex() ).rightJustified( 32, '0' ); } LastFmServiceSettings::LastFmServiceSettings( QWidget *parent, const QVariantList &args ) - : KCModule( LastFmServiceSettingsFactory::componentData(), parent, args ) + : KCModule( parent, args ) , m_config( LastFmServiceConfig::instance() ) { m_configDialog = new Ui::LastFmConfigWidget; m_configDialog->setupUi( this ); - connect( m_config.data(), SIGNAL(updated()), SLOT(onConfigUpdated()) ); + connect( m_config.data(), &LastFmServiceConfig::updated, this, &LastFmServiceSettings::onConfigUpdated ); - connect( m_configDialog->kcfg_ScrobblerUsername, SIGNAL(textChanged(QString)), this, SLOT(settingsChanged()) ); - connect( m_configDialog->kcfg_ScrobblerPassword, SIGNAL(textChanged(QString)), this, SLOT(settingsChanged()) ); - connect( m_configDialog->kcfg_SubmitPlayedSongs, SIGNAL(stateChanged(int)), this, SLOT(settingsChanged()) ); - connect( m_configDialog->kcfg_RetrieveSimilarArtists, SIGNAL(stateChanged(int)), this, SLOT(settingsChanged()) ); - connect( m_configDialog->kcfg_ScrobbleComposer, SIGNAL(stateChanged(int)), this, SLOT(settingsChanged())); - connect( m_configDialog->kcfg_UseFancyRatingTags, SIGNAL(stateChanged(int)), this, SLOT(settingsChanged())); - connect( m_configDialog->kcfg_AnnounceCorrections, SIGNAL(stateChanged(int)), this, SLOT(settingsChanged()) ); - connect( m_configDialog->kcfg_FilterByLabel, SIGNAL(stateChanged(int)), this, SLOT(settingsChanged()) ); - connect( m_configDialog->kcfg_FilteredLabel, SIGNAL(currentIndexChanged(QString)), this, SLOT(settingsChanged()) ); - connect( m_configDialog->testLogin, SIGNAL(clicked()), this, SLOT(testLogin()) ); + connect( m_configDialog->kcfg_ScrobblerUsername, &QLineEdit::textChanged, this, &LastFmServiceSettings::settingsChanged ); + connect( m_configDialog->kcfg_ScrobblerPassword, &QLineEdit::textChanged, this, &LastFmServiceSettings::settingsChanged ); + connect( m_configDialog->kcfg_SubmitPlayedSongs, &QCheckBox::stateChanged, this, &LastFmServiceSettings::settingsChanged ); + connect( m_configDialog->kcfg_RetrieveSimilarArtists, &QCheckBox::stateChanged, this, &LastFmServiceSettings::settingsChanged ); + connect( m_configDialog->kcfg_ScrobbleComposer, &QCheckBox::stateChanged, this, &LastFmServiceSettings::settingsChanged ); + connect( m_configDialog->kcfg_UseFancyRatingTags, &QCheckBox::stateChanged, this, &LastFmServiceSettings::settingsChanged ); + connect( m_configDialog->kcfg_AnnounceCorrections, &QCheckBox::stateChanged, this, &LastFmServiceSettings::settingsChanged ); + connect( m_configDialog->kcfg_FilterByLabel, &QCheckBox::stateChanged, this, &LastFmServiceSettings::settingsChanged ); + connect( m_configDialog->kcfg_FilteredLabel, QOverload::of( &QComboBox::currentIndexChanged ), this, &LastFmServiceSettings::settingsChanged ); + connect( m_configDialog->testLogin, &QPushButton::clicked, this, &LastFmServiceSettings::testLogin ); using namespace Collections; QueryMaker *query = CollectionManager::instance()->queryMaker(); query->setQueryType( QueryMaker::Label ); - connect( query, SIGNAL(newResultReady(Meta::LabelList)), this, SLOT(addNewLabels(Meta::LabelList)) ); + connect( query, &QueryMaker::newLabelsReady, this, &LastFmServiceSettings::addNewLabels ); query->setAutoDelete( true ); query->run(); } LastFmServiceSettings::~LastFmServiceSettings() { delete m_configDialog; } void LastFmServiceSettings::save() { QString dialogUser = m_configDialog->kcfg_ScrobblerUsername->text(); QString dialogPass = m_configDialog->kcfg_ScrobblerPassword->text(); // clear the session key if credentials changed if( m_config->username() != dialogUser || m_config->password() != dialogPass ) m_config->setSessionKey( QString() ); m_config->setUsername( dialogUser ); m_config->setPassword( dialogPass ); m_config->setScrobble( m_configDialog->kcfg_SubmitPlayedSongs->isChecked() ); m_config->setFetchSimilar( m_configDialog->kcfg_RetrieveSimilarArtists->isChecked() ); m_config->setScrobbleComposer( m_configDialog->kcfg_ScrobbleComposer->isChecked() ); m_config->setUseFancyRatingTags( m_configDialog->kcfg_UseFancyRatingTags->isChecked() ); m_config->setAnnounceCorrections( m_configDialog->kcfg_AnnounceCorrections->isChecked() ); m_config->setFilterByLabel( m_configDialog->kcfg_FilterByLabel->isChecked() ); m_config->setFilteredLabel( m_configDialog->kcfg_FilteredLabel->currentText() ); m_config->save(); KCModule::save(); } void LastFmServiceSettings::testLogin() { m_configDialog->testLogin->setEnabled( false ); m_configDialog->testLogin->setText( i18n( "Testing..." ) ); // set the global static Lastfm::Ws stuff lastfm::ws::ApiKey = Amarok::lastfmApiKey(); lastfm::ws::SharedSecret = Amarok::lastfmApiSharedSecret(); lastfm::ws::Username = m_configDialog->kcfg_ScrobblerUsername->text(); if( lastfm::nam() != The::networkAccessManager() ) lastfm::setNetworkAccessManager( The::networkAccessManager() ); debug() << "username:" << QString( QUrl::toPercentEncoding( lastfm::ws::Username ) ); const QString authToken = md5( QString( "%1%2" ).arg( m_configDialog->kcfg_ScrobblerUsername->text() ) .arg( md5( m_configDialog->kcfg_ScrobblerPassword->text().toUtf8() ) ).toUtf8() ); // now authenticate w/ last.fm and get our session key QMap query; query[ "method" ] = "auth.getMobileSession"; query[ "username" ] = m_configDialog->kcfg_ScrobblerUsername->text(); query[ "authToken" ] = authToken; m_authQuery = lastfm::ws::post( query ); - connect( m_authQuery, SIGNAL(finished()), SLOT(onAuthenticated()) ); - connect( m_authQuery, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onError(QNetworkReply::NetworkError)) ); + connect( m_authQuery, &QNetworkReply::finished, this, &LastFmServiceSettings::onAuthenticated ); + connect( m_authQuery, QOverload::of( &QNetworkReply::error ), + this, &LastFmServiceSettings::onError ); } void LastFmServiceSettings::onAuthenticated() { lastfm::XmlQuery lfm; lfm.parse( m_authQuery->readAll() ); switch( m_authQuery->error() ) { case QNetworkReply::NoError: debug() << "NoError"; if( lfm.children( "error" ).size() > 0 ) { debug() << "ERROR from last.fm:" << lfm.text(); m_configDialog->testLogin->setText( i18nc( "The operation was rejected by the server", "Failed" ) ); m_configDialog->testLogin->setEnabled( true ); } else { m_configDialog->testLogin->setText( i18nc( "The operation completed as expected", "Success" ) ); m_configDialog->testLogin->setEnabled( false ); m_configDialog->kcfg_SubmitPlayedSongs->setEnabled( true ); } break; case QNetworkReply::AuthenticationRequiredError: debug() << "AuthenticationFailed"; KMessageBox::error( this, i18n( "Either the username or the password is incorrect, please correct and try again" ), i18n( "Failed" ) ); m_configDialog->testLogin->setText( i18n( "Test Login" ) ); m_configDialog->testLogin->setEnabled( true ); break; default: debug() << "Unhandled QNetworkReply state, probably not important"; } m_authQuery->deleteLater(); } void LastFmServiceSettings::onError( QNetworkReply::NetworkError code ) { if( code == QNetworkReply::NoError ) return; if( code == QNetworkReply::AuthenticationRequiredError ) { onAuthenticated(); return; } KMessageBox::error( this, i18n( "Unable to connect to Last.fm service." ), i18n( "Failed" ) ); m_configDialog->testLogin->setText( i18n( "Test Login" ) ); m_configDialog->testLogin->setEnabled( true ); debug() << "Error occurred during network request: " << m_authQuery->errorString(); m_authQuery->deleteLater(); } void LastFmServiceSettings::onConfigUpdated() { load(); } void LastFmServiceSettings::load() { m_configDialog->kcfg_ScrobblerUsername->setText( m_config->username() ); m_configDialog->kcfg_ScrobblerPassword->setText( m_config->password() ); m_configDialog->kcfg_SubmitPlayedSongs->setChecked( m_config->scrobble() ); m_configDialog->kcfg_RetrieveSimilarArtists->setChecked( m_config->fetchSimilar() ); m_configDialog->kcfg_ScrobbleComposer->setChecked( m_config->scrobbleComposer() ); m_configDialog->kcfg_UseFancyRatingTags->setChecked( m_config->useFancyRatingTags() ); m_configDialog->kcfg_AnnounceCorrections->setChecked( m_config->announceCorrections() ); m_configDialog->kcfg_FilterByLabel->setChecked( m_config->filterByLabel() ); m_configDialog->kcfg_FilteredLabel->setCurrentIndex( filteredLabelComboIndex( m_config->filteredLabel() ) ); if( !m_config->username().isEmpty() && !m_config->password().isEmpty() ) m_configDialog->kcfg_SubmitPlayedSongs->setEnabled( true ); KCModule::load(); } void LastFmServiceSettings::defaults() { m_configDialog->kcfg_SubmitPlayedSongs->setChecked( m_config->defaultScrobble() ); m_configDialog->kcfg_RetrieveSimilarArtists->setChecked( m_config->defaultFetchSimilar() ); m_configDialog->kcfg_ScrobbleComposer->setChecked( m_config->defaultScrobbleComposer() ); m_configDialog->kcfg_UseFancyRatingTags->setChecked( m_config->defaultUseFancyRatingTags() ); m_configDialog->kcfg_AnnounceCorrections->setChecked( m_config->defaultAnnounceCorrections() ); m_configDialog->kcfg_FilterByLabel->setChecked( m_config->defaultFilterByLabel() ); m_configDialog->kcfg_FilteredLabel->setCurrentIndex( filteredLabelComboIndex( m_config->defaultFilteredLabel() ) ); } void LastFmServiceSettings::settingsChanged() { //TODO: Make pretty validation for username and password //with error reporting m_configDialog->testLogin->setText( i18n( "&Test Login" ) ); m_configDialog->testLogin->setEnabled( true ); emit changed( true ); } void LastFmServiceSettings::addNewLabels( const Meta::LabelList &labels ) { foreach( const Meta::LabelPtr &label , labels ) m_configDialog->kcfg_FilteredLabel->addItem( label->name() ); } int LastFmServiceSettings::filteredLabelComboIndex( const QString &label ) { int index = m_configDialog->kcfg_FilteredLabel->findText( label ); if( index == -1) { m_configDialog->kcfg_FilteredLabel->addItem( label ); return m_configDialog->kcfg_FilteredLabel->findText( label ); } else return index; } + +#include "LastFmServiceSettings.moc" diff --git a/src/services/lastfm/LastFmTreeModel.cpp b/src/services/lastfm/LastFmTreeModel.cpp index 326feab0bf..96fdd0e607 100644 --- a/src/services/lastfm/LastFmTreeModel.cpp +++ b/src/services/lastfm/LastFmTreeModel.cpp @@ -1,710 +1,713 @@ /**************************************************************************************** * Copyright (c) 2008 Casey Link * * Copyright (c) 2009 Nikolaj Hald Nielsen * * Copyright (c) 2009 Mark Kretschmann * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "LastFmTreeModel" #include "core/support/Debug.h" #include "LastFmTreeModel.h" #include "AvatarDownloader.h" #include "core-impl/collections/support/CollectionManager.h" #include "AmarokMimeData.h" #include #include #include #include using namespace LastFm; LastFmTreeModel::LastFmTreeModel( QObject *parent ) : QAbstractItemModel( parent ) { m_rootItem = new LastFmTreeItem( LastFm::Root, "Hello" ); setupModelData( m_rootItem ); QNetworkReply *reply; reply = m_user.getNeighbours(); - connect(reply, SIGNAL(finished()), this, SLOT(slotAddNeighbors()) ); + connect(reply, &QNetworkReply::finished, this, &LastFmTreeModel::slotAddNeighbors ); reply = m_user.getFriends(); - connect( reply, SIGNAL(finished()), this, SLOT(slotAddFriends()) ); + connect( reply, &QNetworkReply::finished, this, &LastFmTreeModel::slotAddFriends ); reply = m_user.getTopTags(); - connect( reply, SIGNAL(finished()), this, SLOT(slotAddTags()) ); + connect( reply, &QNetworkReply::finished, this, &LastFmTreeModel::slotAddTags ); reply = m_user.getTopArtists(); - connect( reply, SIGNAL(finished()), this, SLOT(slotAddTopArtists()) ); + connect( reply, &QNetworkReply::finished, this, &LastFmTreeModel::slotAddTopArtists ); } LastFmTreeModel::~LastFmTreeModel() { delete m_rootItem; } void LastFmTreeModel::slotAddNeighbors() { QNetworkReply *reply = qobject_cast( sender() ); if( !reply ) { debug() << __PRETTY_FUNCTION__ << "null reply!"; return; } reply->deleteLater(); lastfm::XmlQuery lfm; if( lfm.parse( reply->readAll() ) ) { QList children = lfm[ "neighbours" ].children( "user" ); int start = m_myNeighbors->childCount(); QModelIndex parent = index( m_myNeighbors->row(), 0 ); beginInsertRows( parent, start, start + children.size() ); foreach( const lastfm::XmlQuery &e, children ) { const QString name = e[ "name" ].text(); LastFmTreeItem* neighbor = new LastFmTreeItem( mapTypeToUrl(LastFm::NeighborsChild, name), LastFm::NeighborsChild, name, m_myNeighbors ); QUrl avatarUrl( e[ QLatin1String("image size=small") ].text() ); if( !avatarUrl.isEmpty() ) neighbor->setAvatarUrl( avatarUrl ); m_myNeighbors->appendChild( neighbor ); appendUserStations( neighbor, name ); } endInsertRows(); emit dataChanged( parent, parent ); } else { debug() << "Got exception in parsing from last.fm:" << lfm.parseError().message(); return; } } void LastFmTreeModel::slotAddFriends() { QNetworkReply *reply = qobject_cast( sender() ); if( !reply ) { debug() << __PRETTY_FUNCTION__ << "null reply!"; return; } reply->deleteLater(); lastfm::XmlQuery lfm; if( lfm.parse( reply->readAll() ) ) { QList children = lfm[ "friends" ].children( "user" ); int start = m_myFriends->childCount(); QModelIndex parent = index( m_myFriends->row(), 0 ); beginInsertRows( parent, start, start + children.size() ); foreach( const lastfm::XmlQuery &e, children ) { const QString name = e[ "name" ].text(); LastFmTreeItem* afriend = new LastFmTreeItem( mapTypeToUrl(LastFm::FriendsChild, name), LastFm::FriendsChild, name, m_myFriends ); QUrl avatarUrl( e[ QLatin1String("image size=small") ].text() ); if( !avatarUrl.isEmpty() ) afriend->setAvatarUrl( avatarUrl ); m_myFriends->appendChild( afriend ); appendUserStations( afriend, name ); } endInsertRows(); emit dataChanged( parent, parent ); } else { debug() << "Got exception in parsing from last.fm:" << lfm.parseError().message(); return; } } void LastFmTreeModel::slotAddTags() { QNetworkReply *reply = qobject_cast( sender() ); if( !reply ) { debug() << __PRETTY_FUNCTION__ << "null reply!"; return; } reply->deleteLater(); QMap listWithWeights = lastfm::Tag::list( reply ); int start = m_myTags->childCount(); QModelIndex parent = index( m_myTags->row(), 0 ); beginInsertRows( parent, start, start + listWithWeights.size() ); QMapIterator it( listWithWeights ); it.toBack(); while( it.hasPrevious() ) { it.previous(); int count = it.key(); QString text = it.value(); QString prettyText = i18nc( "%1 is Last.fm tag name, %2 is its usage count", "%1 (%2)", text, count ); LastFmTreeItem *tag = new LastFmTreeItem( mapTypeToUrl( LastFm::MyTagsChild, text ), LastFm::MyTagsChild, prettyText, m_myTags ); m_myTags->appendChild( tag ); } endInsertRows(); emit dataChanged( parent, parent ); } void LastFmTreeModel::slotAddTopArtists() { QNetworkReply *reply = qobject_cast( sender() ); if( !reply ) { debug() << __PRETTY_FUNCTION__ << "null reply!"; return; } reply->deleteLater(); QMultiMap playcountArtists; lastfm::XmlQuery lfm; if( lfm.parse( reply->readAll() ) ) { foreach( const lastfm::XmlQuery &e, lfm[ "topartists" ].children( "artist" ) ) { QString name = e[ "name" ].text(); int playcount = e[ "playcount" ].text().toInt(); playcountArtists.insert( playcount, name ); } } else { debug() << "Got exception in parsing from last.fm:" << lfm.parseError().message(); return; } int start = m_myTopArtists->childCount(); QModelIndex parent = index( m_myTopArtists->row(), 0 ); beginInsertRows( parent, start, start + playcountArtists.size() ); QMapIterator it( playcountArtists ); it.toBack(); while( it.hasPrevious() ) { it.previous(); int count = it.key(); QString text = it.value(); QString prettyText = i18ncp( "%2 is artist name, %1 is number of plays", "%2 (%1 play)", "%2 (%1 plays)", count, text ); LastFmTreeItem *artist = new LastFmTreeItem( mapTypeToUrl( LastFm::ArtistsChild, text ), LastFm::ArtistsChild, prettyText, m_myTopArtists ); m_myTopArtists->appendChild( artist ); } endInsertRows(); emit dataChanged( parent, parent ); } void LastFmTreeModel::appendUserStations( LastFmTreeItem* item, const QString &user ) { // no need to call begin/endInsertRows() or dataChanged(), we're being called inside // beginInsertRows(). LastFmTreeItem* personal = new LastFmTreeItem( mapTypeToUrl( LastFm::UserChildPersonal, user ), LastFm::UserChildPersonal, i18n( "Personal Radio" ), item ); LastFmTreeItem* neigh = new LastFmTreeItem( mapTypeToUrl( LastFm::UserChildNeighborhood, user ), LastFm::UserChildNeighborhood, i18n( "Neighborhood" ), item ); item->appendChild( personal ); item->appendChild( neigh ); } void LastFmTreeModel::prepareAvatar( QPixmap &avatar, int size ) { // This code is here to stop Qt from crashing on certain weirdly shaped avatars. // We had a case were an avatar got a height of 1px after scaling and it would // crash in the rendering code. This here just fills in the background with // transparency first. if( avatar.width() < size || avatar.height() < size ) { QImage finalAvatar( size, size, QImage::Format_ARGB32 ); finalAvatar.fill( 0 ); QPainter p( &finalAvatar ); QRect r; if( avatar.width() < size ) r = QRect( ( size - avatar.width() ) / 2, 0, avatar.width(), avatar.height() ); else r = QRect( 0, ( size - avatar.height() ) / 2, avatar.width(), avatar.height() ); p.drawPixmap( r, avatar ); p.end(); avatar = QPixmap::fromImage( finalAvatar ); } } void LastFmTreeModel::onAvatarDownloaded( const QString &username, QPixmap avatar ) { sender()->deleteLater(); if( avatar.isNull() || avatar.height() <= 0 || avatar.width() <= 0 ) return; if( username == m_user.name() ) return; int m = avatarSize(); avatar = avatar.scaled( m, m, Qt::KeepAspectRatio, Qt::SmoothTransformation ); prepareAvatar( avatar, m ); m_avatars.insert( username, avatar ); // these 2 categories have a chance to be updated: QList categories; categories << m_myFriends << m_myNeighbors; // now go through all children of the categories and notify view as appropriate foreach( LastFmTreeItem *category, categories ) { QModelIndex parentIdx = index( category->row(), 0 ); for( int i = 0; i < category->childCount(); i++ ) { LastFmTreeItem *item = category->child( i ); if( !item ) continue; if( item->data() == username ) { QModelIndex idx = index( i, 0, parentIdx ); emit dataChanged( idx, idx ); break; // no user is twice in a single category } } } } QIcon LastFmTreeModel::avatar( const QString &username, const QUrl &avatarUrl ) const { QIcon defaultIcon( "filename-artist-amarok" ); if( username.isEmpty() ) return defaultIcon; if( m_avatars.contains(username) ) return m_avatars.value( username ); if( !avatarUrl.isValid() ) return defaultIcon; // insert placeholder so that we don't request the save avatar twice; const_cast( this )->m_avatars.insert( username, defaultIcon ); AvatarDownloader* downloader = new AvatarDownloader(); downloader->downloadAvatar( username, avatarUrl ); - connect( downloader, SIGNAL(avatarDownloaded(QString,QPixmap)), - SLOT(onAvatarDownloaded(QString,QPixmap)) ); + connect( downloader, &AvatarDownloader::avatarDownloaded, + this, &LastFmTreeModel::onAvatarDownloaded ); return defaultIcon; } int LastFmTreeModel::columnCount( const QModelIndex &parent ) const { Q_UNUSED( parent ) return 1; } int LastFmTreeModel::avatarSize() { return 32; } QVariant LastFmTreeModel::data( const QModelIndex &index, int role ) const { if( !index.isValid() ) return QVariant(); LastFmTreeItem *i = static_cast( index.internalPointer() ); if( role == Qt::DisplayRole ) switch( i->type() ) { case MyRecommendations: return i18n( "My Recommendations" ); case PersonalRadio: return i18n( "My Radio Station" ); case MixRadio: return i18n( "My Mix Radio" ); case NeighborhoodRadio: return i18n( "My Neighborhood" ); // case RecentlyPlayed: return tr("Recently Played"); // case RecentlyLoved: return tr("Recently Loved"); // case RecentlyBanned: return tr("Recently Banned"); case TopArtists: return i18n( "My Top Artists" ); case MyTags: return i18n( "My Tags" ); case Friends: return i18n( "Friends" ); case Neighbors: return i18n( "Neighbors" ); // case History: return tr("History"); // case RecentlyPlayedTrack: return m_played.value( index.row() ); // case RecentlyLovedTrack: return m_loved.value( index.row() ); // case RecentlyBannedTrack: return m_banned.value( index.row() ); // case MyTagsChild: return m_tags.value( index.row() ); case FriendsChild: case ArtistsChild: case NeighborsChild: case UserChildPersonal: case UserChildNeighborhood: case MyTagsChild: return i->data(); default: break; } if( role == Qt::DecorationRole ) + { switch( i->type() ) { case MyRecommendations: return QIcon::fromTheme( "lastfm-recommended-radio-amarok" ); case TopArtists: case PersonalRadio: return QIcon::fromTheme( "lastfm-personal-radio-amarok" ); case MixRadio: return QIcon::fromTheme( "lastfm-mix-radio-amarok" ); case NeighborhoodRadio: return QIcon::fromTheme( "lastfm-neighbour-radio-amarok" ); // case RecentlyPlayed: return QIcon::fromTheme( "lastfm-recent-tracks-amarok" ); // case RecentlyLoved: return QIcon::fromTheme( "lastfm-recently-loved-amarok" ); // case RecentlyBanned: return QIcon::fromTheme( "lastfm-recently-banned-amarok" ); case MyTags: return QIcon::fromTheme( "lastfm-my-tags-amarok" ); case Friends: return QIcon::fromTheme( "lastfm-my-friends-amarok" ); case Neighbors: return QIcon::fromTheme( "lastfm-my-neighbours-amarok" ); case RecentlyPlayedTrack: //FALL THROUGH case RecentlyLovedTrack: //FALL THROUGH case RecentlyBannedTrack: return QIcon::fromTheme( "icon_track" ); case MyTagsChild: return QIcon::fromTheme( "lastfm-tag-amarok" ); case FriendsChild: return avatar( i->data().toString(), i->avatarUrl() ); case UserChildPersonal: return QIcon::fromTheme( "lastfm-personal-radio-amarok" ); case UserChildNeighborhood: return QIcon::fromTheme( "lastfm-neighbour-radio-amarok" ); case NeighborsChild: return avatar( i->data().toString(), i->avatarUrl() ); case HistoryStation: return QIcon::fromTheme( "icon_radio" ); default: break; } + } if( role == LastFm::TrackRole ) + { switch( i->type() ) { - case LastFm::MyRecommendations: - case LastFm::PersonalRadio: - case LastFm::MixRadio: - case LastFm::NeighborhoodRadio: - case LastFm::FriendsChild: - case LastFm::NeighborsChild: - case LastFm::MyTagsChild: - case LastFm::ArtistsChild: - case LastFm::UserChildPersonal: - case LastFm::UserChildNeighborhood: - return QVariant::fromValue( i->track() ); - default: - break; + case LastFm::MyRecommendations: + case LastFm::PersonalRadio: + case LastFm::MixRadio: + case LastFm::NeighborhoodRadio: + case LastFm::FriendsChild: + case LastFm::NeighborsChild: + case LastFm::MyTagsChild: + case LastFm::ArtistsChild: + case LastFm::UserChildPersonal: + case LastFm::UserChildNeighborhood: + return QVariant::fromValue( i->track() ); + default: + break; } - + } if( role == LastFm::TypeRole ) return i->type(); return QVariant(); } Qt::ItemFlags LastFmTreeModel::flags( const QModelIndex &index ) const { if( !index.isValid() ) return 0; Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; LastFmTreeItem *i = static_cast( index.internalPointer() ); switch( i->type() ) { case MyRecommendations: case PersonalRadio: case MixRadio: case NeighborhoodRadio: case RecentlyPlayedTrack: case RecentlyLovedTrack: case RecentlyBannedTrack: case MyTagsChild: case FriendsChild: case ArtistsChild: case NeighborsChild: case HistoryStation: case UserChildPersonal: case UserChildNeighborhood: flags |= Qt::ItemIsSelectable; break; default: break; } switch( i->type() ) { case UserChildPersonal: case UserChildNeighborhood: case MyTagsChild: case ArtistsChild: case MyRecommendations: case PersonalRadio: case MixRadio: case NeighborhoodRadio: flags |= Qt::ItemIsDragEnabled; default: break; } return flags; } QModelIndex LastFmTreeModel::index( int row, int column, const QModelIndex &parent ) const { if( !hasIndex( row, column, parent ) ) return QModelIndex(); LastFmTreeItem *parentItem; if( !parent.isValid() ) parentItem = m_rootItem; else parentItem = static_cast( parent.internalPointer() ); LastFmTreeItem *childItem = parentItem->child( row ); if( childItem ) return createIndex( row, column, childItem ); else return QModelIndex(); } QModelIndex LastFmTreeModel::parent( const QModelIndex &index ) const { if( !index.isValid() ) return QModelIndex(); LastFmTreeItem *childItem = static_cast( index.internalPointer() ); LastFmTreeItem *parentItem = childItem->parent(); if( parentItem == m_rootItem ) return QModelIndex(); return createIndex( parentItem->row(), 0, parentItem ); } int LastFmTreeModel::rowCount( const QModelIndex &parent ) const { LastFmTreeItem *parentItem; if( parent.column() > 0 ) return 0; if( !parent.isValid() ) parentItem = m_rootItem; else parentItem = static_cast( parent.internalPointer() ); return parentItem->childCount(); } void LastFmTreeModel::setupModelData( LastFmTreeItem *parent ) { // no need to call beginInsertRows() here, this is only called from constructor parent->appendChild( new LastFmTreeItem( mapTypeToUrl( LastFm::MyRecommendations ), LastFm::MyRecommendations, parent ) ); parent->appendChild( new LastFmTreeItem( mapTypeToUrl( LastFm::PersonalRadio ), LastFm::PersonalRadio, parent ) ); parent->appendChild( new LastFmTreeItem( mapTypeToUrl( LastFm::MixRadio ), LastFm::MixRadio, parent ) ); parent->appendChild( new LastFmTreeItem( mapTypeToUrl( LastFm::NeighborhoodRadio ), LastFm::NeighborhoodRadio, parent ) ); m_myTopArtists = new LastFmTreeItem( LastFm::TopArtists, parent ); parent->appendChild( m_myTopArtists ); m_myTags = new LastFmTreeItem( LastFm::MyTags, parent ); parent->appendChild( m_myTags ); m_myFriends = new LastFmTreeItem( LastFm::Friends, parent ); parent->appendChild( m_myFriends ); m_myNeighbors = new LastFmTreeItem( LastFm::Neighbors, parent ); parent->appendChild( m_myNeighbors ); } QString LastFmTreeModel::mapTypeToUrl( LastFm::Type type, const QString &key ) { QString const encoded_username = QUrl::toPercentEncoding( m_user.name() ); switch( type ) { case MyRecommendations: return "lastfm://user/" + encoded_username + "/recommended"; case PersonalRadio: return "lastfm://user/" + encoded_username + "/personal"; case MixRadio: return "lastfm://user/" + encoded_username + "/mix"; case NeighborhoodRadio: return "lastfm://user/" + encoded_username + "/neighbours"; case MyTagsChild: return "lastfm://usertags/" + encoded_username + "/" + QUrl::toPercentEncoding( key ); case FriendsChild: return "lastfm://user/" + QUrl::toPercentEncoding( key ) + "/personal"; case ArtistsChild: return "lastfm://artist/" + QUrl::toPercentEncoding( key ) + "/similarartists"; case NeighborsChild: return "lastfm://user/" + QUrl::toPercentEncoding( key ) + "/personal"; case UserChildPersonal: return "lastfm://user/" + QUrl::toPercentEncoding( key ) + "/personal"; case UserChildNeighborhood: return "lastfm://user/" + QUrl::toPercentEncoding( key ) + "/neighbours"; default: return ""; } } LastFmTreeItem::LastFmTreeItem( const LastFm::Type &type, const QVariant &data, LastFmTreeItem *parent ) : mType( type ), parentItem( parent ), itemData( data ) { } LastFmTreeItem::LastFmTreeItem( const LastFm::Type &type, LastFmTreeItem *parent ) : mType( type ), parentItem( parent ) { } LastFmTreeItem::LastFmTreeItem( const QString &url, const LastFm::Type &type, LastFmTreeItem *parent ) : mType( type ), parentItem( parent ), mUrl( url ) { } LastFmTreeItem::LastFmTreeItem( const QString &url, const LastFm::Type &type, const QVariant &data, LastFmTreeItem *parent ) : mType( type ), parentItem( parent ), itemData( data ), mUrl( url ) { } LastFmTreeItem::~LastFmTreeItem() { qDeleteAll( childItems ); } void LastFmTreeItem::appendChild( LastFmTreeItem *item ) { childItems.append( item ); } LastFmTreeItem * LastFmTreeItem::child( int row ) { return childItems.value( row ); } int LastFmTreeItem::childCount() const { return childItems.count(); } QVariant LastFmTreeItem::data() const { return itemData; } Meta::TrackPtr LastFmTreeItem::track() const { Meta::TrackPtr track; if( mUrl.isEmpty() ) return track; QUrl url( mUrl ); track = CollectionManager::instance()->trackForUrl( url ); return track; } LastFmTreeItem *LastFmTreeItem::parent() { return parentItem; } int LastFmTreeItem::row() const { if( parentItem ) return parentItem->childItems.indexOf( const_cast( this ) ); return 0; } QMimeData* LastFmTreeModel::mimeData( const QModelIndexList &indices ) const { debug() << "LASTFM drag items : " << indices.size(); Meta::TrackList list; foreach( const QModelIndex &item, indices ) { Meta::TrackPtr track = data( item, LastFm::TrackRole ).value< Meta::TrackPtr >(); if( track ) list << track; } qStableSort( list.begin(), list.end(), Meta::Track::lessThan ); AmarokMimeData *mimeData = new AmarokMimeData(); mimeData->setTracks( list ); return mimeData; } diff --git a/src/services/lastfm/LastFmTreeView.cpp b/src/services/lastfm/LastFmTreeView.cpp index fc7950d009..43ddf9ef82 100644 --- a/src/services/lastfm/LastFmTreeView.cpp +++ b/src/services/lastfm/LastFmTreeView.cpp @@ -1,244 +1,243 @@ /**************************************************************************************** * Copyright (c) 2009 Casey Link * * Copyright (c) 2005-2007 Christian Muehlhaeuser, Last.fm Ltd * * Copyright (c) 2005-2007 Max Howell, Last.fm Ltd * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "LastFmTreeView.h" #include "PopupDropperFactory.h" #include "context/ContextView.h" -#include "context/popupdropper/libpud/PopupDropper.h" -#include "context/popupdropper/libpud/PopupDropperItem.h" #include "core/meta/Meta.h" #include "core/support/Debug.h" #include "services/lastfm/LastFmTreeModel.h" // FIXME just for enums +#include +#include #include -#include +#include + LastFmTreeView::LastFmTreeView ( QWidget* parent ) : Amarok::PrettyTreeView ( parent ) , m_timer ( 0 ) , m_pd( 0 ) , m_appendAction ( 0 ) , m_loadAction ( 0 ) , m_dragMutex() , m_ongoingDrag( false ) { // connect ( this, SIGNAL (activated(QModelIndex)), SLOT (onActivated(QModelIndex)) ); header()->hide(); // setRootIsDecorated( false ); } LastFmTreeView::~LastFmTreeView() {} void LastFmTreeView::contextMenuEvent ( QContextMenuEvent* event ) { m_currentItems.clear(); foreach ( const QModelIndex &i, selectedIndexes() ) { if ( i.isValid() ) m_currentItems << i; } if ( m_currentItems.isEmpty() ) return; QAction separator ( this ); separator.setSeparator ( true ); QActionList actions = createBasicActions( m_currentItems ); actions += &separator; QMenu menu; foreach ( QAction * action, actions ) menu.addAction ( action ); menu.exec ( event->globalPos() ); } QActionList LastFmTreeView::createBasicActions( const QModelIndexList & indices ) { Q_UNUSED( indices ) QActionList actions; QModelIndex index = currentIndex(); QVariant type = model()->data(index, LastFm::TypeRole); switch ( type.toInt() ) { case LastFm::MyRecommendations: case LastFm::PersonalRadio: case LastFm::MixRadio: case LastFm::NeighborhoodRadio: case LastFm::FriendsChild: case LastFm::NeighborsChild: case LastFm::MyTagsChild: case LastFm::ArtistsChild: case LastFm::UserChildPersonal: case LastFm::UserChildNeighborhood: { if ( m_appendAction == 0 ) { m_appendAction = new QAction ( QIcon::fromTheme( "media-track-add-amarok" ), i18n ( "&Add to Playlist" ), this ); m_appendAction->setProperty( "popupdropper_svg_id", "append" ); - connect ( m_appendAction, SIGNAL (triggered()), this, SLOT (slotAppendChildTracks()) ); + connect ( m_appendAction, &QAction::triggered, this, &LastFmTreeView::slotAppendChildTracks ); } actions.append ( m_appendAction ); if ( m_loadAction == 0 ) { m_loadAction = new QAction ( QIcon::fromTheme( "folder-open" ), i18nc ( "Replace the currently loaded tracks with these", "&Replace Playlist" ), this ); m_appendAction->setProperty( "popupdropper_svg_id", "load" ); - connect ( m_loadAction, SIGNAL (triggered()), this, SLOT (slotReplacePlaylistByChildTracks()) ); + connect ( m_loadAction, &QAction::triggered, this, &LastFmTreeView::slotReplacePlaylistByChildTracks ); } actions.append ( m_loadAction ); } default: break; } return actions; } void LastFmTreeView::mouseDoubleClickEvent( QMouseEvent *event ) { QModelIndex index; index = indexAt( event->pos() ); if( index.isValid() && index.internalPointer() ) { playChildTracks( index, Playlist::OnDoubleClickOnSelectedItems ); } } void LastFmTreeView::startDrag(Qt::DropActions supportedActions) { DEBUG_BLOCK //setSelectionMode( QAbstractItemView::NoSelection ); // When a parent item is dragged, startDrag() is called a bunch of times. Here we prevent that: m_dragMutex.lock(); if( m_ongoingDrag ) { m_dragMutex.unlock(); return; } m_ongoingDrag = true; m_dragMutex.unlock(); -/* FIXME: disabled temporarily for KF5 porting if( !m_pd ) m_pd = The::popupDropperFactory()->createPopupDropper( Context::ContextView::self() ); -*/ if( m_pd && m_pd->isHidden() ) { QModelIndexList indices = selectedIndexes(); QActionList actions = createBasicActions( indices ); QFont font; font.setPointSize( 16 ); font.setBold( true ); foreach( QAction * action, actions ) m_pd->addItem( The::popupDropperFactory()->createItem( action ) ); m_currentItems.clear(); foreach( const QModelIndex &index, indices ) { if( index.isValid() && index.internalPointer() ) m_currentItems << index; } PopupDropperItem* subItem; PopupDropper * morePud = 0; if ( actions.count() > 1 ) { morePud = The::popupDropperFactory()->createPopupDropper( 0, true ); foreach( QAction * action, actions ) morePud->addItem( The::popupDropperFactory()->createItem( action ) ); } else m_pd->addItem( The::popupDropperFactory()->createItem( actions[0] ) ); //TODO: Keep bugging i18n team about problems with 3 dots if ( actions.count() > 1 ) { subItem = m_pd->addSubmenu( &morePud, i18n( "More..." ) ); The::popupDropperFactory()->adjustItem( subItem ); } m_pd->show(); } QTreeView::startDrag( supportedActions ); debug() << "After the drag!"; if( m_pd ) { debug() << "clearing PUD"; - connect( m_pd, SIGNAL(fadeHideFinished()), m_pd, SLOT(clear()) ); + connect( m_pd, &PopupDropper::fadeHideFinished, m_pd, &PopupDropper::clear ); m_pd->hide(); } m_dragMutex.lock(); m_ongoingDrag = false; m_dragMutex.unlock(); } void LastFmTreeView::slotReplacePlaylistByChildTracks() { playChildTracks( m_currentItems, Playlist::OnReplacePlaylistAction ); } void LastFmTreeView::slotAppendChildTracks() { playChildTracks( m_currentItems, Playlist::OnAppendToPlaylistAction ); } void LastFmTreeView::playChildTracks( const QModelIndex &item, Playlist::AddOptions insertMode) { QModelIndexList items; items << item; playChildTracks( items, insertMode ); } void LastFmTreeView::playChildTracks ( const QModelIndexList &items, Playlist::AddOptions insertMode ) { debug() << "LASTFM current items : " << items.size(); Meta::TrackList list; foreach ( const QModelIndex &item, items ) { Meta::TrackPtr track = model()->data(item, LastFm::TrackRole).value< Meta::TrackPtr >(); if ( track ) list << track; } qStableSort ( list.begin(), list.end(), Meta::Track::lessThan ); The::playlistController()->insertOptioned( list, insertMode ); } diff --git a/src/services/lastfm/LastFmTreeView.h b/src/services/lastfm/LastFmTreeView.h index da2e4ebe0c..a483a0b9b3 100644 --- a/src/services/lastfm/LastFmTreeView.h +++ b/src/services/lastfm/LastFmTreeView.h @@ -1,76 +1,76 @@ /**************************************************************************************** * Copyright (c) 2009 Casey Link * * Copyright (c) 2005-2007 Christian Muehlhaeuser, Last.fm Ltd * * Copyright (c) 2005-2007 Max Howell, Last.fm Ltd * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef LASTFMTREEVIEW_H #define LASTFMTREEVIEW_H #include "playlist/PlaylistController.h" #include "widgets/PrettyTreeView.h" #include #include -#include +#include class LastFmTreeModel; class QAction; class QContextMenuEvent; class QMouseEvent; class PopupDropper; typedef QList QActionList; class LastFmTreeView : public Amarok::PrettyTreeView { Q_OBJECT public: LastFmTreeView ( QWidget* parent = 0 ); ~LastFmTreeView(); Q_SIGNALS: void statusMessage ( const QString& message ); void plsShowRestState(); void plsShowNowPlaying(); private Q_SLOTS: void slotReplacePlaylistByChildTracks(); void slotAppendChildTracks(); protected: virtual void contextMenuEvent ( QContextMenuEvent* ); void mouseDoubleClickEvent( QMouseEvent *event ); void startDrag( Qt::DropActions supportedActions ); private: enum ContextMenuActionType { ExecQMenu, DoQMenuDefaultAction }; void playChildTracks ( const QModelIndex &item, Playlist::AddOptions insertMode ); void playChildTracks ( const QModelIndexList &items, Playlist::AddOptions insertMode ); QActionList createBasicActions( const QModelIndexList &indcies ); QTimer* m_timer; LastFmTreeModel* m_model; PopupDropper* m_pd; QAction* m_appendAction; QAction* m_loadAction; QModelIndexList m_currentItems; QMutex m_dragMutex; bool m_ongoingDrag; }; #endif diff --git a/src/services/lastfm/LoveTrackAction.cpp b/src/services/lastfm/LoveTrackAction.cpp index f36f726a0a..5b04fb901d 100644 --- a/src/services/lastfm/LoveTrackAction.cpp +++ b/src/services/lastfm/LoveTrackAction.cpp @@ -1,39 +1,39 @@ /**************************************************************************************** * Copyright (c) 2009 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "LoveTrackAction.h" #include "LastFmService.h" #include LoveTrackAction::LoveTrackAction( LastFmService * service ) : GlobalCollectionTrackAction( i18n( "Last.fm: Love" ), service ) , m_service( service ) { setIcon( QIcon::fromTheme( "love-amarok") ); setProperty( "popupdropper_svg_id", "lastfm" ); - connect( this, SIGNAL(triggered(bool)), SLOT(slotTriggered()) ); + connect( this, &LoveTrackAction::triggered, this, &LoveTrackAction::slotTriggered ); } void LoveTrackAction::slotTriggered() { DEBUG_BLOCK m_service->love( track() ); } diff --git a/src/services/lastfm/ScrobblerAdapter.cpp b/src/services/lastfm/ScrobblerAdapter.cpp index 066434384c..b088704997 100644 --- a/src/services/lastfm/ScrobblerAdapter.cpp +++ b/src/services/lastfm/ScrobblerAdapter.cpp @@ -1,298 +1,298 @@ /**************************************************************************************** * Copyright (c) 2007 Shane King * * Copyright (c) 2008 Leo Franchi * * Copyright (c) 2012 Matěj Laitl * * Copyright (c) 2013 Vedant Agarwala * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "lastfm" #include "ScrobblerAdapter.h" #include "MainWindow.h" #include "core/collections/Collection.h" #include "core/interfaces/Logger.h" #include "core/meta/Meta.h" #include "core/meta/support/MetaConstants.h" #include "core/support/Components.h" #include "core/support/Debug.h" #include #include #include ScrobblerAdapter::ScrobblerAdapter( const QString &clientId, const LastFmServiceConfigPtr &config ) : m_scrobbler( clientId ) , m_config( config ) { // work around a bug in liblastfm -- -it doesn't create its config dir, so when it // tries to write the track cache, it fails silently. Last check: liblastfm 1.0.! QList dirs; dirs << lastfm::dir::runtimeData() << lastfm::dir::cache() << lastfm::dir::logs(); foreach( const QDir &dir, dirs ) { if( !dir.exists() ) { debug() << "creating" << dir.absolutePath() << "directory for liblastfm"; dir.mkpath( "." ); } } - connect( The::mainWindow(), SIGNAL(loveTrack(Meta::TrackPtr)), - SLOT(loveTrack(Meta::TrackPtr)) ); - connect( The::mainWindow(), SIGNAL(banTrack(Meta::TrackPtr)), - SLOT(banTrack(Meta::TrackPtr)) ); + connect( The::mainWindow(), &MainWindow::loveTrack, + this, &ScrobblerAdapter::loveTrack ); + connect( The::mainWindow(), &MainWindow::banTrack, + this, &ScrobblerAdapter::banTrack ); - connect( &m_scrobbler, SIGNAL(scrobblesSubmitted(QList)), - SLOT(slotScrobblesSubmitted(QList)) ); - connect( &m_scrobbler, SIGNAL(nowPlayingError(int,QString)), - SLOT(slotNowPlayingError(int,QString))); + connect( &m_scrobbler, &lastfm::Audioscrobbler::scrobblesSubmitted, + this, &ScrobblerAdapter::slotScrobblesSubmitted ); + connect( &m_scrobbler, &lastfm::Audioscrobbler::nowPlayingError, + this, &ScrobblerAdapter::slotNowPlayingError ); } ScrobblerAdapter::~ScrobblerAdapter() { } QString ScrobblerAdapter::prettyName() const { return i18n( "Last.fm" ); } StatSyncing::ScrobblingService::ScrobbleError ScrobblerAdapter::scrobble( const Meta::TrackPtr &track, double playedFraction, const QDateTime &time ) { Q_ASSERT( track ); if( isToBeSkipped( track ) ) { debug() << "scrobble(): refusing track" << track->prettyUrl() << "- contains label:" << m_config->filteredLabel() << "which is marked to be skipped"; return SkippedByUser; } if( track->length() * qMin( 1.0, playedFraction ) < 30 * 1000 ) { debug() << "scrobble(): refusing track" << track->prettyUrl() << "- played time (" << track->length() / 1000 << "*" << playedFraction << "s) shorter than 30 s"; return TooShort; } int playcount = qRound( playedFraction ); if( playcount <= 0 ) { debug() << "scrobble(): refusing track" << track->prettyUrl() << "- played " << "fraction (" << playedFraction * 100 << "%) less than 50 %"; return TooShort; } lastfm::MutableTrack lfmTrack; copyTrackMetadata( lfmTrack, track ); // since liblastfm >= 1.0.3 it interprets following extra property: lfmTrack.setExtra( "playCount", QString::number( playcount ) ); lfmTrack.setTimeStamp( time.isValid() ? time : QDateTime::currentDateTime() ); debug() << "scrobble: " << lfmTrack.artist() << "-" << lfmTrack.album() << "-" << lfmTrack.title() << "source:" << lfmTrack.source() << "duration:" << lfmTrack.duration(); m_scrobbler.cache( lfmTrack ); m_scrobbler.submit(); // since liblastfm 1.0.7, submit() is not called automatically upon cache() switch( lfmTrack.scrobbleStatus() ) { case lastfm::Track::Cached: case lastfm::Track::Submitted: return NoError; case lastfm::Track::Null: case lastfm::Track::Error: break; } return BadMetadata; } void ScrobblerAdapter::updateNowPlaying( const Meta::TrackPtr &track ) { lastfm::MutableTrack lfmTrack; if( track ) { if( isToBeSkipped( track ) ) { debug() << "updateNowPlaying(): refusing track" << track->prettyUrl() << "- contains label:" << m_config->filteredLabel() << "which is marked to be skipped"; return; } copyTrackMetadata( lfmTrack, track ); debug() << "nowPlaying: " << lfmTrack.artist() << "-" << lfmTrack.album() << "-" << lfmTrack.title() << "source:" << lfmTrack.source() << "duration:" << lfmTrack.duration(); m_scrobbler.nowPlaying( lfmTrack ); } else { debug() << "removeNowPlaying"; QNetworkReply *reply = lfmTrack.removeNowPlaying(); // works even with empty lfmTrack - connect( reply, SIGNAL(finished()), reply, SLOT(deleteLater()) ); // don't leak + connect( reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater ); // don't leak } } void ScrobblerAdapter::loveTrack( const Meta::TrackPtr &track ) // slot { if( !track ) return; lastfm::MutableTrack trackInfo; copyTrackMetadata( trackInfo, track ); trackInfo.love(); Amarok::Components::logger()->shortMessage( i18nc( "As in Last.fm", "Loved Track: %1", track->prettyName() ) ); } void ScrobblerAdapter::banTrack( const Meta::TrackPtr &track ) // slot { if( !track ) return; lastfm::MutableTrack trackInfo; copyTrackMetadata( trackInfo, track ); trackInfo.ban(); Amarok::Components::logger()->shortMessage( i18nc( "As in Last.fm", "Banned Track: %1", track->prettyName() ) ); } void ScrobblerAdapter::slotScrobblesSubmitted( const QList &tracks ) { foreach( const lastfm::Track &track, tracks ) { switch( track.scrobbleStatus() ) { case lastfm::Track::Null: warning() << "slotScrobblesSubmitted(): track" << track << "has Null scrobble status, strange"; break; case lastfm::Track::Cached: warning() << "slotScrobblesSubmitted(): track" << track << "has Cached scrobble status, strange"; break; case lastfm::Track::Submitted: if( track.corrected() && m_config->announceCorrections() ) announceTrackCorrections( track ); break; case lastfm::Track::Error: warning() << "slotScrobblesSubmitted(): error scrobbling track" << track << ":" << track.scrobbleErrorText(); break; } } } void ScrobblerAdapter::slotNowPlayingError( int code, const QString &message ) { Q_UNUSED( code ) warning() << "error updating Now Playing status:" << message; } void ScrobblerAdapter::copyTrackMetadata( lastfm::MutableTrack &to, const Meta::TrackPtr &track ) { to.setTitle( track->name() ); QString artistOrComposer; Meta::ComposerPtr composer = track->composer(); if( m_config->scrobbleComposer() && composer ) artistOrComposer = composer->name(); Meta::ArtistPtr artist = track->artist(); if( artistOrComposer.isEmpty() && artist ) artistOrComposer = artist->name(); to.setArtist( artistOrComposer ); Meta::AlbumPtr album = track->album(); Meta::ArtistPtr albumArtist; if( album ) { to.setAlbum( album->name() ); albumArtist = album->hasAlbumArtist() ? album->albumArtist() : Meta::ArtistPtr(); } if( albumArtist ) to.setAlbumArtist( albumArtist->name() ); to.setDuration( track->length() / 1000 ); if( track->trackNumber() >= 0 ) to.setTrackNumber( track->trackNumber() ); lastfm::Track::Source source = lastfm::Track::Player; if( track->type() == "stream/lastfm" ) source = lastfm::Track::LastFmRadio; else if( track->type().startsWith( "stream" ) ) source = lastfm::Track::NonPersonalisedBroadcast; else if( track->collection() && track->collection()->collectionId() != "localCollection" ) source = lastfm::Track::MediaDevice; to.setSource( source ); } static QString printCorrected( qint64 field, const QString &original, const QString &corrected ) { if( corrected.isEmpty() || original == corrected ) return QString(); return i18nc( "%1 is field name such as Album Name; %2 is the original value; %3 is " "the corrected value", "%1 %2 should be corrected to " "%3", Meta::i18nForField( field ), original, corrected ); } static QString printCorrected( qint64 field, const lastfm::AbstractType &original, const lastfm::AbstractType &corrected ) { return printCorrected( field, original.toString(), corrected.toString() ); } void ScrobblerAdapter::announceTrackCorrections( const lastfm::Track &track ) { static const lastfm::Track::Corrections orig = lastfm::Track::Original; static const lastfm::Track::Corrections correct = lastfm::Track::Corrected; QString trackName = i18nc( "%1 is artist, %2 is title", "%1 - %2", track.artist().name(), track.title() ); QStringList lines; lines << i18n( "Last.fm suggests that some tags of track %1 should be " "corrected:", trackName ); QString line; line = printCorrected( Meta::valTitle, track.title( orig ), track.title( correct ) ); if( !line.isEmpty() ) lines << line; line = printCorrected( Meta::valAlbum, track.album( orig ), track.album( correct ) ); if( !line.isEmpty() ) lines << line; line = printCorrected( Meta::valArtist, track.artist( orig ), track.artist( correct ) ); if( !line.isEmpty() ) lines << line; line = printCorrected( Meta::valAlbumArtist, track.albumArtist( orig ), track.albumArtist( correct ) ); if( !line.isEmpty() ) lines << line; Amarok::Components::logger()->longMessage( lines.join( "
" ) ); } bool ScrobblerAdapter::isToBeSkipped( const Meta::TrackPtr &track ) const { Q_ASSERT( track ); if( !m_config->filterByLabel() ) return false; foreach( const Meta::LabelPtr &label, track->labels() ) if( label->name() == m_config->filteredLabel() ) return true; return false; } diff --git a/src/services/lastfm/SimilarArtistsAction.cpp b/src/services/lastfm/SimilarArtistsAction.cpp index 83aaceacf1..fa1860cff9 100644 --- a/src/services/lastfm/SimilarArtistsAction.cpp +++ b/src/services/lastfm/SimilarArtistsAction.cpp @@ -1,41 +1,41 @@ /**************************************************************************************** * Copyright (c) 2008 Dan Meltzer * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "SimilarArtistsAction.h" #include "core/meta/Meta.h" #include "core-impl/collections/support/CollectionManager.h" #include "playlist/PlaylistController.h" -#include +#include #include SimilarArtistsAction::SimilarArtistsAction( QObject *parent ) : GlobalCollectionArtistAction( i18n( "Play Similar Artists from Last.fm" ), parent ) { - connect( this, SIGNAL(triggered(bool)), SLOT(slotTriggered()) ); + connect( this, &SimilarArtistsAction::triggered, this, &SimilarArtistsAction::slotTriggered ); setIcon( QIcon::fromTheme("view-services-lastfm-amarok") ); setProperty( "popupdropper_svg_id", "lastfm" ); } void SimilarArtistsAction::slotTriggered() { const QString url = "lastfm://artist/" + artist()->prettyName() + "/similarartists"; Meta::TrackPtr lastfmtrack = CollectionManager::instance()->trackForUrl( QUrl( url ) ); The::playlistController()->insertOptioned( lastfmtrack, Playlist::OnPlayMediaAction ); } diff --git a/src/services/lastfm/SynchronizationAdapter.cpp b/src/services/lastfm/SynchronizationAdapter.cpp index adc5c99c3e..723b9646e3 100644 --- a/src/services/lastfm/SynchronizationAdapter.cpp +++ b/src/services/lastfm/SynchronizationAdapter.cpp @@ -1,301 +1,299 @@ /**************************************************************************************** * Copyright (c) 2012 Matěj Laitl * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "SynchronizationAdapter.h" #include "MetaValues.h" #include "core/support/Debug.h" #include "core/support/SemaphoreReleaser.h" #include "services/lastfm/SynchronizationTrack.h" #include #include #include #include const int SynchronizationAdapter::s_entriesPerQuery( 200 ); SynchronizationAdapter::SynchronizationAdapter( const LastFmServiceConfigPtr &config ) : m_config( config ) { - connect( this, SIGNAL(startArtistSearch(int)), SLOT(slotStartArtistSearch(int)) ); - connect( this, SIGNAL(startTrackSearch(QString,int)), - SLOT(slotStartTrackSearch(QString,int)) ); - connect( this, SIGNAL(startTagSearch(QString,QString)), - SLOT(slotStartTagSearch(QString,QString)) ); + connect( this, &SynchronizationAdapter::startArtistSearch, this, &SynchronizationAdapter::slotStartArtistSearch ); + connect( this, &SynchronizationAdapter::startTrackSearch, this, &SynchronizationAdapter::slotStartTrackSearch ); + connect( this, &SynchronizationAdapter::startTagSearch, this, &SynchronizationAdapter::slotStartTagSearch ); } SynchronizationAdapter::~SynchronizationAdapter() { } QString SynchronizationAdapter::id() const { return "lastfm"; } QString SynchronizationAdapter::prettyName() const { return i18n( "Last.fm" ); } QString SynchronizationAdapter::description() const { return i18nc( "description of the Last.fm statistics synchronization provider", "slows down track matching" ); } QIcon SynchronizationAdapter::icon() const { return QIcon::fromTheme( "view-services-lastfm-amarok" ); } qint64 SynchronizationAdapter::reliableTrackMetaData() const { return Meta::valArtist | Meta::valAlbum | Meta::valTitle; } qint64 SynchronizationAdapter::writableTrackStatsData() const { bool useRating = m_config->useFancyRatingTags(); return ( useRating ? Meta::valRating : 0 ) | Meta::valLabel; } StatSyncing::Provider::Preference SynchronizationAdapter::defaultPreference() { return StatSyncing::Provider::Never; // don't overload Last.fm servers } QSet SynchronizationAdapter::artists() { DEBUG_BLOCK Q_ASSERT( m_semaphore.available() == 0 ); emit startArtistSearch( 1 ); // Last.fm indexes from 1 m_semaphore.acquire(); QSet ret = m_artists; m_artists.clear(); // save memory debug() << __PRETTY_FUNCTION__ << ret.count() << "artists total"; return ret; } StatSyncing::TrackList SynchronizationAdapter::artistTracks( const QString &artistName ) { /* This method should match track artists case-sensitively, but we don't do it. * Last.fm webservice returns only the preferred capitalisation in artists(), so no * duplicates threat us. */ Q_ASSERT( m_semaphore.available() == 0 ); emit startTrackSearch( artistName, 1 ); // Last.fm indexes from 1 m_semaphore.acquire(); debug() << __PRETTY_FUNCTION__ << m_tracks.count() << "tracks from" << artistName << m_tagQueue.count() << "of them have tags"; // fetch tags QMutableListIterator it( m_tagQueue ); while( it.hasNext() ) { StatSyncing::TrackPtr track = it.next(); emit startTagSearch( track->artist(), track->name() ); m_semaphore.acquire(); it.remove(); } StatSyncing::TrackList ret = m_tracks; m_tracks.clear(); // save memory m_tagQueue.clear(); // paranoia return ret; } void SynchronizationAdapter::slotStartArtistSearch( int page ) { QString user = m_config->username(); QNetworkReply *reply = lastfm::Library::getArtists( user, s_entriesPerQuery, page ); - connect( reply, SIGNAL(finished()), SLOT(slotArtistsReceived()) ); + connect( reply, &QNetworkReply::finished, this, &SynchronizationAdapter::slotArtistsReceived ); } void SynchronizationAdapter::slotStartTrackSearch( QString artistName, int page ) { lastfm::Artist artist( artistName ); QString user = m_config->username(); QNetworkReply *reply = lastfm::Library::getTracks( user, artist, s_entriesPerQuery, page ); - connect( reply, SIGNAL(finished()), SLOT(slotTracksReceived()) ); + connect( reply, &QNetworkReply::finished, this, &SynchronizationAdapter::slotTracksReceived ); } void SynchronizationAdapter::slotStartTagSearch( QString artistName, QString trackName ) { lastfm::MutableTrack track; track.setArtist( artistName ); track.setTitle( trackName ); QNetworkReply *reply = track.getTags(); - connect( reply, SIGNAL(finished()), SLOT(slotTagsReceived()) ); + connect( reply, &QNetworkReply::finished, this, &SynchronizationAdapter::slotTagsReceived ); } void SynchronizationAdapter::slotArtistsReceived() { SemaphoreReleaser releaser( &m_semaphore ); QNetworkReply *reply = qobject_cast( sender() ); if( !reply ) { warning() << __PRETTY_FUNCTION__ << "cannot cast sender to QNetworkReply. (?)"; return; } reply->deleteLater(); lastfm::XmlQuery lfm; if( !lfm.parse( reply->readAll() ) ) { warning() << __PRETTY_FUNCTION__ << "Error parsing Last.fm reply:" << lfm.parseError().message(); return; } lastfm::XmlQuery artists = lfm[ "artists" ]; bool ok = false; int page = artists.attribute( "page" ).toInt( &ok ); if( !ok ) { warning() << __PRETTY_FUNCTION__ << "cannot read page number"; return; } int totalPages = artists.attribute( "totalPages" ).toInt( &ok ); if( !ok ) { warning() << __PRETTY_FUNCTION__ << "cannot read total number or pages"; return; } debug() << __PRETTY_FUNCTION__ << "page" << page << "of" << totalPages; // following is based on lastfm::Artist::list(): foreach( const lastfm::XmlQuery &xq, lfm.children( "artist" ) ) { lastfm::Artist artist( xq ); m_artists.insert( artist.name() ); } // Last.fm indexes from 1! if( page < totalPages ) { releaser.dontRelease(); // don't release the semaphore yet emit startArtistSearch( page + 1 ); } } void SynchronizationAdapter::slotTracksReceived() { SemaphoreReleaser releaser( &m_semaphore ); QNetworkReply *reply = qobject_cast( sender() ); if( !reply ) { warning() << __PRETTY_FUNCTION__ << "cannot cast sender to QNetworkReply. (?)"; return; } reply->deleteLater(); lastfm::XmlQuery lfm; if( !lfm.parse( reply->readAll() ) ) { warning() << __PRETTY_FUNCTION__ << "Error parsing Last.fm reply:" << lfm.parseError().message(); return; } lastfm::XmlQuery tracks = lfm[ "tracks" ]; bool ok = false; int page = tracks.attribute( "page" ).toInt( &ok ); if( !ok ) { warning() << __PRETTY_FUNCTION__ << "cannot read page number"; return; } int totalPages = tracks.attribute( "totalPages" ).toInt( &ok ); if( !ok ) { warning() << __PRETTY_FUNCTION__ << "cannot read total number or pages"; return; } QString searchedArtist = tracks.attribute( "artist" ); if( searchedArtist.isEmpty() ) { warning() << __PRETTY_FUNCTION__ << "searchedArtist in Last.fm reply is empty"; return; } // following is based on lastfm::Track::list(): foreach( const lastfm::XmlQuery &xq, lfm.children( "track" ) ) { QString name = xq[ "name" ].text(); int playCount = xq[ "playcount" ].text().toInt(); int tagCount = xq[ "tagcount" ].text().toInt(); QString artist = xq[ "artist" ][ "name" ].text(); QString album = xq[ "album" ][ "name" ].text(); bool useRatings = m_config->useFancyRatingTags(); StatSyncing::TrackPtr track( new SynchronizationTrack( artist, album, name, playCount, useRatings ) ); m_tracks.append( track ); if( tagCount > 0 ) m_tagQueue.append( track ); } // Last.fm indexes from 1! if( page < totalPages ) { releaser.dontRelease(); // don't release the semaphore yet emit startTrackSearch( searchedArtist, page + 1 ); } } void SynchronizationAdapter::slotTagsReceived() { SemaphoreReleaser releaser( &m_semaphore ); QNetworkReply *reply = qobject_cast( sender() ); if( !reply ) { warning() << __PRETTY_FUNCTION__ << "cannot cast sender to QNetworkReply. (?)"; return; } reply->deleteLater(); lastfm::XmlQuery lfm; if( !lfm.parse( reply->readAll() ) ) { warning() << __PRETTY_FUNCTION__ << "Error parsing Last.fm reply:" << lfm.parseError().message(); return; } QSet tags; foreach( const lastfm::XmlQuery &xq, lfm.children( "tag" ) ) { tags.insert( xq[ "name" ].text() ); } Q_ASSERT( !m_tagQueue.isEmpty() ); SynchronizationTrack *track = dynamic_cast( m_tagQueue.first().data() ); Q_ASSERT( track ); track->parseAndSaveLastFmTags( tags ); } diff --git a/src/services/lastfm/SynchronizationTrack.cpp b/src/services/lastfm/SynchronizationTrack.cpp index 39c23c7eab..4b3a09d7ed 100644 --- a/src/services/lastfm/SynchronizationTrack.cpp +++ b/src/services/lastfm/SynchronizationTrack.cpp @@ -1,255 +1,255 @@ /**************************************************************************************** * Copyright (c) 2012 Matěj Laitl * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "SynchronizationTrack.h" #include "core/support/Debug.h" #include "core/support/SemaphoreReleaser.h" #include #include #include #include SynchronizationTrack::SynchronizationTrack( QString artist, QString album, QString name, int playCount, bool useFancyRatingTags ) : m_artist( artist ) , m_album( album ) , m_name( name ) , m_rating( 0 ) , m_newRating( 0 ) , m_playCount( playCount ) , m_useFancyRatingTags( useFancyRatingTags ) { // ensure this object is created in a main thread Q_ASSERT( thread() == QCoreApplication::instance()->thread() ); - connect( this, SIGNAL(startTagAddition(QStringList)), - SLOT(slotStartTagAddition(QStringList)) ); - connect( this, SIGNAL(startTagRemoval()), - SLOT(slotStartTagRemoval()) ); + connect( this, &SynchronizationTrack::startTagAddition, + this, &SynchronizationTrack::slotStartTagAddition ); + connect( this, &SynchronizationTrack::startTagRemoval, + this, &SynchronizationTrack::slotStartTagRemoval ); } QString SynchronizationTrack::name() const { return m_name; } QString SynchronizationTrack::album() const { return m_album; } QString SynchronizationTrack::artist() const { return m_artist; } int SynchronizationTrack::rating() const { return m_rating; } void SynchronizationTrack::setRating( int rating ) { m_newRating = rating; } QDateTime SynchronizationTrack::firstPlayed() const { return QDateTime(); // no support on Last.fm side yet } QDateTime SynchronizationTrack::lastPlayed() const { return QDateTime(); // no support on Last.fm side yet } int SynchronizationTrack::playCount() const { return m_playCount; } QSet SynchronizationTrack::labels() const { return m_labels; } void SynchronizationTrack::setLabels( const QSet &labels ) { m_newLabels = labels; } void SynchronizationTrack::commit() { if( m_newRating == m_rating && m_newLabels == m_labels ) return; const QSet existingLabels = m_labels | m_ratingLabels; if( m_useFancyRatingTags ) { // implicitly we remove all ratingLabels here by not including them in m_newLabels if( m_newRating > 0 ) { QString ratingLabel = QString( "%1 of 10 stars" ).arg( m_newRating ); m_newLabels.insert( ratingLabel ); m_ratingLabels = QSet() << ratingLabel; } } else m_newLabels |= m_ratingLabels; // preserve all rating labels QSet toAdd = m_newLabels - existingLabels; QSet toRemove = existingLabels - m_newLabels; // first remove, than add Last.fm may limit number of track tags if( !toRemove.isEmpty() ) { Q_ASSERT( m_semaphore.available() == 0 ); m_tagsToRemove = toRemove.toList(); emit startTagRemoval(); m_semaphore.acquire(); // wait for the job to complete m_tagsToRemove.clear(); } if( !toAdd.isEmpty() ) { Q_ASSERT( m_semaphore.available() == 0 ); emit startTagAddition( toAdd.toList() ); m_semaphore.acquire(); // wait for the job to complete } m_rating = m_newRating; m_labels = m_newLabels - m_ratingLabels; } void SynchronizationTrack::parseAndSaveLastFmTags( const QSet &tags ) { m_labels.clear(); m_ratingLabels.clear(); m_rating = 0; // we still match and explicitly ignore rating tags even in m_useFancyRatingTags is false QRegExp rx( "([0-9]{1,3}) of ([0-9]{1,3}) stars", Qt::CaseInsensitive ); foreach( const QString &tag, tags ) { if( rx.exactMatch( tag ) ) { m_ratingLabels.insert( tag ); QStringList texts = rx.capturedTexts(); if( texts.count() != 3 ) continue; qreal numerator = texts.at( 1 ).toDouble(); qreal denominator = texts.at( 2 ).toDouble(); if( denominator == 0.0 ) continue; m_rating = qBound( 0, qRound( 10.0 * numerator / denominator ), 10 ); } else m_labels.insert( tag ); } if( !m_useFancyRatingTags || m_ratingLabels.count() > 1 ) m_rating = 0; // ambiguous or not requested m_newLabels = m_labels; m_newRating = m_rating; } void SynchronizationTrack::slotStartTagAddition( QStringList tags ) { lastfm::MutableTrack track; track.setArtist( m_artist ); track.setAlbum( m_album ); track.setTitle( m_name ); if( tags.count() > 10 ) tags = tags.mid( 0, 10 ); // Last.fm says 10 tags is max QNetworkReply *reply = track.addTags( tags ); - connect( reply, SIGNAL(finished()), SLOT(slotTagsAdded()) ); + connect( reply, &QNetworkReply::finished, this, &SynchronizationTrack::slotTagsAdded ); } void SynchronizationTrack::slotStartTagRemoval() { Q_ASSERT( m_tagsToRemove.count() ); lastfm::MutableTrack track; track.setArtist( m_artist ); track.setAlbum( m_album ); track.setTitle( m_name ); QNetworkReply *reply = track.removeTag( m_tagsToRemove.takeFirst() ); - connect( reply, SIGNAL(finished()), SLOT(slotTagRemoved()) ); + connect( reply, &QNetworkReply::finished, this, &SynchronizationTrack::slotTagRemoved ); } void SynchronizationTrack::slotTagsAdded() { SemaphoreReleaser releaser( &m_semaphore ); QNetworkReply *reply = qobject_cast( sender() ); if( !reply ) { warning() << __PRETTY_FUNCTION__ << "cannot cast sender to QNetworkReply. (?)"; return; } reply->deleteLater(); lastfm::XmlQuery lfm; if( !lfm.parse( reply->readAll() ) ) { warning() << __PRETTY_FUNCTION__ << "error adding tags:" << lfm.parseError().message(); return; } } void SynchronizationTrack::slotTagRemoved() { SemaphoreReleaser releaser( &m_semaphore ); QNetworkReply *reply = qobject_cast( sender() ); if( !reply ) { warning() << __PRETTY_FUNCTION__ << "cannot cast sender to QNetworkReply. (?)"; return; } reply->deleteLater(); lastfm::XmlQuery lfm; if( !lfm.parse( reply->readAll() ) ) { warning() << __PRETTY_FUNCTION__ << "error removing a tag:" << lfm.parseError().message(); return; } // remove the next one, sadly only one at a time can be removed if( !m_tagsToRemove.isEmpty() ) { releaser.dontRelease(); emit startTagRemoval(); } } diff --git a/src/services/lastfm/amarok_lastfm_shared_export.h b/src/services/lastfm/amarok_lastfm_shared_export.h index 18261c1e79..b665b12e75 100644 --- a/src/services/lastfm/amarok_lastfm_shared_export.h +++ b/src/services/lastfm/amarok_lastfm_shared_export.h @@ -1,36 +1,36 @@ /**************************************************************************************** * Copyright (c) 2010 Téo Mrnjavac * * Copyright (c) 2012 Matěj Laitl * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef AMAROK_LASTFM_SHARED_EXPORT_H #define AMAROK_LASTFM_SHARED_EXPORT_H -/* needed for KDE_EXPORT and KDE_IMPORT macros */ -#include +/* needed for Q_DECL_EXPORT and Q_DECL_IMPORT macros */ +#include #ifndef AMAROK_LASTFM_SHARED_EXPORT # ifdef MAKE_AMAROK_SERVICE_LASTFM_SHARED_LIB /* We are building this library */ -# define AMAROK_LASTFM_SHARED_EXPORT KDE_EXPORT +# define AMAROK_LASTFM_SHARED_EXPORT Q_DECL_EXPORT # else /* We are using this library */ -# define AMAROK_LASTFM_SHARED_EXPORT KDE_IMPORT +# define AMAROK_LASTFM_SHARED_EXPORT Q_DECL_IMPORT # endif // MAKE_AMAROK_SERVICE_LASTFM_SHARED_LIB #endif // AMAROK_LASTFM_SHARED_EXPORT #endif //AMAROK_LASTFM_SHARED_EXPORT_H diff --git a/src/services/lastfm/amarok_service_lastfm.desktop b/src/services/lastfm/amarok_service_lastfm.desktop index 7c93f15075..b6aa45b54e 100644 --- a/src/services/lastfm/amarok_service_lastfm.desktop +++ b/src/services/lastfm/amarok_service_lastfm.desktop @@ -1,125 +1,124 @@ [Desktop Entry] Type=Service -ServiceTypes=KPluginInfo Icon=view-services-lastfm-amarok Name=Last.fm Name[bg]=Last.fm Name[bs]=Last.fm Name[ca]=Last.fm Name[ca@valencia]=Last.fm Name[cs]=Last.fm Name[csb]=Last.fm Name[da]=Last.fm Name[de]=Last.fm Name[el]=Last.fm Name[en_GB]=Last.fm Name[eo]=Last.fm Name[es]=Last.fm Name[et]=Last.fm Name[eu]=Last.fm Name[fi]=Last.fm Name[fr]=Last.fm Name[ga]=Last.fm Name[gl]=Last.fm Name[hu]=Last.fm Name[id]=Last.fm Name[is]=Last.fm Name[it]=Last.fm Name[ja]=Last.fm Name[km]=Last.fm Name[ko]=Last.fm Name[lt]=Last.fm Name[lv]=Last.fm Name[nb]=Last.fm Name[nds]=Last.fm Name[nl]=Last.fm Name[nn]=Last.fm Name[pa]=Last.fm Name[pl]=Last.fm Name[pt]=Last.fm Name[pt_BR]=Last.fm Name[ro]=Last.fm Name[ru]=Last.fm Name[sk]=Last.fm Name[sl]=Last.fm Name[sq]=Last.fm Name[sr]=ЛастФМ Name[sr@ijekavian]=ЛастФМ Name[sr@ijekavianlatin]=Last.fm Name[sr@latin]=Last.fm Name[sv]=Last.fm Name[th]=บริการ Last.fm Name[tr]=Last.fm Name[ug]=Last.fm Name[uk]=Last.fm Name[wa]=Last.fm Name[x-test]=xxLast.fmxx Name[zh_CN]=Last.fm Name[zh_TW]=Last.fm Comment=A service that integrates Last.fm functionality into Amarok Comment[bg]=Услуга, която вгражда Last.fm в Amarok Comment[bs]=Servis koji ugrađuje LastFM‑ove mogućnosti u Amarok Comment[ca]=Un servei que integra la funcionalitat de Last.fm en l'Amarok Comment[ca@valencia]=Un servei que integra la funcionalitat de Last.fm en l'Amarok Comment[cs]=Služba, která do Amaroku integruje funkcionalitu last.fm Comment[da]=En tjeneste der integrerer Last.fm-funktionalitet i Amarok Comment[de]=Ein Dienst zum Einbinden der Funktionen von Last.fm in Amarok Comment[el]=Μία υπηρεσία που ενσωματώνει λειτουργίες του Last.fm στο Amarok Comment[en_GB]=A service that integrates Last.fm functionality into Amarok Comment[es]=Un servicio que integra la funcionalidad de Last.fm en Amarok Comment[et]=Last.fm-i funktsioonid Amarokki lõimiv teenus Comment[eu]=Last.fm-eko funtzionaltasuna Amarok.-en bateratzen duen zerbitzua Comment[fi]=Last.fm-toiminnot Amarokiin yhdistävä palvelu Comment[fr]=Un service intégrant des fonctionnalités de « Last.fm » dans Amarok Comment[ga]=Seirbhís a chomhtháthaíonn feidhmiúlacht Last.fm le hAmarok Comment[gl]=Un servizo que integra a funcionalidade de Last.fm en Amarok Comment[hu]=A Last.fm funkcióit az Amarokba integráló szolgáltatás Comment[id]=Layanan yang terintegrasi dengan Last.fm berfungsi di dalam Amarok Comment[is]=Þjónusta sem fellir Last.fm aðgerðir inn í Amarok Comment[it]=Un servizio che integra le funzionalità di Last.fm in Amarok Comment[ja]=Last.fm の機能を Amarok に統合するサービス Comment[km]=សេវា​ដែល​រួម​បញ្ចូល​មុខងារ Last.fm ទៅ​ក្នុង Amarok Comment[ko]=Amarok에 last.fm 기능을 통합하는 서비스 Comment[lt]=Ši tarnyba integruoja Last.fm funkcijas į Amaroką Comment[lv]=Pakalpojums, kurš integrē last.fm atbalstu amarok Comment[nb]=En tjeneste som integrerer funksjonalitet i Last.fm inn i Amarok Comment[nds]=En Deenst, de Funkschonen vun "Last.fm" för Amarok praatstellt Comment[nl]=Een dienst die Last.fm-functionaliteit in Amarok integreert Comment[pl]=Usługa, która integruje funkcjonalność Last.fm z Amarokiem Comment[pt]=Um serviço que integra a funcionalidade do Last.fm no Amarok Comment[pt_BR]=Um serviço que adiciona a funcionalidade do Last.fm ao Amarok Comment[ro]=Serviciu ce integrează funcționalitatea Last.fm în Amarok Comment[ru]=Служба, добавляющая в Amarok взаимодействие с Last.fm Comment[sk]=Služba, ktorá integruje funkcionalitu Last.fm do Amaroku Comment[sl]=Storitev, ki vgradi zmožnosti Last.fm v Amarok Comment[sr]=Сервис који уграђује ЛастФМ‑ове могућности у Амарок Comment[sr@ijekavian]=Сервис који уграђује ЛастФМ‑ове могућности у Амарок Comment[sr@ijekavianlatin]=Servis koji ugrađuje last.fm‑ove mogućnosti u Amarok Comment[sr@latin]=Servis koji ugrađuje last.fm‑ove mogućnosti u Amarok Comment[sv]=En tjänst som integrerar funktioner från Last.fm i Amarok Comment[tr]=Last.fm işlevlerini Amarok ile bütünleştiren bir hizmet Comment[uk]=Служба, яка інтегрує функціональність Last.fm до Amarok Comment[x-test]=xxA service that integrates Last.fm functionality into Amarokxx Comment[zh_CN]=一个在 Amarok 中集成 Last.fm 功能的服务 Comment[zh_TW]=一種將 Last.fm 其功能性整合到 Amarok 中的服務 ServiceTypes=Amarok/Plugin X-KDE-Library=amarok_service_lastfm X-KDE-Amarok-authors=Shane King X-KDE-Amarok-email=kde@dontletsstart.com X-KDE-Amarok-framework-version=73 X-KDE-Amarok-name=LastFmService X-KDE-Amarok-rank=100 X-KDE-Amarok-version=1 X-KDE-PluginInfo-Author=Shane King X-KDE-PluginInfo-Email=kde@dontletsstart.com X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Category=Service X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-KDE-Library=amarok_service_lastfm X-KDE-PluginInfo-Name=amarok_service_lastfm diff --git a/src/services/lastfm/biases/LastFmBias.cpp b/src/services/lastfm/biases/LastFmBias.cpp index 202ce6aa1a..af4ae2a987 100644 --- a/src/services/lastfm/biases/LastFmBias.cpp +++ b/src/services/lastfm/biases/LastFmBias.cpp @@ -1,692 +1,692 @@ /**************************************************************************************** * Copyright (c) 2009 Leo Franchi * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "LastFmBias" #include "LastFmBias.h" #include "core/meta/Meta.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include "core-impl/collections/support/CollectionManager.h" -#include -#include +#include +#include #include #include #include #include #include #include #include #include #include #include #include #include QString Dynamic::LastFmBiasFactory::i18nName() const { return i18nc("Name of the \"Last.fm\" similar bias", "Last.fm similar"); } QString Dynamic::LastFmBiasFactory::name() const { return Dynamic::LastFmBias::sName(); } QString Dynamic::LastFmBiasFactory::i18nDescription() const { return i18nc("Description of the \"Last.fm\" bias", "The \"Last.fm\" similar bias looks up tracks on Last.fm and only adds similar tracks."); } Dynamic::BiasPtr Dynamic::LastFmBiasFactory::createBias() { return Dynamic::BiasPtr( new Dynamic::LastFmBias() ); } // ----- LastFmBias -------- Dynamic::LastFmBias::LastFmBias() : SimpleMatchBias() , m_match( SimilarArtist ) , m_mutex( QMutex::Recursive ) { loadDataFromFile(); } Dynamic::LastFmBias::~LastFmBias() { // TODO: kill all running queries } void Dynamic::LastFmBias::fromXml( QXmlStreamReader *reader ) { while (!reader->atEnd()) { reader->readNext(); if( reader->isStartElement() ) { QStringRef name = reader->name(); if( name == "match" ) m_match = matchForName( reader->readElementText(QXmlStreamReader::SkipChildElements) ); else { debug()<<"Unexpected xml start element"<name()<<"in input"; reader->skipCurrentElement(); } } else if( reader->isEndElement() ) { break; } } } void Dynamic::LastFmBias::toXml( QXmlStreamWriter *writer ) const { writer->writeTextElement( "match", nameForMatch( m_match ) ); } QString Dynamic::LastFmBias::sName() { return "lastfm_similarartists"; } QString Dynamic::LastFmBias::name() const { return Dynamic::LastFmBias::sName(); } QString Dynamic::LastFmBias::toString() const { switch( m_match ) { case SimilarTrack: return i18nc("Last.fm bias representation", "Similar to the previous track (as reported by Last.fm)"); case SimilarArtist: return i18nc("Last.fm bias representation", "Similar to the previous artist (as reported by Last.fm)"); } return QString(); } QWidget* Dynamic::LastFmBias::widget( QWidget* parent ) { QWidget *widget = new QWidget( parent ); QVBoxLayout *layout = new QVBoxLayout( widget ); QLabel *imageLabel = new QLabel(); - imageLabel->setPixmap( QPixmap( KStandardDirs::locate( "data", "amarok/images/lastfm.png" ) ) ); + imageLabel->setPixmap( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/lastfm.png" ) ) ); QLabel *label = new QLabel( i18n( "Last.fm thinks the track is similar to" ) ); QRadioButton *rb1 = new QRadioButton( i18n( "the previous track's artist" ) ); QRadioButton *rb2 = new QRadioButton( i18n( "the previous track" ) ); rb1->setChecked( m_match == SimilarArtist ); rb2->setChecked( m_match == SimilarTrack ); - connect( rb1, SIGNAL(toggled(bool)), - this, SLOT(setMatchTypeArtist(bool)) ); + connect( rb1, &QRadioButton::toggled, + this, &LastFmBias::setMatchTypeArtist ); layout->addWidget( imageLabel ); layout->addWidget( label ); layout->addWidget( rb1 ); layout->addWidget( rb2 ); return widget; } Dynamic::TrackSet Dynamic::LastFmBias::matchingTracks( const Meta::TrackList& playlist, int contextCount, int finalCount, Dynamic::TrackCollectionPtr universe ) const { Q_UNUSED( contextCount ); Q_UNUSED( finalCount ); if( playlist.isEmpty() ) return Dynamic::TrackSet( universe, true ); // determine the last track and artist Meta::TrackPtr lastTrack = playlist.last(); Meta::ArtistPtr lastArtist = lastTrack->artist(); m_currentTrack = lastTrack->name(); m_currentArtist = lastArtist ? lastArtist->name() : QString(); { QMutexLocker locker( &m_mutex ); if( m_match == SimilarArtist ) { if( m_currentArtist.isEmpty() ) return Dynamic::TrackSet( universe, true ); if( m_tracksMap.contains( m_currentArtist ) ) return m_tracksMap.value( m_currentArtist ); } else if( m_match == SimilarTrack ) { if( m_currentTrack.isEmpty() ) return Dynamic::TrackSet( universe, true ); QString key = m_currentTrack + '|' + m_currentArtist; if( m_tracksMap.contains( key ) ) return m_tracksMap.value( key ); } } m_tracks = Dynamic::TrackSet( universe, false ); QTimer::singleShot(0, const_cast(this), - SLOT(newQuery())); // create the new query from my parent thread + &LastFmBias::newQuery); // create the new query from my parent thread return Dynamic::TrackSet(); } bool Dynamic::LastFmBias::trackMatches( int position, const Meta::TrackList& playlist, int contextCount ) const { Q_UNUSED( contextCount ); if( position <= 0 || position >= playlist.count()) return false; // determine the last track and artist Meta::TrackPtr lastTrack = playlist[position-1]; Meta::ArtistPtr lastArtist = lastTrack->artist(); QString lastTrackName = lastTrack->name(); QString lastArtistName = lastArtist ? lastArtist->name() : QString(); Meta::TrackPtr currentTrack = playlist[position]; Meta::ArtistPtr currentArtist = currentTrack->artist(); QString currentTrackName = currentTrack->name(); QString currentArtistName = currentArtist ? currentArtist->name() : QString(); { QMutexLocker locker( &m_mutex ); if( m_match == SimilarArtist ) { if( lastArtistName.isEmpty() ) return true; if( currentArtistName.isEmpty() ) return false; if( lastArtistName == currentArtistName ) return true; if( m_similarArtistMap.contains( lastArtistName ) ) return m_similarArtistMap.value( lastArtistName ).contains( currentArtistName ); } else if( m_match == SimilarTrack ) { if( lastTrackName.isEmpty() ) return true; if( currentTrackName.isEmpty() ) return false; if( lastTrackName == currentTrackName ) return true; TitleArtistPair lastKey( lastTrackName, lastArtistName ); TitleArtistPair currentKey( currentTrackName, currentArtistName ); if( m_similarTrackMap.contains( lastKey ) ) return m_similarTrackMap.value( lastKey ).contains( currentKey ); } } debug() << "didn't have a cached suggestions for track:" << lastTrackName; return false; } void Dynamic::LastFmBias::invalidate() { SimpleMatchBias::invalidate(); m_tracksMap.clear(); } void Dynamic::LastFmBias::newQuery() { DEBUG_BLOCK; debug() << "similarArtists:"< similarTracks; { QMutexLocker locker( &m_mutex ); if( m_match == SimilarArtist ) { if( m_similarArtistMap.contains( m_currentArtist ) ) { similarArtists = m_similarArtistMap.value( m_currentArtist ); debug() << "for"<queryMaker() ); // - construct the query m_qm->beginOr(); if( m_match == SimilarArtist ) { foreach( const QString &name, similarArtists ) { m_qm->addFilter( Meta::valArtist, name, true, true ); } } else if( m_match == SimilarTrack ) { foreach( const TitleArtistPair &name, similarTracks ) { m_qm->beginAnd(); m_qm->addFilter( Meta::valTitle, name.first, true, true ); m_qm->addFilter( Meta::valArtist, name.second, true, true ); m_qm->endAndOr(); } } m_qm->endAndOr(); m_qm->setQueryType( Collections::QueryMaker::Custom ); m_qm->addReturnValue( Meta::valUniqueId ); - connect( m_qm.data(), SIGNAL(newResultReady(QStringList)), - this, SLOT(updateReady(QStringList)) ); - connect( m_qm.data(), SIGNAL(queryDone()), - this, SLOT(updateFinished()) ); + connect( m_qm.data(), &Collections::QueryMaker::newResultReady, + this, &LastFmBias::updateReady ); + connect( m_qm.data(), &Collections::QueryMaker::queryDone, + this, &LastFmBias::updateFinished ); // - run the query - m_qm.data()->run(); + m_qm->run(); } void Dynamic::LastFmBias::newSimilarQuery() { DEBUG_BLOCK QMap< QString, QString > params; // params[ "limit" ] = "70"; if( m_match == SimilarArtist ) { params[ "method" ] = "artist.getSimilar"; params[ "artist" ] = m_currentArtist; QNetworkReply* request = lastfm::ws::get( params ); - connect( request, SIGNAL(finished()), - this, SLOT(similarArtistQueryDone()) ); + connect( request, &QNetworkReply::finished, + this, &LastFmBias::similarArtistQueryDone ); } else if( m_match == SimilarTrack ) { // if( track->mb // TODO add mbid if the track has one params[ "method" ] = "track.getSimilar"; params[ "artist" ] = m_currentArtist; params[ "track" ] = m_currentTrack; QNetworkReply* request = lastfm::ws::get( params ); - connect( request, SIGNAL(finished()), - this, SLOT(similarTrackQueryDone()) ); + connect( request, &QNetworkReply::finished, + this, &LastFmBias::similarTrackQueryDone ); } } void Dynamic::LastFmBias::similarArtistQueryDone() // slot { DEBUG_BLOCK QNetworkReply* reply = qobject_cast(sender()); if( !reply ) { queryFailed( "job was deleted from under us...wtf! blame the gerbils." ); return; } reply->deleteLater(); QByteArray data = reply->readAll(); // debug() << "artistQuery has data:" << data; QDomDocument d; if( !d.setContent( data ) ) { queryFailed( "Got invalid XML data from last.fm!" ); return; } QDomNodeList nodes = d.elementsByTagName( "artist" ); QStringList similarArtists; for( int i =0; i < nodes.size(); ++i ) { QDomElement n = nodes.at( i ).toElement(); // n.firstChildElement( "match" ).text().toFloat() * 100, similarArtists.append( n.firstChildElement( "name" ).text() ); } QMutexLocker locker( &m_mutex ); m_similarArtistMap.insert( m_currentArtist, similarArtists ); saveDataToFile(); // -- try again to do the query newQuery(); } void Dynamic::LastFmBias::similarTrackQueryDone() { DEBUG_BLOCK QNetworkReply* reply = qobject_cast(sender()); if( !reply ) { queryFailed( "who send this...wtf! blame the gerbils." ); return; } reply->deleteLater(); // double match value, qpair title - artist QMap< int, QPair > similar; QByteArray data = reply->readAll(); // debug() << "trackQuery has data:" << data; QDomDocument d; if( !d.setContent( data ) ) { queryFailed( "Got invalid XML data from last.fm!" ); return; } QDomNodeList nodes = d.elementsByTagName( "track" ); QList similarTracks; for( int i =0; i < nodes.size(); ++i ) { QDomElement n = nodes.at( i ).toElement(); // n.firstChildElement( "match" ).text().toFloat() * 100, TitleArtistPair pair( n.firstChildElement( "name" ).text(), n.firstChildElement( "artist" ).firstChildElement( "name" ).text() ); similarTracks.append( pair ); } QMutexLocker locker( &m_mutex ); TitleArtistPair key( m_currentTrack, m_currentArtist ); m_similarTrackMap.insert( key, similarTracks ); saveDataToFile(); // -- try again to do the query newQuery(); } void Dynamic::LastFmBias::queryFailed( const char *message ) { debug() << message; m_tracks.reset( false ); emit resultReady( m_tracks ); return; } void Dynamic::LastFmBias::saveDataToFile() const { QFile file( Amarok::saveLocation() + "dynamic_lastfm_similar.xml" ); if( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) return; QXmlStreamWriter writer( &file ); writer.setAutoFormatting( true ); writer.writeStartDocument(); writer.writeStartElement( QLatin1String("lastfmSimilar") ); // -- write the similar artists foreach( const QString& key, m_similarArtistMap.keys() ) { writer.writeStartElement( QLatin1String("similarArtist") ); writer.writeTextElement( QLatin1String("artist"), key ); foreach( const QString& name, m_similarArtistMap.value( key ) ) { writer.writeTextElement( QLatin1String("similar"), name ); } writer.writeEndElement(); } // -- write the similar tracks foreach( const TitleArtistPair& key, m_similarTrackMap.keys() ) { writer.writeStartElement( QLatin1String("similarTrack") ); writer.writeStartElement( QLatin1String("track") ); writer.writeTextElement( QLatin1String("title"), key.first ); writer.writeTextElement( QLatin1String("artist"), key.second ); writer.writeEndElement(); foreach( const TitleArtistPair& name, m_similarTrackMap.value( key ) ) { writer.writeStartElement( QLatin1String("similar") ); writer.writeTextElement( QLatin1String("title"), name.first ); writer.writeTextElement( QLatin1String("artist"), name.second ); writer.writeEndElement(); } writer.writeEndElement(); } writer.writeEndElement(); writer.writeEndDocument(); } void Dynamic::LastFmBias::readSimilarArtists( QXmlStreamReader *reader ) { QString key; QList artists; while (!reader->atEnd()) { reader->readNext(); QStringRef name = reader->name(); if( reader->isStartElement() ) { if( name == QLatin1String("artist") ) key = reader->readElementText(QXmlStreamReader::SkipChildElements); else if( name == QLatin1String("similar") ) artists.append( reader->readElementText(QXmlStreamReader::SkipChildElements) ); else reader->skipCurrentElement(); } else if( reader->isEndElement() ) { break; } } m_similarArtistMap.insert( key, artists ); } Dynamic::LastFmBias::TitleArtistPair Dynamic::LastFmBias::readTrack( QXmlStreamReader *reader ) { TitleArtistPair track; while (!reader->atEnd()) { reader->readNext(); QStringRef name = reader->name(); if( reader->isStartElement() ) { if( name == QLatin1String("title") ) track.first = reader->readElementText(QXmlStreamReader::SkipChildElements); else if( name == QLatin1String("artist") ) track.second = reader->readElementText(QXmlStreamReader::SkipChildElements); else reader->skipCurrentElement(); } else if( reader->isEndElement() ) { break; } } return track; } void Dynamic::LastFmBias::readSimilarTracks( QXmlStreamReader *reader ) { TitleArtistPair key; QList tracks; while (!reader->atEnd()) { reader->readNext(); QStringRef name = reader->name(); if( reader->isStartElement() ) { if( name == QLatin1String("track") ) key = readTrack( reader ); else if( name == QLatin1String("similar") ) tracks.append( readTrack( reader ) ); else reader->skipCurrentElement(); } else if( reader->isEndElement() ) { break; } } m_similarTrackMap.insert( key, tracks ); } void Dynamic::LastFmBias::loadDataFromFile() { m_similarArtistMap.clear(); m_similarTrackMap.clear(); QFile file( Amarok::saveLocation() + "dynamic_lastfm_similar.xml" ); if( !file.exists() || !file.open( QIODevice::ReadOnly ) ) return; QXmlStreamReader reader( &file ); while (!reader.atEnd()) { reader.readNext(); QStringRef name = reader.name(); if( reader.isStartElement() ) { if( name == QLatin1String("lastfmSimilar") ) { ; // just recurse into the element } else if( name == QLatin1String("similarArtist") ) { readSimilarArtists( &reader ); } else if( name == QLatin1String("similarTrack") ) { readSimilarTracks( &reader ); } else { reader.skipCurrentElement(); } } else if( reader.isEndElement() ) { break; } } } Dynamic::LastFmBias::MatchType Dynamic::LastFmBias::match() const { return m_match; } void Dynamic::LastFmBias::setMatch( Dynamic::LastFmBias::MatchType value ) { m_match = value; invalidate(); emit changed( BiasPtr(this) ); } void Dynamic::LastFmBias::setMatchTypeArtist( bool matchArtist ) { setMatch( matchArtist ? SimilarArtist : SimilarTrack ); } QString Dynamic::LastFmBias::nameForMatch( Dynamic::LastFmBias::MatchType match ) { switch( match ) { case SimilarArtist: return "artist"; case SimilarTrack: return "track"; } return QString(); } Dynamic::LastFmBias::MatchType Dynamic::LastFmBias::matchForName( const QString &name ) { if( name == "artist" ) return SimilarArtist; else if( name == "track" ) return SimilarTrack; else return SimilarArtist; } diff --git a/src/services/lastfm/biases/WeeklyTopBias.cpp b/src/services/lastfm/biases/WeeklyTopBias.cpp index 25d55441ba..da377e3eb9 100644 --- a/src/services/lastfm/biases/WeeklyTopBias.cpp +++ b/src/services/lastfm/biases/WeeklyTopBias.cpp @@ -1,491 +1,489 @@ /**************************************************************************************** * Copyright (c) 2009 Leo Franchi * * Copyright (c) 2011 Ralf Engels * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "WeeklyTopBias.h" #include "core/meta/Meta.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include "core-impl/collections/support/CollectionManager.h" -#include +#include #include #include #include #include #include #include #include #include #include QString Dynamic::WeeklyTopBiasFactory::i18nName() const { return i18nc("Name of the \"WeeklyTop\" bias", "Last.fm weekly top artist"); } QString Dynamic::WeeklyTopBiasFactory::name() const { return Dynamic::WeeklyTopBias::sName(); } QString Dynamic::WeeklyTopBiasFactory::i18nDescription() const { return i18nc("Description of the \"WeeklyTop\" bias", "The \"WeeklyTop\" bias adds tracks that are in the weekly top chart of Last.fm."); } Dynamic::BiasPtr Dynamic::WeeklyTopBiasFactory::createBias() { return Dynamic::BiasPtr( new Dynamic::WeeklyTopBias() ); } // ----- WeeklyTopBias -------- Dynamic::WeeklyTopBias::WeeklyTopBias() : SimpleMatchBias() , m_weeklyTimesJob( ) { m_range.from = QDateTime::currentDateTime(); m_range.to = QDateTime::currentDateTime(); loadFromFile(); } Dynamic::WeeklyTopBias::~WeeklyTopBias() { } void Dynamic::WeeklyTopBias::fromXml( QXmlStreamReader *reader ) { loadFromFile(); while (!reader->atEnd()) { reader->readNext(); if( reader->isStartElement() ) { QStringRef name = reader->name(); if( name == "from" ) m_range.from = QDateTime::fromTime_t( reader->readElementText(QXmlStreamReader::SkipChildElements).toLong() ); else if( name == "to" ) m_range.to = QDateTime::fromTime_t( reader->readElementText(QXmlStreamReader::SkipChildElements).toLong() ); else { debug()<<"Unexpected xml start element"<skipCurrentElement(); } } else if( reader->isEndElement() ) { break; } } } void Dynamic::WeeklyTopBias::toXml( QXmlStreamWriter *writer ) const { writer->writeTextElement( "from", QString::number( m_range.from.toTime_t() ) ); writer->writeTextElement( "to", QString::number( m_range.to.toTime_t() ) ); } QString Dynamic::WeeklyTopBias::sName() { return "lastfm_weeklytop"; } QString Dynamic::WeeklyTopBias::name() const { return Dynamic::WeeklyTopBias::sName(); } QString Dynamic::WeeklyTopBias::toString() const { return i18nc("WeeklyTopBias bias representation", "Tracks from the Last.fm top lists from %1 to %2", m_range.from.toString(), m_range.to.toString() ); } QWidget* Dynamic::WeeklyTopBias::widget( QWidget* parent ) { QWidget *widget = new QWidget( parent ); QVBoxLayout *layout = new QVBoxLayout( widget ); QLabel *label = new QLabel( i18nc( "in WeeklyTopBias. Label for the date widget", "from:" ) ); QDateTimeEdit *fromEdit = new QDateTimeEdit( QDate::currentDate().addDays( -7 ) ); fromEdit->setMinimumDate( QDateTime::fromTime_t( 1111320001 ).date() ); // That's the first week in last fm fromEdit->setMaximumDate( QDate::currentDate() ); fromEdit->setCalendarPopup( true ); if( m_range.from.isValid() ) fromEdit->setDateTime( m_range.from ); - connect( fromEdit, SIGNAL(dateTimeChanged(QDateTime)), - this, SLOT(fromDateChanged(QDateTime)) ); + connect( fromEdit, &QDateTimeEdit::dateTimeChanged, this, &WeeklyTopBias::fromDateChanged ); label->setBuddy( fromEdit ); layout->addWidget( label ); layout->addWidget( fromEdit ); label = new QLabel( i18nc( "in WeeklyTopBias. Label for the date widget", "to:" ) ); QDateTimeEdit *toEdit = new QDateTimeEdit( QDate::currentDate().addDays( -7 ) ); toEdit->setMinimumDate( QDateTime::fromTime_t( 1111320001 ).date() ); // That's the first week in last fm toEdit->setMaximumDate( QDate::currentDate() ); toEdit->setCalendarPopup( true ); if( m_range.to.isValid() ) toEdit->setDateTime( m_range.to ); - connect( toEdit, SIGNAL(dateTimeChanged(QDateTime)), - this, SLOT(toDateChanged(QDateTime)) ); + connect( toEdit, &QDateTimeEdit::dateTimeChanged, this, &WeeklyTopBias::toDateChanged ); label->setBuddy( toEdit ); layout->addWidget( label ); layout->addWidget( toEdit ); return widget; } bool Dynamic::WeeklyTopBias::trackMatches( int position, const Meta::TrackList& playlist, int contextCount ) const { Q_UNUSED( contextCount ); if( position < 0 || position >= playlist.count()) return false; // - determine the current artist Meta::TrackPtr currentTrack = playlist[position-1]; Meta::ArtistPtr currentArtist = currentTrack->artist(); QString currentArtistName = currentArtist ? currentArtist->name() : QString(); // - collect all the artists QStringList artists; bool weeksMissing = false; uint fromTime = m_range.from.toTime_t(); uint toTime = m_range.to.toTime_t(); uint lastWeekTime = 0; foreach( uint weekTime, m_weeklyFromTimes ) { if( weekTime > fromTime && weekTime < toTime && lastWeekTime ) { if( m_weeklyArtistMap.contains( lastWeekTime ) ) { artists.append( m_weeklyArtistMap.value( lastWeekTime ) ); // debug() << "found already-saved data for week:" << lastWeekTime << m_weeklyArtistMap.value( lastWeekTime ); } else { weeksMissing = true; } } lastWeekTime = weekTime; } if( weeksMissing ) warning() << "didn't have a cached suggestions for weeks:" << m_range.from << "to" << m_range.to; return artists.contains( currentArtistName ); } void Dynamic::WeeklyTopBias::newQuery() { DEBUG_BLOCK; // - check if we have week times if( m_weeklyFromTimes.isEmpty() ) { newWeeklyTimesQuery(); return; // not yet ready to do construct a query maker } // - collect all the artists QStringList artists; bool weeksMissing = false; uint fromTime = m_range.from.toTime_t(); uint toTime = m_range.to.toTime_t(); uint lastWeekTime = 0; foreach( uint weekTime, m_weeklyFromTimes ) { if( weekTime > fromTime && weekTime < toTime && lastWeekTime ) { if( m_weeklyArtistMap.contains( lastWeekTime ) ) { artists.append( m_weeklyArtistMap.value( lastWeekTime ) ); // debug() << "found already-saved data for week:" << lastWeekTime << m_weeklyArtistMap.value( lastWeekTime ); } else { weeksMissing = true; } } lastWeekTime = weekTime; } if( weeksMissing ) { newWeeklyArtistQuery(); return; // not yet ready to construct a query maker } // ok, I need a new query maker m_qm.reset( CollectionManager::instance()->queryMaker() ); // - construct the query m_qm->beginOr(); foreach( const QString &artist, artists ) { // debug() << "adding artist to query:" << artist; m_qm->addFilter( Meta::valArtist, artist, true, true ); } m_qm->endAndOr(); m_qm->setQueryType( Collections::QueryMaker::Custom ); m_qm->addReturnValue( Meta::valUniqueId ); - connect( m_qm.data(), SIGNAL(newResultReady(QStringList)), - this, SLOT(updateReady(QStringList)) ); - connect( m_qm.data(), SIGNAL(queryDone()), - this, SLOT(updateFinished()) ); + connect( m_qm.data(), &Collections::QueryMaker::newResultReady, + this, &WeeklyTopBias::updateReady ); + connect( m_qm.data(), &Collections::QueryMaker::queryDone, + this, &WeeklyTopBias::updateFinished ); // - run the query - m_qm.data()->run(); + m_qm->run(); } void Dynamic::WeeklyTopBias::newWeeklyTimesQuery() { DEBUG_BLOCK QMap< QString, QString > params; params[ "method" ] = "user.getWeeklyChartList" ; params[ "user" ] = lastfm::ws::Username; m_weeklyTimesJob = lastfm::ws::get( params ); - connect( m_weeklyTimesJob, SIGNAL(finished()), - this, SLOT(weeklyTimesQueryFinished()) ); + connect( m_weeklyTimesJob, &QNetworkReply::finished, + this, &WeeklyTopBias::weeklyTimesQueryFinished ); } void Dynamic::WeeklyTopBias::newWeeklyArtistQuery() { DEBUG_BLOCK debug() << "getting top artist info from" << m_range.from << "to" << m_range.to; // - check if we have week times if( m_weeklyFromTimes.isEmpty() ) { newWeeklyTimesQuery(); return; // not yet ready to do construct a query maker } // fetch 5 at a time, so as to conform to lastfm api requirements uint jobCount = m_weeklyArtistJobs.count(); if( jobCount >= 5 ) return; uint fromTime = m_range.from.toTime_t(); uint toTime = m_range.to.toTime_t(); uint lastWeekTime = 0; foreach( uint weekTime, m_weeklyFromTimes ) { if( weekTime > fromTime && weekTime < toTime && lastWeekTime ) { if( m_weeklyArtistMap.contains( lastWeekTime ) ) { // we already have the data } else if( m_weeklyArtistJobs.contains( lastWeekTime ) ) { // we already fetch the data } else { QMap< QString, QString > params; params[ "method" ] = "user.getWeeklyArtistChart"; params[ "user" ] = lastfm::ws::Username; params[ "from" ] = QString::number( lastWeekTime ); params[ "to" ] = QString::number( m_weeklyToTimes[m_weeklyFromTimes.indexOf(lastWeekTime)] ); QNetworkReply* reply = lastfm::ws::get( params ); - connect( reply, SIGNAL(finished()), - this, SLOT(weeklyArtistQueryFinished()) ); + connect( reply, &QNetworkReply::finished, + this, &WeeklyTopBias::weeklyArtistQueryFinished ); m_weeklyArtistJobs.insert( lastWeekTime, reply ); jobCount++; if( jobCount >= 5 ) return; } } lastWeekTime = weekTime; } } void Dynamic::WeeklyTopBias::weeklyArtistQueryFinished() { DEBUG_BLOCK QNetworkReply *reply = qobject_cast( sender() ); if( !reply ) { warning() << "Failed to get qnetwork reply in finished slot."; return; } lastfm::XmlQuery lfm; if( lfm.parse( reply->readAll() ) ) { // debug() << "got response:" << lfm; QStringList artists; for( int i = 0; i < lfm[ "weeklyartistchart" ].children( "artist" ).size(); i++ ) { if( i == 12 ) // only up to 12 artist. break; lastfm::XmlQuery artist = lfm[ "weeklyartistchart" ].children( "artist" ).at( i ); artists.append( artist[ "name" ].text() ); } uint week = QDomElement( lfm[ "weeklyartistchart" ] ).attribute( "from" ).toUInt(); m_weeklyArtistMap.insert( week, artists ); debug() << "got artists:" << artists << week; if( m_weeklyArtistJobs.contains( week) ) { m_weeklyArtistJobs.remove( week ); } else { warning() << "Got a reply for a week"<deleteLater(); saveDataToFile(); newQuery(); // try again to get the tracks } void Dynamic::WeeklyTopBias::weeklyTimesQueryFinished() // SLOT { DEBUG_BLOCK if( !m_weeklyTimesJob ) return; // argh. where does this come from QDomDocument doc; if( !doc.setContent( m_weeklyTimesJob->readAll() ) ) { debug() << "couldn't parse XML from rangeJob!"; return; } QDomNodeList nodes = doc.elementsByTagName( "chart" ); if( nodes.count() == 0 ) { debug() << "USER has no history! can't do this!"; return; } for( int i = 0; i < nodes.size(); i++ ) { QDomNode n = nodes.at( i ); m_weeklyFromTimes.append( n.attributes().namedItem( "from" ).nodeValue().toUInt() ); m_weeklyToTimes.append( n.attributes().namedItem( "to" ).nodeValue().toUInt() ); // debug() << "weeklyTimesResult"<deleteLater(); newQuery(); // try again to get the tracks } void Dynamic::WeeklyTopBias::fromDateChanged( const QDateTime& d ) // SLOT { if( d > m_range.to ) return; m_range.from = d; invalidate(); emit changed( BiasPtr( this ) ); } void Dynamic::WeeklyTopBias::toDateChanged( const QDateTime& d ) // SLOT { if( d < m_range.from ) return; m_range.to = d; invalidate(); emit changed( BiasPtr( this ) ); } void Dynamic::WeeklyTopBias::loadFromFile() { QFile file( Amarok::saveLocation() + "dynamic_lastfm_topweeklyartists.xml" ); file.open( QIODevice::ReadOnly | QIODevice::Text ); QTextStream in( &file ); while( !in.atEnd() ) { QString line = in.readLine(); m_weeklyArtistMap.insert( line.split( '#' )[ 0 ].toUInt(), line.split( '#' )[ 1 ].split( '^' ) ); } file.close(); } void Dynamic::WeeklyTopBias::saveDataToFile() const { QFile file( Amarok::saveLocation() + "dynamic_lastfm_topweeklyartists.xml" ); file.open( QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text ); QTextStream out( &file ); foreach( uint key, m_weeklyArtistMap.keys() ) { out << key << "#" << m_weeklyArtistMap[ key ].join( "^" ) << endl; } file.close(); } diff --git a/src/services/lastfm/meta/LastFmMeta.cpp b/src/services/lastfm/meta/LastFmMeta.cpp index 1eaa2bcdf6..677106f525 100644 --- a/src/services/lastfm/meta/LastFmMeta.cpp +++ b/src/services/lastfm/meta/LastFmMeta.cpp @@ -1,448 +1,449 @@ /**************************************************************************************** * Copyright (c) 2007 Maximilian Kossick * * Copyright (c) 2008 Shane King * * Copyright (c) 2008 Leo Franchi * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "LastFmMeta.h" #include "EngineController.h" #include "services/lastfm/meta/LastFmMeta_p.h" #include "services/lastfm/meta/LastFmMultiPlayableCapability.h" #include "services/lastfm/meta/LastFmStreamInfoCapability.h" #include #include #include using namespace LastFm; Track::Track( const QString &lastFmUri ) : QObject() , Meta::Track() , d( new Private() ) { d->lastFmUri = QUrl( lastFmUri ); d->t = this; init(); } Track::Track( lastfm::Track track ) : QObject() , Meta::Track() , d( new Private() ) { d->t = this; d->track = track.title(); d->lastFmTrack = track; QMap< QString, QString > params; params[ "method" ] = "track.getInfo"; params[ "artist" ] = track.artist(); params[ "track" ] = track.title(); d->trackFetch = lastfm::ws::post( params ); - connect( d->trackFetch, SIGNAL(finished()), SLOT(slotResultReady()) ); + connect( d->trackFetch, &QNetworkReply::finished, this, &Track::slotResultReady ); } Track::~Track() { delete d; } void Track::init( int id /* = -1*/ ) { if( id != -1 ) d->lastFmUri = QUrl( "lastfm://play/tracks/" + QString::number( id ) ); d->length = 0; d->albumPtr = Meta::AlbumPtr( new LastFmAlbum( d ) ); d->artistPtr = Meta::ArtistPtr( new LastFmArtist( d ) ); d->genrePtr = Meta::GenrePtr( new LastFmGenre( d ) ); d->composerPtr = Meta::ComposerPtr( new LastFmComposer( d ) ); d->yearPtr = Meta::YearPtr( new LastFmYear( d ) ); QAction *banAction = new QAction( QIcon::fromTheme( "remove-amarok" ), i18n( "Last.fm: &Ban" ), this ); banAction->setShortcut( i18n( "Ctrl+B" ) ); banAction->setStatusTip( i18n( "Ban this track" ) ); - connect( banAction, SIGNAL(triggered()), this, SLOT(ban()) ); + connect( banAction, &QAction::triggered, this, &Track::ban ); m_trackActions.append( banAction ); QAction *skipAction = new QAction( QIcon::fromTheme( "media-seek-forward-amarok" ), i18n( "Last.fm: &Skip" ), this ); skipAction->setShortcut( i18n( "Ctrl+S" ) ); skipAction->setStatusTip( i18n( "Skip this track" ) ); - connect( skipAction, SIGNAL(triggered()), this, SIGNAL(skipTrack()) ); + connect( skipAction, &QAction::triggered, this, &Track::skipTrack ); m_trackActions.append( skipAction ); QThread *mainThread = QCoreApplication::instance()->thread(); bool foreignThread = QThread::currentThread() != mainThread; if( foreignThread ) { moveToThread( mainThread ); // the actions are children and are moved together with parent d->moveToThread( mainThread ); } } QString Track::name() const { if( d->track.isEmpty() ) { return streamName(); } else { return d->track; } } QString Track::sortableName() const { // TODO return name(); } QUrl Track::playableUrl() const { - return d->lastFmUri.toString(); + return d->lastFmUri; } QUrl Track::internalUrl() const { return QUrl( d->trackPath ); } QString Track::prettyUrl() const { return d->lastFmUri.toString(); } QString Track::uidUrl() const { return d->lastFmUri.toString(); } QString Track::notPlayableReason() const { return networkNotPlayableReason(); } Meta::AlbumPtr Track::album() const { return d->albumPtr; } Meta::ArtistPtr Track::artist() const { return d->artistPtr; } Meta::GenrePtr Track::genre() const { return d->genrePtr; } Meta::ComposerPtr Track::composer() const { return d->composerPtr; } Meta::YearPtr Track::year() const { return d->yearPtr; } qreal Track::bpm() const { return -1.0; } QString Track::comment() const { return QString(); } int Track::trackNumber() const { return 0; } int Track::discNumber() const { return 0; } qint64 Track::length() const { return d->length; } int Track::filesize() const { return 0; //stream } int Track::sampleRate() const { return 0; //does the engine deliver this? } int Track::bitrate() const { return 0; //does the engine deliver this?? } QString Track::type() const { return "stream/lastfm"; } bool Track::inCollection() const { return false; } Collections::Collection* Track::collection() const { return 0; } void Track::setTrackInfo( const lastfm::Track &track ) { if( !track.isNull() ) d->setTrackInfo( track ); } QString Track::streamName() const { // parse the url to get a name if we don't have a track name (ie we're not playing the station) // do it as name rather than prettyname so it shows up nice in the playlist. QStringList elements = d->lastFmUri.toString().split( '/', QString::SkipEmptyParts ); if( elements.size() >= 2 && elements[0] == "lastfm:" ) { QString customPart = QUrl::fromPercentEncoding( elements[2].toUtf8() ); if( elements[1] == "globaltags" ) { // lastfm://globaltag/ if( elements.size() >= 3 ) return i18n( "Global Tag Radio: \"%1\"", customPart ); } else if( elements[1] == "usertags" ) { // lastfm://usertag/ if( elements.size() >= 3 ) return i18n( "User Tag Radio: \"%1\"", customPart ); } else if( elements[1] == "artist" ) { if( elements.size() >= 4 ) { // lastfm://artist//similarartists if( elements[3] == "similarartists" ) return i18n( "Similar Artists to \"%1\"", customPart ); // lastfm://artist//fans else if( elements[3] == "fans" ) return i18n( "Artist Fan Radio: \"%1\"", customPart ); } } else if( elements[1] == "user" ) { if( elements.size() >= 4 ) { // lastfm://user//neighbours if( elements[3] == "neighbours" ) return i18n( "%1's Neighbor Radio", elements[2] ); // lastfm://user//personal else if( elements[3] == "personal" ) return i18n( "%1's Personal Radio", elements[2] ); // lastfm://user//mix else if( elements[3] == "mix" ) return i18n( "%1's Mix Radio", elements[2] ); // lastfm://user//recommended else if( elements.size() < 5 && elements[3] == "recommended" ) return i18n( "%1's Recommended Radio", elements[2] ); // lastfm://user//recommended/ else if( elements.size() >= 5 && elements[3] == "recommended" ) return i18n( "%1's Recommended Radio (Popularity %2)", elements[2], elements[4] ); } } else if( elements[1] == "group" ) { // lastfm://group/ if( elements.size() >= 3 ) return i18n( "Group Radio: %1", elements[2] ); } else if( elements[1] == "play" ) { if( elements.size() >= 4 ) { // lastfm://play/tracks/ if ( elements[2] == "tracks" ) return i18n( "Track Radio" ); // lastfm://play/artists/ else if ( elements[2] == "artists" ) return i18n( "Artist Radio" ); } } } return d->lastFmUri.toString(); } void Track::ban() { DEBUG_BLOCK + d->wsReply = lastfm::MutableTrack( d->lastFmTrack ).ban(); - connect( d->wsReply, SIGNAL(finished()), this, SLOT(slotWsReply()) ); - if( The::engineController()->currentTrack() == this ) + connect( d->wsReply, &QNetworkReply::finished, this, &Track::slotWsReply ); + if( The::engineController()->currentTrack().data() == this ) emit skipTrack(); } void Track::slotResultReady() { if( d->trackFetch->error() == QNetworkReply::NoError ) { lastfm::XmlQuery lfm; if( lfm.parse( d->trackFetch->readAll() ) ) { QString id = lfm[ "track" ][ "id" ].text(); QString streamable = lfm[ "track" ][ "streamable" ].text(); if( streamable.toInt() == 1 ) init( id.toInt() ); else init(); } else { debug() << "Got exception in parsing from last.fm:" << lfm.parseError().message(); } } else { init(); } d->trackFetch->deleteLater(); } void Track::slotWsReply() { if( d->wsReply->error() == QNetworkReply::NoError ) { //debug() << "successfully completed WS transaction"; } else { debug() << "ERROR in last.fm ban!" << d->wsReply->error(); } } bool Track::hasCapabilityInterface( Capabilities::Capability::Type type ) const { return type == Capabilities::Capability::MultiPlayable || type == Capabilities::Capability::SourceInfo || type == Capabilities::Capability::Actions || type == Capabilities::Capability::StreamInfo; } Capabilities::Capability* Track::createCapabilityInterface( Capabilities::Capability::Type type ) { switch( type ) { case Capabilities::Capability::MultiPlayable: return new LastFmMultiPlayableCapability( this ); case Capabilities::Capability::SourceInfo: return new ServiceSourceInfoCapability( this ); case Capabilities::Capability::Actions: return new Capabilities::ActionsCapability( m_trackActions ); case Capabilities::Capability::StreamInfo: return new LastFmStreamInfoCapability( this ); default: return 0; } } Meta::StatisticsPtr Track::statistics() { if( d->statsStore ) return d->statsStore; return Meta::Track::statistics(); } QString LastFm::Track::sourceName() { return "Last.fm"; } QString LastFm::Track::sourceDescription() { return i18n( "Last.fm is cool..." ); } QPixmap LastFm::Track::emblem() { if ( !d->track.isEmpty() ) - return QPixmap( KStandardDirs::locate( "data", "amarok/images/emblem-lastfm.png" ) ); + return QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-lastfm.png" ) ); else return QPixmap(); } QString LastFm::Track::scalableEmblem() { if ( !d->track.isEmpty() ) - return KStandardDirs::locate( "data", "amarok/images/emblem-lastfm-scalable.svg" ); + return QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-lastfm-scalable.svg" ); else return QString(); } #include "moc_LastFmMeta_p.cpp" diff --git a/src/services/lastfm/meta/LastFmMeta.h b/src/services/lastfm/meta/LastFmMeta.h index e2a6563d6c..b259b401b2 100644 --- a/src/services/lastfm/meta/LastFmMeta.h +++ b/src/services/lastfm/meta/LastFmMeta.h @@ -1,122 +1,122 @@ /**************************************************************************************** * Copyright (c) 2007 Maximilian Kossick * * Copyright (c) 2008 Shane King * * Copyright (c) 2008 Leo Franchi * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef AMAROK_LASTFMMETA_H #define AMAROK_LASTFMMETA_H #include "core/meta/Meta.h" #include "core/capabilities/Capability.h" #include "services/ServiceMetaBase.h" // for the SourceInfoProvider namespace lastfm { class Track; } namespace LastFm { class Track : public QObject, public Meta::Track, public SourceInfoProvider { Q_OBJECT public: class Private; Track( const QString &lastFmUri ); Track( lastfm::Track track ); //Convienience Constructor to allow constructing a Meta::LastFmTrack from a LastFmTrack (confusing?) virtual ~Track(); // methods inherited from Meta::Base virtual QString name() const; virtual QString sortableName() const; // methods inherited from Meta::Track virtual QUrl playableUrl() const; virtual QString prettyUrl() const; virtual QString uidUrl() const; virtual QString notPlayableReason() const; virtual Meta::AlbumPtr album() const; virtual Meta::ArtistPtr artist() const; virtual Meta::GenrePtr genre() const; virtual Meta::ComposerPtr composer() const; virtual Meta::YearPtr year() const; virtual qreal bpm() const; virtual QString comment() const; virtual int trackNumber() const; virtual int discNumber() const; virtual qint64 length() const; virtual int filesize() const; virtual int sampleRate() const; virtual int bitrate() const; virtual QString type() const; virtual bool inCollection() const; virtual Collections::Collection *collection() const; virtual bool hasCapabilityInterface( Capabilities::Capability::Type type ) const; virtual Capabilities::Capability* createCapabilityInterface( Capabilities::Capability::Type type ); virtual Meta::StatisticsPtr statistics(); // own methods: void setTrackInfo( const lastfm::Track &trackInfo ); virtual QString sourceName(); virtual QString sourceDescription(); virtual QPixmap emblem(); virtual QString scalableEmblem(); //LastFm specific methods, cast the object to LastFm::Track to use them //you can cast the Track when type() returns "stream/lastfm" (or use a dynamic cast:) QUrl internalUrl() const; // this returns the private temporary url to the .mp3, DO NOT USE, // if you are asking, it has already expired QString streamName() const; // A nice name for the stream.. public Q_SLOTS: void ban(); private Q_SLOTS: void slotResultReady(); void slotWsReply(); Q_SIGNALS: void skipTrack(); // needed for communication with multiplayablecapability private: void init( int id = -1 ); //use a d-pointer because some code is going to work directly with LastFm::Track Private * const d; QList< QAction * > m_trackActions; }; class LastFmProviderCapability : public Capabilities::Capability { public: LastFmProviderCapability(); ~LastFmProviderCapability(); }; - typedef KSharedPtr TrackPtr; + typedef AmarokSharedPointer TrackPtr; } #endif diff --git a/src/services/lastfm/meta/LastFmMeta_p.h b/src/services/lastfm/meta/LastFmMeta_p.h index 9e33de2c1c..785a2dedc4 100644 --- a/src/services/lastfm/meta/LastFmMeta_p.h +++ b/src/services/lastfm/meta/LastFmMeta_p.h @@ -1,377 +1,376 @@ /**************************************************************************************** * Copyright (c) 2007 Maximilian Kossick * * Copyright (c) 2008 Leo Franchi * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef AMAROK_LASTFMMETA_P_H #define AMAROK_LASTFMMETA_P_H #include "core/support/Debug.h" #include "amarokconfig.h" #include "core/meta/Meta.h" #include "core/support/Amarok.h" #include "core-impl/support/TagStatisticsStore.h" -#include -#include -#include +#include #include #include #include #include +#include #include #include #include #include #include namespace LastFm { class Track::Private : public QObject { Q_OBJECT public: Track *t; lastfm::Track lastFmTrack; // this is how we love, ban, etc QUrl trackPath; QUrl lastFmUri; QImage albumArt; QString artist; QString album; QString track; qint64 length; //not sure what these are for but they exist in the LastFmBundle QString albumUrl; QString artistUrl; QString trackUrl; QString imageUrl; Meta::ArtistPtr artistPtr; Meta::AlbumPtr albumPtr; Meta::GenrePtr genrePtr; Meta::ComposerPtr composerPtr; Meta::YearPtr yearPtr; QNetworkReply* trackFetch; QNetworkReply* wsReply; Meta::StatisticsPtr statsStore; uint currentTrackStartTime; public: Private() : lastFmUri( QUrl() ) , currentTrackStartTime( 0 ) { artist = QString ( "Last.fm" ); } ~Private() { } void notifyObservers(); void setTrackInfo( const lastfm::Track &trackInfo ) { DEBUG_BLOCK bool newTrackInfo = artist != trackInfo.artist() || album != trackInfo.album() || track != trackInfo.title(); lastFmTrack = trackInfo; artist = trackInfo.artist(); album = trackInfo.album(); track = trackInfo.title(); length = trackInfo.duration() * 1000; trackPath = trackInfo.url(); // need to reset other items albumUrl = ""; trackUrl = ""; albumArt = QImage(); if( newTrackInfo ) { statsStore = new TagStatisticsStore( t ); currentTrackStartTime = QDateTime::currentDateTime().toTime_t(); } notifyObservers(); if( !trackInfo.isNull() ) { QMap< QString, QString > params; params[ "method" ] = "track.getInfo"; params[ "artist" ] = artist; params[ "track" ] = track; m_userFetch = lastfm::ws::post( params ); connect( m_userFetch, SIGNAL( finished() ), SLOT( requestResult() ) ); } } public Q_SLOTS: void requestResult( ) { if( !m_userFetch ) return; if( m_userFetch->error() == QNetworkReply::NoError ) { lastfm::XmlQuery lfm; if( lfm.parse( m_userFetch->readAll() ) ) { albumUrl = lfm[ "track" ][ "album" ][ "url" ].text(); trackUrl = lfm[ "track" ][ "url" ].text(); artistUrl = lfm[ "track" ][ "artist" ][ "url" ].text(); notifyObservers(); imageUrl = lfm[ "track" ][ "album" ][ "image size=large" ].text(); if( !imageUrl.isEmpty() ) { KIO::Job* job = KIO::storedGet( QUrl( imageUrl ), KIO::Reload, KIO::HideProgressInfo ); connect( job, SIGNAL( result( KJob* ) ), this, SLOT( fetchImageFinished( KJob* ) ) ); } } else { debug() << "Got exception in parsing from last.fm:" << lfm.parseError().message(); return; } } } void fetchImageFinished( KJob* job ) { if( job->error() == 0 ) { const int size = 100; QImage img = QImage::fromData( static_cast( job )->data() ); if( !img.isNull() ) { img.scaled( size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); albumArt = img; } else albumArt = QImage(); } else { //use default image albumArt = QImage(); } notifyObservers(); } private: QNetworkReply* m_userFetch; }; // internal helper classes class LastFmArtist : public Meta::Artist { public: LastFmArtist( Track::Private *dptr ) : Meta::Artist() , d( dptr ) {} Meta::TrackList tracks() { return Meta::TrackList(); } QString name() const { if( d ) return d->artist; return QString( "Last.fm" ); } Track::Private * const d; friend class Track::Private; }; class LastFmAlbum : public Meta::Album { public: LastFmAlbum( Track::Private *dptr ) : Meta::Album() , d( dptr ) {} bool isCompilation() const { return false; } bool hasAlbumArtist() const { return false; } Meta::ArtistPtr albumArtist() const { return Meta::ArtistPtr(); } Meta::TrackList tracks() { return Meta::TrackList(); } QString name() const { if( d ) return d->album; return QString(); } QImage image( int size ) const { if( !d || d->albumArt.isNull() ) { //return Meta::Album::image( size, withShadow ); //TODO implement shadow //TODO improve this if ( size <= 1 ) size = 100; QString sizeKey = QString::number( size ) + '@'; QImage image; QDir cacheCoverDir = QDir( Amarok::saveLocation( "albumcovers/cache/" ) ); if( cacheCoverDir.exists( sizeKey + "lastfm-default-cover.png" ) ) image = QImage( cacheCoverDir.filePath( sizeKey + "lastfm-default-cover.png" ) ); else { - QImage orgImage = QImage( KStandardDirs::locate( "data", "amarok/images/lastfm-default-cover.png" ) ); //optimize this! + QImage orgImage = QImage( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/lastfm-default-cover.png" ) ); //optimize this! //scaled() does not change the original image but returns a scaled copy image = orgImage.scaled( size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation ); image.save( cacheCoverDir.filePath( sizeKey + "lastfm-default-cover.png" ), "PNG" ); } return image; } if( d->albumArt.width() != size && size > 0 ) return d->albumArt.scaled( size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); return d->albumArt; } QUrl imageLocation( int size ) { Q_UNUSED( size ); if( d && !d->imageUrl.isEmpty() ) return QUrl( d->imageUrl ); return QUrl(); } // return true since we handle our own fetching bool hasImage( int size = 1 ) const { Q_UNUSED( size ); return true; } Track::Private * const d; friend class Track::Private; }; class LastFmGenre : public Meta::Genre { public: LastFmGenre( Track::Private *dptr ) : Meta::Genre() , d( dptr ) {} QString name() const { return QString(); } Meta::TrackList tracks() { return Meta::TrackList(); } Track::Private * const d; friend class Track::Private; }; class LastFmComposer : public Meta::Composer { public: LastFmComposer( Track::Private *dptr ) : Meta::Composer() , d( dptr ) {} QString name() const { return QString(); } Meta::TrackList tracks() { return Meta::TrackList(); } Track::Private * const d; friend class Track::Private; }; class LastFmYear : public Meta::Year { public: LastFmYear( Track::Private *dptr ) : Meta::Year() , d( dptr ) {} QString name() const { return QString(); } Meta::TrackList tracks() { return Meta::TrackList(); } Track::Private * const d; friend class Track::Private; }; void Track::Private::notifyObservers() { // TODO: only notify what actually has changed t->notifyObservers(); static_cast( t->album().data() )->notifyObservers(); static_cast( t->artist().data() )->notifyObservers(); } } #endif diff --git a/src/services/lastfm/meta/LastFmMultiPlayableCapability.cpp b/src/services/lastfm/meta/LastFmMultiPlayableCapability.cpp index 25e16f1865..22fc8dad68 100644 --- a/src/services/lastfm/meta/LastFmMultiPlayableCapability.cpp +++ b/src/services/lastfm/meta/LastFmMultiPlayableCapability.cpp @@ -1,109 +1,108 @@ /**************************************************************************************** * Copyright (c) 2008 Dan Meltzer * * Copyright (c) 2012 Matěj Laitl * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "LastFmMultiPlayableCapability.h" #include "EngineController.h" LastFmMultiPlayableCapability::LastFmMultiPlayableCapability( LastFm::Track *track ) : Capabilities::MultiPlayableCapability() , m_url( track->internalUrl() ) , m_track( track ) { - connect( track, SIGNAL(skipTrack()), this, SLOT(skip()) ); + connect( track, &LastFm::Track::skipTrack, this, &LastFmMultiPlayableCapability::skip ); Q_ASSERT( The::mainWindow() ); - connect( The::mainWindow(), SIGNAL(skipTrack()), SLOT(skip()) ); + connect( The::mainWindow(), &MainWindow::skipTrack, this, &LastFmMultiPlayableCapability::skip ); // we only update underlying Last.fm metadata once it starts playing, prevent wrong // metadata Last.fm submissions etc. Q_ASSERT( EngineController::instance() ); - connect( EngineController::instance(), SIGNAL(trackPlaying(Meta::TrackPtr)), - SLOT(slotTrackPlaying(Meta::TrackPtr)) ); + connect( EngineController::instance(), &EngineController::trackPlaying, + this, &LastFmMultiPlayableCapability::slotTrackPlaying ); } LastFmMultiPlayableCapability::~LastFmMultiPlayableCapability() { } void LastFmMultiPlayableCapability::fetchFirst() { DEBUG_BLOCK m_tuner = new lastfm::RadioTuner( lastfm::RadioStation( m_track->uidUrl() ) ); m_tuner->setParent( this ); // memory management - connect( m_tuner, SIGNAL(trackAvailable()), SLOT(slotNewTrackAvailable()) ); - connect( m_tuner, SIGNAL(error(lastfm::ws::Error,QString)), - SLOT(error(lastfm::ws::Error)) ); + connect( m_tuner, &lastfm::RadioTuner::trackAvailable, this, &LastFmMultiPlayableCapability::slotNewTrackAvailable ); + connect( m_tuner, &lastfm::RadioTuner::error, this, &LastFmMultiPlayableCapability::error ); } void LastFmMultiPlayableCapability::fetchNext() { DEBUG_BLOCK m_currentTrack = m_tuner->takeNextTrack(); emit playableUrlFetched( m_currentTrack.url() ); } void LastFmMultiPlayableCapability::slotTrackPlaying( const Meta::TrackPtr &track ) { // time to update underlying track with metadata // warning: this depends on MetaProxy::Track operator== returning true // between proxy and underlying track! if( track == m_track ) m_track->setTrackInfo( m_currentTrack ); } void LastFmMultiPlayableCapability::slotNewTrackAvailable() { DEBUG_BLOCK if( m_currentTrack.isNull() ) // we only force a track change at the beginning { fetchNext(); // we update metadata immediatelly for the very first track m_track->setTrackInfo( m_currentTrack ); } } void LastFmMultiPlayableCapability::skip() { DEBUG_BLOCK fetchNext(); } void LastFmMultiPlayableCapability::error( lastfm::ws::Error e ) { // last.fm is returning an AuthenticationFailed message when the user is not // a subscriber, even if the credentials are OK if( e == lastfm::ws::SubscribersOnly || e == lastfm::ws::AuthenticationFailed ) { Amarok::Components::logger()->longMessage( i18n( "To listen to Last.fm streams " "and radio you need to be a paying Last.fm subscriber and you need to " "stream from a supported " "country. All other Last.fm features work fine." ) ); } else { Amarok::Components::logger()->longMessage( i18n( "Error starting track from Last.fm radio" ) ); } } diff --git a/src/services/lastfm/meta/LastFmMultiPlayableCapability.h b/src/services/lastfm/meta/LastFmMultiPlayableCapability.h index b0869543e6..a164724f05 100644 --- a/src/services/lastfm/meta/LastFmMultiPlayableCapability.h +++ b/src/services/lastfm/meta/LastFmMultiPlayableCapability.h @@ -1,61 +1,61 @@ /**************************************************************************************** * Copyright (c) 2008 Shane King * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef AMAROK_MULTIPLAYABLECAPABILITYIMPL_P_H #define AMAROK_MULTIPLAYABLECAPABILITYIMPL_P_H #include "core/interfaces/Logger.h" #include "core/support/Components.h" #include "core/support/Debug.h" #include "MainWindow.h" #include "LastFmMeta.h" #include "core/meta/forward_declarations.h" #include "core/capabilities/MultiPlayableCapability.h" -#include +#include #include #include #include #include class LastFmMultiPlayableCapability : public Capabilities::MultiPlayableCapability { Q_OBJECT public: LastFmMultiPlayableCapability( LastFm::Track *track ); virtual ~LastFmMultiPlayableCapability(); // Capabilities::MultiPlayableCapability methods virtual void fetchFirst(); virtual void fetchNext(); private Q_SLOTS: void slotTrackPlaying( const Meta::TrackPtr &track ); void slotNewTrackAvailable(); void skip(); void error( lastfm::ws::Error e ); private: QUrl m_url; LastFm::TrackPtr m_track; lastfm::Track m_currentTrack; lastfm::RadioTuner *m_tuner; }; #endif diff --git a/src/services/magnatune/CMakeLists.txt b/src/services/magnatune/CMakeLists.txt index 03fa01ae13..316007478b 100644 --- a/src/services/magnatune/CMakeLists.txt +++ b/src/services/magnatune/CMakeLists.txt @@ -1,85 +1,87 @@ include_directories( ../ ../../ ../../core-impl/collections ../../statusbar ${CMAKE_CURRENT_BINARY_DIR}/../../.. - - ) +) add_subdirectory( images ) ########### next target ############### set(amarok_service_magnatune_PART_SRCS MagnatuneActions.cpp MagnatuneAlbumDownloader.cpp MagnatuneCollectionLocation.cpp MagnatuneConfig.cpp MagnatuneDatabaseHandler.cpp MagnatuneDatabaseWorker.cpp MagnatuneDownloadDialog.cpp MagnatuneDownloadInfo.cpp MagnatuneInfoParser.cpp MagnatuneMeta.cpp MagnatuneNeedUpdateWidget.cpp MagnatuneDownloadHandler.cpp MagnatuneRedownloadDialog.cpp MagnatuneRedownloadHandler.cpp MagnatuneSqlCollection.cpp MagnatuneStore.cpp MagnatuneUrlRunner.cpp MagnatuneXmlParser.cpp ) ki18n_wrap_ui( amarok_service_magnatune_PART_SRCS MagnatuneDownloadDialogBase.ui MagnatuneNeedUpdateWidget.ui MagnatuneRedownloadDialogBase.ui MagnatuneSignupDialogBase.ui ) add_library(amarok_service_magnatunestore MODULE ${amarok_service_magnatune_PART_SRCS}) + target_link_libraries(amarok_service_magnatunestore amarokcore amaroklib - KF5::KDELibs4Support KF5::ConfigWidgets KF5::KIOCore KF5::ThreadWeaver Qt5::Core Qt5::Widgets ) - install(TARGETS amarok_service_magnatunestore DESTINATION ${PLUGIN_INSTALL_DIR} ) +kcoreaddons_desktop_to_json(amarok_service_magnatunestore amarok_service_magnatunestore.desktop SERVICE_TYPES ${CMAKE_SOURCE_DIR}/src/amarok-plugin.desktop) + ########### next target ############### set(kcm_amarok_service_magnatune_PART_SRCSS MagnatuneSettingsModule.cpp MagnatuneConfig.cpp ) ki18n_wrap_ui( kcm_amarok_service_magnatune_PART_SRCSS MagnatuneConfigWidget.ui ) add_library(kcm_amarok_service_magnatunestore MODULE ${kcm_amarok_service_magnatune_PART_SRCSS} ) target_link_libraries( kcm_amarok_service_magnatunestore - KF5::KDELibs4Support + amarokcore KF5::ConfigWidgets KF5::ThreadWeaver Qt5::Core Qt5::Gui ) -install(TARGETS kcm_amarok_service_magnatunestore DESTINATION ${PLUGIN_INSTALL_DIR}) +install(TARGETS kcm_amarok_service_magnatunestore DESTINATION ${PLUGIN_INSTALL_DIR}) + +kcoreaddons_desktop_to_json(kcm_amarok_service_magnatunestore amarok_service_magnatunestore_config.desktop SERVICE_TYPES kcmodule.desktop) ########### install files ############### -install( FILES amarok_service_magnatunestore.desktop DESTINATION ${SERVICES_INSTALL_DIR}) -install( FILES amarok_service_magnatunestore_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}) +install( FILES amarok_service_magnatunestore.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) +install( FILES amarok_service_magnatunestore_config.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) diff --git a/src/services/magnatune/MagnatuneActions.cpp b/src/services/magnatune/MagnatuneActions.cpp index 9e8099b27f..dc9bcd92a3 100644 --- a/src/services/magnatune/MagnatuneActions.cpp +++ b/src/services/magnatune/MagnatuneActions.cpp @@ -1,60 +1,60 @@ /**************************************************************************************** * Copyright (c) 2008 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneActions.h" #include "SvgHandler.h" #include MagnatuneDownloadAction::MagnatuneDownloadAction( const QString &text, Meta::MagnatuneAlbum * album ) : QAction( QIcon::fromTheme("download-amarok" ), text, album ) , m_album( album ) { setProperty( "popupdropper_svg_id", "append" ); - connect( this, SIGNAL(triggered(bool)), SLOT(slotTriggered()) ); + connect( this, &QAction::triggered, this, &MagnatuneDownloadAction::slotTriggered ); } MagnatuneDownloadAction::~MagnatuneDownloadAction() { } void MagnatuneDownloadAction::slotTriggered() { DEBUG_BLOCK m_album->download(); } MagnatuneAddToFavoritesAction::MagnatuneAddToFavoritesAction( const QString &text, Meta::MagnatuneAlbum * album ) : QAction( QIcon::fromTheme("favorites" ), text, album ) , m_album( album ) { setProperty( "popupdropper_svg_id", "append" ); - connect( this, SIGNAL(triggered(bool)), SLOT(slotTriggered()) ); + connect( this, &QAction::triggered, this, &MagnatuneAddToFavoritesAction::slotTriggered ); } MagnatuneAddToFavoritesAction::~MagnatuneAddToFavoritesAction() { } void MagnatuneAddToFavoritesAction::slotTriggered() { DEBUG_BLOCK m_album->addToFavorites(); } diff --git a/src/services/magnatune/MagnatuneAlbumDownloader.cpp b/src/services/magnatune/MagnatuneAlbumDownloader.cpp index 9eb253a7e3..c149e510ac 100644 --- a/src/services/magnatune/MagnatuneAlbumDownloader.cpp +++ b/src/services/magnatune/MagnatuneAlbumDownloader.cpp @@ -1,154 +1,179 @@ /**************************************************************************************** * Copyright (c) 2006,2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneAlbumDownloader.h" #include "core/support/Amarok.h" #include "core/support/Components.h" #include "core/support/Debug.h" #include "core/interfaces/Logger.h" #include "MagnatuneMeta.h" +#include -#include +#include #include MagnatuneAlbumDownloader::MagnatuneAlbumDownloader() : QObject() - , m_albumDownloadJob( ) + , m_albumDownloadJob( Q_NULLPTR ) + , m_coverDownloadJob( Q_NULLPTR ) , m_currentAlbumFileName() { - m_tempDir = new KTempDir(); + m_tempDir = new QTemporaryDir(); } MagnatuneAlbumDownloader::~MagnatuneAlbumDownloader() { delete m_tempDir; - m_tempDir = 0; } void MagnatuneAlbumDownloader::downloadAlbum( MagnatuneDownloadInfo info ) { DEBUG_BLOCK m_currentAlbumInfo = info; QUrl downloadUrl = info.completeDownloadUrl(); m_currentAlbumUnpackLocation = info.unpackLocation(); debug() << "Download: " << downloadUrl.url() << " to: " << m_currentAlbumUnpackLocation; m_currentAlbumFileName = info.albumCode() + ".zip"; - debug() << "Using temporary location: " << m_tempDir->name() + m_currentAlbumFileName; + debug() << "Using temporary location: " << m_tempDir->path() + '/' + m_currentAlbumFileName; - m_albumDownloadJob = KIO::file_copy( downloadUrl, QUrl( m_tempDir->name() + m_currentAlbumFileName ), -1, KIO::Overwrite | KIO::HideProgressInfo ); + m_albumDownloadJob = KIO::file_copy( downloadUrl, QUrl::fromLocalFile( m_tempDir->path() + '/' + m_currentAlbumFileName ), -1, KIO::Overwrite | KIO::HideProgressInfo ); - connect( m_albumDownloadJob, SIGNAL(result(KJob*)), SLOT(albumDownloadComplete(KJob*)) ); + connect( m_albumDownloadJob, &KJob::result, this, &MagnatuneAlbumDownloader::albumDownloadComplete ); QString msgText; if( !info.albumName().isEmpty() && !info.artistName().isEmpty() ) { msgText = i18n( "Downloading '%1' by %2 from Magnatune.com", info.albumName(), info.artistName() ); } else { msgText = i18n( "Downloading album from Magnatune.com" ); } Amarok::Components::logger()->newProgressOperation( m_albumDownloadJob, msgText, this, SLOT(albumDownloadAborted()) ); } - - - void MagnatuneAlbumDownloader::albumDownloadComplete( KJob * downloadJob ) { DEBUG_BLOCK debug() << "album download complete"; - if ( !downloadJob->error() == 0 ) + if ( downloadJob->error() ) { //TODO: error handling here return ; } if ( downloadJob != m_albumDownloadJob ) return ; //not the right job, so let's ignore it const QString finalAlbumPath = m_currentAlbumUnpackLocation + '/' + m_currentAlbumInfo.artistName() + '/' + m_currentAlbumInfo.albumName(); //ok, now we have the .zip file downloaded. All we need is to unpack it to the desired location and add it to the collection. - KZip kzip( m_tempDir->name() + m_currentAlbumFileName ); + KZip kzip( m_tempDir->path() + '/' + m_currentAlbumFileName ); if ( !kzip.open( QIODevice::ReadOnly ) ) { Amarok::Components::logger()->shortMessage( i18n( "Magnatune download seems to have failed. Cannot read zip file" ) ); emit( downloadComplete( false ) ); return; } - debug() << m_tempDir->name() + m_currentAlbumFileName << " opened for decompression"; + debug() << m_tempDir->path() + '/' + m_currentAlbumFileName << " opened for decompression"; const KArchiveDirectory * directory = kzip.directory(); Amarok::Components::logger()->shortMessage( i18n( "Uncompressing Magnatune.com download..." ) ); //Is this really blocking with no progress status!? Why is it not a KJob? debug() << "decompressing to " << finalAlbumPath; directory->copyTo( m_currentAlbumUnpackLocation ); debug() << "done!"; //now I really want to add the album cover to the same folder where I just unzipped the album... The //only way of getting the actual location where the album was unpacked is using the artist and album names QString coverUrlString = m_currentAlbumInfo.coverUrl(); QUrl downloadUrl( coverUrlString.replace( "_200.jpg", ".jpg") ); debug() << "Adding cover " << downloadUrl.url() << " to collection at " << finalAlbumPath; - m_albumDownloadJob = KIO::file_copy( downloadUrl, QUrl( finalAlbumPath + "/cover.jpg" ), -1, KIO::Overwrite | KIO::HideProgressInfo ); + m_coverDownloadJob = KIO::file_copy( downloadUrl, QUrl::fromLocalFile( finalAlbumPath + "/cover.jpg" ), -1, KIO::Overwrite | KIO::HideProgressInfo ); - connect( m_albumDownloadJob, SIGNAL(result(KJob*)), SLOT(coverAddComplete(KJob*)) ); + connect( m_coverDownloadJob, &KJob::result, this, &MagnatuneAlbumDownloader::coverDownloadComplete ); - Amarok::Components::logger()->newProgressOperation( m_albumDownloadJob, i18n( "Adding album cover to collection" ), this, SLOT(coverAddAborted()) ); + Amarok::Components::logger()->newProgressOperation( m_coverDownloadJob, i18n( "Adding album cover to collection" ), this, SLOT(coverAddAborted()) ); emit( downloadComplete( true ) ); +} +void +MagnatuneAlbumDownloader::coverDownloadComplete(KJob* downloadJob) +{ + DEBUG_BLOCK + + debug() << "cover download complete"; + if ( downloadJob->error() ) + { + //TODO: error handling here + return ; + } + if ( downloadJob != m_coverDownloadJob ) + return ; //not the right job, so let's ignore it + + //TODO: storing of cover here } void MagnatuneAlbumDownloader::albumDownloadAborted( ) { DEBUG_BLOCK m_albumDownloadJob->kill(); - m_albumDownloadJob = 0; + m_albumDownloadJob = Q_NULLPTR; debug() << "Aborted album download"; emit( downloadComplete( false ) ); +} + +void +MagnatuneAlbumDownloader::coverAddAborted() +{ + DEBUG_BLOCK + m_coverDownloadJob->kill(); + m_coverDownloadJob = Q_NULLPTR; + debug() << "Aborted cover download"; + + emit( downloadComplete( false ) ); } diff --git a/src/services/magnatune/MagnatuneAlbumDownloader.h b/src/services/magnatune/MagnatuneAlbumDownloader.h index 325cd01449..04c6a60449 100644 --- a/src/services/magnatune/MagnatuneAlbumDownloader.h +++ b/src/services/magnatune/MagnatuneAlbumDownloader.h @@ -1,76 +1,78 @@ /**************************************************************************************** * Copyright (c) 2006,2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef MAGNATUNEALBUMDOWNLOADER_H #define MAGNATUNEALBUMDOWNLOADER_H #include "MagnatuneDownloadInfo.h" #include "MagnatuneMeta.h" -#include -#include +#include +#include #include +#include -#include /** This class encapsulates the downloading of an album once all required information has been retrieved @author Nikolaj Hald Nielsen */ -class MagnatuneAlbumDownloader: public QObject +class MagnatuneAlbumDownloader : public QObject { Q_OBJECT public: MagnatuneAlbumDownloader(); ~MagnatuneAlbumDownloader(); Q_SIGNALS: /** * This signal is emitted when a download is finished or cancelled * @param success true is download completed, false if download was cancelled. */ void downloadComplete( bool success ); public Q_SLOTS: /** * Initiates the download of an album - * @param url A MagnatuneDownloadInfo object containing all needed information + * @param info A MagnatuneDownloadInfo object containing all needed information */ void downloadAlbum( MagnatuneDownloadInfo info ); protected: - KIO::FileCopyJob * m_albumDownloadJob; + KIO::FileCopyJob *m_albumDownloadJob; + KIO::FileCopyJob *m_coverDownloadJob; QString m_currentAlbumUnpackLocation; QString m_currentAlbumFileName; MagnatuneDownloadInfo m_currentAlbumInfo; - KTempDir * m_tempDir; + QTemporaryDir * m_tempDir; protected Q_SLOTS: /** * Unzip the downloaded album - * @param downLoadJob + * @param downloadJob */ void albumDownloadComplete( KJob* downloadJob ); void albumDownloadAborted(); - + void coverDownloadComplete( KJob* downloadJob ); + void coverAddAborted(); }; #endif diff --git a/src/services/magnatune/MagnatuneCollectionLocation.cpp b/src/services/magnatune/MagnatuneCollectionLocation.cpp index 4ec21e77e1..ed7b082fb7 100644 --- a/src/services/magnatune/MagnatuneCollectionLocation.cpp +++ b/src/services/magnatune/MagnatuneCollectionLocation.cpp @@ -1,65 +1,67 @@ /**************************************************************************************** * Copyright (c) 2008 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneCollectionLocation.h" #include -#include - +#include #include +#include + +#include #include using namespace Collections; MagnatuneCollectionLocation::MagnatuneCollectionLocation( MagnatuneSqlCollection *parentCollection ) : ServiceCollectionLocation( parentCollection ) { } MagnatuneCollectionLocation::~MagnatuneCollectionLocation() { } void MagnatuneCollectionLocation::showSourceDialog( const Meta::TrackList &tracks, bool removeSources ) { QDialog dialog; dialog.setWindowTitle( i18n( "Preview Tracks" ) ); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QVBoxLayout *mainLayout = new QVBoxLayout; dialog.setLayout(mainLayout); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); - dialog.connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); - dialog.connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); QLabel *label = new QLabel( i18n( "The tracks you are about to copy are Magnatune.com preview streams. For better quality and advert free streams, consider buying an album download. Remember that when buying from Magnatune the artist gets 50%. Also if you buy using Amarok, you support the Amarok project with 10%." ) ); label->setWordWrap ( true ); label->setMaximumWidth( 400 ); mainLayout->addWidget(label); mainLayout->addWidget(buttonBox); dialog.exec(); if ( dialog.result() == QDialog::Rejected ) abort(); CollectionLocation::showSourceDialog( tracks, removeSources ); // to get transcoding dialog } diff --git a/src/services/magnatune/MagnatuneConfig.cpp b/src/services/magnatune/MagnatuneConfig.cpp index 5842c63134..731794d002 100644 --- a/src/services/magnatune/MagnatuneConfig.cpp +++ b/src/services/magnatune/MagnatuneConfig.cpp @@ -1,230 +1,229 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) version 3 or * * any later version accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of * * version 3 of the license. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneConfig.h" #include "MagnatuneMeta.h" +#include "core/support/Amarok.h" -#include -#include #include -#include MagnatuneConfig::MagnatuneConfig() { load(); } MagnatuneConfig::~MagnatuneConfig() { } void MagnatuneConfig::load() { m_hasChanged = false; - kDebug() << "load"; - KConfigGroup config = KGlobal::config()->group( "Service_Magnatune" ); +// qDebug() << "load"; + + KConfigGroup config = Amarok::config( "Service_Magnatune" ); m_isMember = config.readEntry( "isMember", false ); m_autoUpdate = config.readEntry( "autoUpdateDatabase", false ); m_membershipType = config.readEntry( "membershipType", -1 ); if( m_membershipType == -1 ) { //try to read the old style string version if that is present and valid. QString oldMEmbershipType = config.readEntry( "membershipType", QString() ); if( oldMEmbershipType.toLower() == "stream" ) m_membershipType = MagnatuneConfig::STREAM; else if ( oldMEmbershipType.toLower() == "download" ) m_membershipType = MagnatuneConfig::DOWNLOAD; else m_membershipType = MagnatuneConfig::DOWNLOAD; //default to download for now. } m_username = config.readEntry( "username", QString() ); m_password = config.readEntry( "password", QString() ); m_email = config.readEntry( "email", QString() ); qulonglong defaultLong = 0; m_lastUpdateTimestamp = config.readEntry( "lastUpdate", defaultLong ); QString streamTypeString = config.readEntry( "streamType", QString() ); //make ogg the default if ( streamTypeString == "mp3" ) m_streamType = MagnatuneMetaFactory::MP3; else if ( streamTypeString == "lofi_mp3" ) m_streamType = MagnatuneMetaFactory::LOFI; else m_streamType = MagnatuneMetaFactory::OGG; } void MagnatuneConfig::save() { - kDebug() << "save"; + qDebug() << "save"; if ( m_hasChanged ) { - KConfigGroup config = KGlobal::config()->group( "Service_Magnatune" ); + KConfigGroup config = Amarok::config( "Service_Magnatune" ); config.writeEntry( "isMember", m_isMember ); config.writeEntry( "autoUpdateDatabase", m_autoUpdate ); config.writeEntry( "membershipType", m_membershipType ); config.writeEntry( "username", m_username ); config.writeEntry( "password", m_password ); config.writeEntry( "lastUpdate", QVariant( m_lastUpdateTimestamp ) ); config.writeEntry( "email", m_email ); QString streamTypeString; if ( m_streamType == MagnatuneMetaFactory::MP3 ) streamTypeString = "mp3"; else if ( m_streamType == MagnatuneMetaFactory::LOFI ) streamTypeString = "lofi_mp3"; else streamTypeString = "ogg"; config.writeEntry( "streamType", streamTypeString ); } } bool MagnatuneConfig::isMember() { return m_isMember; } void MagnatuneConfig::setIsMember( bool isMember ) { m_hasChanged = true; m_isMember = isMember; } bool MagnatuneConfig::autoUpdateDatabase() { return m_autoUpdate; } void MagnatuneConfig::setAutoUpdateDatabase( bool value ) { m_hasChanged = true; m_autoUpdate = value; } int MagnatuneConfig::membershipType() { return m_membershipType; } void MagnatuneConfig::setMembershipType( int membershipType ) { m_hasChanged = true; m_membershipType = membershipType; } QString MagnatuneConfig::membershipPrefix() { QString prefix; if( m_membershipType == MagnatuneConfig::STREAM ) prefix = "stream"; else prefix = "download"; return prefix; } QString MagnatuneConfig::username() { return m_username; } QString MagnatuneConfig::password() { return m_password; } void MagnatuneConfig::setUsername( const QString &username ) { m_hasChanged = true; m_username = username; } void MagnatuneConfig::setPassword( const QString &password ) { m_hasChanged = true; m_password = password; } int MagnatuneConfig::streamType() const { return m_streamType; } void MagnatuneConfig::setStreamType( int theValue ) { m_streamType = theValue; } qulonglong MagnatuneConfig::lastUpdateTimestamp() { return m_lastUpdateTimestamp; } void MagnatuneConfig::setLastUpdateTimestamp( qulonglong timestamp ) { m_hasChanged = true; m_lastUpdateTimestamp = timestamp; } QString MagnatuneConfig::email() { return m_email; } void MagnatuneConfig::setEmail( const QString &email ) { m_email = email; m_hasChanged = true; } diff --git a/src/services/magnatune/MagnatuneConfigWidget.ui b/src/services/magnatune/MagnatuneConfigWidget.ui index 6aee259067..fdcfda0b16 100644 --- a/src/services/magnatune/MagnatuneConfigWidget.ui +++ b/src/services/magnatune/MagnatuneConfigWidget.ui @@ -1,275 +1,275 @@ MagnatuneConfigWidget 0 0 402 511 Updates If you check this box, Amarok will periodically check for Magnatune database updates and download them automatically. true Update Magnatune database automatically Redownloads Enter your e-mail here to be able to redownload any previous purchase from Magnatune directly from within Amarok. true E-mail: - + true Membership Options I am a member Qt::Horizontal 40 20 Membership type: false Stream Download Username: - + false Password: - + false <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'DejaVu Serif'; font-size:8pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">For more information about the Magnatune.com memberships, and to sign up, go to </span><a href="http://magnatune.com/compare_plans?referal_id=amarok"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0000ff;">http://magnatune.com/compare_plans</span></a></p></body></html> true true 0 0 Stream Options Preview stream type: Ogg High Quality Mp3 Low Quality Mp3 - KLineEdit + QLineEdit QLineEdit -
klineedit.h
+
qlineedit.h
isMemberCheckbox toggled(bool) typeComboBox setEnabled(bool) 76 51 252 81 isMemberCheckbox toggled(bool) usernameEdit setEnabled(bool) 76 51 252 115 isMemberCheckbox toggled(bool) passwordEdit setEnabled(bool) 76 51 252 150
diff --git a/src/services/magnatune/MagnatuneDatabaseHandler.cpp b/src/services/magnatune/MagnatuneDatabaseHandler.cpp index 2d5fc599f4..24bb6ea6a7 100644 --- a/src/services/magnatune/MagnatuneDatabaseHandler.cpp +++ b/src/services/magnatune/MagnatuneDatabaseHandler.cpp @@ -1,310 +1,310 @@ /**************************************************************************************** * Copyright (c) 2006,2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneDatabaseHandler.h" #include #include #include using namespace Meta; MagnatuneDatabaseHandler::MagnatuneDatabaseHandler() {} MagnatuneDatabaseHandler::~MagnatuneDatabaseHandler() {} void MagnatuneDatabaseHandler::createDatabase( ) { //Get database instance auto sqlDb = StorageManager::instance()->sqlStorage(); QString autoIncrement = "AUTO_INCREMENT"; // create table containing tracks QString queryString = "CREATE TABLE magnatune_tracks (" "id INTEGER PRIMARY KEY " + autoIncrement + ',' + "name " + sqlDb->textColumnType() + ',' + "track_number INTEGER," "length INTEGER," "album_id INTEGER," "artist_id INTEGER," "preview_lofi " + sqlDb->exactTextColumnType() + ',' + "preview_ogg " + sqlDb->exactTextColumnType() + ',' + "preview_url " + sqlDb->exactTextColumnType() + ") ENGINE = MyISAM;"; debug() << "Creating mangnatune_tracks: " << queryString; QStringList result = sqlDb->query( queryString ); sqlDb->query( "CREATE INDEX magnatune_tracks_album_id ON magnatune_tracks(album_id);" ); sqlDb->query( "CREATE INDEX magnatune_tracks_artist_id ON magnatune_tracks(artist_id);" ); //Create album table queryString = "CREATE TABLE magnatune_albums (" "id INTEGER PRIMARY KEY " + autoIncrement + ',' + "name " + sqlDb->textColumnType() + ',' + "year INTEGER," "artist_id INTEGER," "album_code " + sqlDb->textColumnType() + ',' + "cover_url " + sqlDb->exactTextColumnType() + ',' + "description " + sqlDb->exactTextColumnType() + ") ENGINE = MyISAM;"; debug() << "Creating Mangnatune_albums: " << queryString; result = sqlDb->query( queryString ); sqlDb->query( "CREATE INDEX magnatune_albums_name ON magnatune_albums(name);" ); sqlDb->query( "CREATE INDEX magnatune_albums_artist_id ON magnatune_albums(artist_id);" ); //Create artist table queryString = "CREATE TABLE magnatune_artists (" "id INTEGER PRIMARY KEY " + autoIncrement + ',' + "name " + sqlDb->textColumnType() + ',' + "artist_page " + sqlDb->exactTextColumnType() + ',' + "description " + sqlDb->textColumnType() + ',' + "photo_url " + sqlDb->exactTextColumnType() + ") ENGINE = MyISAM;"; debug() << "Creating mangnatune_artist: " << queryString; result = sqlDb->query( queryString ); sqlDb->query( "CREATE INDEX magnatune_artists_name ON magnatune_artists(name);" ); //create genre table queryString = "CREATE TABLE magnatune_genre (" "id INTEGER PRIMARY KEY " + autoIncrement + ',' + "name " + sqlDb->textColumnType() + ',' + "album_id INTEGER" + ") ENGINE = MyISAM;"; result = sqlDb->query( queryString ); sqlDb->query( "CREATE INDEX magnatune_genre_name ON magnatune_genre(name);" ); sqlDb->query( "CREATE INDEX magnatune_genre_album_id ON magnatune_genre(album_id);" ); //create moods table queryString = "CREATE TABLE magnatune_moods (" "id INTEGER PRIMARY KEY " + autoIncrement + ',' + "track_id INTEGER," + "mood " + sqlDb->textColumnType() + ") ENGINE = MyISAM;"; debug() << "Creating mangnatune_moods: " << queryString; result = sqlDb->query( queryString ); } void MagnatuneDatabaseHandler::destroyDatabase( ) { auto sqlDb = StorageManager::instance()->sqlStorage(); QStringList result = sqlDb->query( "DROP TABLE IF EXISTS magnatune_tracks;" ); result = sqlDb->query( "DROP TABLE IF EXISTS magnatune_albums;" ); result = sqlDb->query( "DROP TABLE IF EXISTS magnatune_artists;" ); result = sqlDb->query( "DROP TABLE IF EXISTS magnatune_genre;" ); result = sqlDb->query( "DROP TABLE IF EXISTS magnatune_moods;" ); /* that would only work for db2/oracle. Other databases connect the index to the table (which we just dropped) result = sqlDb->query( "DROP INDEX magnatune_tracks_artist_id;"); result = sqlDb->query( "DROP INDEX magnatune_tracks_album_id;"); result = sqlDb->query( "DROP INDEX magnatune_album_name;"); result = sqlDb->query( "DROP INDEX magnatune_album_artist_id;"); result = sqlDb->query( "DROP INDEX magnatune_artist_name;"); result = sqlDb->query( "DROP INDEX magnatune_genre_album_id;"); result = sqlDb->query( "DROP INDEX magnatune_genre_name;"); */ /* if ( sqlDb->type() == DbConnection::postgresql ) { sqlDb->query( QString( "DROP SEQUENCE magnatune_track_seq;" ) ); sqlDb->query( QString( "DROP SEQUENCE magnatune_album_seq;" ) ); sqlDb->query( QString( "DROP SEQUENCE magnatune_artist_seq;" ) ); sqlDb->query( QString( "DROP SEQUENCE magnatune_moods_seq;" ) ); }*/ } int MagnatuneDatabaseHandler::insertTrack( ServiceTrack *track ) { MagnatuneTrack * mTrack = static_cast ( track ); auto sqlDb = StorageManager::instance()->sqlStorage(); QString queryString = "INSERT INTO magnatune_tracks ( name, track_number, length, " "album_id, artist_id, preview_lofi, preview_ogg, preview_url ) VALUES ( '" + sqlDb->escape( mTrack->name()) + "', " + QString::number( mTrack->trackNumber() ) + ", " + QString::number( mTrack->length() * 1000 ) + ", " + QString::number( mTrack->albumId() ) + ", " + QString::number( mTrack->artistId() ) + ", '" + sqlDb->escape( mTrack->lofiUrl() ) + "', '" + sqlDb->escape( mTrack->oggUrl() ) + "', '" + sqlDb->escape( mTrack->uidUrl() ) + "' );"; // debug() << "Adding Magnatune track " << queryString; int trackId = sqlDb->insert( queryString, NULL ); return trackId; } int MagnatuneDatabaseHandler::insertAlbum( ServiceAlbum *album ) { MagnatuneAlbum * mAlbum = static_cast ( album ); QString queryString; auto sqlDb = StorageManager::instance()->sqlStorage(); queryString = "INSERT INTO magnatune_albums ( name, year, artist_id, " "album_code, cover_url, description ) VALUES ( '" + sqlDb->escape( sqlDb->escape( mAlbum->name() ) ) + "', " + QString::number( mAlbum->launchYear() ) + ", " + QString::number( mAlbum->artistId() ) + ", '" + sqlDb->escape( mAlbum->albumCode() ) + "', '" + sqlDb->escape( mAlbum->coverUrl() ) + "', '" + sqlDb->escape( mAlbum->description() )+ "' );"; //debug() << "Adding Magnatune album " << queryString; return sqlDb->insert( queryString, 0 ); } int MagnatuneDatabaseHandler::insertArtist( ServiceArtist *artist ) { MagnatuneArtist * mArtist = static_cast ( artist ); QString queryString; auto sqlDb = StorageManager::instance()->sqlStorage(); queryString = "INSERT INTO magnatune_artists ( name, artist_page, description, " "photo_url ) VALUES ( '" + sqlDb->escape( mArtist->name() ) + "', '" - + sqlDb->escape( mArtist->magnatuneUrl()) + "', '" + + sqlDb->escape( mArtist->magnatuneUrl().url() ) + "', '" + sqlDb->escape( mArtist->description() ) + "', '" - + sqlDb->escape( mArtist->photoUrl() ) + "' );"; + + sqlDb->escape( mArtist->photoUrl().url() ) + "' );"; //debug() << "Adding Magnatune artist " << queryString; return sqlDb->insert( queryString, 0 ); } void MagnatuneDatabaseHandler::begin( ) { auto sqlDb = StorageManager::instance()->sqlStorage(); QString queryString = "BEGIN;"; sqlDb->query( queryString ); } void MagnatuneDatabaseHandler::commit( ) { auto sqlDb = StorageManager::instance()->sqlStorage(); QString queryString = "COMMIT;"; sqlDb->query( queryString ); sqlDb->query( "FLUSH TABLES;" ); } void MagnatuneDatabaseHandler::insertMoods(int trackId, const QStringList &moods) { QString queryString; auto sqlDb = StorageManager::instance()->sqlStorage(); foreach( const QString &mood, moods ) { queryString = "INSERT INTO magnatune_moods ( track_id, mood ) VALUES ( " + QString::number( trackId ) + ", '" + sqlDb->escape( mood ) + "' );"; //debug() << "Adding Magnatune mood: " << queryString; sqlDb->insert( queryString, NULL ); } } int MagnatuneDatabaseHandler::getArtistIdByExactName(const QString & name) { auto sqlDb = StorageManager::instance()->sqlStorage(); QString queryString = "SELECT id from magnatune_artists WHERE name='" + sqlDb->escape( name ) + "';"; QStringList result = sqlDb->query( queryString ); //debug() << "Looking for id of artist " << name << ":"; if ( result.size() < 1 ) return -1; int artistId = result.first().toInt(); //debug() << " Found: " << QString::number( artistId ) << ":"; return artistId; } int MagnatuneDatabaseHandler::getAlbumIdByAlbumCode(const QString & albumcode) { auto sqlDb = StorageManager::instance()->sqlStorage(); QString queryString = "SELECT id from magnatune_albums WHERE album_code='" + sqlDb->escape( albumcode ) + "';"; QStringList result = sqlDb->query( queryString ); //debug() << "Looking for id of album " << albumcode << ":"; if ( result.size() < 1 ) return -1; int albumId = result.first().toInt(); //debug() << " Found: " << QString::number( albumId ) << ":"; return albumId; } int MagnatuneDatabaseHandler::insertGenre(ServiceGenre * genre) { QString queryString; auto sqlDb = StorageManager::instance()->sqlStorage(); queryString = "INSERT INTO magnatune_genre ( album_id, name " ") VALUES ( " + QString::number ( genre->albumId() ) + ", '" + sqlDb->escape( genre->name() ) + "' );"; //debug() << "Adding Jamendo genre " << queryString; return sqlDb->insert( queryString, 0 ); } diff --git a/src/services/magnatune/MagnatuneDatabaseWorker.cpp b/src/services/magnatune/MagnatuneDatabaseWorker.cpp index bcf48943b4..a536f283e5 100644 --- a/src/services/magnatune/MagnatuneDatabaseWorker.cpp +++ b/src/services/magnatune/MagnatuneDatabaseWorker.cpp @@ -1,220 +1,220 @@ /**************************************************************************************** * Copyright (c) 2008 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneDatabaseWorker.h" #include #include MagnatuneDatabaseWorker::MagnatuneDatabaseWorker() : QObject() , ThreadWeaver::Job() , m_registry( 0 ) { - connect( this, SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(completeJob()) ); + connect( this, &MagnatuneDatabaseWorker::done, this, &MagnatuneDatabaseWorker::completeJob ); } MagnatuneDatabaseWorker::~MagnatuneDatabaseWorker() { } void MagnatuneDatabaseWorker::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) { Q_UNUSED(self); Q_UNUSED(thread); DEBUG_BLOCK switch ( m_task ) { case FETCH_MODS: doFetchMoodMap(); break; case FETCH_MOODY_TRACKS: doFetchTrackswithMood(); break; case ALBUM_BY_SKU: doFetchAlbumBySku(); break; default: break; } } void MagnatuneDatabaseWorker::defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { Q_EMIT started(self); ThreadWeaver::Job::defaultBegin(self, thread); } void MagnatuneDatabaseWorker::defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { ThreadWeaver::Job::defaultEnd(self, thread); if (!self->success()) { Q_EMIT failed(self); } Q_EMIT done(self); } void MagnatuneDatabaseWorker::completeJob() { DEBUG_BLOCK switch ( m_task ) { case FETCH_MODS: emit( gotMoodMap( m_moodMap ) ); break; case FETCH_MOODY_TRACKS: emit( gotMoodyTracks( m_moodyTracks ) ); break; case ALBUM_BY_SKU: emit( gotAlbumBySku( m_album ) ); break; default: break; } deleteLater(); } void MagnatuneDatabaseWorker::fetchMoodMap() { m_task = FETCH_MODS; m_moodMap.clear(); } void MagnatuneDatabaseWorker::fetchTrackswithMood( const QString &mood, int noOfTracks, ServiceSqlRegistry * registry ) { m_task = FETCH_MOODY_TRACKS; m_mood = mood; m_noOfTracks = noOfTracks; m_registry = registry; m_moodyTracks.clear(); } void MagnatuneDatabaseWorker::fetchAlbumBySku( const QString & sku, ServiceSqlRegistry * registry ) { DEBUG_BLOCK m_task = ALBUM_BY_SKU; m_sku = sku; m_registry = registry; } void MagnatuneDatabaseWorker::doFetchMoodMap() { auto sqlDb = StorageManager::instance()->sqlStorage(); QString queryString = "select count( mood ), mood from magnatune_moods GROUP BY mood;"; debug() << "Querying for moods: " << queryString; QStringList result = sqlDb->query( queryString ); debug() << "result: " << result; while ( !result.isEmpty() ) { int count = result.takeFirst().toInt(); QString string = result.takeFirst(); m_moodMap.insert( string, count ); } } void MagnatuneDatabaseWorker::doFetchTrackswithMood() { auto sqlDb = StorageManager::instance()->sqlStorage(); //ok, a huge join turned out to be _really_ slow, so lets chop up the query a bit... QString queryString = "SELECT DISTINCT track_id FROM magnatune_moods WHERE mood =\"" + m_mood + "\" ORDER BY RANDOM() LIMIT " + QString::number( m_noOfTracks, 10 ) + ';'; QStringList result = sqlDb->query( queryString ); int rowCount = ( m_registry->factory()->getTrackSqlRowCount() + m_registry->factory()->getAlbumSqlRowCount() + m_registry->factory()->getArtistSqlRowCount() + m_registry->factory()->getGenreSqlRowCount() ); foreach( const QString &idString, result ) { QString queryString = "SELECT DISTINCT "; queryString += m_registry->factory()->getTrackSqlRows() + ',' + m_registry->factory()->getAlbumSqlRows() + ',' + m_registry->factory()->getArtistSqlRows() + ',' + m_registry->factory()->getGenreSqlRows(); queryString += " FROM magnatune_tracks LEFT JOIN magnatune_albums ON magnatune_tracks.album_id = magnatune_albums.id LEFT JOIN magnatune_artists ON magnatune_albums.artist_id = magnatune_artists.id LEFT JOIN magnatune_genre ON magnatune_genre.album_id = magnatune_albums.id"; queryString += " WHERE magnatune_tracks.id = " + idString; queryString += " GROUP BY magnatune_tracks.id"; //debug() << "Querying for moody tracks: " << queryString; QStringList result = sqlDb->query( queryString ); //debug() << "result: " << result; int resultRows = result.count() / rowCount; for( int i = 0; i < resultRows; i++ ) { QStringList row = result.mid( i*rowCount, rowCount ); Meta::TrackPtr trackptr = m_registry->getTrack( row ); m_moodyTracks.append( trackptr ); } } } void MagnatuneDatabaseWorker::doFetchAlbumBySku() { DEBUG_BLOCK ServiceMetaFactory * metaFactory = m_registry->factory(); QString rows = metaFactory->getAlbumSqlRows() + ',' + metaFactory->getArtistSqlRows(); auto sqlDb = StorageManager::instance()->sqlStorage(); QString queryString = "SELECT " + rows + " FROM magnatune_albums LEFT JOIN magnatune_artists ON magnatune_albums.artist_id = magnatune_artists.id WHERE album_code = '" + m_sku + "';"; debug() << "Querying for album: " << queryString; QStringList result = sqlDb->query( queryString ); debug() << "result: " << result; if ( result.count() == metaFactory->getAlbumSqlRowCount() + metaFactory->getArtistSqlRowCount() ) { Meta::AlbumPtr albumPtr = m_registry->getAlbum( result ); //make a magnatune album out of this... m_album = dynamic_cast( albumPtr.data() ); } else { m_album = 0; } } diff --git a/src/services/magnatune/MagnatuneDownloadDialog.cpp b/src/services/magnatune/MagnatuneDownloadDialog.cpp index 05847a3c87..8839ba486a 100644 --- a/src/services/magnatune/MagnatuneDownloadDialog.cpp +++ b/src/services/magnatune/MagnatuneDownloadDialog.cpp @@ -1,102 +1,99 @@ /**************************************************************************************** * Copyright (c) 2006,2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneDownloadDialog.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" -#include -#include +#include +#include - - -MagnatuneDownloadDialog::MagnatuneDownloadDialog( QWidget *parent, Qt::WFlags fl ) +MagnatuneDownloadDialog::MagnatuneDownloadDialog( QWidget *parent, Qt::WindowFlags fl ) : QDialog( parent, fl ) { setupUi(this); - downloadTargetURLRequester->fileDialog()->setMode( KFile::Directory ); + downloadTargetURLRequester->setMode( KFile::Directory ); } MagnatuneDownloadDialog::~MagnatuneDownloadDialog() { } void MagnatuneDownloadDialog::downloadButtonClicked( ) { if ( m_currentDownloadInfo.password().isEmpty() ) return; QString format = formatComboBox->currentText(); QString path = downloadTargetURLRequester->url().url(); //store to config for next download: KConfigGroup config = Amarok::config( "Service_Magnatune" ); config.writeEntry( "Download Format", format ); config.writeEntry( "Download Path", path ); m_currentDownloadInfo.setFormatSelection( format ); QUrl unpackLocation = downloadTargetURLRequester->url(); - unpackLocation.adjustPath( QUrl::AddTrailingSlash ); - m_currentDownloadInfo.setUnpackUrl( unpackLocation.directory( QUrl::ObeyTrailingSlash ) ); + m_currentDownloadInfo.setUnpackUrl( unpackLocation.path() ); emit( downloadAlbum( m_currentDownloadInfo ) ); close(); } void MagnatuneDownloadDialog::setDownloadInfo( MagnatuneDownloadInfo info ) { m_currentDownloadInfo = info; DownloadFormatMap formatMap = info.formatMap(); DownloadFormatMap::Iterator it; for ( it = formatMap.begin(); it != formatMap.end(); ++it ) { formatComboBox->addItem( it.key() ); } infoEdit->setText( info.downloadMessage() ); //restore format and path from last time, if any. KConfigGroup config = Amarok::config("Service_Magnatune"); QString format = config.readEntry( "Download Format", QString() ); QString path = config.readEntry( "Download Path", QString() ); if ( !format.isEmpty() ) { int index = formatComboBox->findText( format ); if ( index != -1 ) formatComboBox->setCurrentIndex( index ); } if ( !path.isEmpty() ) { - downloadTargetURLRequester->setUrl( QUrl(path) ); + downloadTargetURLRequester->setUrl( QUrl::fromLocalFile(path) ); } } /*$SPECIALIZATION$*/ diff --git a/src/services/magnatune/MagnatuneDownloadDialog.h b/src/services/magnatune/MagnatuneDownloadDialog.h index df0a50bd0a..4d947a3e89 100644 --- a/src/services/magnatune/MagnatuneDownloadDialog.h +++ b/src/services/magnatune/MagnatuneDownloadDialog.h @@ -1,84 +1,82 @@ /**************************************************************************************** * Copyright (c) 2006,2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef MAGNATUNEDOWNLOADDIALOG_H #define MAGNATUNEDOWNLOADDIALOG_H #include "ui_MagnatuneDownloadDialogBase.h" #include "MagnatuneDownloadInfo.h" #include /** Dialog for choosing download format and location. Also displays additional info from Magnatune.com. @author Nikolaj Hald Nielsen */ class MagnatuneDownloadDialog : public QDialog, public Ui::magnatuneDownloadDialogBase { Q_OBJECT public: /** * Overridden constructor. * @param parent Pointer to the parent QWidget. - * @param name Name of this widget. - * @param modal Sets modal state. * @param fl Additional dialog flags. */ - explicit MagnatuneDownloadDialog( QWidget* parent = 0, Qt::WFlags fl = 0 ); + explicit MagnatuneDownloadDialog( QWidget* parent = 0, Qt::WindowFlags fl = 0 ); /** * Destructor */ ~MagnatuneDownloadDialog(); /*$PUBLIC_FUNCTIONS$*/ /** * Sets the current download info - * @param the MagnatuneDownloadInfo class containing the information abut the + * @param info The MagnatuneDownloadInfo class containing the information abut the * download to display */ void setDownloadInfo( MagnatuneDownloadInfo info ); Q_SIGNALS: /** * Signal emitted when all needed info has been gathered and handler * should start album download. * @param completedInfo A DownloadInfo object containing all needed information */ void downloadAlbum( MagnatuneDownloadInfo completedInfo ); public Q_SLOTS: /*$PUBLIC_SLOTS$*/ protected: /*$PROTECTED_FUNCTIONS$*/ MagnatuneDownloadInfo m_currentDownloadInfo; protected Q_SLOTS: /*$PROTECTED_SLOTS$*/ /** * Slot for recieving notification when the download button is clicked. */ void downloadButtonClicked(); }; #endif diff --git a/src/services/magnatune/MagnatuneDownloadHandler.cpp b/src/services/magnatune/MagnatuneDownloadHandler.cpp index 966f0be0dd..dc6ff06638 100644 --- a/src/services/magnatune/MagnatuneDownloadHandler.cpp +++ b/src/services/magnatune/MagnatuneDownloadHandler.cpp @@ -1,207 +1,207 @@ /**************************************************************************************** * Copyright (c) 2006,2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneDownloadHandler.h" #include "core/interfaces/Logger.h" #include "core/support/Amarok.h" #include "core/support/Components.h" #include "core/support/Debug.h" #include "MagnatuneDatabaseHandler.h" #include "MagnatuneConfig.h" #include -#include #include #include +#include #include using namespace Meta; MagnatuneDownloadHandler::MagnatuneDownloadHandler() : QObject() , m_downloadDialog( 0 ) , m_albumDownloader( 0 ) , m_currentAlbum( 0 ) , m_membershipDownload( false ) { } MagnatuneDownloadHandler::~MagnatuneDownloadHandler() { delete m_downloadDialog; delete m_albumDownloader; } void MagnatuneDownloadHandler::downloadAlbum( MagnatuneAlbum * album ) { DEBUG_BLOCK m_currentAlbum = album; //do we have a membership that allows free downloads? MagnatuneConfig config; if ( config.isMember() && config.membershipType() == MagnatuneConfig::DOWNLOAD ) { debug() << "membership download..."; membershipDownload( config.membershipType(), config.username(), config.password() ); } } void MagnatuneDownloadHandler::membershipDownload( int membershipType, const QString &username, const QString &password ) { QString type; if( membershipType == MagnatuneConfig::STREAM ) type = "stream"; else type = "download"; - QString purchaseURL = "http://" + username + ":" + password + "@" + type + ".magnatune.com/buy/membership_free_dl_xml?sku=" + m_currentAlbum->albumCode() + "&id=amarok"; + QUrl purchaseURL = QUrl::fromUserInput( "http://" + username + ":" + password + "@" + type + ".magnatune.com/buy/membership_free_dl_xml?sku=" + m_currentAlbum->albumCode() + "&id=amarok" ); m_membershipDownload = true; - m_resultDownloadJob = KIO::storedGet( QUrl( purchaseURL ), KIO::NoReload, KIO::HideProgressInfo ); + m_resultDownloadJob = KIO::storedGet( purchaseURL, KIO::NoReload, KIO::HideProgressInfo ); Amarok::Components::logger()->newProgressOperation( m_resultDownloadJob, i18n( "Processing download" ) ); - connect( m_resultDownloadJob, SIGNAL(result(KJob*)), SLOT(xmlDownloadComplete(KJob*)) ); + connect( m_resultDownloadJob, &KJob::result, this, &MagnatuneDownloadHandler::xmlDownloadComplete ); } void MagnatuneDownloadHandler::xmlDownloadComplete( KJob * downloadJob ) { debug() << "xml download complete"; - if ( !downloadJob->error() == 0 ) + if ( downloadJob->error() ) { //TODO: error handling here debug() << "Job error... " << downloadJob->error(); return ; } if ( downloadJob != m_resultDownloadJob ) { debug() << "Wrong job..."; return ; //not the right job, so let's ignore it } KIO::StoredTransferJob* const storedJob = static_cast( downloadJob ); QString resultXml = QString( storedJob->data() ); debug() << endl << endl << "result: " << resultXml; if ( m_albumDownloader == 0 ) { m_albumDownloader = new MagnatuneAlbumDownloader(); - connect( m_albumDownloader, SIGNAL(downloadComplete(bool)), this, SLOT(albumDownloadComplete(bool)) ); + connect( m_albumDownloader, &MagnatuneAlbumDownloader::downloadComplete, this, &MagnatuneDownloadHandler::albumDownloadComplete ); } if ( m_downloadDialog == 0 ) { m_downloadDialog = new MagnatuneDownloadDialog( m_parent ); m_downloadDialog->setModal( true ); - connect( m_downloadDialog, SIGNAL(downloadAlbum(MagnatuneDownloadInfo)), m_albumDownloader, SLOT(downloadAlbum(MagnatuneDownloadInfo)) ); + connect( m_downloadDialog, &MagnatuneDownloadDialog::downloadAlbum, m_albumDownloader, &MagnatuneAlbumDownloader::downloadAlbum ); //connect( m_downloadDialog, SIGNAL(rejected()), this, SLOT(albumPurchaseCancelled()) ); } MagnatuneDownloadInfo downloadInfo; if ( downloadInfo.initFromString( resultXml, m_membershipDownload ) ) { downloadInfo.setAlbumCode( m_currentAlbum->albumCode() ); downloadInfo.setCoverUrl( m_currentAlbum->coverUrl() ); downloadInfo.setAlbumName( m_currentAlbum->prettyName() ); downloadInfo.setArtistName( m_currentAlbum->albumArtist()->prettyName() ); if ( m_membershipDownload ) { MagnatuneConfig config; downloadInfo.setMembershipInfo( config.username(), config.password() ); } else { saveDownloadInfo( resultXml ); } m_downloadDialog->setDownloadInfo( downloadInfo ); //m_purchaseDialog->close(); m_downloadDialog->show(); } else { KMessageBox::information( m_parent, i18n( "There seems to be an error in the supplied membership information. Please correct this and try again."),i18n("Could not process download") ); } } void MagnatuneDownloadHandler::setParent( QWidget * parent ) { m_parent = parent; } void MagnatuneDownloadHandler::saveDownloadInfo( const QString &infoXml ) { MagnatuneDatabaseHandler dbHandler; QDir purchaseDir( Amarok::saveLocation( "magnatune.com/purchases/" ) ); debug() << "magnatune save location" << purchaseDir.absolutePath(); //if directory does not exist, create it if ( ! purchaseDir.exists () ) { purchaseDir.mkdir( "." ); } QString fileName = m_currentAlbum->albumArtist()->name() + " - " + m_currentAlbum->name(); QFile file( purchaseDir.absolutePath() + '/' + fileName ); //Skip if file already exists if ( file.exists () ) return ; //write info if ( file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { QTextStream stream( &file ); stream << infoXml << "\n"; file.close(); } } void MagnatuneDownloadHandler::albumDownloadComplete( bool success ) { //cleanup time! debug() << "MagnatuneDownloadHandler::albumDownloadComplete"; delete m_downloadDialog; m_downloadDialog = 0; emit( downloadCompleted( success ) ); } diff --git a/src/services/magnatune/MagnatuneDownloadInfo.cpp b/src/services/magnatune/MagnatuneDownloadInfo.cpp index 884d85e818..b99945c3f1 100644 --- a/src/services/magnatune/MagnatuneDownloadInfo.cpp +++ b/src/services/magnatune/MagnatuneDownloadInfo.cpp @@ -1,357 +1,357 @@ /**************************************************************************************** * Copyright (c) 2006,2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneDownloadInfo.h" #include "core/support/Debug.h" -#include +#include #include #include MagnatuneDownloadInfo::MagnatuneDownloadInfo() { m_selectedDownloadFormat.clear(); } MagnatuneDownloadInfo::~MagnatuneDownloadInfo() {} bool MagnatuneDownloadInfo::initFromString( const QString &downloadInfoString, bool membershipDownload ) { m_membershipDownload = membershipDownload; //complete overkill to do a full SAX2 parser for this at the moment... I think.... // lets make sure that this is actually a valid result int testIndex = downloadInfoString.indexOf( "" ); if ( testIndex == -1 ) { return false; }; int startIndex; int endIndex; if ( membershipDownload == false ) { startIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( startIndex != -1 ) { endIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( endIndex != -1 ) { startIndex += 13; debug() << "found username: " << downloadInfoString.mid( startIndex, endIndex - startIndex ); m_userName = downloadInfoString.mid( startIndex, endIndex - startIndex ); } else { return false; } } else { return false; } startIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( startIndex != -1 ) { endIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( endIndex != -1 ) { startIndex += 13; debug() << "found password: " << downloadInfoString.mid( startIndex, endIndex - startIndex ); m_password = downloadInfoString.mid( startIndex, endIndex - startIndex ); } else { return false; } } else { return false; } } else { m_userName.clear(); m_password.clear(); } startIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( startIndex != -1 ) { endIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( endIndex != -1 ) { startIndex += 12; debug() << "found wav"; m_downloadFormats[ "Wav" ] = downloadInfoString.mid( startIndex, endIndex - startIndex ).replace( "&", "&" ); } } startIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( startIndex != -1 ) { endIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( endIndex != -1 ) { startIndex += 16; debug() << "found 128k mp3"; m_downloadFormats[ "128 kbit/s MP3" ] = downloadInfoString.mid( startIndex, endIndex - startIndex ).replace( "&", "&" ); } } startIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( startIndex != -1 ) { endIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( endIndex != -1 ) { startIndex += 12; debug() << "found ogg-vorbis"; m_downloadFormats[ "Ogg-Vorbis" ] = downloadInfoString.mid( startIndex, endIndex - startIndex ).replace( "&", "&" ); } } startIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( startIndex != -1 ) { endIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( endIndex != -1 ) { startIndex += 12; debug() << "found vbr mp3"; m_downloadFormats[ "VBR MP3" ] = downloadInfoString.mid( startIndex, endIndex - startIndex ).replace( "&", "&" ); } } startIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( startIndex != -1 ) { endIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( endIndex != -1 ) { startIndex += 13; debug() << "found flac"; m_downloadFormats[ "FLAC" ] = downloadInfoString.mid( startIndex, endIndex - startIndex ).replace( "&", "&" ); } } startIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( startIndex != -1 ) { endIndex = downloadInfoString.indexOf( "", 0, Qt::CaseInsensitive ); if ( endIndex != -1 ) { startIndex += 9; debug() << "found dl-message"; m_downloadMessage = downloadInfoString.mid( startIndex, endIndex - startIndex ).replace( "&", "&" ); } } return true; } bool MagnatuneDownloadInfo::initFromRedownloadXml( const QDomElement &element ) { m_artistName = element.firstChildElement( "artist" ).text(); m_albumName = element.firstChildElement( "album" ).text(); m_userName = element.firstChildElement( "username" ).text(); m_password = element.firstChildElement( "password" ).text(); m_albumCode = element.firstChildElement( "sku" ).text(); m_coverUrl = element.firstChildElement( "cover" ).text(); //get formats QDomNode formats = element.firstChildElement( "formats" ); QString wav_url = formats.firstChildElement( "wav_zip" ).text(); m_downloadFormats[ "Wav" ] = wav_url; QString mp3_url = formats.firstChildElement( "mp3_zip" ).text(); m_downloadFormats[ "128 kbit/s MP3" ] = mp3_url; QString vbr_url = formats.firstChildElement( "vbr_zip" ).text(); m_downloadFormats[ "VBR MP3" ] = vbr_url; QString ogg_url = formats.firstChildElement( "ogg_zip" ).text(); m_downloadFormats[ "Ogg-Vorbis" ] = ogg_url; QString flac_url = formats.firstChildElement( "flac_zip" ).text(); m_downloadFormats[ "FLAC" ] = flac_url; m_downloadMessage = i18n( "Redownload of a previously purchased album \"%1\" by \"%2\" from Magnatune.com.\n\nUsername: %3\nPassword: %4\n", m_albumName, m_artistName, m_userName, m_password ); return true; } QString MagnatuneDownloadInfo::userName( ) { return m_userName; } QString MagnatuneDownloadInfo::password( ) { return m_password; } QString MagnatuneDownloadInfo::downloadMessage( ) { return m_downloadMessage; } DownloadFormatMap MagnatuneDownloadInfo::formatMap() { return m_downloadFormats; } void MagnatuneDownloadInfo::setFormatSelection( const QString &selectedFormat ) { m_selectedDownloadFormat = selectedFormat; } bool MagnatuneDownloadInfo::isReadyForDownload( ) { return !m_selectedDownloadFormat.isEmpty(); } QUrl MagnatuneDownloadInfo::completeDownloadUrl( ) { QString url = m_downloadFormats[ m_selectedDownloadFormat ]; QUrl downloadUrl(url); downloadUrl.setUserName(m_userName); downloadUrl.setPassword(m_password); return downloadUrl; } void MagnatuneDownloadInfo::setUnpackUrl( const QString &unpackUrl ) { m_unpackUrl = unpackUrl; } QString MagnatuneDownloadInfo::unpackLocation( ) { return m_unpackUrl; } QString MagnatuneDownloadInfo::albumCode() { return m_albumCode; } void MagnatuneDownloadInfo::setAlbumCode( const QString &albumCode ) { m_albumCode = albumCode; } bool MagnatuneDownloadInfo::isMembershipDownload() { return m_membershipDownload; } void MagnatuneDownloadInfo::setMembershipInfo( const QString &username, const QString &password ) { m_userName = username; m_password = password; } QString MagnatuneDownloadInfo::albumName() { return m_albumName; } const QString MagnatuneDownloadInfo::albumName() const { return m_albumName; } QString MagnatuneDownloadInfo::artistName() { return m_artistName; } const QString MagnatuneDownloadInfo::artistName() const { return m_artistName; } QString MagnatuneDownloadInfo::coverUrl() { return m_coverUrl; } const QString MagnatuneDownloadInfo::coverUrl() const { return m_coverUrl; } void MagnatuneDownloadInfo::setAlbumName( const QString &albumName ) { m_albumName = albumName; } void MagnatuneDownloadInfo::setArtistName( const QString &artistName ) { m_artistName = artistName; } void MagnatuneDownloadInfo::setCoverUrl( const QString &coverUrl ) { m_coverUrl = coverUrl; } diff --git a/src/services/magnatune/MagnatuneInfoParser.cpp b/src/services/magnatune/MagnatuneInfoParser.cpp index 143b9429d6..ec75149c66 100644 --- a/src/services/magnatune/MagnatuneInfoParser.cpp +++ b/src/services/magnatune/MagnatuneInfoParser.cpp @@ -1,338 +1,338 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneInfoParser.h" #include "core/support/Components.h" #include "core/interfaces/Logger.h" #include "MagnatuneConfig.h" -#include +#include using namespace Meta; void MagnatuneInfoParser::getInfo(ArtistPtr artist) { showLoading( i18n( "Loading artist info..." ) ); MagnatuneArtist * magnatuneArtist = dynamic_cast( artist.data() ); if ( magnatuneArtist == 0) return; // first get the entire artist html page /* QString tempFile; QString orgHtml;*/ m_infoDownloadJob = KIO::storedGet( magnatuneArtist->magnatuneUrl(), KIO::Reload, KIO::HideProgressInfo ); Amarok::Components::logger()->newProgressOperation( m_infoDownloadJob, i18n( "Fetching %1 Artist Info", magnatuneArtist->prettyName() ) ); - connect( m_infoDownloadJob, SIGNAL(result(KJob*)), SLOT(artistInfoDownloadComplete(KJob*)) ); + connect( m_infoDownloadJob, &KJob::result, this, &MagnatuneInfoParser::artistInfoDownloadComplete ); } void MagnatuneInfoParser::getInfo(AlbumPtr album) { showLoading( i18n( "Loading album info..." ) ); MagnatuneAlbum * magnatuneAlbum = dynamic_cast( album.data() ); const QString artistName = album->albumArtist()->name(); QString infoHtml = ""; infoHtml += generateHomeLink(); infoHtml += "
"; infoHtml += artistName; infoHtml += "
"; infoHtml += magnatuneAlbum->name(); infoHtml += "

"; infoHtml += "coverUrl() + "\" align=\"middle\" border=\"1\">"; // Disable Genre line in Magnatune applet since, well, it doesn't actually put a genre there... // Nikolaj, FYI: either the thumbnails aren't working, or they aren't getting through the proxy here. That would be odd, however, as the database and // all HTML are coming through the proxy //infoHtml += "

" + i18n( "Genre: ");// + magnatuneAlbum-> infoHtml += "
" + i18n( "Release Year: %1", QString::number( magnatuneAlbum->launchYear() ) ); if ( !magnatuneAlbum->description().isEmpty() ) { //debug() << "MagnatuneInfoParser: Writing description: '" << album->getDescription() << "'"; infoHtml += "

" + i18n( "Description:" ) + "

" + magnatuneAlbum->description(); } infoHtml += "



" + i18n( "From Magnatune.com" ) + "
"; infoHtml += ""; emit ( info( infoHtml ) ); } void MagnatuneInfoParser::getInfo(TrackPtr track) { Q_UNUSED( track ); return; } void MagnatuneInfoParser::artistInfoDownloadComplete( KJob *downLoadJob ) { - if ( !downLoadJob->error() == 0 ) + if ( downLoadJob->error() != 0 ) { //TODO: error handling here return ; } if ( downLoadJob != m_infoDownloadJob ) return ; //not the right job, so let's ignore it QString infoString = ( (KIO::StoredTransferJob* ) downLoadJob )->data(); //debug() << "MagnatuneInfoParser: Artist info downloaded: " << infoString; infoString = extractArtistInfo( infoString ); //debug() << "html: " << trimmedInfo; emit ( info( infoString ) ); } QString MagnatuneInfoParser::extractArtistInfo( const QString &artistPage ) { QString trimmedHtml; int sectionStart = artistPage.indexOf( "" ); int sectionEnd = artistPage.indexOf( "", sectionStart ); trimmedHtml = artistPage.mid( sectionStart, sectionEnd - sectionStart ); int buyStartIndex = trimmedHtml.indexOf( "" ); int buyEndIndex; //we are going to integrate the buying of music (I hope) so remove these links while ( buyStartIndex != -1 ) { buyEndIndex = trimmedHtml.indexOf( "", buyStartIndex ) + 18; trimmedHtml.remove( buyStartIndex, buyEndIndex - buyStartIndex ); buyStartIndex = trimmedHtml.indexOf( "", buyStartIndex ); } QString infoHtml = ""; infoHtml += generateHomeLink(); infoHtml += trimmedHtml; infoHtml += ""; return infoHtml; } void MagnatuneInfoParser::getFrontPage() { if( !m_cachedFrontpage.isEmpty() ) { emit ( info( m_cachedFrontpage ) ); return; } showLoading( i18n( "Loading Magnatune.com frontpage..." ) ); m_pageDownloadJob = KIO::storedGet( QUrl("http://magnatune.com/amarok_frontpage.html"), KIO::Reload, KIO::HideProgressInfo ); Amarok::Components::logger()->newProgressOperation( m_pageDownloadJob, i18n( "Fetching Magnatune.com front page" ) ); - connect( m_pageDownloadJob, SIGNAL(result(KJob*)), SLOT(frontpageDownloadComplete(KJob*)) ); + connect( m_pageDownloadJob, &KJob::result, this, &MagnatuneInfoParser::frontpageDownloadComplete ); } void MagnatuneInfoParser::getFavoritesPage() { MagnatuneConfig config; if ( !config.isMember() ) return; showLoading( i18n( "Loading your Magnatune.com favorites page..." ) ); QString type; if( config.membershipType() == MagnatuneConfig::STREAM ) type = "stream"; else type = "download"; QString user = config.username(); QString password = config.password(); - QString url = "http://" + user + ":" + password + "@" + type.toLower() + ".magnatune.com/member/amarok_favorites.php"; + QUrl url = QUrl::fromUserInput( "http://" + user + ":" + password + "@" + type.toLower() + ".magnatune.com/member/amarok_favorites.php" ); - m_pageDownloadJob = KIO::storedGet( QUrl( url ), KIO::Reload, KIO::HideProgressInfo ); + m_pageDownloadJob = KIO::storedGet( url, KIO::Reload, KIO::HideProgressInfo ); Amarok::Components::logger()->newProgressOperation( m_pageDownloadJob, i18n( "Loading your Magnatune.com favorites page..." ) ); - connect( m_pageDownloadJob, SIGNAL(result(KJob*)), SLOT(userPageDownloadComplete(KJob*)) ); + connect( m_pageDownloadJob, &KJob::result, this, &MagnatuneInfoParser::userPageDownloadComplete ); } void MagnatuneInfoParser::getRecommendationsPage() { MagnatuneConfig config; if ( !config.isMember() ) return; showLoading( i18n( "Loading your personal Magnatune.com recommendations page..." ) ); QString type; if( config.membershipType() == MagnatuneConfig::STREAM ) type = "stream"; else type = "download"; QString user = config.username(); QString password = config.password(); - QString url = "http://" + user + ":" + password + "@" + type.toLower() + ".magnatune.com/member/amarok_recommendations.php"; + QUrl url = QUrl::fromUserInput( "http://" + user + ":" + password + "@" + type.toLower() + ".magnatune.com/member/amarok_recommendations.php" ); - m_pageDownloadJob = KIO::storedGet( QUrl( url ), KIO::Reload, KIO::HideProgressInfo ); + m_pageDownloadJob = KIO::storedGet( url, KIO::Reload, KIO::HideProgressInfo ); Amarok::Components::logger()->newProgressOperation( m_pageDownloadJob, i18n( "Loading your personal Magnatune.com recommendations page..." ) ); - connect( m_pageDownloadJob, SIGNAL(result(KJob*)), SLOT(userPageDownloadComplete(KJob*)) ); + connect( m_pageDownloadJob, &KJob::result, this, &MagnatuneInfoParser::userPageDownloadComplete ); } void MagnatuneInfoParser::frontpageDownloadComplete( KJob * downLoadJob ) { - if ( !downLoadJob->error() == 0 ) + if ( downLoadJob->error() != 0 ) { //TODO: error handling here return ; } if ( downLoadJob != m_pageDownloadJob ) return ; //not the right job, so let's ignore it QString infoString = ((KIO::StoredTransferJob* )downLoadJob)->data(); //insert menu MagnatuneConfig config; if( config.isMember() ) infoString.replace( "", generateMemberMenu() ); //insert fancy amarok url links to the artists infoString = createArtistLinks( infoString ); if( m_cachedFrontpage.isEmpty() ) m_cachedFrontpage = infoString; emit ( info( infoString ) ); } void MagnatuneInfoParser::userPageDownloadComplete( KJob * downLoadJob ) { if ( !downLoadJob->error() == 0 ) { //TODO: error handling here return ; } if ( downLoadJob != m_pageDownloadJob ) return ; //not the right job, so let's ignore it QString infoString = ((KIO::StoredTransferJob* )downLoadJob)->data(); //insert menu MagnatuneConfig config; if( config.isMember() ) infoString.replace( "", generateMemberMenu() ); //make sure that any pages that use the old command name "service_magnatune" replaces it with "service-magnatune" infoString.replace( "service_magnatune", "service-magnatune" ); emit ( info( infoString ) ); } QString MagnatuneInfoParser::generateMemberMenu() { QString homeUrl = "amarok://service-magnatune?command=show_home"; QString favoritesUrl = "amarok://service-magnatune?command=show_favorites"; QString recommendationsUrl = "amarok://service-magnatune?command=show_recommendations"; QString menu = "
" "[Home] " "[Favorites] " "[Recommendations] " "
"; return menu; } QString MagnatuneInfoParser::generateHomeLink() { QString homeUrl = "amarok://service-magnatune?command=show_home"; QString link = "
" "[Home] " "
"; return link; } QString MagnatuneInfoParser::createArtistLinks( const QString &page ) { //the artist name is wrapped in artist QString returnPage = page; int startTokenLength = QString( "" ).length(); int offset = 0; int startTokenIndex = page.indexOf( "", offset ); int endTokenIndex = 0; while( startTokenIndex != -1 ) { endTokenIndex = page.indexOf( "", startTokenIndex ); if( endTokenIndex == -1 ) break; //bail out offset = endTokenIndex; //get the artist namespace int artistLength = endTokenIndex - ( startTokenIndex + startTokenLength ); QString artist = page.mid( startTokenIndex + startTokenLength, artistLength ); //replace in the artist amarok url QString replaceString = "" + artist + ""; QString artistLink = "" + artist + ""; returnPage = returnPage.replace( replaceString, artistLink ); startTokenIndex = page.indexOf( "", offset ); } return returnPage; } diff --git a/src/services/magnatune/MagnatuneMeta.cpp b/src/services/magnatune/MagnatuneMeta.cpp index ec5cf99ed9..88e5c8b006 100644 --- a/src/services/magnatune/MagnatuneMeta.cpp +++ b/src/services/magnatune/MagnatuneMeta.cpp @@ -1,393 +1,398 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneMeta.h" #include "MagnatuneStore.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include "core-impl/support/UrlStatisticsStore.h" #include "MagnatuneActions.h" #include "MagnatuneConfig.h" #include "SvgHandler.h" -#include -#include - #include +#include + +#include using namespace Meta; MagnatuneMetaFactory::MagnatuneMetaFactory( const QString & dbPrefix, MagnatuneStore * store ) : ServiceMetaFactory( dbPrefix ) , m_membershipPrefix( QString() ) , m_streamType( OGG ) , m_userName( QString() ) , m_password( QString() ) , m_store( store ) {} void MagnatuneMetaFactory::setMembershipInfo( const QString &prefix, const QString &userName, const QString &password ) { m_membershipPrefix = prefix; m_userName = userName; m_password = password; } void MagnatuneMetaFactory::setStreamType(int type) { m_streamType = type; } int MagnatuneMetaFactory::getTrackSqlRowCount() { return ServiceMetaFactory::getTrackSqlRowCount() + 2; } QString MagnatuneMetaFactory::getTrackSqlRows() { QString sqlRows = ServiceMetaFactory::getTrackSqlRows(); sqlRows += ", "; sqlRows += tablePrefix() + "_tracks.preview_lofi, "; sqlRows += tablePrefix() + "_tracks.preview_ogg "; return sqlRows; } TrackPtr MagnatuneMetaFactory::createTrack(const QStringList & rows) { MagnatuneTrack *track = new MagnatuneTrack( rows ); if ( m_streamType == OGG ) { track->setUidUrl( track->oggUrl() ); } else if ( m_streamType == LOFI ) { track->setUidUrl( track->lofiUrl() ); } track->setStatisticsProvider( Meta::StatisticsPtr( new UrlStatisticsStore( track ) ) ); if ( !m_membershipPrefix.isEmpty() ) { QString url = track->uidUrl(); url.replace( "http://he3.", "http://" + m_userName + ":" + m_password + "@" + m_membershipPrefix + "." ); if ( m_streamType == MP3 ) { url.replace( ".mp3", "_nospeech.mp3" ); } else if ( m_streamType == OGG ) { url.replace( ".ogg", "_nospeech.ogg" ); } track->setUidUrl( url ); if ( m_membershipPrefix == "download" ) track->setDownloadMembership(); } return Meta::TrackPtr( track ); } int MagnatuneMetaFactory::getAlbumSqlRowCount() { return ServiceMetaFactory::getAlbumSqlRowCount() + 3; } QString MagnatuneMetaFactory::getAlbumSqlRows() { QString sqlRows = ServiceMetaFactory::getAlbumSqlRows(); sqlRows += ", "; sqlRows += tablePrefix() + "_albums.cover_url, "; sqlRows += tablePrefix() + "_albums.year, "; sqlRows += tablePrefix() + "_albums.album_code "; return sqlRows; } AlbumPtr MagnatuneMetaFactory::createAlbum(const QStringList & rows) { MagnatuneAlbum * album = new MagnatuneAlbum( rows ); album->setStore( m_store ); if ( m_membershipPrefix == "download" ) album->setDownloadMembership(); album->setSourceName( "Magnatune.com" ); return AlbumPtr( album ); } int MagnatuneMetaFactory::getArtistSqlRowCount() { return ServiceMetaFactory::getArtistSqlRowCount() + 2; } QString MagnatuneMetaFactory::getArtistSqlRows() { QString sqlRows = ServiceMetaFactory::getArtistSqlRows(); sqlRows += ", "; sqlRows += tablePrefix() + "_artists.photo_url, "; sqlRows += tablePrefix() + "_artists.artist_page "; return sqlRows; } ArtistPtr MagnatuneMetaFactory::createArtist(const QStringList & rows) { MagnatuneArtist * artist = new MagnatuneArtist( rows ); artist->setSourceName( "Magnatune.com" ); return ArtistPtr( artist ); } GenrePtr MagnatuneMetaFactory::createGenre(const QStringList & rows) { MagnatuneGenre * genre = new MagnatuneGenre( rows ); genre->setSourceName( "Magnatune.com" ); return GenrePtr( genre ); } /////////////////////////////////////////////////////////////////////////////// // class MagnatuneTrack /////////////////////////////////////////////////////////////////////////////// MagnatuneTrack::MagnatuneTrack( const QString &name ) : ServiceTrack( name ) , m_downloadMembership ( false ) {} MagnatuneTrack::MagnatuneTrack(const QStringList & resultRow) : ServiceTrack( resultRow ) , m_downloadMembership ( false ) { m_lofiUrl = resultRow[7]; m_oggUrl = resultRow[8]; } QString MagnatuneTrack::lofiUrl() { return m_lofiUrl; } void MagnatuneTrack::setLofiUrl(const QString & url) { m_lofiUrl = url; } QString Meta::MagnatuneTrack::oggUrl() const { return m_oggUrl; } void Meta::MagnatuneTrack::setOggUrl( const QString& url ) { m_oggUrl = url; } void Meta::MagnatuneTrack::setDownloadMembership() { m_downloadMembership = true; } QString Meta::MagnatuneTrack::sourceName() { return "Magnatune.com"; } QString Meta::MagnatuneTrack::sourceDescription() { return i18n( "The non evil record label that is fair to artists and customers alike" ); } QPixmap Meta::MagnatuneTrack::emblem() { - return QPixmap( KStandardDirs::locate( "data", "amarok/images/emblem-magnatune.png" ) ); + return QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-magnatune.png" ) ); } QList< QString > Meta::MagnatuneTrack::moods() { return m_moods; } void Meta::MagnatuneTrack::setMoods(QList< QString > moods) { m_moods = moods; } void Meta::MagnatuneTrack::download() { DEBUG_BLOCK MagnatuneAlbum * mAlbum = dynamic_cast ( album().data() ); if ( mAlbum ) - mAlbum->store()->download( this ); + mAlbum->store()->downloadTrack( this ); } void Meta::MagnatuneTrack::setAlbumPtr( Meta::AlbumPtr album ) { ServiceTrack::setAlbumPtr( album ); //get year from magnatue album: MagnatuneAlbum * ma = dynamic_cast( album.data() ); if ( ma ) { YearPtr year = YearPtr( new ServiceYear( QString::number( ma->launchYear() ) ) ); setYear( year ); } } /////////////////////////////////////////////////////////////////////////////// // class MagnatuneArtist /////////////////////////////////////////////////////////////////////////////// MagnatuneArtist::MagnatuneArtist( const QString &name ) : ServiceArtist( name ) {} MagnatuneArtist::MagnatuneArtist(const QStringList & resultRow) : ServiceArtist( resultRow ) { - m_photoUrl = resultRow[3]; - m_magnatuneUrl = resultRow[4]; + auto list = QUrl::fromStringList( resultRow ); + + if( list.size() < 5 ) + return; + + m_photoUrl = list.at(3); + m_magnatuneUrl = list.at(4); } -void MagnatuneArtist::setPhotoUrl( const QString &photoUrl ) +void MagnatuneArtist::setPhotoUrl( const QUrl &photoUrl ) { m_photoUrl = photoUrl; } -QString MagnatuneArtist::photoUrl( ) const +QUrl MagnatuneArtist::photoUrl( ) const { return m_photoUrl; } -void MagnatuneArtist::setMagnatuneUrl( const QString & magnatuneUrl ) +void MagnatuneArtist::setMagnatuneUrl( const QUrl & magnatuneUrl ) { m_magnatuneUrl = magnatuneUrl; } -QString MagnatuneArtist::magnatuneUrl() const +QUrl MagnatuneArtist::magnatuneUrl() const { return m_magnatuneUrl; } /////////////////////////////////////////////////////////////////////////////// // class MagnatuneAlbum /////////////////////////////////////////////////////////////////////////////// MagnatuneAlbum::MagnatuneAlbum( const QString &name ) : ServiceAlbumWithCover( name ) , m_coverUrl() , m_launchYear( 0 ) , m_albumCode() , m_store( 0 ) , m_downloadMembership( false ) {} MagnatuneAlbum::MagnatuneAlbum(const QStringList & resultRow) : ServiceAlbumWithCover( resultRow ) , m_downloadMembership ( false ) { m_coverUrl = resultRow[4]; m_launchYear = resultRow[5].toInt(); m_albumCode = resultRow[6]; m_store = 0; } MagnatuneAlbum::~ MagnatuneAlbum() {} void MagnatuneAlbum::setLaunchYear( int launchYear ) { m_launchYear = launchYear; } int MagnatuneAlbum::launchYear( ) const { return m_launchYear; } void MagnatuneAlbum::setAlbumCode(const QString & albumCode) { m_albumCode = albumCode; } QString MagnatuneAlbum::albumCode() { return m_albumCode; } void MagnatuneAlbum::setCoverUrl(const QString & coverUrl) { m_coverUrl = coverUrl; } QString MagnatuneAlbum::coverUrl() const { return m_coverUrl; } void Meta::MagnatuneAlbum::setStore(MagnatuneStore * store) { m_store = store; } MagnatuneStore * Meta::MagnatuneAlbum::store() { return m_store; } void Meta::MagnatuneAlbum::setDownloadMembership() { DEBUG_BLOCK m_downloadMembership = true; } void Meta::MagnatuneAlbum::download() { DEBUG_BLOCK if ( store() ) - store()->download( this ); + store()->downloadAlbum( this ); } void Meta::MagnatuneAlbum::addToFavorites() { DEBUG_BLOCK if ( store() ) store()->addToFavorites( albumCode() ); } /////////////////////////////////////////////////////////////////////////////// // class MagnatuneGenre /////////////////////////////////////////////////////////////////////////////// MagnatuneGenre::MagnatuneGenre( const QString & name ) : ServiceGenre( name ) {} MagnatuneGenre::MagnatuneGenre( const QStringList & resultRow ) : ServiceGenre( resultRow ) {} diff --git a/src/services/magnatune/MagnatuneMeta.h b/src/services/magnatune/MagnatuneMeta.h index 330a1ddba9..775d7db7e6 100644 --- a/src/services/magnatune/MagnatuneMeta.h +++ b/src/services/magnatune/MagnatuneMeta.h @@ -1,194 +1,194 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef MAGNATUNEMETA_H #define MAGNATUNEMETA_H #include "../ServiceMetaBase.h" #include "../ServiceAlbumCoverDownloader.h" #include #include #include #include class MagnatuneAlbumCoverDownloader; class MagnatuneStore; namespace Meta { class MagnatuneTrack : public ServiceTrack { Q_OBJECT public: MagnatuneTrack( const QString &name ); MagnatuneTrack( const QStringList &resultRow ); QString lofiUrl(); void setLofiUrl( const QString &url ); QList moods(); void setMoods( QList moods ); void setDownloadMembership(); virtual QString sourceName(); virtual QString sourceDescription(); virtual QPixmap emblem(); virtual bool isBookmarkable() const { return true; } virtual QString collectionName() const { return "Magnatune.com"; } virtual bool simpleFiltering() const { return false; } void setOggUrl( const QString& url ); QString oggUrl() const; void setAlbumPtr( Meta::AlbumPtr album ); public Q_SLOTS: void download(); private: QString m_lofiUrl; QString m_oggUrl; bool m_downloadMembership; QList m_moods; }; class MagnatuneArtist : public ServiceArtist { private: - QString m_photoUrl; - QString m_magnatuneUrl; + QUrl m_photoUrl; + QUrl m_magnatuneUrl; public: MagnatuneArtist( const QString &name ); MagnatuneArtist( const QStringList &resultRow ); - void setPhotoUrl( const QString &photoUrl ); - QString photoUrl() const; + void setPhotoUrl( const QUrl &photoUrl ); + QUrl photoUrl() const; - void setMagnatuneUrl( const QString &url ); - QString magnatuneUrl() const; + void setMagnatuneUrl( const QUrl &url ); + QUrl magnatuneUrl() const; virtual bool isBookmarkable() const { return true; } virtual QString collectionName() const { return "Magnatune.com"; } virtual bool simpleFiltering() const { return false; } }; class MagnatuneAlbum : public ServiceAlbumWithCover { Q_OBJECT private: QString m_coverUrl; int m_launchYear; QString m_albumCode; MagnatuneStore * m_store; bool m_downloadMembership; public: MagnatuneAlbum( const QString &name ); MagnatuneAlbum( const QStringList &resultRow ); ~MagnatuneAlbum(); virtual QString downloadPrefix() const { return "magnatune"; } void setLaunchYear( int launchYear ); int launchYear() const; virtual void setCoverUrl( const QString &coverUrl ); virtual QString coverUrl() const; virtual QUrl imageLocation( int size = 1 ) { Q_UNUSED( size ); return QUrl( coverUrl() ); } void setAlbumCode( const QString &albumCode ); QString albumCode(); void setStore( MagnatuneStore * store ); MagnatuneStore * store(); void setDownloadMembership(); virtual bool isBookmarkable() const { return true; } virtual QString collectionName() const { return "Magnatune.com"; } virtual bool simpleFiltering() const { return false; } public Q_SLOTS: void download(); void addToFavorites(); }; class MagnatuneGenre : public ServiceGenre { public: MagnatuneGenre( const QString &name ); MagnatuneGenre( const QStringList &resultRow ); virtual bool isBookmarkable() const { return true; } virtual QString collectionName() const { return "Magnatune.com"; } virtual bool simpleFiltering() const { return false; } }; } class MagnatuneMetaFactory : public ServiceMetaFactory { public: enum { OGG = 0, MP3 = 1, LOFI = 2 }; MagnatuneMetaFactory( const QString &dbPrefix, MagnatuneStore * store ); virtual ~MagnatuneMetaFactory() {} virtual int getTrackSqlRowCount(); virtual QString getTrackSqlRows(); virtual Meta::TrackPtr createTrack( const QStringList &rows ); virtual int getAlbumSqlRowCount(); virtual QString getAlbumSqlRows(); virtual Meta::AlbumPtr createAlbum( const QStringList &rows ); virtual int getArtistSqlRowCount(); virtual QString getArtistSqlRows(); virtual Meta::ArtistPtr createArtist( const QStringList &rows ); //virtual int getGenreSqlRowCount(); //virtual QString getGenreSqlRows(); virtual Meta::GenrePtr createGenre( const QStringList &rows ); //stuff for supporting the new membership services at Magnatune.com void setMembershipInfo ( const QString &prefix, const QString &userName, const QString &password ); void setStreamType( int type ); private: QString m_membershipPrefix; int m_streamType; QString m_userName; QString m_password; MagnatuneStore * m_store; }; #endif diff --git a/src/services/magnatune/MagnatuneNeedUpdateWidget.cpp b/src/services/magnatune/MagnatuneNeedUpdateWidget.cpp index 044cf4b972..a76f9ca754 100644 --- a/src/services/magnatune/MagnatuneNeedUpdateWidget.cpp +++ b/src/services/magnatune/MagnatuneNeedUpdateWidget.cpp @@ -1,63 +1,63 @@ /**************************************************************************************** * Copyright (c) 2012 Edward "hades" Toroshchin * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneNeedUpdateWidget.h" #include "ui_MagnatuneNeedUpdateWidget.h" #include "MagnatuneConfig.h" #include MagnatuneNeedUpdateWidget::MagnatuneNeedUpdateWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MagnatuneNeedUpdateWidget) { ui->setupUi(this); - connect(ui->update, SIGNAL(clicked()), SLOT(startUpdate())); - connect(ui->autoUpdate, SIGNAL(stateChanged(int)), SLOT(saveSettings())); + connect(ui->update, &QPushButton::clicked, this, &MagnatuneNeedUpdateWidget::startUpdate ); + connect(ui->autoUpdate, &QCheckBox::stateChanged, this, &MagnatuneNeedUpdateWidget::saveSettings ); ui->autoUpdate->setCheckState( MagnatuneConfig().autoUpdateDatabase()? Qt::Checked : Qt::Unchecked ); } void MagnatuneNeedUpdateWidget::enable() { ui->update->setEnabled(true); } void MagnatuneNeedUpdateWidget::disable() { ui->update->setEnabled(false); } void MagnatuneNeedUpdateWidget::startUpdate() { disable(); emit wantUpdate(); } void MagnatuneNeedUpdateWidget::saveSettings() { DEBUG_BLOCK MagnatuneConfig config; config.setAutoUpdateDatabase( ui->autoUpdate->checkState() == Qt::Checked ); config.save(); } diff --git a/src/services/magnatune/MagnatuneRedownloadDialog.cpp b/src/services/magnatune/MagnatuneRedownloadDialog.cpp index 56faaccd28..2249695c1f 100644 --- a/src/services/magnatune/MagnatuneRedownloadDialog.cpp +++ b/src/services/magnatune/MagnatuneRedownloadDialog.cpp @@ -1,102 +1,101 @@ /**************************************************************************************** * Copyright (c) 2006,2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneRedownloadDialog.h" #include "core/support/Debug.h" #include -MagnatuneRedownloadDialog::MagnatuneRedownloadDialog(QWidget* parent, const char* name, bool modal, Qt::WFlags fl) -: QDialog(parent, fl) +MagnatuneRedownloadDialog::MagnatuneRedownloadDialog(QWidget* parent, const char* name, bool modal, Qt::WindowFlags fl) + : QDialog(parent, fl) { setObjectName( name ); setModal( modal ); setupUi(this); redownloadButton->setEnabled ( false ); redownloadListView->header()->setStretchLastSection( true ); redownloadListView->setRootIsDecorated( false ); - connect( redownloadListView, SIGNAL(itemSelectionChanged()), SLOT(selectionChanged()) ); + connect( redownloadListView, &QTreeWidget::itemSelectionChanged, this, &MagnatuneRedownloadDialog::selectionChanged ); } MagnatuneRedownloadDialog::~MagnatuneRedownloadDialog() { } void MagnatuneRedownloadDialog::setRedownloadItems( const QStringList &items ) { QStringListIterator it(items); while ( it.hasNext() ) { QString currentItem = it.next(); debug() << "Adding item to redownload dialog: " << currentItem; redownloadListView->addTopLevelItem( new QTreeWidgetItem( QStringList(currentItem) ) ); } debug() << "Nothing more to add..."; } void MagnatuneRedownloadDialog::setRedownloadItems( QList previousPurchases ) { m_infoMap.clear(); foreach( const MagnatuneDownloadInfo &prevPurchase, previousPurchases ) { const QString albumText = prevPurchase.artistName() + " - " + prevPurchase.albumName(); QTreeWidgetItem *item = new QTreeWidgetItem( QStringList( albumText ) ); m_infoMap.insert( item, prevPurchase ); redownloadListView->addTopLevelItem( item ); } } -void MagnatuneRedownloadDialog::redownload( ) +void MagnatuneRedownloadDialog::slotRedownload( ) { - QTreeWidgetItem * current = redownloadListView->currentItem(); if ( m_infoMap.keys().contains( current ) ) { emit( redownload( m_infoMap.value( current ) ) ); } //emit ( redownload( redownloadListView->currentItem()->text( 0 ) ) ); hide(); } void MagnatuneRedownloadDialog::reject( ) { hide(); emit(cancelled()); } void MagnatuneRedownloadDialog::selectionChanged( ) { if (redownloadListView->currentItem() != 0) { redownloadButton->setEnabled ( true ); } else { redownloadButton->setEnabled ( false ); } } /*$SPECIALIZATION$*/ diff --git a/src/services/magnatune/MagnatuneRedownloadDialog.h b/src/services/magnatune/MagnatuneRedownloadDialog.h index fbb6ce5c39..06775ac45c 100644 --- a/src/services/magnatune/MagnatuneRedownloadDialog.h +++ b/src/services/magnatune/MagnatuneRedownloadDialog.h @@ -1,59 +1,59 @@ /**************************************************************************************** * Copyright (c) 2006,2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef MAGNATUNE_REDOWNLOAD_DIALOG_H #define MAGNATUNE_REDOWNLOAD_DIALOG_H #include "ui_MagnatuneRedownloadDialogBase.h" #include "MagnatuneDownloadInfo.h" #include class MagnatuneRedownloadDialog : public QDialog, public Ui::magnatuneReDownloadDialogBase { Q_OBJECT public: - explicit MagnatuneRedownloadDialog( QWidget* parent = 0, const char* name = 0, bool modal = false, Qt::WFlags fl = 0 ); + explicit MagnatuneRedownloadDialog( QWidget* parent = 0, const char* name = 0, bool modal = false, Qt::WindowFlags fl = 0 ); ~MagnatuneRedownloadDialog(); /*$PUBLIC_FUNCTIONS$*/ void setRedownloadItems( const QStringList &items ); void setRedownloadItems( QList previousPurchases ); Q_SIGNALS: - void redownload( const QString &downloadInfoFileName ); +// void redownload( const QString &downloadInfoFileName ); void redownload( MagnatuneDownloadInfo info ); void cancelled(); public Q_SLOTS: /*$PUBLIC_SLOTS$*/ protected: QMap m_infoMap; protected Q_SLOTS: /*$PROTECTED_SLOTS$*/ - void redownload(); + void slotRedownload(); void selectionChanged(); void reject(); }; #endif diff --git a/src/services/magnatune/MagnatuneRedownloadHandler.cpp b/src/services/magnatune/MagnatuneRedownloadHandler.cpp index a2c2d6085d..74375405a6 100644 --- a/src/services/magnatune/MagnatuneRedownloadHandler.cpp +++ b/src/services/magnatune/MagnatuneRedownloadHandler.cpp @@ -1,203 +1,204 @@ /**************************************************************************************** * Copyright (c) 2006,2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneRedownloadHandler.h" #include "MagnatuneConfig.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include "core/support/Components.h" #include "core/interfaces/Logger.h" -#include +#include #include #include "QDir" MagnatuneRedownloadHandler::MagnatuneRedownloadHandler(QWidget * parent) { m_parent = parent; m_redownloadDialog = 0; m_downloadDialog = 0; m_albumDownloader = 0; } MagnatuneRedownloadHandler::~MagnatuneRedownloadHandler() { } void MagnatuneRedownloadHandler::showRedownloadDialog( ) { fetchServerSideRedownloadList(); return; } QStringList MagnatuneRedownloadHandler::GetPurchaseList( ) { debug() << "MagnatuneRedownloadHandler::GetPurchaseList( )"; QStringList returnList; QDir purchaseInfoDir( Amarok::saveLocation( "magnatune.com/purchases/" ) ); if ( !purchaseInfoDir.exists () ) { return returnList; } purchaseInfoDir.setFilter( QDir::Files); purchaseInfoDir.setSorting( QDir::Name ); const QFileInfoList list = purchaseInfoDir.entryInfoList(); QFileInfoList::const_iterator it( list.begin() ); QFileInfo fi; while ( it != list.end() ) { fi = *it; returnList.append( fi.fileName() ); ++it; } debug() << "Done parsing previous purchases!"; return returnList; } void MagnatuneRedownloadHandler::redownload( MagnatuneDownloadInfo info ) { if ( m_albumDownloader == 0 ) { m_albumDownloader = new MagnatuneAlbumDownloader(); - connect( m_albumDownloader, SIGNAL(downloadComplete(bool)), this, SLOT(albumDownloadComplete(bool)) ); + connect( m_albumDownloader, &MagnatuneAlbumDownloader::downloadComplete, this, &MagnatuneRedownloadHandler::albumDownloadComplete ); } - if (m_downloadDialog == 0) { + if ( m_downloadDialog == 0 ) + { m_downloadDialog = new MagnatuneDownloadDialog(m_parent); - connect( m_downloadDialog, SIGNAL(downloadAlbum(MagnatuneDownloadInfo)), m_albumDownloader, SLOT(downloadAlbum(MagnatuneDownloadInfo)) ); + connect( m_downloadDialog, &MagnatuneDownloadDialog::downloadAlbum, m_albumDownloader, &MagnatuneAlbumDownloader::downloadAlbum ); } debug() << "Showing download dialog"; m_downloadDialog->setDownloadInfo( info ); m_downloadDialog->show(); } void MagnatuneRedownloadHandler::selectionDialogCancelled( ) { if (m_redownloadDialog != 0) { m_redownloadDialog->hide(); delete m_redownloadDialog; m_redownloadDialog = 0; } } void MagnatuneRedownloadHandler::albumDownloadComplete( bool success ) { Q_UNUSED( success ); //cleanup time! if (m_downloadDialog != 0) { delete m_downloadDialog; m_downloadDialog = 0; } if (m_albumDownloader != 0) { delete m_albumDownloader; m_albumDownloader = 0; } } void MagnatuneRedownloadHandler::fetchServerSideRedownloadList() { DEBUG_BLOCK //do we have an email set, if not, ask the user for one. MagnatuneConfig config; QString email = config.email(); if ( email.isEmpty() ) { //TODO ask for the email return; } - QString redownloadApiUrl = "http://magnatune.com/buy/redownload_xml?email=" + email; + QUrl redownloadApiUrl = QUrl::fromUserInput( "http://magnatune.com/buy/redownload_xml?email=" + email ); - m_redownloadApiJob = KIO::storedGet( QUrl( redownloadApiUrl ), KIO::NoReload, KIO::HideProgressInfo ); + m_redownloadApiJob = KIO::storedGet( redownloadApiUrl, KIO::NoReload, KIO::HideProgressInfo ); Amarok::Components::logger()->newProgressOperation( m_redownloadApiJob, i18n( "Getting list of previous Magnatune.com purchases" ) ); - connect( m_redownloadApiJob, SIGNAL(result(KJob*)), SLOT(redownloadApiResult(KJob*)) ); + connect( m_redownloadApiJob, &KIO::TransferJob::result, this, &MagnatuneRedownloadHandler::redownloadApiResult ); } void MagnatuneRedownloadHandler::redownloadApiResult( KJob* job ) { -DEBUG_BLOCK + DEBUG_BLOCK - if ( !job->error() == 0 ) + if ( job->error() != 0 ) { //TODO: error handling here debug() << "Job error... " << job->error(); return ; } if ( job != m_redownloadApiJob ) { debug() << "Wrong job..."; return ; //not the right job, so let's ignore it } KIO::StoredTransferJob* const storedJob = static_cast( job ); QString resultXml = QString( storedJob->data() ); debug() << endl << endl << "result: " << resultXml; QList previousPurchasesInfoList; QDomDocument doc; doc.setContent( resultXml ); QDomNodeList downloads = doc.elementsByTagName( "download" ); for( int i = 0; i < downloads.size(); i++ ) { QDomElement downloadElement = downloads.item( i ).toElement(); MagnatuneDownloadInfo info; if ( info.initFromRedownloadXml( downloadElement ) ) previousPurchasesInfoList << info; } - if (m_redownloadDialog == 0) { + if (m_redownloadDialog == 0) + { m_redownloadDialog = new MagnatuneRedownloadDialog( m_parent ); - //connect( m_redownloadDialog, SIGNAL(redownload(QString)), this, SLOT(redownload(QString)) ); - connect( m_redownloadDialog, SIGNAL(redownload(MagnatuneDownloadInfo)), this, SLOT(redownload(MagnatuneDownloadInfo)) ); - connect( m_redownloadDialog, SIGNAL(cancelled()), this, SLOT(selectionDialogCancelled()) ); + connect( m_redownloadDialog, &MagnatuneRedownloadDialog::redownload, this, &MagnatuneRedownloadHandler::redownload ); + connect( m_redownloadDialog, &MagnatuneRedownloadDialog::cancelled, this, &MagnatuneRedownloadHandler::selectionDialogCancelled ); } m_redownloadDialog->setRedownloadItems( previousPurchasesInfoList ); m_redownloadDialog->show(); } diff --git a/src/services/magnatune/MagnatuneRedownloadHandler.h b/src/services/magnatune/MagnatuneRedownloadHandler.h index 8cdd33ae44..91b59ebdc2 100644 --- a/src/services/magnatune/MagnatuneRedownloadHandler.h +++ b/src/services/magnatune/MagnatuneRedownloadHandler.h @@ -1,78 +1,76 @@ /**************************************************************************************** * Copyright (c) 2006,2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef MAGNATUNE_REDOWNLOAD_HANDLER_H #define MAGNATUNE_REDOWNLOAD_HANDLER_H #include "MagnatuneAlbumDownloader.h" #include "MagnatuneDownloadDialog.h" #include "MagnatuneDownloadInfo.h" #include "MagnatuneRedownloadDialog.h" #include /** This class handles the redownloading of previously downloaded albums @author Nikolaj Hald Nielsen */ -class MagnatuneRedownloadHandler : public QObject { +class MagnatuneRedownloadHandler : public QObject +{ Q_OBJECT public: MagnatuneRedownloadHandler(QWidget * parent); ~MagnatuneRedownloadHandler(); /** * Calls forth the redownload dialog. */ void showRedownloadDialog(); Q_SIGNALS: void reDownloadCompleted( bool success ); protected: QStringList GetPurchaseList( ); /** * Attempt to get a list of previous purchases for an email. * If set, use the email from the magnatune settings, otherwise, QueryMaker * the user (and then save it to settings) */ void fetchServerSideRedownloadList(); QWidget * m_parent; MagnatuneRedownloadDialog * m_redownloadDialog; MagnatuneDownloadDialog * m_downloadDialog; MagnatuneAlbumDownloader * m_albumDownloader; KIO::TransferJob * m_redownloadApiJob; protected Q_SLOTS: void redownload( MagnatuneDownloadInfo info ); void selectionDialogCancelled(); void albumDownloadComplete( bool success ); void redownloadApiResult( KJob* job ); - - - }; #endif diff --git a/src/services/magnatune/MagnatuneSettingsModule.cpp b/src/services/magnatune/MagnatuneSettingsModule.cpp index b6e1a03629..47c781cbac 100644 --- a/src/services/magnatune/MagnatuneSettingsModule.cpp +++ b/src/services/magnatune/MagnatuneSettingsModule.cpp @@ -1,111 +1,109 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) version 3 or * * any later version accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of * * version 3 of the license. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneSettingsModule.h" #include "MagnatuneMeta.h" #include "ui_MagnatuneConfigWidget.h" -#include +#include K_PLUGIN_FACTORY_WITH_JSON( MagnatuneSettingsModuleFactory, "amarok_service_magnatunestore_config.json", registerPlugin(); ) MagnatuneSettingsModule::MagnatuneSettingsModule( QWidget *parent, const QVariantList &args ) : KCModule( parent, args ) { m_configDialog = new Ui::MagnatuneConfigWidget; m_configDialog->setupUi( this ); m_configDialog->passwordEdit->setEchoMode( QLineEdit::Password ); - connect ( m_configDialog->usernameEdit, SIGNAL(textChanged(QString)), this, SLOT(settingsChanged()) ); - connect ( m_configDialog->passwordEdit, SIGNAL(textChanged(QString)), this, SLOT(settingsChanged()) ); - connect ( m_configDialog->emailEdit, SIGNAL(textChanged(QString)), this, SLOT(settingsChanged()) ); - connect ( m_configDialog->typeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(settingsChanged()) ); - connect ( m_configDialog->isMemberCheckbox, SIGNAL(stateChanged(int)), this, SLOT(settingsChanged()) ); - connect ( m_configDialog->streamTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(settingsChanged()) ); - connect ( m_configDialog->autoUpdateDatabase, SIGNAL(stateChanged(int)), this, SLOT(settingsChanged()) ); + connect ( m_configDialog->usernameEdit, &QLineEdit::textChanged, this, &MagnatuneSettingsModule::settingsChanged ); + connect ( m_configDialog->passwordEdit, &QLineEdit::textChanged, this, &MagnatuneSettingsModule::settingsChanged ); + connect ( m_configDialog->emailEdit, &QLineEdit::textChanged, this, &MagnatuneSettingsModule::settingsChanged ); + connect ( m_configDialog->typeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &MagnatuneSettingsModule::settingsChanged ); + connect ( m_configDialog->isMemberCheckbox, &QCheckBox::stateChanged, this, &MagnatuneSettingsModule::settingsChanged ); + connect ( m_configDialog->streamTypeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &MagnatuneSettingsModule::settingsChanged ); + connect ( m_configDialog->autoUpdateDatabase, &QCheckBox::stateChanged, this, &MagnatuneSettingsModule::settingsChanged ); load(); - } MagnatuneSettingsModule::~MagnatuneSettingsModule() { delete m_configDialog; } void MagnatuneSettingsModule::save() { m_config.setIsMember( m_configDialog->isMemberCheckbox->checkState() == Qt::Checked ); m_config.setAutoUpdateDatabase( m_configDialog->autoUpdateDatabase->checkState() == Qt::Checked ); int typeIndex = m_configDialog->typeComboBox->currentIndex(); if( typeIndex == 0 ) m_config.setMembershipType( MagnatuneConfig::STREAM ); else m_config.setMembershipType( MagnatuneConfig::DOWNLOAD ); m_config.setUsername( m_configDialog->usernameEdit->text() ); m_config.setPassword( m_configDialog->passwordEdit->text() ); m_config.setEmail( m_configDialog->emailEdit->text() ); QString streamTypeString = m_configDialog->streamTypeComboBox->currentText(); m_config.setStreamType( m_configDialog->streamTypeComboBox->currentIndex() ); m_config.save(); KCModule::save(); - } void MagnatuneSettingsModule::load() { if ( m_config.isMember() ) m_configDialog->isMemberCheckbox->setCheckState( Qt::Checked ); else m_configDialog->isMemberCheckbox->setCheckState( Qt::Unchecked ); m_configDialog->autoUpdateDatabase->setCheckState( m_config.autoUpdateDatabase()? Qt::Checked : Qt::Unchecked ); int index = 0; if ( m_config.membershipType() == MagnatuneConfig::STREAM ) index = 0; else if ( m_config.membershipType() == MagnatuneConfig::DOWNLOAD ) index = 1; m_configDialog->typeComboBox->setCurrentIndex( index ); m_configDialog->usernameEdit->setText( m_config.username() ); m_configDialog->passwordEdit->setText( m_config.password() ); m_configDialog->emailEdit->setText( m_config.email() ); m_configDialog->streamTypeComboBox->setCurrentIndex( m_config.streamType() ); KCModule::load(); } void MagnatuneSettingsModule::defaults() { } void MagnatuneSettingsModule::settingsChanged() { emit changed( true ); } - +#include diff --git a/src/services/magnatune/MagnatuneStore.cpp b/src/services/magnatune/MagnatuneStore.cpp index 9b29987b53..e80c35b00a 100644 --- a/src/services/magnatune/MagnatuneStore.cpp +++ b/src/services/magnatune/MagnatuneStore.cpp @@ -1,747 +1,746 @@ /**************************************************************************************** * Copyright (c) 2006,2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneStore.h" #include "core/support/Amarok.h" #include "core/support/Components.h" #include "core/interfaces/Logger.h" #include "amarokurls/AmarokUrlHandler.h" #include "browsers/CollectionTreeItem.h" +#include "browsers/CollectionTreeView.h" #include "browsers/SingleCollectionTreeItemModel.h" #include "EngineController.h" #include "MagnatuneConfig.h" #include "MagnatuneDatabaseWorker.h" #include "MagnatuneInfoParser.h" #include "MagnatuneNeedUpdateWidget.h" #include "browsers/InfoProxy.h" #include "MagnatuneUrlRunner.h" #include "ui_MagnatuneSignupDialogBase.h" #include "../ServiceSqlRegistry.h" #include "core-impl/collections/support/CollectionManager.h" #include "core/support/Debug.h" #include "playlist/PlaylistModelStack.h" #include "widgets/SearchWidget.h" #include -#include -#include //locate() -#include -#include -#include -#include - #include #include +#include +#include #include #include +#include -#include +#include +#include +#include //////////////////////////////////////////////////////////////////////////////////////////////////////////// // class MagnatuneServiceFactory //////////////////////////////////////////////////////////////////////////////////////////////////////////// MagnatuneServiceFactory::MagnatuneServiceFactory() : ServiceFactory() { } void MagnatuneServiceFactory::init() { DEBUG_BLOCK MagnatuneStore* service = new MagnatuneStore( this, "Magnatune.com" ); m_initialized = true; emit newService( service ); } QString MagnatuneServiceFactory::name() { return "Magnatune.com"; } KConfigGroup MagnatuneServiceFactory::config() { return Amarok::config( "Service_Magnatune" ); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // class MagnatuneStore //////////////////////////////////////////////////////////////////////////////////////////////////////////// MagnatuneStore::MagnatuneStore( MagnatuneServiceFactory* parent, const char *name ) : ServiceBase( name, parent ) , m_downloadHandler( 0 ) , m_redownloadHandler( 0 ) , m_needUpdateWidget( 0 ) , m_downloadInProgress( 0 ) , m_currentAlbum( 0 ) , m_streamType( MagnatuneMetaFactory::OGG ) , m_magnatuneTimestamp( 0 ) , m_registry( 0 ) , m_signupInfoWidget( 0 ) { - setObjectName(name); DEBUG_BLOCK + setObjectName(name); //initTopPanel( ); setShortDescription( i18n( "\"Fair trade\" online music store" ) ); setIcon( QIcon::fromTheme( "view-services-magnatune-amarok" ) ); // xgettext: no-c-format setLongDescription( i18n( "Magnatune.com is a different kind of record company with the motto \"We are not evil!\" 50% of every purchase goes directly to the artist and if you purchase an album through Amarok, the Amarok project receives a 10% commission. Magnatune.com also offers \"all you can eat\" memberships that lets you download as much of their music as you like." ) ); - setImagePath( KStandardDirs::locate( "data", "amarok/images/hover_info_magnatune.png" ) ); + setImagePath( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/hover_info_magnatune.png" ) ); //initBottomPanel(); // m_currentlySelectedItem = 0; m_polished = false; //polish( ); //FIXME not happening when shown for some reason //do this stuff now to make us function properly as a track provider on startup. The expensive stuff will //not happen untill the model is added to the view anyway. MagnatuneMetaFactory * metaFactory = new MagnatuneMetaFactory( "magnatune", this ); MagnatuneConfig config; if ( config.isMember() ) { setMembership( config.membershipType(), config.username(), config.password() ); metaFactory->setMembershipInfo( config.membershipPrefix(), m_username, m_password ); } setStreamType( config.streamType() ); metaFactory->setStreamType( m_streamType ); m_registry = new ServiceSqlRegistry( metaFactory ); m_collection = new Collections::MagnatuneSqlCollection( "magnatune", "Magnatune.com", metaFactory, m_registry ); CollectionManager::instance()->addTrackProvider( m_collection ); setServiceReady( true ); } MagnatuneStore::~MagnatuneStore() { CollectionManager::instance()->removeTrackProvider( m_collection ); delete m_registry; delete m_collection; } void MagnatuneStore::download( ) { DEBUG_BLOCK if ( m_downloadInProgress ) return; if ( !m_polished ) polish(); debug() << "here"; //check if we need to start a download or show the signup dialog if( !m_isMember || m_membershipType != MagnatuneConfig::DOWNLOAD ) { showSignupDialog(); return; } m_downloadInProgress = true; m_downloadAlbumButton->setEnabled( false ); if ( !m_downloadHandler ) { m_downloadHandler = new MagnatuneDownloadHandler(); m_downloadHandler->setParent( this ); - connect( m_downloadHandler, SIGNAL(downloadCompleted(bool)), this, SLOT(downloadCompleted(bool)) ); + connect( m_downloadHandler, &MagnatuneDownloadHandler::downloadCompleted, + this, &MagnatuneStore::downloadCompleted ); } if ( m_currentAlbum != 0 ) m_downloadHandler->downloadAlbum( m_currentAlbum ); } -void MagnatuneStore::download( Meta::MagnatuneTrack * track ) +void MagnatuneStore::downloadTrack( Meta::MagnatuneTrack * track ) { Meta::MagnatuneAlbum * album = dynamic_cast( track->album().data() ); if ( album ) - download( album ); + downloadAlbum( album ); } -void MagnatuneStore::download( Meta::MagnatuneAlbum * album ) +void MagnatuneStore::downloadAlbum( Meta::MagnatuneAlbum * album ) { - DEBUG_BLOCK if ( m_downloadInProgress ) return; if ( !m_polished ) polish(); m_downloadInProgress = true; m_downloadAlbumButton->setEnabled( false ); if ( !m_downloadHandler ) { m_downloadHandler = new MagnatuneDownloadHandler(); m_downloadHandler->setParent( this ); - connect( m_downloadHandler, SIGNAL(downloadCompleted(bool)), this, SLOT(downloadCompleted(bool)) ); + connect( m_downloadHandler, &MagnatuneDownloadHandler::downloadCompleted, + this, &MagnatuneStore::downloadCompleted ); } m_downloadHandler->downloadAlbum( album ); } void MagnatuneStore::initTopPanel( ) { - QMenu *filterMenu = new QMenu( 0 ); QAction *action = filterMenu->addAction( i18n("Artist") ); - connect( action, SIGNAL(triggered(bool)), SLOT(sortByArtist()) ); + connect( action, &QAction::triggered, this, &MagnatuneStore::sortByArtist ); action = filterMenu->addAction( i18n( "Artist / Album" ) ); - connect( action, SIGNAL(triggered(bool)), SLOT(sortByArtistAlbum()) ); + connect( action, &QAction::triggered, this, &MagnatuneStore::sortByArtistAlbum ); action = filterMenu->addAction( i18n( "Album" ) ) ; - connect( action, SIGNAL(triggered(bool)), SLOT(sortByAlbum()) ); + connect( action, &QAction::triggered, this, &MagnatuneStore::sortByAlbum ); action = filterMenu->addAction( i18n( "Genre / Artist" ) ); - connect( action, SIGNAL(triggered(bool)), SLOT(sortByGenreArtist()) ); + connect( action, &QAction::triggered, this, &MagnatuneStore::sortByGenreArtist ); action = filterMenu->addAction( i18n( "Genre / Artist / Album" ) ); - connect( action, SIGNAL(triggered(bool)), SLOT(sortByGenreArtistAlbum()) ); + connect( action, &QAction::triggered, this, &MagnatuneStore::sortByGenreArtistAlbum ); QAction *filterMenuAction = new QAction( QIcon::fromTheme( "preferences-other" ), i18n( "Sort Options" ), this ); filterMenuAction->setMenu( filterMenu ); m_searchWidget->toolBar()->addSeparator(); m_searchWidget->toolBar()->addAction( filterMenuAction ); QToolButton *tbutton = qobject_cast( m_searchWidget->toolBar()->widgetForAction( filterMenuAction ) ); if( tbutton ) tbutton->setPopupMode( QToolButton::InstantPopup ); QMenu * actionsMenu = new QMenu( 0 ); action = actionsMenu->addAction( i18n( "Re-download" ) ); - connect( action, SIGNAL(triggered(bool)), SLOT(processRedownload()) ); + connect( action, &QAction::triggered, this, &MagnatuneStore::processRedownload ); m_updateAction = actionsMenu->addAction( i18n( "Update Database" ) ); - connect( m_updateAction, SIGNAL(triggered(bool)), SLOT(updateButtonClicked()) ); + connect( m_updateAction, &QAction::triggered, this, &MagnatuneStore::updateButtonClicked ); QAction *actionsMenuAction = new QAction( QIcon::fromTheme( "list-add" ), i18n( "Tools" ), this ); actionsMenuAction->setMenu( actionsMenu ); m_searchWidget->toolBar()->addAction( actionsMenuAction ); tbutton = qobject_cast( m_searchWidget->toolBar()->widgetForAction( actionsMenuAction ) ); if( tbutton ) tbutton->setPopupMode( QToolButton::InstantPopup ); } void MagnatuneStore::initBottomPanel() { //m_bottomPanel->setMaximumHeight( 24 ); m_downloadAlbumButton = new QPushButton; m_downloadAlbumButton->setParent( m_bottomPanel ); MagnatuneConfig config; if ( config.isMember() && config.membershipType() == MagnatuneConfig::DOWNLOAD ) { m_downloadAlbumButton->setText( i18n( "Download Album" ) ); m_downloadAlbumButton->setEnabled( false ); } else if ( config.isMember() ) m_downloadAlbumButton->hide(); else { m_downloadAlbumButton->setText( i18n( "Signup" ) ); m_downloadAlbumButton->setEnabled( true ); } m_downloadAlbumButton->setObjectName( "downloadButton" ); m_downloadAlbumButton->setIcon( QIcon::fromTheme( "download-amarok" ) ); - connect( m_downloadAlbumButton, SIGNAL(clicked()) , this, SLOT(download()) ); + connect( m_downloadAlbumButton, &QPushButton::clicked, this, &MagnatuneStore::download ); if ( !config.lastUpdateTimestamp() ) { m_needUpdateWidget = new MagnatuneNeedUpdateWidget(m_bottomPanel); - connect( m_needUpdateWidget, SIGNAL(wantUpdate()), SLOT(updateButtonClicked()) ); + connect( m_needUpdateWidget, &MagnatuneNeedUpdateWidget::wantUpdate, + this, &MagnatuneStore::updateButtonClicked ); m_downloadAlbumButton->setParent(0); } } void MagnatuneStore::updateButtonClicked() { DEBUG_BLOCK m_updateAction->setEnabled( false ); if ( m_needUpdateWidget ) m_needUpdateWidget->disable(); updateMagnatuneList(); } bool MagnatuneStore::updateMagnatuneList() { DEBUG_BLOCK //download new list from magnatune debug() << "MagnatuneStore: start downloading xml file"; - KTemporaryFile tempFile; - tempFile.setSuffix( ".bz2" ); + QTemporaryFile tempFile; +// tempFile.setSuffix( ".bz2" ); tempFile.setAutoRemove( false ); // file will be removed in MagnatuneXmlParser if( !tempFile.open() ) { return false; //error } m_tempFileName = tempFile.fileName(); - m_listDownloadJob = KIO::file_copy( QUrl("http://magnatune.com/info/album_info_xml.bz2"), QUrl( m_tempFileName ), 0700 , KIO::HideProgressInfo | KIO::Overwrite ); + m_listDownloadJob = KIO::file_copy( QUrl("http://magnatune.com/info/album_info_xml.bz2"), QUrl::fromLocalFile( m_tempFileName ), 0700 , KIO::HideProgressInfo | KIO::Overwrite ); Amarok::Components::logger()->newProgressOperation( m_listDownloadJob, i18n( "Downloading Magnatune.com database..." ), this, SLOT(listDownloadCancelled()) ); - connect( m_listDownloadJob, SIGNAL(result(KJob*)), - this, SLOT(listDownloadComplete(KJob*)) ); - + connect( m_listDownloadJob, &KJob::result, + this, &MagnatuneStore::listDownloadComplete ); return true; } void MagnatuneStore::listDownloadComplete( KJob * downLoadJob ) { DEBUG_BLOCK debug() << "MagnatuneStore: xml file download complete"; if ( downLoadJob != m_listDownloadJob ) { debug() << "wrong job, ignoring...."; return ; //not the right job, so let's ignore it } m_updateAction->setEnabled( true ); if ( downLoadJob->error() != 0 ) { debug() << "Got an error, bailing out: " << downLoadJob->errorString(); //TODO: error handling here return ; } Amarok::Components::logger()->shortMessage( i18n( "Updating the local Magnatune database." ) ); MagnatuneXmlParser * parser = new MagnatuneXmlParser( m_tempFileName ); parser->setDbHandler( new MagnatuneDatabaseHandler() ); - connect( parser, SIGNAL(doneParsing()), SLOT(doneParsing()) ); + connect( parser, &MagnatuneXmlParser::doneParsing, this, &MagnatuneStore::doneParsing ); ThreadWeaver::Queue::instance()->enqueue( QSharedPointer(parser) ); } void MagnatuneStore::listDownloadCancelled( ) { DEBUG_BLOCK m_listDownloadJob->kill(); m_listDownloadJob = 0; debug() << "Aborted xml download"; m_updateAction->setEnabled( true ); if ( m_needUpdateWidget ) m_needUpdateWidget->enable(); } void MagnatuneStore::doneParsing() { debug() << "MagnatuneStore: done parsing"; m_collection->emitUpdated(); //update the last update timestamp MagnatuneConfig config; if ( m_magnatuneTimestamp == 0 ) config.setLastUpdateTimestamp( QDateTime::currentDateTime().toTime_t() ); else config.setLastUpdateTimestamp( m_magnatuneTimestamp ); config.save(); if ( m_needUpdateWidget ) { m_needUpdateWidget->setParent(0); m_needUpdateWidget->deleteLater(); m_needUpdateWidget = 0; m_downloadAlbumButton->setParent(m_bottomPanel); } } void MagnatuneStore::processRedownload( ) { debug() << "Process redownload"; if ( m_redownloadHandler == 0 ) { m_redownloadHandler = new MagnatuneRedownloadHandler( this ); } m_redownloadHandler->showRedownloadDialog(); } void MagnatuneStore::downloadCompleted( bool ) { delete m_downloadHandler; m_downloadHandler = 0; m_downloadAlbumButton->setEnabled( true ); m_downloadInProgress = false; debug() << "Purchase operation complete"; //TODO: display some kind of success dialog here? } void MagnatuneStore::itemSelected( CollectionTreeItem * selectedItem ) { DEBUG_BLOCK //only care if the user has a download membership if( !m_isMember || m_membershipType != MagnatuneConfig::DOWNLOAD ) return; //we only enable the purchase button if there is only one item selected and it happens to //be an album or a track Meta::DataPtr dataPtr = selectedItem->data(); if ( typeid( * dataPtr.data() ) == typeid( Meta::MagnatuneTrack ) ) { debug() << "is right type (track)"; Meta::MagnatuneTrack * track = static_cast ( dataPtr.data() ); m_currentAlbum = static_cast ( track->album().data() ); m_downloadAlbumButton->setEnabled( true ); } else if ( typeid( * dataPtr.data() ) == typeid( Meta::MagnatuneAlbum ) ) { m_currentAlbum = static_cast ( dataPtr.data() ); debug() << "is right type (album) named " << m_currentAlbum->name(); m_downloadAlbumButton->setEnabled( true ); } else { debug() << "is wrong type"; m_downloadAlbumButton->setEnabled( false ); } } void MagnatuneStore::addMoodyTracksToPlaylist( const QString &mood, int count ) { - MagnatuneDatabaseWorker * databaseWorker = new MagnatuneDatabaseWorker(); + MagnatuneDatabaseWorker *databaseWorker = new MagnatuneDatabaseWorker(); databaseWorker->fetchTrackswithMood( mood, count, m_registry ); - connect( databaseWorker, SIGNAL(gotMoodyTracks(Meta::TrackList)), this, SLOT(moodyTracksReady(Meta::TrackList)) ); + connect( databaseWorker, &MagnatuneDatabaseWorker::gotMoodyTracks, this, &MagnatuneStore::moodyTracksReady ); ThreadWeaver::Queue::instance()->enqueue( QSharedPointer(databaseWorker) ); } void MagnatuneStore::polish() { DEBUG_BLOCK; if (!m_polished) { m_polished = true; initTopPanel( ); initBottomPanel(); QList levels; levels << CategoryId::Genre << CategoryId::Artist << CategoryId::Album; m_magnatuneInfoParser = new MagnatuneInfoParser(); setInfoParser( m_magnatuneInfoParser ); setModel( new SingleCollectionTreeItemModel( m_collection, levels ) ); - connect( m_contentView, SIGNAL(itemSelected(CollectionTreeItem*)), this, SLOT(itemSelected(CollectionTreeItem*)) ); + connect( qobject_cast(m_contentView), &CollectionTreeView::itemSelected, + this, &MagnatuneStore::itemSelected ); //add a custom url runner + MagnatuneUrlRunner *runner = new MagnatuneUrlRunner(); - MagnatuneUrlRunner * runner = new MagnatuneUrlRunner(); - - connect( runner, SIGNAL(showFavorites()), this, SLOT(showFavoritesPage()) ); - connect( runner, SIGNAL(showHome()), this, SLOT(showHomePage()) ); - connect( runner, SIGNAL(showRecommendations()), this, SLOT(showRecommendationsPage()) ); - connect( runner, SIGNAL(buyOrDownload(QString)), this, SLOT(download(QString)) ); - connect( runner, SIGNAL(removeFromFavorites(QString)), this, SLOT(removeFromFavorites(QString)) ); + connect( runner, &MagnatuneUrlRunner::showFavorites, this, &MagnatuneStore::showFavoritesPage ); + connect( runner, &MagnatuneUrlRunner::showHome, this, &MagnatuneStore::showHomePage ); + connect( runner, &MagnatuneUrlRunner::showRecommendations, this, &MagnatuneStore::showRecommendationsPage ); + connect( runner, &MagnatuneUrlRunner::buyOrDownload, this, &MagnatuneStore::downloadSku ); + connect( runner, &MagnatuneUrlRunner::removeFromFavorites, this, &MagnatuneStore::removeFromFavorites ); The::amarokUrlHandler()->registerRunner( runner, runner->command() ); } - const QUrl url( KStandardDirs::locate( "data", "amarok/data/" ) ); - QString imagePath = url.url(); + QString imagePath = QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/data/" ); + const QUrl url = QUrl::fromLocalFile( imagePath ); MagnatuneInfoParser * parser = dynamic_cast ( infoParser() ); if ( parser ) parser->getFrontPage(); //get a mood map we can show to the cloud view MagnatuneDatabaseWorker * databaseWorker = new MagnatuneDatabaseWorker(); databaseWorker->fetchMoodMap(); - connect( databaseWorker, SIGNAL(gotMoodMap(QMap)), this, SLOT(moodMapReady(QMap)) ); + connect( databaseWorker, &MagnatuneDatabaseWorker::gotMoodMap, this, &MagnatuneStore::moodMapReady ); ThreadWeaver::Queue::instance()->enqueue( QSharedPointer(databaseWorker) ); if ( MagnatuneConfig().autoUpdateDatabase() ) checkForUpdates(); } void MagnatuneStore::setMembership( int type, const QString & username, const QString & password) { m_isMember = true; m_membershipType = type; m_username = username; m_password = password; } void MagnatuneStore::moodMapReady(QMap< QString, int > map) { QVariantMap variantMap; QList strings; QList weights; QVariantMap dbusActions; foreach( const QString &key, map.keys() ) { strings << key; weights << map.value( key ); QString escapedKey = key; escapedKey.replace( ' ', "%20" ); QVariantMap action; action["component"] = "/ServicePluginManager"; action["function"] = "sendMessage"; action["arg1"] = QString( "Magnatune.com"); action["arg2"] = QString( "addMoodyTracks %1 10").arg( escapedKey ); dbusActions[key] = action; } variantMap["cloud_name"] = QVariant( "Magnatune Moods" ); variantMap["cloud_strings"] = QVariant( strings ); variantMap["cloud_weights"] = QVariant( weights ); variantMap["cloud_actions"] = QVariant( dbusActions ); The::infoProxy()->setCloud( variantMap ); } void MagnatuneStore::setStreamType( int type ) { m_streamType = type; } void MagnatuneStore::checkForUpdates() { m_updateTimestampDownloadJob = KIO::storedGet( QUrl("http://magnatune.com/info/last_update_timestamp"), KIO::Reload, KIO::HideProgressInfo ); - connect( m_updateTimestampDownloadJob, SIGNAL(result(KJob*)), SLOT(timestampDownloadComplete(KJob*)) ); + connect( m_updateTimestampDownloadJob, &KJob::result, this, &MagnatuneStore::timestampDownloadComplete ); } void MagnatuneStore::timestampDownloadComplete( KJob * job ) { DEBUG_BLOCK if ( job->error() != 0 ) { //TODO: error handling here return ; } if ( job != m_updateTimestampDownloadJob ) return ; //not the right job, so let's ignore it QString timestampString = ( ( KIO::StoredTransferJob* ) job )->data(); debug() << "Magnatune timestamp: " << timestampString; bool ok; qulonglong magnatuneTimestamp = timestampString.toULongLong( &ok ); MagnatuneConfig config; qulonglong localTimestamp = config.lastUpdateTimestamp(); debug() << "Last update timestamp: " << QString::number( localTimestamp ); if ( ok && magnatuneTimestamp > localTimestamp ) { m_magnatuneTimestamp = magnatuneTimestamp; updateButtonClicked(); } } void MagnatuneStore::moodyTracksReady( Meta::TrackList tracks ) { DEBUG_BLOCK The::playlistController()->insertOptioned( tracks, Playlist::Replace ); } QString MagnatuneStore::messages() { QString text = i18n( "The Magnatune.com service accepts the following messages: \n\n\taddMoodyTracks mood count: Adds a number of random tracks with the specified mood to the playlist. The mood argument must have spaces escaped with %%20" ); return text; } QString MagnatuneStore::sendMessage( const QString & message ) { QStringList args = message.split( ' ', QString::SkipEmptyParts ); if ( args.size() < 1 ) { return i18n( "ERROR: No arguments supplied" ); } if ( args[0] == "addMoodyTracks" ) { if ( args.size() != 3 ) { return i18n( "ERROR: Wrong number of arguments for addMoodyTracks" ); } QString mood = args[1]; mood = mood.replace( "%20", " " ); bool ok; int count = args[2].toInt( &ok ); if ( !ok ) return i18n( "ERROR: Parse error for argument 2 ( count )" ); addMoodyTracksToPlaylist( mood, count ); return i18n( "ok" ); } return i18n( "ERROR: Unknown argument." ); } void MagnatuneStore::showFavoritesPage() { DEBUG_BLOCK m_magnatuneInfoParser->getFavoritesPage(); } void MagnatuneStore::showHomePage() { DEBUG_BLOCK m_magnatuneInfoParser->getFrontPage(); } void MagnatuneStore::showRecommendationsPage() { DEBUG_BLOCK m_magnatuneInfoParser->getRecommendationsPage(); } -void MagnatuneStore::download( const QString &sku ) +void MagnatuneStore::downloadSku( const QString &sku ) { DEBUG_BLOCK debug() << "sku: " << sku; MagnatuneDatabaseWorker * databaseWorker = new MagnatuneDatabaseWorker(); databaseWorker->fetchAlbumBySku( sku, m_registry ); - connect( databaseWorker, SIGNAL(gotAlbumBySku(Meta::MagnatuneAlbum*)), this, SLOT(download(Meta::MagnatuneAlbum*)) ); + connect( databaseWorker, &MagnatuneDatabaseWorker::gotAlbumBySku, this, &MagnatuneStore::downloadAlbum ); ThreadWeaver::Queue::instance()->enqueue( QSharedPointer(databaseWorker) ); } void MagnatuneStore::addToFavorites( const QString &sku ) { DEBUG_BLOCK MagnatuneConfig config; if( !config.isMember() ) return; QString url = "http://%1:%2@%3.magnatune.com/member/favorites?action=add_api&sku=%4"; url = url.arg( config.username(), config.password(), config.membershipPrefix(), sku ); debug() << "favorites url: " << url; m_favoritesJob = KIO::storedGet( QUrl( url ), KIO::Reload, KIO::HideProgressInfo ); - connect( m_favoritesJob, SIGNAL(result(KJob*)), SLOT(favoritesResult(KJob*)) ); + connect( m_favoritesJob, &KJob::result, this, &MagnatuneStore::favoritesResult ); } void MagnatuneStore::removeFromFavorites( const QString &sku ) { DEBUG_BLOCK MagnatuneConfig config; if( !config.isMember() ) return; QString url = "http://%1:%2@%3.magnatune.com/member/favorites?action=remove_api&sku=%4"; url = url.arg( config.username(), config.password(), config.membershipPrefix(), sku ); debug() << "favorites url: " << url; m_favoritesJob = KIO::storedGet( QUrl( url ), KIO::Reload, KIO::HideProgressInfo ); - connect( m_favoritesJob, SIGNAL(result(KJob*)), SLOT(favoritesResult(KJob*)) ); + connect( m_favoritesJob, &KJob::result, this, &MagnatuneStore::favoritesResult ); } void MagnatuneStore::favoritesResult( KJob* addToFavoritesJob ) { if( addToFavoritesJob != m_favoritesJob ) return; QString result = m_favoritesJob->data(); Amarok::Components::logger()->longMessage( result ); //show the favorites page showFavoritesPage(); } void MagnatuneStore::showSignupDialog() { if ( m_signupInfoWidget== 0 ) { m_signupInfoWidget = new QDialog; Ui::SignupDialog ui; ui.setupUi( m_signupInfoWidget ); } m_signupInfoWidget->show(); } diff --git a/src/services/magnatune/MagnatuneStore.h b/src/services/magnatune/MagnatuneStore.h index a79404f9dc..34f67c4ee5 100644 --- a/src/services/magnatune/MagnatuneStore.h +++ b/src/services/magnatune/MagnatuneStore.h @@ -1,244 +1,240 @@ /**************************************************************************************** * Copyright (c) 2006,2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef AMAROKMAGNATUNESTORE_H #define AMAROKMAGNATUNESTORE_H #include "core/support/Amarok.h" #include "MagnatuneDownloadHandler.h" #include "MagnatuneRedownloadHandler.h" #include "MagnatuneXmlParser.h" #include "MagnatuneDatabaseHandler.h" #include "MagnatuneSqlCollection.h" #include "../ServiceBase.h" -#include -#include +#include #include #include -#include #include -#include class MagnatuneInfoParser; class MagnatuneNeedUpdateWidget; class MagnatuneServiceFactory : public ServiceFactory { Q_PLUGIN_METADATA(IID AmarokPluginFactory_iid FILE "amarok_service_magnatunestore.json") Q_INTERFACES(Plugins::PluginFactory) Q_OBJECT public: MagnatuneServiceFactory(); virtual ~MagnatuneServiceFactory() {} virtual void init(); virtual QString name(); virtual KConfigGroup config(); virtual bool possiblyContainsTrack( const QUrl &url ) const { return url.url().contains( "magnatune.com", Qt::CaseInsensitive ); } }; /** Amarok browser that displays all the music available at magnatune.com and makes it available for previewing and purchasing. Implemented as a singleton @author Nikolaj Hald Nielsen */ class MagnatuneStore: public ServiceBase { Q_OBJECT public: /** * Constructor */ MagnatuneStore( MagnatuneServiceFactory* parent, const char *name ); /** * Destructor */ ~MagnatuneStore(); void setMembership( int type, const QString &username, const QString &password ); /** * OGG, MP3 or LOFI */ void setStreamType( int ); /** * Do not do expensive initializations before we are actually shown */ void polish(); // bool updateContextView(); virtual Collections::Collection * collection() { return m_collection; } virtual QString messages(); virtual QString sendMessage( const QString &message ); public Q_SLOTS: /** * Slot for catching cancelled list downloads */ void listDownloadCancelled(); - void download( Meta::MagnatuneTrack * track ); + void downloadTrack( Meta::MagnatuneTrack * track ); - void download( Meta::MagnatuneAlbum * album ); + void downloadAlbum( Meta::MagnatuneAlbum * album ); void showFavoritesPage(); void showHomePage(); void showRecommendationsPage(); void addToFavorites( const QString &sku ); void removeFromFavorites( const QString &sku ); private Q_SLOTS: /** * Slot called when the download album button is clicked. Starts a download */ void download(); - void download( const QString &sku ); + void downloadSku( const QString &sku ); /** * Slot for recieving notification that the update button has been clicked. */ void updateButtonClicked(); /** * Slot for recieving notification when the Magnatune xml file has been downloaded. * Triggers a parse of the file to get the info added to the databse * @param downLoadJob The calling download Job */ void listDownloadComplete( KJob* downLoadJob ); /** * Slot called when the parsing of the Magnatuin xml file is completed. * Triggers an update of the list view and the genre combo box */ void doneParsing(); /** * Starts the process of redownloading a previously bought album */ void processRedownload(); /** * Slot for recieving notifications of completed download operations * @param success Was the operation a success? */ void downloadCompleted( bool success ); /** * Adds all tracks with a common mood to the playlist * @param mood The mood of the tracks to add */ void addMoodyTracksToPlaylist( const QString &mood, int count ); /** * Checks if download button should be enabled - * @param selection the new selection - * @param deseleted items that were previously selected but have been deselected + * @param selectedItem the new selected item */ void itemSelected( CollectionTreeItem * selectedItem ); void moodMapReady( QMap map ); void moodyTracksReady( Meta::TrackList tracks ); void timestampDownloadComplete( KJob * job ); void favoritesResult( KJob* addToFavoritesJob ); private: /** * Helper function that initializes the button panel below the list view */ void initBottomPanel(); /** * Helper function that initializes the genre selection panel above the list view */ void initTopPanel(); /** * Starts downloading an updated track list xml file from * http://magnatune.com/info/album_info.xml * @return Currently always returns true */ bool updateMagnatuneList(); void checkForUpdates(); /** * Adds a magnatune preview track to the playlist. * @param item The track to add */ //void addTrackToPlaylist ( Meta::MagnatuneTrack *item ); void showSignupDialog(); static MagnatuneStore *s_instance; QString m_currentInfoUrl; MagnatuneDownloadHandler *m_downloadHandler; MagnatuneRedownloadHandler *m_redownloadHandler; QPushButton *m_downloadAlbumButton; MagnatuneNeedUpdateWidget *m_needUpdateWidget; QAction * m_updateAction; - bool m_downloadInProgress; + bool m_downloadInProgress; Meta::MagnatuneAlbum * m_currentAlbum; KIO::FileCopyJob * m_listDownloadJob; KIO::StoredTransferJob* m_updateTimestampDownloadJob; KIO::StoredTransferJob* m_favoritesJob; Collections::MagnatuneSqlCollection * m_collection; QString m_tempFileName; bool m_isMember; int m_membershipType; QString m_username; QString m_password; int m_streamType; qulonglong m_magnatuneTimestamp; ServiceSqlRegistry * m_registry; MagnatuneInfoParser * m_magnatuneInfoParser; QDialog *m_signupInfoWidget; }; #endif diff --git a/src/services/magnatune/MagnatuneUrlRunner.cpp b/src/services/magnatune/MagnatuneUrlRunner.cpp index f0afc7c238..4c4e219c92 100644 --- a/src/services/magnatune/MagnatuneUrlRunner.cpp +++ b/src/services/magnatune/MagnatuneUrlRunner.cpp @@ -1,92 +1,92 @@ /**************************************************************************************** * Copyright (c) 2009 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneUrlRunner.h" -#include +#include MagnatuneUrlRunner::MagnatuneUrlRunner() : QObject() , AmarokUrlRunnerBase() { } MagnatuneUrlRunner::~MagnatuneUrlRunner() { } QString MagnatuneUrlRunner::command() const { return "service-magnatune"; } QString MagnatuneUrlRunner::prettyCommand() const { return i18nc( "A type of command that triggers an action in the integrated Magnatune.com service", "Magnatune" ); } QIcon MagnatuneUrlRunner::icon() const { return QIcon::fromTheme( "view-services-magnatune-amarok" ); } bool MagnatuneUrlRunner::run( AmarokUrl url ) { DEBUG_BLOCK if ( !url.isNull() ) { QString command = url.args().value( "command" ); if( command == "show_favorites" ) { emit( showFavorites() ); } else if ( command == "show_home" ) { emit( showHome() ); } else if ( command == "show_recommendations" ) { emit( showRecommendations() ); } else if ( command == "download" || command == "purchase" || command == "buy" ) { //allow some aliases for this command as the context might make one of //them more appropriate. In any case, non and stream members will be given the //purchase dialog and will have to pay, download members will get the //free download if ( url.args().keys().contains( "sku" ) ) { QString sku = url.args().value( "sku" ); emit( buyOrDownload( sku ) ); } } else if ( command == "remove_favorite" ) { if ( url.args().keys().contains( "sku" ) ) { QString sku = url.args().value( "sku" ); debug() << "remove from favorites sku: " << sku; emit( removeFromFavorites( sku ) ); } } } return true; } diff --git a/src/services/magnatune/MagnatuneXmlParser.cpp b/src/services/magnatune/MagnatuneXmlParser.cpp index 3226bd3c0b..2b3a956b10 100644 --- a/src/services/magnatune/MagnatuneXmlParser.cpp +++ b/src/services/magnatune/MagnatuneXmlParser.cpp @@ -1,433 +1,433 @@ /**************************************************************************************** * Copyright (c) 2006,2007 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "MagnatuneXmlParser.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include "core/support/Components.h" #include "core/interfaces/Logger.h" #include -#include +#include #include #include using namespace Meta; MagnatuneXmlParser::MagnatuneXmlParser( const QString &filename ) : QObject() , ThreadWeaver::Job() { m_sFileName = filename; - connect( this, SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(completeJob()) ); + connect( this, &MagnatuneXmlParser::done, this, &MagnatuneXmlParser::completeJob ); } MagnatuneXmlParser::~MagnatuneXmlParser() { QFile(m_sFileName).remove(); qDeleteAll(m_currentAlbumTracksList); } void MagnatuneXmlParser::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) { Q_UNUSED(self); Q_UNUSED(thread); readConfigFile( m_sFileName ); } void MagnatuneXmlParser::defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { Q_EMIT started(self); ThreadWeaver::Job::defaultBegin(self, thread); } void MagnatuneXmlParser::defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { ThreadWeaver::Job::defaultEnd(self, thread); if (!self->success()) { Q_EMIT failed(self); } Q_EMIT done(self); } void MagnatuneXmlParser::completeJob( ) { Amarok::Components::logger()->longMessage( i18ncp( "First part of: Magnatune.com database update complete. Database contains 3 tracks on 4 albums from 5 artists.", "Magnatune.com database update complete. Database contains 1 track on ", "Magnatune.com database update complete. Database contains %1 tracks on ", m_nNumberOfTracks) + i18ncp( "Middle part of: Magnatune.com database update complete. Database contains 3 tracks on 4 albums from 5 artists.", "1 album from ", "%1 albums from ", m_nNumberOfAlbums) + i18ncp( "Last part of: Magnatune.com database update complete. Database contains 3 tracks on 4 albums from 5 artists.", "1 artist.", "%1 artists.", m_nNumberOfArtists ) , Amarok::Logger::Information ); emit doneParsing(); deleteLater(); } void MagnatuneXmlParser::readConfigFile( const QString &filename ) { DEBUG_BLOCK m_nNumberOfTracks = 0; m_nNumberOfAlbums = 0; m_nNumberOfArtists = 0; QDomDocument doc( "config" ); if ( !QFile::exists( filename ) ) { debug() << "Magnatune xml file does not exist"; return; } QIODevice *file = KFilterDev::deviceForFile( filename, "application/x-bzip2", true ); if ( !file || !file->open( QIODevice::ReadOnly ) ) { debug() << "MagnatuneXmlParser::readConfigFile error reading file"; return ; } if ( !doc.setContent( file ) ) { debug() << "MagnatuneXmlParser::readConfigFile error parsing file"; file->close(); return ; } file->close(); delete file; m_dbHandler->destroyDatabase(); m_dbHandler->createDatabase(); //run through all the elements QDomElement docElem = doc.documentElement(); m_dbHandler->begin(); //start transaction (MAJOR speedup!!) parseElement( docElem ); m_dbHandler->commit(); //complete transaction return ; } void MagnatuneXmlParser::parseElement( const QDomElement &e ) { QString sElementName = e.tagName(); sElementName == "Album" ? parseAlbum( e ) : parseChildren( e ); } void MagnatuneXmlParser::parseChildren( const QDomElement &e ) { QDomNode n = e.firstChild(); while ( !n.isNull() ) { if ( n.isElement() ) parseElement( n.toElement() ); n = n.nextSibling(); } } void MagnatuneXmlParser::parseAlbum( const QDomElement &e ) { //DEBUG_BLOCK QString sElementName; QString name; QString albumCode; QStringList magnatuneGenres; int launchYear = 0; QString coverUrl; QString description; QString artistName; QString artistDescription; - QString artistPhotoUrl; + QUrl artistPhotoUrl; QString mp3Genre; - QString artistPageUrl; + QUrl artistPageUrl; QDomNode n = e.firstChild(); QDomElement childElement; while ( !n.isNull() ) { if ( n.isElement() ) { childElement = n.toElement(); QString sElementName = childElement.tagName(); if ( sElementName == "albumname" ) - //printf(("|--+" + childElement.text() + "\n").toAscii()); + //printf(("|--+" + childElement.text() + "\n").toLatin1()); //m_currentAlbumItem = new MagnatuneListViewAlbumItem( m_currentArtistItem); name = childElement.text(); else if ( sElementName == "albumsku" ) albumCode = childElement.text(); else if ( sElementName == "magnatunegenres" ) magnatuneGenres = childElement.text().split(',', QString::SkipEmptyParts); else if ( sElementName == "launchdate" ) { QString dateString = childElement.text(); QDate date = QDate::fromString( dateString, Qt::ISODate ); launchYear = date.year(); } else if ( sElementName == "cover_small" ) coverUrl = childElement.text(); else if ( sElementName == "artist" ) artistName = childElement.text(); else if ( sElementName == "artistdesc" ) artistDescription = childElement.text(); else if ( sElementName == "artistphoto" ) - artistPhotoUrl = childElement.text() ; + artistPhotoUrl = QUrl( childElement.text() ); else if ( sElementName == "mp3genre" ) mp3Genre = childElement.text(); else if ( sElementName == "home" ) - artistPageUrl = childElement.text(); + artistPageUrl = QUrl( childElement.text() ); else if ( sElementName == "Track" ) parseTrack( childElement ); else if ( sElementName == "album_notes" ) description = childElement.text(); } n = n.nextSibling(); } m_pCurrentAlbum.reset(new MagnatuneAlbum( name )); m_pCurrentAlbum->setAlbumCode( albumCode); m_pCurrentAlbum->setLaunchYear( launchYear ); m_pCurrentAlbum->setCoverUrl( coverUrl ); m_pCurrentAlbum->setDescription( description ); // now we should have gathered all info about current album (and artist)... //Time to add stuff to the database //check if artist already exists, if not, create him/her/them/it int artistId; if ( artistNameIdMap.contains( artistName ) ) { artistId = artistNameIdMap.value( artistName ); } else { //does not exist, lets create it... m_pCurrentArtist.reset(new MagnatuneArtist( artistName )); m_pCurrentArtist->setDescription( artistDescription ); m_pCurrentArtist->setPhotoUrl( artistPhotoUrl ); m_pCurrentArtist->setMagnatuneUrl( artistPageUrl ); //this is tricky in postgresql, returns id as 0 (we are within a transaction, might be the cause...) artistId = m_dbHandler->insertArtist( m_pCurrentArtist.data() ); m_nNumberOfArtists++; if ( artistId == 0 ) { artistId = m_dbHandler->getArtistIdByExactName( m_pCurrentArtist->name() ); } m_pCurrentArtist->setId( artistId ); artistNameIdMap.insert( m_pCurrentArtist->name() , artistId ); } m_pCurrentAlbum->setArtistId( artistId ); int albumId = m_dbHandler->insertAlbum( m_pCurrentAlbum.data() ); if ( albumId == 0 ) // again, postgres can play tricks on us... { albumId = m_dbHandler->getAlbumIdByAlbumCode( m_pCurrentAlbum->albumCode() ); } m_pCurrentAlbum->setId( albumId ); m_nNumberOfAlbums++; QList::iterator it; for ( it = m_currentAlbumTracksList.begin(); it != m_currentAlbumTracksList.end(); ++it ) { ( *it )->setAlbumId( m_pCurrentAlbum->id() ); ( *it )->setArtistId( artistId ); int trackId = m_dbHandler->insertTrack( ( *it ) ); m_dbHandler->insertMoods( trackId, ( *it )->moods() ); m_nNumberOfTracks++; } // handle genres foreach( const QString &genreName, magnatuneGenres ) { //debug() << "inserting genre with album_id = " << albumId << " and name = " << genreName; ServiceGenre currentGenre( genreName ); currentGenre.setAlbumId( albumId ); m_dbHandler->insertGenre( ¤tGenre ); } magnatuneGenres.clear(); qDeleteAll(m_currentAlbumTracksList); m_currentAlbumTracksList.clear(); } void MagnatuneXmlParser::parseTrack( const QDomElement &e ) { //DEBUG_BLOCK m_currentTrackMoodList.clear(); QString trackName; QString trackNumber; QString streamingUrl; QString sElementName; QDomElement childElement; MagnatuneTrack * pCurrentTrack = new MagnatuneTrack( QString() ); QDomNode n = e.firstChild(); while ( !n.isNull() ) { if ( n.isElement() ) { childElement = n.toElement(); QString sElementName = childElement.tagName(); if ( sElementName == "trackname" ) { pCurrentTrack->setTitle( childElement.text() ); } else if ( sElementName == "url" ) { pCurrentTrack->setUidUrl( childElement.text() ); } else if ( sElementName == "oggurl" ) { pCurrentTrack->setOggUrl( childElement.text() ); } else if ( sElementName == "mp3lofi" ) { pCurrentTrack->setLofiUrl( childElement.text() ); } else if ( sElementName == "tracknum" ) { pCurrentTrack->setTrackNumber( childElement.text().toInt() ); } else if ( sElementName == "seconds" ) { pCurrentTrack->setLength( childElement.text().toInt() ); } else if ( sElementName == "moods" ) { parseMoods( childElement ); } } n = n.nextSibling(); } pCurrentTrack->setMoods( m_currentTrackMoodList ); m_currentAlbumTracksList.append( pCurrentTrack ); } void MagnatuneXmlParser::parseMoods( const QDomElement &e ) { //DEBUG_BLOCK QDomNode n = e.firstChild(); QDomElement childElement; while ( !n.isNull() ) { if ( n.isElement() ) { childElement = n.toElement(); QString sElementName = childElement.tagName(); if ( sElementName == "mood" ) { m_currentTrackMoodList.append( childElement.text() ); } else { //error, should not be here.... } } n = n.nextSibling(); } } void MagnatuneXmlParser::setDbHandler(MagnatuneDatabaseHandler * dbHandler) { m_dbHandler = dbHandler; } diff --git a/src/services/magnatune/amarok_service_magnatunestore.desktop b/src/services/magnatune/amarok_service_magnatunestore.desktop index d46c0fa1d4..7d159da20a 100644 --- a/src/services/magnatune/amarok_service_magnatunestore.desktop +++ b/src/services/magnatune/amarok_service_magnatunestore.desktop @@ -1,133 +1,131 @@ [Desktop Entry] Type=Service -ServiceTypes=KPluginInfo Icon=view-services-magnatune-amarok Name=Magnatune Store Name[bg]=Магазин Magnatune Name[bs]=Magnatune prodavnica Name[ca]=Botiga Magnatune Name[ca@valencia]=Botiga Magnatune Name[cs]=Obchod Magnatune Name[csb]=Króm Magnatune Name[da]=Magnatune-butikken Name[de]=Magnatune-Online-Shop Name[el]=Magnatune Store Name[en_GB]=Magnatune Store Name[eo]=Magnatune Vendejo Name[es]=Tienda Magnatune Name[et]=Magnatune pood Name[eu]=Magnatune denda Name[fi]=Magnatune-verkkokauppa Name[fr]=Site de vente en ligne « Magnatune » Name[ga]=Siopa Magnatune Name[gl]=Tenda de Magnatune Name[he]=חנות Magnatune Name[hne]=मेगनाट्यून स्टोर Name[hu]=Magnatune áruház Name[id]=Magnatune Store Name[is]=Magnatune verslun Name[it]=Negozio Magnatune Name[ja]=Magnatune ストア Name[km]=ការ​ទុក Magnatune Name[ko]=Magnatune 상점 Name[ku]=Marketa Magnatune Name[lt]=Magnatune parduotuvė Name[lv]=Magnatune veikals Name[nb]=Magnatune-butikken Name[nds]=Magnatune Name[nl]=Magnatune-winkel Name[nn]=Magnatune-butikk Name[pa]=Magnatune ਸਟੋਰ Name[pl]=Sklep Magnatune Name[pt]=Loja Magnatune Name[pt_BR]=Loja Magnatune Name[ro]=Magazin Magnatune Name[ru]=Магазин Magnatune Name[sk]=Obchod Magnatune Name[sl]=Trgovina Magnatune Name[sq]=Dyqani i Mangatune Name[sr]=Магнатјунова продавница Name[sr@ijekavian]=Магнатјунова продавница Name[sr@ijekavianlatin]=Magnatuneova prodavnica Name[sr@latin]=Magnatuneova prodavnica Name[sv]=Magnatune butik Name[th]=ร้านค้า Magnatune Name[tr]=Magnatune Mağazası Name[ug]=Magnatune دۇكىنى Name[uk]=Магазин Magnatune Name[wa]=Botike Magnatune Name[x-test]=xxMagnatune Storexx Name[zh_CN]=Magnatune 商店 Name[zh_TW]=Magnatune 商店 Comment=Preview and buy music from the non-evil Magnatune record label Comment[bg]=Обзор и купуване на музика от Magnatune Comment[bs]=Pregledajte i kupujte muziku od nezlobivog izdavača Magnatjuna Comment[ca]=Vista prèvia i compra de música del segell de música Magnatune (non-evil) Comment[ca@valencia]=Vista prèvia i compra de música del segell de música Magnatune (non-evil) Comment[cs]=Poslechnout ukázku a koupit hudbu z Magnatune Comment[da]=Prøvelyt og køb musik fra det ikke-onde pladeselskab Magnatune Comment[de]=Musik des Musiklabels Magnatune probehören und herunterladen Comment[el]=Ακούστε και αγοράστε μουσική από τη μη-διαβολική δισκογραφική εταιρεία Magnatune Comment[en_GB]=Preview and buy music from the non-evil Magnatune record label Comment[es]=Previsualizar y comprar música desde la discográfica no demoníaca Magnatune Comment[et]=Kena firma Magnatune pakutava muusika eelvaatlus ja ostmine Comment[eu]=Aurreikusi eta erosi musika Magnatune disko dendatik Comment[fi]=Kuuntele ja osta musiikkia Magnatunesta (ei-niin-paha verkkokauppa :) Comment[fr]=Écouter et acheter de la musique du label d'enregistrement équitable « Magnatune » Comment[ga]=Brabhsáil agus ceannaigh ceol ó lipéad Magnatune nach bhfuil olc Comment[gl]=Previsualice e merque música do selo discográfico Magnatune Comment[hu]=Hallgasson bele zenébe és vásároljon zenét a gonoszságtól mentes Magnatune kiadótól Comment[id]=Pratilik dan beli musik dari non-evil Magnatune record label Comment[is]=Hlusta á og kaupa tónlist af vinalegu Magnatune útgáfunni Comment[it]=Ascolta in anteprima e acquista musica dalla non-malefica etichetta discografica Magnatune Comment[ja]=友好的な Magnatune レコードレーベルから音楽を試聴して購入できます Comment[km]=មើល​ជា​មុន និង​ទិញ​តន្ត្រី​ពី​ស្លាក​កំណត់​ត្រា​ Magnatune ដែល​មិន​ខូច Comment[ko]=Magnatune에서 만드는 음악을 미리 듣고 구입하기 Comment[ku]=Pêşdîtina û kirîna muzîkê ji Magnatune Comment[lt]=Perklausyti ir nusipirkti muziką iš įrašų kompanijos Magnatune, kurios šūkis „blogiui – ne“ Comment[lv]=Priekšskatiet un pērciet mūziku no Magnatune ierakstu veikala Comment[nb]=Prøvehør og kjøp musikk fra ikke-onde Magnatune musikkutgiver Comment[nds]=Musik vun de "nich böse" Firma Magnatune proovhören un köpen Comment[nl]=Luister naar en koop muziek van het niet-demonische Magnatude-platenlabel Comment[nn]=Høyr på og kjøp musikk frå det ikkjevonde Magnatune-plateselskapet Comment[pl]=Przeglądaj i kupuj muzykę z nie-diabelnego wydawnictwa Magnatune Comment[pt]=Antever e comprar músicas da editor de discos não-maléfica Magnatune Comment[pt_BR]=Ouça e compre músicas da gravadora boazinha Magnatune Comment[ro]=Previzualizează și cumpără muzică de la casa de discuri ne-diabolică Magnatune Comment[ru]=Просмотр и покупка музыки в звукозаписывающем лейбле Magnatune Comment[sk]=Vypočuť a kúpiť hudbu z Magnatune Comment[sl]=Poslušajte in kupite glasbo od prijazne založniške hiše Magnatune Comment[sr]=Прегледајте и купујте музику од незлобивог издавача Магнатјуна Comment[sr@ijekavian]=Прегледајте и купујте музику од незлобивог издавача Магнатјуна Comment[sr@ijekavianlatin]=Pregledajte i kupujte muziku od nezlobivog izdavača Magnatunea Comment[sr@latin]=Pregledajte i kupujte muziku od nezlobivog izdavača Magnatunea Comment[sv]=Förhandsgranska och köp musik från det icke-onda skivbolaget Magnatune Comment[th]=ฟังตัวอย่างและซื้อเพลงจากบริการ non-evil Magnatune record Comment[tr]="Non-evil" Magnatune kayıt etiketinden müzik önizleyin ve satın alın Comment[uk]=Прослухайте і купіть музику з ненав’язливої агенції звукозапису Magnatune Comment[wa]=Prévey eyet atchter del muzike do nén må label d' eredjistraedje Magnatune Comment[x-test]=xxPreview and buy music from the non-evil Magnatune record labelxx Comment[zh_CN]=预览并购买来自不作恶的 Magnatune 唱片公司的音乐 Comment[zh_TW]=從標為 non-evil 的 Magnatune 唱片標籤的音樂中,預聽並購買 ServiceTypes=Amarok/Plugin -X-KDE-Library=amarok_service_magnatunestore X-KDE-Amarok-authors=Nikolaj Hald Nielsen X-KDE-Amarok-email=nhnFreespirit@gmail.com X-KDE-Amarok-framework-version=73 X-KDE-Amarok-name=MagnatuneStore X-KDE-Amarok-rank=100 X-KDE-Amarok-version=1 X-KDE-PluginInfo-Author=Nikolaj Hald Nielsen X-KDE-PluginInfo-Email=nhnFreespirit@gmail.com X-KDE-PluginInfo-Version=2.0 X-KDE-PluginInfo-Category=Service X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-KDE-Library=amarok_service_magnatunestore X-KDE-PluginInfo-Name=amarok_service_magnatunestore diff --git a/src/services/mp3tunes/CMakeLists.txt b/src/services/mp3tunes/CMakeLists.txt index 143b46731a..eb260e7750 100644 --- a/src/services/mp3tunes/CMakeLists.txt +++ b/src/services/mp3tunes/CMakeLists.txt @@ -1,142 +1,139 @@ -if(OPENSSL_FOUND OR LIBGCRYPT_FOUND) -if(LIBXML2_FOUND AND CURL_FOUND) - if(LOUDMOUTH_FOUND AND GLIB2_FOUND AND GOBJECT_FOUND AND QT5_GLIB_SUPPORT) - include_directories( - ../ - ../../ - ../../core-impl/collections - ../../statusbar - ./libmp3tunes - ./harmonydaemon - ${LIBXML2_INCLUDE_DIR} - ${CURL_INCLUDE_DIRS} - ${CMAKE_CURRENT_BINARY_DIR}/../../.. - - - ) - ########### next target ############### - - include_directories( - ./harmonydaemon - ${GLIB2_INCLUDE_DIR} - ${GOBJECT_INCLUDE_DIR} - ${LOUDMOUTH_INCLUDE_DIRS} - ) - if(LIBGCRYPT_FOUND) - add_definitions(-DHAVE_LIBGCRYPT) - else() - include_directories(${OPENSSL_INCLUDE_DIR}) - add_definitions(-DHAVE_OPENSSL) - endif() - - set(amarok_service_mp3tunes_harmony_PART_SRCS - harmonydaemon/Mp3tunesHarmonyDownload.cpp - harmonydaemon/Mp3tunesHarmonyDaemon.cpp - harmonydaemon/Mp3tunesHarmonyClient.cpp - harmonydaemon/AmarokClient.cpp - harmonydaemon/main.cpp - libmp3tunes/md5.c - libmp3tunes/locker.c - libmp3tunes/harmony.c - ) - qt5_add_dbus_adaptor(amarok_service_mp3tunes_harmony_PART_SRCS - harmonydaemon/org.kde.amarok.Mp3tunesHarmonyDaemon.xml - harmonydaemon/Mp3tunesHarmonyDaemon.h - Mp3tunesHarmonyDaemon - ) - - add_executable(amarokmp3tunesharmonydaemon - ${amarok_service_mp3tunes_harmony_PART_SRCS} - ) - ecm_mark_nongui_executable(amarokmp3tunesharmonydaemon) - - target_link_libraries(amarokmp3tunesharmonydaemon - amarokcore - amaroklib - KF5::KDELibs4Support - ${GLIB2_LIBRARIES} - ${GOBJECT_LIBRARIES} - ${LOUDMOUTH_LIBRARIES} - ${LIBXML2_LIBRARIES} - ${CURL_LIBRARIES} - ) - - if(LIBGCRYPT_FOUND) - target_link_libraries(amarokmp3tunesharmonydaemon ${LIBGCRYPT_LIBS}) - else() - target_link_libraries(amarokmp3tunesharmonydaemon crypto ${OPENSSL_LIBRARIES}) - endif() - - install(TARGETS amarokmp3tunesharmonydaemon ${INSTALL_TARGETS_DEFAULT_ARGS} ) - ########### next target ############### - - set(amarok_service_mp3tunes_PART_SRCS - Mp3tunesService.cpp - Mp3tunesServiceCollection.cpp - Mp3tunesServiceCollectionLocation.cpp - Mp3tunesServiceQueryMaker.cpp - Mp3tunesMeta.cpp - Mp3tunesConfig.cpp - Mp3tunesLockerMeta.cpp - Mp3tunesLocker.cpp - Mp3tunesWorkers.cpp - Mp3tunesHarmonyHandler.cpp - - libmp3tunes/locker.c - libmp3tunes/md5.c - ) - - qt5_add_dbus_adaptor(amarok_service_mp3tunes_PART_SRCS - org.kde.amarok.Mp3tunesHarmonyHandler.xml - Mp3tunesHarmonyHandler.h - Mp3tunesHarmonyHandler - ) - - add_library(amarok_service_mp3tunes MODULE ${amarok_service_mp3tunes_PART_SRCS}) - target_link_libraries(amarok_service_mp3tunes - amarokcore - amaroklib - KF5::KDELibs4Support - - KF5::ThreadWeaver - ${LIBXML2_LIBRARIES} - ${CURL_LIBRARIES} - Qt5::Network - ) - - if(LIBGCRYPT_FOUND) - target_link_libraries(amarok_service_mp3tunes ${LIBGCRYPT_LIBS}) - else() - #${OPENSSL_LIBRARIES} returns -lssl, not -lcrypto. we only need -lcrypto. - target_link_libraries(amarok_service_mp3tunes crypto ${OPENSSL_LIBRARIES}) - endif() - - - install(TARGETS amarok_service_mp3tunes DESTINATION ${PLUGIN_INSTALL_DIR} ) - - ########### next target ############### - - set(kcm_amarok_service_mp3tunes_PART_SRCSS - Mp3tunesSettingsModule.cpp - Mp3tunesConfig.cpp - ) - - ki18n_wrap_ui( kcm_amarok_service_mp3tunes_PART_SRCSS Mp3tunesConfigWidget.ui ) - - - add_library(kcm_amarok_service_mp3tunes MODULE ${kcm_amarok_service_mp3tunes_PART_SRCSS} ) - - - target_link_libraries( kcm_amarok_service_mp3tunes Qt5::Network ) - - install(TARGETS kcm_amarok_service_mp3tunes DESTINATION ${PLUGIN_INSTALL_DIR}) - - - ########### install files ############### - - install( FILES amarok_service_mp3tunes.desktop DESTINATION ${SERVICES_INSTALL_DIR}) - install( FILES amarok_service_mp3tunes_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}) - endif() +include_directories( + ../ + ../../ + ../../core-impl/collections + ../../statusbar + ./libmp3tunes + ./harmonydaemon + ${LIBXML2_INCLUDE_DIR} + ${CURL_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR}/../../.. + + +) +########### next target ############### + +include_directories( + ./harmonydaemon + ${GLIB2_INCLUDE_DIR} + ${GOBJECT_INCLUDE_DIR} + ${LOUDMOUTH_INCLUDE_DIRS} +) + +if(LIBGCRYPT_FOUND) + add_definitions(-DHAVE_LIBGCRYPT) +else() + include_directories(${OPENSSL_INCLUDE_DIR}) + add_definitions(-DHAVE_OPENSSL) endif() + +set(amarok_service_mp3tunes_harmony_PART_SRCS + harmonydaemon/Mp3tunesHarmonyDownload.cpp + harmonydaemon/Mp3tunesHarmonyDaemon.cpp + harmonydaemon/Mp3tunesHarmonyClient.cpp + harmonydaemon/AmarokClient.cpp + harmonydaemon/main.cpp + libmp3tunes/md5.c + libmp3tunes/locker.c + libmp3tunes/harmony.c +) + +qt5_add_dbus_adaptor(amarok_service_mp3tunes_harmony_PART_SRCS + harmonydaemon/org.kde.amarok.Mp3tunesHarmonyDaemon.xml + harmonydaemon/Mp3tunesHarmonyDaemon.h + Mp3tunesHarmonyDaemon +) + +add_executable(amarokmp3tunesharmonydaemon + ${amarok_service_mp3tunes_harmony_PART_SRCS} +) + +ecm_mark_nongui_executable(amarokmp3tunesharmonydaemon) + +target_link_libraries(amarokmp3tunesharmonydaemon + amarokcore + amaroklib + ${GLIB2_LIBRARIES} + ${GOBJECT_LIBRARIES} + ${LOUDMOUTH_LIBRARIES} + ${LIBXML2_LIBRARIES} + ${CURL_LIBRARIES} +) + +if(LIBGCRYPT_FOUND) + target_link_libraries(amarokmp3tunesharmonydaemon ${LIBGCRYPT_LIBS}) +else() + target_link_libraries(amarokmp3tunesharmonydaemon crypto ${OPENSSL_LIBRARIES}) +endif() + +install(TARGETS amarokmp3tunesharmonydaemon ${INSTALL_TARGETS_DEFAULT_ARGS} ) + +########### next target ############### + +set(amarok_service_mp3tunes_PART_SRCS + Mp3tunesService.cpp + Mp3tunesServiceCollection.cpp + Mp3tunesServiceCollectionLocation.cpp + Mp3tunesServiceQueryMaker.cpp + Mp3tunesMeta.cpp + Mp3tunesConfig.cpp + Mp3tunesLockerMeta.cpp + Mp3tunesLocker.cpp + Mp3tunesWorkers.cpp + Mp3tunesHarmonyHandler.cpp + + libmp3tunes/locker.c + libmp3tunes/md5.c +) + +qt5_add_dbus_adaptor(amarok_service_mp3tunes_PART_SRCS + org.kde.amarok.Mp3tunesHarmonyHandler.xml + Mp3tunesHarmonyHandler.h + Mp3tunesHarmonyHandler +) + +add_library(amarok_service_mp3tunes MODULE ${amarok_service_mp3tunes_PART_SRCS}) + +target_link_libraries(amarok_service_mp3tunes + amarokcore + amaroklib + KF5::ConfigCore + KF5::ThreadWeaver + ${LIBXML2_LIBRARIES} + ${CURL_LIBRARIES} + Qt5::Network +) + +if(LIBGCRYPT_FOUND) + target_link_libraries(amarok_service_mp3tunes ${LIBGCRYPT_LIBS}) +else() + #${OPENSSL_LIBRARIES} returns -lssl, not -lcrypto. we only need -lcrypto. + target_link_libraries(amarok_service_mp3tunes crypto ${OPENSSL_LIBRARIES}) endif() + +install(TARGETS amarok_service_mp3tunes DESTINATION ${PLUGIN_INSTALL_DIR} ) + +kcoreaddons_desktop_to_json(amarok_service_mp3tunes amarok_service_mp3tunes.desktop SERVICE_TYPES ${CMAKE_SOURCE_DIR}/src/amarok-plugin.desktop) + +########### next target ############### + +set(kcm_amarok_service_mp3tunes_PART_SRCSS + Mp3tunesSettingsModule.cpp + Mp3tunesConfig.cpp +) + +ki18n_wrap_ui( kcm_amarok_service_mp3tunes_PART_SRCSS Mp3tunesConfigWidget.ui ) + +add_library(kcm_amarok_service_mp3tunes MODULE ${kcm_amarok_service_mp3tunes_PART_SRCSS} ) + +target_link_libraries( kcm_amarok_service_mp3tunes + amarokcore + Qt5::Network + KF5::ConfigWidgets +) + +install(TARGETS kcm_amarok_service_mp3tunes DESTINATION ${PLUGIN_INSTALL_DIR}) +install( FILES amarok_service_mp3tunes_config.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) + +kcoreaddons_desktop_to_json(kcm_amarok_service_mp3tunes amarok_service_mp3tunes_config.desktop SERVICE_TYPES kcmodule.desktop) diff --git a/src/services/mp3tunes/Mp3tunesConfig.cpp b/src/services/mp3tunes/Mp3tunesConfig.cpp index 753bfb20f9..0d5e47641e 100644 --- a/src/services/mp3tunes/Mp3tunesConfig.cpp +++ b/src/services/mp3tunes/Mp3tunesConfig.cpp @@ -1,177 +1,176 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * Copyright (c) 2008 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "Mp3tunesConfig.h" -#include -#include +#include "core/support/Amarok.h" + #include -#include #include Mp3tunesConfig::Mp3tunesConfig() { m_hasChanged = false; load(); } Mp3tunesConfig::~Mp3tunesConfig() { } void Mp3tunesConfig::load() { - kDebug( 14310 ) << "load"; - KConfigGroup config = KGlobal::config()->group( "Service_Mp3tunes" ); +// kDebug( 14310 ) << "load"; + KConfigGroup config = Amarok::config( "Service_Mp3tunes" ); m_email = config.readEntry( "email", QString() ); m_password = config.readEntry( "password", QString() ); m_identifier = config.readEntry( "identifier", QString() ); m_pin = config.readEntry( "pin", QString() ); m_harmonyEmail = config.readEntry( "harmonyEmail", QString() ); m_partnerToken = config.readEntry( "partnerToken", QString( "4895500420" ) ); m_harmonyEnabled = config.readEntry( "harmonyEnabled", false ); if( m_identifier.isEmpty() ) { foreach( const QNetworkInterface &iface, QNetworkInterface::allInterfaces() ) { QString addr = iface.hardwareAddress(); if( addr != "00:00:00:00:00:00" ) { addr.remove( ':' ); - kDebug( 14310 ) << "Using iface \"" << iface.name() << " addr: " << addr; +// kDebug( 14310 ) << "Using iface \"" << iface.name() << " addr: " << addr; setIdentifier( addr + m_partnerToken ); save(); break; } } } } void Mp3tunesConfig::save() { - kDebug( 14310 ) << "save"; +// kDebug( 14310 ) << "save"; if ( m_hasChanged ) { - KConfigGroup config = KGlobal::config()->group( "Service_Mp3tunes" ); + KConfigGroup config = Amarok::config( "Service_Mp3tunes" ); config.writeEntry( "email", m_email ); config.writeEntry( "password", m_password ); config.writeEntry( "identifier", m_identifier ); config.writeEntry( "harmonyEnabled", m_harmonyEnabled ); config.writeEntry( "partnerToken", m_partnerToken ); config.writeEntry( "harmonyEmail", m_harmonyEmail ); config.writeEntry( "pin", m_pin ); } } QString Mp3tunesConfig::email() { return m_email; } QString Mp3tunesConfig::password() { return m_password; } QString Mp3tunesConfig::partnerToken() { return m_partnerToken; } QString Mp3tunesConfig::identifier() { return m_identifier; } QString Mp3tunesConfig::pin() { return m_pin; } QString Mp3tunesConfig::harmonyEmail() { return m_harmonyEmail; } bool Mp3tunesConfig::harmonyEnabled() { return m_harmonyEnabled; } void Mp3tunesConfig::setHarmonyEnabled( bool enabled ) { - kDebug( 14310 ) << "set harmony"; +// kDebug( 14310 ) << "set harmony"; if ( enabled != m_harmonyEnabled ) { m_harmonyEnabled = enabled; m_hasChanged = true; } } void Mp3tunesConfig::setIdentifier( const QString &ident ) { - kDebug( 14310 ) << "set hwaddress"; +// kDebug( 14310 ) << "set hwaddress"; if ( ident != m_identifier ) { m_identifier = ident; m_hasChanged = true; } } void Mp3tunesConfig::setEmail( const QString &email ) { - kDebug( 14310 ) << "set email"; +// kDebug( 14310 ) << "set email"; if ( email != m_email ) { m_email = email; m_hasChanged = true; } } void Mp3tunesConfig::setPassword( const QString &password ) { - kDebug( 14310 ) << "set Password"; +// kDebug( 14310 ) << "set Password"; if( password != m_password ) { m_password = password; m_hasChanged = true; } } void Mp3tunesConfig::setPartnerToken( const QString &token ) { - kDebug( 14310 ) << "set token"; +// kDebug( 14310 ) << "set token"; if( token != m_partnerToken ) { m_partnerToken = token; m_hasChanged = true; } } void Mp3tunesConfig::setPin( const QString &pin ) { - kDebug( 14310 ) << "set pin"; +// kDebug( 14310 ) << "set pin"; if( pin != m_pin ) { m_pin = pin; m_hasChanged = true; } } void Mp3tunesConfig::setHarmonyEmail( const QString &harmonyEmail ) { - kDebug( 14310 ) << "set harmonyEmail"; +// kDebug( 14310 ) << "set harmonyEmail"; if( harmonyEmail != m_harmonyEmail ) { m_harmonyEmail = harmonyEmail; m_hasChanged = true; } } diff --git a/src/services/mp3tunes/Mp3tunesHarmonyHandler.cpp b/src/services/mp3tunes/Mp3tunesHarmonyHandler.cpp index c1ee1c935b..a6538144b2 100644 --- a/src/services/mp3tunes/Mp3tunesHarmonyHandler.cpp +++ b/src/services/mp3tunes/Mp3tunesHarmonyHandler.cpp @@ -1,256 +1,256 @@ /**************************************************************************************** * Copyright (c) 2008 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "Mp3tunesHarmonyHandler.h" #include "mp3tunesharmonyhandleradaptor.h" #include "App.h" #include "core/support/Debug.h" #include #include #include Mp3tunesHarmonyHandler::Mp3tunesHarmonyHandler( QString identifier, QString email, QString pin ) : QObject( qApp ) , m_daemon( 0 ) , m_identifier( identifier ) , m_email( email ) , m_pin( pin ) { new Mp3tunesHarmonyHandlerAdaptor( this ); QDBusConnection::sessionBus().registerObject("/Mp3tunesHarmonyHandler", this); debug() << "All aboard the DBUS!"; } Mp3tunesHarmonyHandler::~Mp3tunesHarmonyHandler() { stopDaemon(); if( m_daemon ) delete m_daemon; } bool Mp3tunesHarmonyHandler::startDaemon() { m_daemon = new AmarokProcess( this ); if( m_email.isEmpty() && m_pin.isEmpty() ) *m_daemon << "amarokmp3tunesharmonydaemon" << m_identifier; else if( !m_email.isEmpty() && !m_pin.isEmpty() ) *m_daemon << "amarokmp3tunesharmonydaemon" << m_identifier << m_email << m_pin; m_daemon->setOutputChannelMode( KProcess::OnlyStdoutChannel ); - connect( m_daemon, SIGNAL(finished(int)), SLOT(slotFinished()) ); - connect( m_daemon, SIGNAL(error(QProcess::ProcessError)), SLOT(slotError(QProcess::ProcessError)) ); + connect( m_daemon, QOverload::of( &QProcess::finished ), this, &Mp3tunesHarmonyHandler::slotFinished ); + connect( m_daemon, QOverload::of( &AmarokProcess::error ), this, &Mp3tunesHarmonyHandler::slotError ); m_daemon->start(); sleep(3); // sleep for 3 seconds to allow the process to start and register. return m_daemon->waitForStarted( -1 ); } void Mp3tunesHarmonyHandler::stopDaemon() { if( daemonRunning() ) m_daemon->close(); } void Mp3tunesHarmonyHandler::slotFinished( ) { m_daemon->deleteLater(); m_daemon = 0; } void Mp3tunesHarmonyHandler::slotError( QProcess::ProcessError error ) { if( error == QProcess::Crashed ) { //handleRestart(); } } bool Mp3tunesHarmonyHandler::daemonRunning() { if( !m_daemon ) return false; debug() << "Daemon process is running"; return true; } bool Mp3tunesHarmonyHandler::daemonConnected() { DEBUG_BLOCK if( !daemonRunning() ) return false; QString name = "org.kde.amarok.Mp3tunesHarmonyDaemon-" + QString::number( m_daemon->pid() ); debug() << "Making Dbus call about daemonConnected to: " << name; QDBusMessage m = QDBusMessage::createMethodCall( name, "/Mp3tunesHarmonyDaemon", "", "daemonConnected" ); QDBusMessage response = QDBusConnection::sessionBus().call( m ); if( response.type() == QDBusMessage::ErrorMessage ) { debug() << "Got ERROR response daemonConnected"; debug() << response.errorName() << ": " << response.errorMessage(); } QList args = response.arguments(); if( args.count() == 1) { if( args[0].toString() == "true" ) { debug() << "Daemon Connected"; return true; } else if( args[0].toString() == "false" ) { debug() << "Daemon Not Connected"; return false; } } debug() << "Unexpected DBUS return. " << args.count(); return false; } void Mp3tunesHarmonyHandler::makeConnection() { DEBUG_BLOCK if( !daemonRunning() ) return; QString name = "org.kde.amarok.Mp3tunesHarmonyDaemon-" + QString::number( m_daemon->pid() ); debug() << "Making Dbus call about makeConnection to: " << name; QDBusMessage m = QDBusMessage::createMethodCall( name, "/Mp3tunesHarmonyDaemon", "", "makeConnection" ); QDBusMessage response = QDBusConnection::sessionBus().call( m ); if( response.type() == QDBusMessage::ErrorMessage ) { debug() << "Got ERROR response makeConnection"; debug() << response.errorName() << ": " << response.errorMessage(); } } void Mp3tunesHarmonyHandler::breakConnection() { DEBUG_BLOCK if( !daemonRunning() ) return; QString name = "org.kde.amarok.Mp3tunesHarmonyDaemon-" + QString::number( m_daemon->pid() ); //QString name = "org.kde.amarok.Mp3tunesHarmonyDaemon"; debug() << "Making Dbus call about breakConnection to: " << name; QDBusMessage m = QDBusMessage::createMethodCall( name, "/Mp3tunesHarmonyDaemon", "", "breakConnection" ); QDBusMessage response = QDBusConnection::sessionBus().call( m ); if( response.type() == QDBusMessage::ErrorMessage ) { debug() << "Got ERROR response "; debug() << response.errorName() << ": " << response.errorMessage(); } } QString Mp3tunesHarmonyHandler::pin() { DEBUG_BLOCK if( !daemonRunning() ) return QString(); QString name = "org.kde.amarok.Mp3tunesHarmonyDaemon-" + QString::number( m_daemon->pid() ); //QString name = "org.kde.amarok.Mp3tunesHarmonyDaemon"; debug() << "Making Dbus call about pin to: " << name; QDBusMessage m = QDBusMessage::createMethodCall( name, "/Mp3tunesHarmonyDaemon", "", "pin" ); QDBusMessage response = QDBusConnection::sessionBus().call( m ); if( response.type() == QDBusMessage::ErrorMessage ) { debug() << "Got ERROR response pin"; debug() << response.errorName() << ": " << response.errorMessage(); } QList args = response.arguments(); if( args.count() == 1) { return args[0].toString(); } return QString(); } QString Mp3tunesHarmonyHandler::email() { DEBUG_BLOCK if( !daemonRunning() ) return QString(); QString name = "org.kde.amarok.Mp3tunesHarmonyDaemon-" + QString::number( m_daemon->pid() ); //QString name = "org.kde.amarok.Mp3tunesHarmonyDaemon"; debug() << "Making Dbus call about email to: " << name; QDBusMessage m = QDBusMessage::createMethodCall( name, "/Mp3tunesHarmonyDaemon", "", "email" ); QDBusMessage response = QDBusConnection::sessionBus().call( m ); if( response.type() == QDBusMessage::ErrorMessage ) { debug() << "Got ERROR response email"; debug() << response.errorName() << ": " << response.errorMessage(); } QList args = response.arguments(); if( args.count() == 1) { return args[0].toString(); } return QString(); } void Mp3tunesHarmonyHandler::emitError( const QString &error ) { emit( signalError( error ) ); } void Mp3tunesHarmonyHandler::emitWaitingForEmail( const QString &pin ) { emit( waitingForEmail( pin ) ); } void Mp3tunesHarmonyHandler::emitWaitingForPin() { emit( waitingForPin() ); } void Mp3tunesHarmonyHandler::emitConnected() { emit( connected() ); } void Mp3tunesHarmonyHandler::emitDisconnected() { emit( disconnected() ); } void Mp3tunesHarmonyHandler::emitDownloadReady( const QVariantMap &download ) { emit( downloadReady( download ) ); } void Mp3tunesHarmonyHandler::emitDownloadPending( const QVariantMap &download ) { emit( downloadReady( download ) ); } diff --git a/src/services/mp3tunes/Mp3tunesMeta.cpp b/src/services/mp3tunes/Mp3tunesMeta.cpp index 43ce3091e6..5a52ae2651 100644 --- a/src/services/mp3tunes/Mp3tunesMeta.cpp +++ b/src/services/mp3tunes/Mp3tunesMeta.cpp @@ -1,73 +1,76 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * Copyright (c) 2007,2008 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "Mp3tunesMeta.h" #include "core/support/Amarok.h" +#include #include -#include +#include + +#include + -#include using namespace Meta; Mp3TunesTrack::Mp3TunesTrack( const QString& title ) : ServiceTrack( title ) , m_filetype() { } QString Mp3TunesTrack::type() const { return "mp3"; } void Mp3TunesTrack::setType( const QString &type ) { m_filetype = type; } QString Mp3TunesTrack::sourceName() { return "MP3tunes.com"; } QString Mp3TunesTrack::sourceDescription() { return i18n( "Online music locker where you can safely store and access your music: http://mp3tunes.com" ); } -QPixmap Mp3TunesTrack::emblem() { return KStandardDirs::locate( "data", "amarok/images/emblem-mp3tunes.png" ); } +QPixmap Mp3TunesTrack::emblem() { return QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-mp3tunes.png" ); } //// Mp3TunesAlbum //// Mp3TunesAlbum::Mp3TunesAlbum( const QString &name ) : ServiceAlbumWithCover( name ) { } Mp3TunesAlbum::Mp3TunesAlbum(const QStringList & resultRow) : ServiceAlbumWithCover( resultRow ) { } Mp3TunesAlbum::~ Mp3TunesAlbum() { } void Mp3TunesAlbum::setCoverUrl( const QString &coverURL ) { m_coverURL = coverURL; } QString Mp3TunesAlbum::coverUrl( ) const { return m_coverURL; } diff --git a/src/services/mp3tunes/Mp3tunesMeta.h b/src/services/mp3tunes/Mp3tunesMeta.h index c8f9e97691..72dfa1745e 100644 --- a/src/services/mp3tunes/Mp3tunesMeta.h +++ b/src/services/mp3tunes/Mp3tunesMeta.h @@ -1,71 +1,69 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * Copyright (c) 2007,2008 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef MP3TUNESMETA_H #define MP3TUNESMETA_H #include "../ServiceMetaBase.h" #include "../ServiceAlbumCoverDownloader.h" -#include - #include +#include #include #include -#include namespace Meta { class Mp3TunesTrack : public ServiceTrack { public: Mp3TunesTrack( const QString& title ); virtual QString sourceName(); virtual QString sourceDescription(); virtual QPixmap emblem(); virtual QString type() const; void setType( const QString &type ); private: QString m_filetype; }; class Mp3TunesAlbum : public ServiceAlbumWithCover { private: QString m_coverURL; public: Mp3TunesAlbum( const QString &name ); Mp3TunesAlbum( const QStringList &resultRow ); ~Mp3TunesAlbum(); virtual QString downloadPrefix() const { return "mp3tunes"; } virtual void setCoverUrl( const QString &coverURL ); virtual QString coverUrl() const; }; } #endif diff --git a/src/services/mp3tunes/Mp3tunesService.cpp b/src/services/mp3tunes/Mp3tunesService.cpp index bbfe4b83ed..ae064ff770 100644 --- a/src/services/mp3tunes/Mp3tunesService.cpp +++ b/src/services/mp3tunes/Mp3tunesService.cpp @@ -1,372 +1,375 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * Copyright (c) 2008 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "Mp3tunesService" #include "Mp3tunesService.h" #include "browsers/SingleCollectionTreeItemModel.h" #include "core-impl/collections/support/CollectionManager.h" #include "core/interfaces/Logger.h" #include "core/support/Components.h" #include "core/support/Debug.h" #include "Mp3tunesConfig.h" -#include +#include +#include + #include #include #include Mp3tunesServiceFactory::Mp3tunesServiceFactory() : ServiceFactory() {} void Mp3tunesServiceFactory::init() { DEBUG_BLOCK ServiceBase *service = createService(); if( service ) { m_initialized = true; emit newService( service ); } } ServiceBase* Mp3tunesServiceFactory::createService() { Mp3tunesConfig config; //The user activated the service, but didn't fill the email/password? Don't start it. // if( config.email().isEmpty() || config.password().isEmpty() ) // return 0; ServiceBase* service = new Mp3tunesService( this, "MP3tunes.com", config.partnerToken(), config.email(), config.password(), config.harmonyEnabled() ); return service; } QString Mp3tunesServiceFactory::name() { return "MP3tunes.com"; } KConfigGroup Mp3tunesServiceFactory::config() { return Amarok::config( "Service_Mp3tunes" ); } bool Mp3tunesServiceFactory::possiblyContainsTrack(const QUrl &url) const { QRegExp rx( "http://content.mp3tunes.com/storage/locker(?:get|play)/(.*)\\?(?:sid|partner_token)=.*" ) ; int matches = rx.indexIn( url.url() ); if( matches == -1 ) { return false; // not a mp3tunes url } QStringList list = rx.capturedTexts(); QString filekey = list.value( 1 ); // Because list[0] is the url itself. if ( filekey.isEmpty() ) { return false; } return true; // for now: if it's a mp3tunes url.. it's likely the track is in the locker } Mp3tunesService::Mp3tunesService( Mp3tunesServiceFactory* parent, const QString & name, const QString &token, const QString &email, const QString &password, bool harmonyEnabled ) : ServiceBase( name, parent ) , m_email( email ) , m_password( password ) , m_harmonyEnabled( harmonyEnabled ) , m_partnerToken( token ) , m_authenticated( false ) , m_authenticationFailed( false ) , m_sessionId ( QString() ) , m_collection( 0 ) , m_loginWorker( 0 ) , m_harmony( 0 ) { DEBUG_BLOCK setShortDescription( i18n( "The MP3tunes Locker: Your Music Everywhere!" ) ); setIcon( QIcon::fromTheme( "view-services-mp3tunes-amarok" ) ); debug() << "Making new Locker Object"; m_locker = new Mp3tunesLocker( "4895500420" ); debug() << "MP3tunes running automated authenticate. email: " << email << " pass: " << password; authenticate( email, password ); if( m_harmonyEnabled ) { enableHarmony(); } polish(); } Mp3tunesService::~Mp3tunesService() { delete m_locker; // delete m_daemon; if( m_collection ) { CollectionManager::instance()->removeTrackProvider( m_collection ); delete m_collection; } } void Mp3tunesService::polish() { initTopPanel(); initBottomPanel(); if ( !m_authenticated && !m_authenticationFailed ) authenticate( m_email, m_password ); } void Mp3tunesService::initTopPanel() { m_menubar->clear(); //Disable this menu bar until liblastfm is improved, and this feature can //be implemented correctly. /*QMenu * actionsMenu = m_menubar->addMenu( i18n( "AutoSync" ) ); if( m_harmonyEnabled ) { QAction * action = new QAction( i18n( "Disable AutoSync" ), m_menubar ); connect( action, SIGNAL(triggered(bool)), SLOT(disableHarmony()) ); actionsMenu->addAction( action ); } else { QAction * action = new QAction( i18n( "Enable AutoSync" ), m_menubar ); connect( action, SIGNAL(triggered(bool)), SLOT(enableHarmony()) ); actionsMenu->addAction( action ); } m_menubar->show();*/ } void Mp3tunesService::initBottomPanel() { m_bottomPanel->hide(); } void Mp3tunesService::enableHarmony() { DEBUG_BLOCK if( !m_harmony ) { debug() << "Making new Daemon"; Mp3tunesConfig config; debug () << "Using identifier: " << config.identifier(); if( config.pin().isEmpty() ) m_harmony = new Mp3tunesHarmonyHandler( config.identifier() ); //first time harmony login else m_harmony = new Mp3tunesHarmonyHandler( config.identifier(), //they're not harmony virgins config.email(), config.pin() ); // qRegisterMetaType("Mp3tunesHarmonyDownload"); - connect( m_harmony, SIGNAL(disconnected()), - this, SLOT(harmonyDisconnected())); - connect( m_harmony, SIGNAL(waitingForEmail(QString)), - this, SLOT(harmonyWaitingForEmail(QString)) ); - connect( m_harmony, SIGNAL(waitingForPin()), - this, SLOT(harmonyWaitingForPin()) ); - connect( m_harmony, SIGNAL(connected()), - this, SLOT(harmonyConnected()) ); - connect( m_harmony, SIGNAL(signalError(QString)), - this, SLOT(harmonyError(QString)) ); - connect( m_harmony, SIGNAL(downloadReady(QVariantMap)), - this, SLOT(harmonyDownloadReady(QVariantMap)) ); - connect( m_harmony, SIGNAL(downloadPending(QVariantMap)), - this, SLOT(harmonyDownloadPending(QVariantMap)) ); + connect( m_harmony, &Mp3tunesHarmonyHandler::disconnected, + this, &Mp3tunesService::harmonyDisconnected ); + connect( m_harmony, &Mp3tunesHarmonyHandler::waitingForEmail, + this, &Mp3tunesService::harmonyWaitingForEmail ); + connect( m_harmony, &Mp3tunesHarmonyHandler::waitingForPin, + this, &Mp3tunesService::harmonyWaitingForPin ); + connect( m_harmony, &Mp3tunesHarmonyHandler::connected, + this, &Mp3tunesService::harmonyConnected ); + connect( m_harmony, &Mp3tunesHarmonyHandler::signalError, + this, &Mp3tunesService::harmonyError ); + connect( m_harmony, &Mp3tunesHarmonyHandler::downloadReady, + this, &Mp3tunesService::harmonyDownloadReady ); + connect( m_harmony, &Mp3tunesHarmonyHandler::downloadPending, + this, &Mp3tunesService::harmonyDownloadPending ); debug() << "starting harmony"; m_harmony->startDaemon(); if( m_harmony->daemonRunning() ) { debug() << "harmony started.. making connection"; m_harmony->makeConnection(); } if( m_harmony->daemonConnected() ) debug() << "harmony connected"; else debug() << "harmony failed to connected"; //Close your eyes. Cross your legs. Touch middle fingers to thumbs. Extend your arms. //OOOooommmmm } debug() << "Daemon running"; m_harmonyEnabled = true; Amarok::Components::logger()->shortMessage( i18n( "MP3tunes AutoSync Enabled" ) ); polish(); } void Mp3tunesService::disableHarmony() { DEBUG_BLOCK if( !m_harmony ) return; debug() << "stopping daemon"; m_harmony->stopDaemon(); m_harmony = 0; m_harmonyEnabled = false; polish(); Amarok::Components::logger()->shortMessage( i18n( "MP3tunes AutoSync Disabled" ) ); } void Mp3tunesService::authenticate( const QString & uname, const QString & passwd ) { DEBUG_BLOCK if( m_loginWorker ) return; if ( uname.isEmpty() || passwd.isEmpty() ) return; m_loginWorker = new Mp3tunesLoginWorker( m_locker, uname, passwd); //debug() << "Connecting finishedLogin -> authentication complete."; - connect( m_loginWorker, SIGNAL(finishedLogin(QString)), this, - SLOT(authenticationComplete(QString)) ); + connect( m_loginWorker, &Mp3tunesLoginWorker::finishedLogin, + this, &Mp3tunesService::authenticationComplete ); //debug() << "Connection complete. Enqueueing.."; ThreadWeaver::Queue::instance()->enqueue( QSharedPointer(m_loginWorker) ); //debug() << "LoginWorker queue"; Amarok::Components::logger()->shortMessage( i18n( "Authenticating" ) ); } void Mp3tunesService::authenticationComplete( const QString & sessionId ) { DEBUG_BLOCK m_loginWorker = 0; debug() << "Authentication reply: " << sessionId; if ( sessionId.isEmpty() ) { QString error = i18n("MP3tunes failed to Authenticate."); if ( !m_locker->errorMessage().isEmpty() ) { error = m_locker->errorMessage(); // Not sure how to i18n this } Amarok::Components::logger()->longMessage( error ); setServiceReady( false ); m_authenticationFailed = true; } else { m_sessionId = sessionId; m_authenticated = true; m_collection = new Collections::Mp3tunesServiceCollection( this, m_sessionId, m_locker ); CollectionManager::instance()->addTrackProvider( m_collection ); QList levels; levels << CategoryId::Artist << CategoryId::Album; setModel( new SingleCollectionTreeItemModel( m_collection, levels ) ); setServiceReady( true ); } polish(); } void Mp3tunesService::harmonyDisconnected() { DEBUG_BLOCK debug() << "Harmony Disconnected!"; Amarok::Components::logger()->shortMessage( i18n( "MP3tunes Harmony: Disconnected" ) ); } void Mp3tunesService::harmonyWaitingForEmail( const QString &pin ) { DEBUG_BLOCK debug() << "Waiting for user to input PIN: " << pin; Amarok::Components::logger()->shortMessage( i18n( "MP3tunes Harmony: Waiting for PIN Input" ) ); KMessageBox::information( this, "Please go to mp3tunes.com/pin and enter the following pin.\n\tPIN: " + pin, "MP3tunes Harmony", QString(), KMessageBox::AllowLink ); } void Mp3tunesService::harmonyWaitingForPin() { DEBUG_BLOCK QString pin = m_harmony->pin(); debug() << "Waiting for user to input PIN: " << pin; Amarok::Components::logger()->shortMessage( i18n( "MP3tunes Harmony: Waiting for PIN Input" ) ); KMessageBox::information( this, "Please go to mp3tunes.com/pin and enter the following pin.\n\tPIN: " + pin, "MP3tunes Harmony", QString(), KMessageBox::AllowLink ); } void Mp3tunesService::harmonyConnected() { DEBUG_BLOCK debug() << "Harmony Connected!"; Amarok::Components::logger()->shortMessage( i18n( "MP3tunes Harmony: Successfully Connected" ) ); /* at this point since the user has input the pin, we will save the info for later authentication*/ Mp3tunesConfig config; debug() << "Setting Config email: " << m_harmony->email() << " pin: " << m_harmony->pin(); config.setHarmonyEmail( m_harmony->email() ); config.setPin( m_harmony->pin() ); config.save(); } void Mp3tunesService::harmonyError( const QString &error ) { DEBUG_BLOCK debug() << "Harmony Error: " << error; Amarok::Components::logger()->longMessage( i18n( "MP3tunes Harmony Error\n%1", error ) ); } void Mp3tunesService::harmonyDownloadReady( const QVariantMap &download ) { DEBUG_BLOCK - debug() << "Got message about ready: " << download["trackTitle"].toString() << " by " << download["artistName"].toString() << " on " << download["albumTitle"].toString(); - foreach( Collections::Collection *coll, CollectionManager::instance()->collections().keys() ) { + debug() << "Got message about ready: " << download["trackTitle"] << " by " << download["artistName"] << " on " << download["albumTitle"]; + foreach( Collections::Collection *coll, CollectionManager::instance()->collections().keys() ) + { if( coll && coll->isWritable() && m_collection ) { debug() << "got collection" << coll->prettyName(); if ( coll->collectionId() == "localCollection") { //TODO Allow user to choose which collection to sync down to. debug() << "got local collection"; Collections::CollectionLocation *dest = coll->location(); Collections::CollectionLocation *source = m_collection->location(); - if( !m_collection->possiblyContainsTrack( download["url"].toString() ) ) + if( !m_collection->possiblyContainsTrack( download["url"].toUrl() ) ) return; //TODO some sort of error handling - Meta::TrackPtr track( m_collection->trackForUrl( download["url"].toString() ) ); + Meta::TrackPtr track( m_collection->trackForUrl( download["url"].toUrl() ) ); source->prepareCopy( track, dest ); break; } } } } void Mp3tunesService::harmonyDownloadPending( const QVariantMap &download ) { DEBUG_BLOCK - debug() << "Got message about ready: " << download["trackTitle"].toString() << " by " << download["artistName"].toString() << " on " << download["albumTitle"].toString(); + debug() << "Got message about ready: " << download["trackTitle"] << " by " << download["artistName"] << " on " << download["albumTitle"]; } diff --git a/src/services/mp3tunes/Mp3tunesServiceCollection.cpp b/src/services/mp3tunes/Mp3tunesServiceCollection.cpp index d6e58419e3..60fca8fa20 100644 --- a/src/services/mp3tunes/Mp3tunesServiceCollection.cpp +++ b/src/services/mp3tunes/Mp3tunesServiceCollection.cpp @@ -1,165 +1,165 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * Copyright (c) 2008 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "Mp3tunesServiceCollection.h" #include "Mp3tunesLockerMeta.h" #include "Mp3tunesMeta.h" #include "Mp3tunesServiceCollectionLocation.h" #include "Mp3tunesServiceQueryMaker.h" #include "Mp3tunesWorkers.h" #include "core/support/Debug.h" #include #include #include #include using namespace Collections; Mp3tunesServiceCollection::Mp3tunesServiceCollection( ServiceBase * service, const QString &sessionId, Mp3tunesLocker * locker ) : ServiceCollection( service, "Mp3tunesCollection", "Mp3tunesCollection" ) , m_sessionId( sessionId ) , m_locker( locker ) { } Mp3tunesServiceCollection::~Mp3tunesServiceCollection() { } QueryMaker * Mp3tunesServiceCollection::queryMaker() { return new Mp3tunesServiceQueryMaker( m_locker, m_sessionId, this ); } QString Mp3tunesServiceCollection::collectionId() const { return QLatin1String( "MP3tunesLocker" ); } QString Mp3tunesServiceCollection::prettyName() const { return i18n( "MP3tunes Locker" ); } bool Mp3tunesServiceCollection::possiblyContainsTrack(const QUrl &url) const { QRegExp rx( "http://content.mp3tunes.com/storage/locker(?:get|play)/(.*)\\?(?:sid|partner_token)=.*" ) ; int matches = rx.indexIn( url.url() ); if( matches == -1 ) { return false; // not a mp3tunes url } return true; // for now: if it's a mp3tunes url.. it's likely the track is in the locker } Meta::TrackPtr Mp3tunesServiceCollection::trackForUrl( const QUrl &url ) { DEBUG_BLOCK if( !m_locker->authenticated() ) m_locker->login(); QRegExp rx( "http://content.mp3tunes.com/storage/locker(?:get|play)/(.*)\\?(?:sid|partner_token)=.*" ) ; rx.indexIn( url.url() ); QStringList list = rx.capturedTexts(); QString filekey = list[1]; // Because list[0] is the url itself. if ( filekey.isEmpty() ) { debug() << "not a track"; return Meta::TrackPtr(); // It's not an mp3tunes track } debug() << "filekey: " << filekey; Meta::Mp3TunesTrack * serviceTrack = new Meta::Mp3TunesTrack( QString() ); serviceTrack->setUidUrl( url.url() ); Mp3tunesTrackFromFileKeyFetcher* trackFetcher = new Mp3tunesTrackFromFileKeyFetcher( m_locker, filekey ); m_tracksFetching[filekey] = serviceTrack; - connect( trackFetcher, SIGNAL(trackFetched(Mp3tunesLockerTrack&)), this, SLOT(trackForUrlComplete(Mp3tunesLockerTrack&)) ); + connect( trackFetcher, &Mp3tunesTrackFromFileKeyFetcher::trackFetched, this, &Mp3tunesServiceCollection::trackForUrlComplete ); //debug() << "Connection complete. Enqueueing.."; ThreadWeaver::Queue::instance()->enqueue( QSharedPointer(trackFetcher) ); //debug() << "m_trackFetcher queue"; return Meta::TrackPtr( serviceTrack ); } void Mp3tunesServiceCollection::trackForUrlComplete( Mp3tunesLockerTrack &track ) { DEBUG_BLOCK //Lets get this thing debug() << "got track: " << track.trackTitle(); QString filekey = track.trackFileKey(); if( !m_tracksFetching.contains( filekey ) ) { debug() << "track not found in QMap"; return; } Meta::Mp3TunesTrack * serviceTrack = m_tracksFetching.take( filekey ); //Building a Meta::Track QString title = track.trackTitle().isEmpty() ? "Unknown" : track.trackTitle(); serviceTrack->setTitle( title ); serviceTrack->setId( track.trackId() ); serviceTrack->setUidUrl( track.playUrl() ); //was: setUrl serviceTrack->setDownloadableUrl( track.downloadUrl() ); serviceTrack->setLength( track.trackLength() ); serviceTrack->setTrackNumber( track.trackNumber() ); serviceTrack->setYear( track.albumYear() ); //Building a Meta::Album title = track.albumTitle().isEmpty() ? "Unknown" : track.albumTitle(); Meta::Mp3TunesAlbum * serviceAlbum = new Meta::Mp3TunesAlbum( title ); QString albumIdStr = QString::number( track.albumId() ); serviceAlbum->setId( track.albumId() ); QString coverUrl = "http://content.mp3tunes.com/storage/albumartget/?alternative=1&partner_token=&sid="; coverUrl.replace( "", m_locker->sessionId() ); coverUrl.replace( "", m_locker->partnerToken() ); coverUrl.replace( "", albumIdStr ); serviceAlbum->setCoverUrl(coverUrl); Meta::AlbumPtr albumPtr( serviceAlbum ); serviceTrack->setAlbumPtr( albumPtr ); // Building a Meta::Artist QString name = track.artistName().isEmpty() ? "Unknown" : track.artistName(); Meta::ServiceArtist * serviceArtist = new Meta::ServiceArtist( name ); serviceArtist->setId( track.artistId() ); Meta::ArtistPtr artistPtr( serviceArtist ); serviceTrack->setArtist( artistPtr ); serviceAlbum->setArtistName( name ); serviceAlbum->setAlbumArtist( artistPtr ); // serviceTrack->update( Meta::TrackPtr( serviceTrack ) ); } Mp3tunesLocker* Mp3tunesServiceCollection::locker() const { return m_locker; } CollectionLocation* Mp3tunesServiceCollection::location() { return new Mp3tunesServiceCollectionLocation( this ); } diff --git a/src/services/mp3tunes/Mp3tunesServiceCollectionLocation.cpp b/src/services/mp3tunes/Mp3tunesServiceCollectionLocation.cpp index 23effa7792..620eb2aabe 100644 --- a/src/services/mp3tunes/Mp3tunesServiceCollectionLocation.cpp +++ b/src/services/mp3tunes/Mp3tunesServiceCollectionLocation.cpp @@ -1,87 +1,87 @@ /**************************************************************************************** * Copyright (c) 2008 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "Mp3tunesServiceCollectionLocation.h" #include "Mp3tunesWorkers.h" #include "core/interfaces/Logger.h" #include "core/support/Components.h" #include #include #include #include "core/support/Debug.h" using namespace Collections; Mp3tunesServiceCollectionLocation::Mp3tunesServiceCollectionLocation( Mp3tunesServiceCollection *parentCollection ) : ServiceCollectionLocation( parentCollection ) , m_collection( parentCollection ) { DEBUG_BLOCK } Mp3tunesServiceCollectionLocation::~Mp3tunesServiceCollectionLocation() { } QString Mp3tunesServiceCollectionLocation::prettyLocation() const { return i18n( "MP3tunes Locker" ); } bool Mp3tunesServiceCollectionLocation::isWritable() const { return true; } void Mp3tunesServiceCollectionLocation::copyUrlsToCollection ( const QMap &sources, const Transcoding::Configuration &configuration ) { DEBUG_BLOCK Q_UNUSED( configuration ); // TODO: we might support transcoding here QStringList urls; QString error; debug() << "sources has " << sources.count(); foreach( const Meta::TrackPtr &track, sources.keys() ) { debug() << "copying " << sources[ track ] << " to mp3tunes locker"; debug() << "file is at " << sources[ track ].toDisplayString(); QString supported_types = "mp3 mp4 m4a m4b m4p aac wma ogg"; if( supported_types.contains( track->type() ) ) { debug() << "Added " << sources[ track ].toDisplayString() << " to queue."; urls.push_back( sources[ track ].toDisplayString() ); } else { error = i18n( "Only the following types of tracks can be uploaded to MP3tunes: mp3, mp4, m4a, m4p, aac, wma, and ogg. " ); debug() << "File type not supprted " << track->type(); } } if( !error.isEmpty() ) Amarok::Components::logger()->longMessage( error ); Mp3tunesSimpleUploader * uploadWorker = new Mp3tunesSimpleUploader( m_collection->locker(), urls ); - connect( uploadWorker, SIGNAL(uploadComplete()), this, SLOT(slotCopyOperationFinished()) ); + connect( uploadWorker, &Mp3tunesSimpleUploader::uploadComplete, this, &Mp3tunesServiceCollectionLocation::slotCopyOperationFinished ); ThreadWeaver::Queue::instance()->enqueue( QSharedPointer(uploadWorker) ); } diff --git a/src/services/mp3tunes/Mp3tunesServiceQueryMaker.cpp b/src/services/mp3tunes/Mp3tunesServiceQueryMaker.cpp index 3589c4aa68..cdaaf2edbc 100644 --- a/src/services/mp3tunes/Mp3tunesServiceQueryMaker.cpp +++ b/src/services/mp3tunes/Mp3tunesServiceQueryMaker.cpp @@ -1,465 +1,465 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * Copyright (c) 2007 Adam Pigg * * Copyright (c) 2007,2008 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "Mp3tunesServiceQueryMaker.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include "Mp3tunesMeta.h" #include "Mp3tunesWorkers.h" #include "core-impl/collections/support/MemoryMatcher.h" #include #include #include #include using namespace Collections; class Mp3tunesServiceQueryMaker::Private { public: enum QueryType { NONE, TRACK, ARTIST, ALBUM, COMPOSER, YEAR, GENRE, CUSTOM }; QueryType type; int maxsize; }; Mp3tunesServiceQueryMaker::Mp3tunesServiceQueryMaker( Mp3tunesServiceCollection * collection, const QString &sessionId ) : DynamicServiceQueryMaker() , m_storedTransferJob( ) , d( new Private ) { DEBUG_BLOCK m_collection = collection; m_sessionId = sessionId; d->type = Private::NONE; d->maxsize = -1; } Mp3tunesServiceQueryMaker::Mp3tunesServiceQueryMaker( Mp3tunesLocker * locker, const QString &sessionId, Mp3tunesServiceCollection * collection ) : DynamicServiceQueryMaker() , m_storedTransferJob( ) , d( new Private ) { DEBUG_BLOCK m_collection = collection; m_sessionId = sessionId; m_locker = locker; d->type = Private::NONE; d->maxsize = -1; } Mp3tunesServiceQueryMaker::~Mp3tunesServiceQueryMaker() { delete d; } void Mp3tunesServiceQueryMaker::run() { DEBUG_BLOCK if ( m_storedTransferJob != 0 ) return; m_collection->acquireReadLock(); //naive implementation, fix this //note: we are not handling filtering yet if ( d->type == Private::NONE ) //TODO error handling return; if ( d->type == Private::ARTIST ) fetchArtists(); else if ( d->type == Private::ALBUM ) fetchAlbums(); else if ( d->type == Private::TRACK ) fetchTracks(); m_collection->releaseLock(); } void Mp3tunesServiceQueryMaker::abortQuery() {} QueryMaker* Mp3tunesServiceQueryMaker::setQueryType( QueryType type ) { switch( type ) { case QueryMaker::Artist: case QueryMaker::AlbumArtist: { DEBUG_BLOCK d->type = Private::ARTIST; return this; } case QueryMaker::Album: { DEBUG_BLOCK d->type = Private::ALBUM; return this; } case QueryMaker::Track: { DEBUG_BLOCK d->type = Private::TRACK; return this; } case QueryMaker::Genre: case QueryMaker::Composer: case QueryMaker::Year: case QueryMaker::Custom: case QueryMaker::None: default: //TODO: Implement. return this; } } QueryMaker * Mp3tunesServiceQueryMaker::addMatch( const Meta::ArtistPtr & artist ) { DEBUG_BLOCK if ( m_parentAlbumId.isEmpty() ) { const Meta::ServiceArtist * serviceArtist = static_cast< const Meta::ServiceArtist * >( artist.data() ); m_parentArtistId = QString::number( serviceArtist->id() ); debug() << "artist parent id set to: " << m_parentArtistId; } return this; } QueryMaker * Mp3tunesServiceQueryMaker::addMatch(const Meta::AlbumPtr & album) { DEBUG_BLOCK const Meta::ServiceAlbum * serviceAlbum = static_cast< const Meta::ServiceAlbum * >( album.data() ); m_parentAlbumId = QString::number( serviceAlbum->id() ); debug() << "album parent id set to: " << m_parentAlbumId; m_parentArtistId.clear(); return this; } void Mp3tunesServiceQueryMaker::handleResult() { DEBUG_BLOCK } void Mp3tunesServiceQueryMaker::handleResult( const Meta::ArtistList & artists ) { DEBUG_BLOCK if ( d->maxsize >= 0 && artists.count() > d->maxsize ) { - emit newResultReady( artists.mid( 0, d->maxsize ) ); + emit newArtistsReady( artists.mid( 0, d->maxsize ) ); } else { - emit newResultReady( artists ); + emit newArtistsReady( artists ); } } void Mp3tunesServiceQueryMaker::handleResult( const Meta::AlbumList &albums ) { DEBUG_BLOCK if ( d->maxsize >= 0 && albums.count() > d->maxsize ) { - emit newResultReady( albums.mid( 0, d->maxsize ) ); + emit newAlbumsReady( albums.mid( 0, d->maxsize ) ); } else { - emit newResultReady( albums ); + emit newAlbumsReady( albums ); } } void Mp3tunesServiceQueryMaker::handleResult(const Meta::TrackList & tracks) { DEBUG_BLOCK if ( d->maxsize >= 0 && tracks.count() > d->maxsize ) { - emit newResultReady( tracks.mid( 0, d->maxsize ) ); + emit newTracksReady( tracks.mid( 0, d->maxsize ) ); } else { - emit newResultReady( tracks ); + emit newTracksReady( tracks ); } } void Mp3tunesServiceQueryMaker::fetchArtists() { DEBUG_BLOCK if ( !m_artistFilter.isEmpty() ) { debug() << "Artist Filtering"; Mp3tunesSearchMonkey * searchMonkey = new Mp3tunesSearchMonkey( m_locker, m_artistFilter, Mp3tunesSearchResult::ArtistQuery ); - connect( searchMonkey, SIGNAL(searchComplete(QList)), this, SLOT(artistDownloadComplete(QList)) ); + connect( searchMonkey, &Mp3tunesSearchMonkey::searchArtistComplete, this, &Mp3tunesServiceQueryMaker::artistDownloadComplete ); ThreadWeaver::Queue::instance()->enqueue( QSharedPointer(searchMonkey) ); //Go! } else if( m_locker->sessionValid() ) { debug() << "Artist Fetching"; Mp3tunesArtistFetcher * artistFetcher = new Mp3tunesArtistFetcher( m_locker ); - connect( artistFetcher, SIGNAL(artistsFetched(QList)), this, SLOT(artistDownloadComplete(QList)) ); + connect( artistFetcher, &Mp3tunesArtistFetcher::artistsFetched, this, &Mp3tunesServiceQueryMaker::artistDownloadComplete ); ThreadWeaver::Queue::instance()->enqueue( QSharedPointer(artistFetcher) ); } } void Mp3tunesServiceQueryMaker::fetchAlbums() { DEBUG_BLOCK Meta::AlbumList albums; debug() << "Fetching Albums for parentArtist id: " << m_parentArtistId; if ( !m_parentArtistId.isEmpty() ) { albums = matchAlbums( m_collection, m_collection->artistById( m_parentArtistId.toInt() ) ); } else { debug() << "parent id empty"; return; } if ( albums.count() > 0 ) { handleResult( albums ); } else if ( m_locker->sessionValid() ) { Mp3tunesAlbumWithArtistIdFetcher * albumFetcher = new Mp3tunesAlbumWithArtistIdFetcher( m_locker, m_parentArtistId.toInt() ); - connect( albumFetcher, SIGNAL(albumsFetched(QList)), this, SLOT(albumDownloadComplete(QList)) ); + connect( albumFetcher, &Mp3tunesAlbumWithArtistIdFetcher::albumsFetched, this, &Mp3tunesServiceQueryMaker::albumDownloadComplete ); ThreadWeaver::Queue::instance()->enqueue( QSharedPointer(albumFetcher) ); } else { debug() << "Session Invalid"; } } void Mp3tunesServiceQueryMaker::fetchTracks() { DEBUG_BLOCK Meta::AlbumList albums; Meta::TrackList tracks; debug() << "album parent id: " << m_parentAlbumId; debug() << "artist parent id: " << m_parentArtistId; if ( !m_parentArtistId.isEmpty() ) { ArtistMatcher artistMatcher( m_collection->artistById( m_parentArtistId.toInt() ) ); tracks = artistMatcher.match( m_collection->trackMap().values() ); } else if ( !m_parentAlbumId.isEmpty() ) { AlbumMatcher albumMatcher( m_collection->albumById( m_parentAlbumId.toInt() ) ); tracks = albumMatcher.match( m_collection->trackMap().values() ); } else { debug() << "parent id empty"; return; } if ( tracks.count() > 0 ) { debug() << tracks.count() << " Tracks selected"; handleResult( tracks ); emit queryDone(); } else if ( m_locker->sessionValid() ) { if( !m_parentArtistId.isEmpty() ) { debug() << "Creating track w/ artist id Fetch Worker"; Mp3tunesTrackWithArtistIdFetcher * trackFetcher = new Mp3tunesTrackWithArtistIdFetcher( m_locker, m_parentArtistId.toInt() ); - connect( trackFetcher, SIGNAL(tracksFetched(QList)), this, SLOT(trackDownloadComplete(QList)) ); + connect( trackFetcher, &Mp3tunesTrackWithArtistIdFetcher::tracksFetched, this, &Mp3tunesServiceQueryMaker::trackDownloadComplete ); ThreadWeaver::Queue::instance()->enqueue( QSharedPointer(trackFetcher) ); //Go! } else if ( !m_parentAlbumId.isEmpty() ) { debug() << "Creating track w/ album id Fetch Worker"; Mp3tunesTrackWithAlbumIdFetcher * trackFetcher = new Mp3tunesTrackWithAlbumIdFetcher( m_locker, m_parentAlbumId.toInt() ); - connect( trackFetcher, SIGNAL(tracksFetched(QList)), this, SLOT(trackDownloadComplete(QList)) ); + connect( trackFetcher, &Mp3tunesTrackWithAlbumIdFetcher::tracksFetched, this, &Mp3tunesServiceQueryMaker::trackDownloadComplete ); ThreadWeaver::Queue::instance()->enqueue( QSharedPointer(trackFetcher) ); //Go! } } else { debug() << "Session Invalid"; return; } } void Mp3tunesServiceQueryMaker::artistDownloadComplete( QList artistList ) { DEBUG_BLOCK Meta::ArtistList artists; debug() << "Received artists"; foreach(const Mp3tunesLockerArtist &artist, artistList) { Meta::ServiceArtist * serviceArtist = new Meta::ServiceArtist( artist.artistName() ); //debug() << "Adding artist: " << artist.artistName(); serviceArtist->setId( artist.artistId() ); Meta::ArtistPtr artistPtr( serviceArtist ); artists.push_back( artistPtr ); m_collection->acquireWriteLock(); m_collection->addArtist( artistPtr ); m_collection->releaseLock(); } handleResult( artists ); emit queryDone(); } void Mp3tunesServiceQueryMaker::albumDownloadComplete( QList albumsList ) { DEBUG_BLOCK debug() << "Received albums"; Meta::AlbumList albums; foreach(const Mp3tunesLockerAlbum &album, albumsList) { QString title = album.albumTitle(); if ( title.contains("* PlayMix") ) continue; if ( title.isEmpty() ) title = "Unknown"; QString albumIdStr = QString::number( album.albumId() ); int albumId = album.albumId(); bool hasArt = album.hasArt(); Meta::Mp3TunesAlbum * serviceAlbum = new Meta::Mp3TunesAlbum( title ); if ( hasArt ) { QString coverUrl = "http://content.mp3tunes.com/storage/albumartget/?alternative=1&partner_token=&sid="; coverUrl.replace( "", m_locker->sessionId() ); coverUrl.replace( "", m_locker->partnerToken() ); coverUrl.replace( "", albumIdStr ); serviceAlbum->setCoverUrl(coverUrl); } Meta::AlbumPtr albumPtr( serviceAlbum ); //debug() << "Adding album: " << title; serviceAlbum->setId( albumId ); m_collection->acquireWriteLock(); m_collection->addAlbum( albumPtr ); m_collection->releaseLock(); Meta::ArtistPtr artistPtr = m_collection->artistById( album.artistId() ); if ( artistPtr.data() != 0 ) { //debug() << "Found parent artist"; serviceAlbum->setAlbumArtist( artistPtr ); } albums.push_back( albumPtr ); } handleResult( albums ); emit queryDone(); } void Mp3tunesServiceQueryMaker::trackDownloadComplete( QList tracksList ) { DEBUG_BLOCK //debug() << "Received Tracks"; Meta::TrackList tracks; //so lets figure out what we got here: foreach(const Mp3tunesLockerTrack &track, tracksList) { QString title = track.trackTitle(); if ( title.isEmpty() ) title = "Unknown"; Meta::Mp3TunesTrack * serviceTrack = new Meta::Mp3TunesTrack( title ); Meta::TrackPtr trackPtr( serviceTrack ); // debug() << "Adding track: " << title; serviceTrack->setId( track.trackId() ); serviceTrack->setUidUrl( track.playUrl() ); serviceTrack->setDownloadableUrl( track.downloadUrl() ); serviceTrack->setLength( track.trackLength() ); serviceTrack->setTrackNumber( track.trackNumber() ); serviceTrack->setYear( track.albumYear() ); debug() << "setting type: " << Amarok::extension( track.trackFileName() ); serviceTrack->setType( Amarok::extension( track.trackFileName() ) ); //debug() << "set type"; m_collection->acquireWriteLock(); //debug() << "adding track"; m_collection->addTrack( trackPtr ); //debug() << "added tracktrack"; m_collection->releaseLock(); QString albumId = QString::number( track.albumId() ); QString artistId = QString::number( track.artistId() ); Meta::ArtistPtr artistPtr = m_collection->artistById( artistId.toInt() ); if ( artistPtr.data() != 0 ) { debug() << "Found parent artist"; Meta::ServiceArtist *artist = dynamic_cast< Meta::ServiceArtist * > ( artistPtr.data() ); serviceTrack->setArtist( artistPtr ); artist->addTrack( trackPtr ); } Meta::AlbumPtr albumPtr = m_collection->albumById( albumId.toInt() ); if ( albumPtr.data() != 0 ) { debug() << "Found parent album"; Meta::ServiceAlbum *album = dynamic_cast< Meta::ServiceAlbum * > ( albumPtr.data() ); serviceTrack->setAlbumPtr( albumPtr ); album->addTrack( trackPtr ); } tracks.push_back( trackPtr ); } //ThreadWeaver::Weaver::instance()->dequeue( job ); //job->deleteLater(); handleResult( tracks ); emit queryDone(); } QueryMaker * Mp3tunesServiceQueryMaker::addFilter(qint64 value, const QString & filter, bool /*matchBegin*/, bool /*matchEnd*/) { DEBUG_BLOCK //debug() << "value: " << value; //for now, only accept artist filters if ( value == Meta::valArtist ) { //debug() << "Filter: " << filter; m_artistFilter = filter; } return this; } int Mp3tunesServiceQueryMaker::validFilterMask() { //we only supprt artist filters for now... return ArtistFilter; } diff --git a/src/services/mp3tunes/Mp3tunesSettingsModule.cpp b/src/services/mp3tunes/Mp3tunesSettingsModule.cpp index 5cf5999b29..d7e98ce28e 100644 --- a/src/services/mp3tunes/Mp3tunesSettingsModule.cpp +++ b/src/services/mp3tunes/Mp3tunesSettingsModule.cpp @@ -1,83 +1,81 @@ /**************************************************************************************** * Copyright (c) 2007 Nikolaj Hald Nielsen * * Copyright (c) 2008 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "Mp3tunesSettingsModule.h" #include "ui_Mp3tunesConfigWidget.h" -#include +#include -K_PLUGIN_FACTORY( Mp3tunesSettingsFactory, registerPlugin(); ) -K_EXPORT_PLUGIN( Mp3tunesSettingsFactory( "kcm_amarok_mp3tunes" ) ) +K_PLUGIN_FACTORY_WITH_JSON( Mp3tunesSettingsFactory, "amarok_service_mp3tunes_config.json", registerPlugin(); ) Mp3tunesSettingsModule::Mp3tunesSettingsModule( QWidget *parent, const QVariantList &args ) - : KCModule( Mp3tunesSettingsFactory::componentData(), parent, args ) + : KCModule( parent, args ) { m_configDialog = new Ui::Mp3tunesConfigWidget; m_configDialog->setupUi( this ); //m_configDialog->pinEdit->setReadOnly( true ); m_configDialog->passwordEdit->setEchoMode( QLineEdit::Password ); - connect ( m_configDialog->emailEdit, SIGNAL(textChanged(QString)), this, SLOT(settingsChanged()) ); - connect ( m_configDialog->passwordEdit, SIGNAL(textChanged(QString)), this, SLOT(settingsChanged()) ); - //connect ( m_configDialog->enableHarmony, SIGNAL(stateChanged(int)), this, SLOT(settingsChanged()) ); + connect ( m_configDialog->emailEdit, &QLineEdit::textChanged, this, &Mp3tunesSettingsModule::settingsChanged ); + connect ( m_configDialog->passwordEdit, &QLineEdit::textChanged, this, &Mp3tunesSettingsModule::settingsChanged ); load(); } Mp3tunesSettingsModule::~Mp3tunesSettingsModule() { delete m_configDialog; } void Mp3tunesSettingsModule::save() { m_config.setEmail( m_configDialog->emailEdit->text() ); m_config.setPassword( m_configDialog->passwordEdit->text() ); //m_config.setPin( m_configDialog->pinEdit->text() ); //m_config.setHarmonyEnabled( m_configDialog->enableHarmony->isChecked() ); m_config.save(); KCModule::save(); } void Mp3tunesSettingsModule::load() { m_configDialog->emailEdit->setText( m_config.email() ); m_configDialog->passwordEdit->setText( m_config.password() ); //m_configDialog->enableHarmony->setChecked( m_config.harmonyEnabled() ); //m_configDialog->pinEdit->setText( m_config.pin() ); KCModule::load(); } void Mp3tunesSettingsModule::defaults() { m_config.setEmail( QString() ); m_config.setPassword( QString() ); //m_config.setHarmonyEnabled( false ); load(); } void Mp3tunesSettingsModule::settingsChanged() { emit changed( true ); } #include "Mp3tunesSettingsModule.moc" diff --git a/src/services/mp3tunes/Mp3tunesWorkers.cpp b/src/services/mp3tunes/Mp3tunesWorkers.cpp index de7c82d2ee..7c4a531cab 100644 --- a/src/services/mp3tunes/Mp3tunesWorkers.cpp +++ b/src/services/mp3tunes/Mp3tunesWorkers.cpp @@ -1,469 +1,469 @@ /**************************************************************************************** * Copyright (c) 2007,2008 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "Mp3tunesWorkers.h" #include "core/interfaces/Logger.h" #include "core/support/Components.h" #include "core/support/Debug.h" #include "Mp3tunesMeta.h" -#include +#include #include Mp3tunesLoginWorker::Mp3tunesLoginWorker( Mp3tunesLocker* locker, const QString & username, const QString & password ) : QObject() , ThreadWeaver::Job() , m_locker( locker ) , m_sessionId() , m_username( username ) , m_password( password ) { - connect( this, SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(completeJob()) ); + connect( this, &Mp3tunesLoginWorker::done, this, &Mp3tunesLoginWorker::completeJob ); } Mp3tunesLoginWorker::~Mp3tunesLoginWorker() { } void Mp3tunesLoginWorker::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) { Q_UNUSED(self); Q_UNUSED(thread); DEBUG_BLOCK if(m_locker != 0) { debug() << "Calling Locker login.."; m_sessionId = m_locker->login(m_username, m_password); debug() << "Login Complete. SessionId = " << m_sessionId; } else { debug() << "Locker is NULL"; } } void Mp3tunesLoginWorker::defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { Q_EMIT started(self); ThreadWeaver::Job::defaultBegin(self, thread); } void Mp3tunesLoginWorker::defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { ThreadWeaver::Job::defaultEnd(self, thread); if (!self->success()) { Q_EMIT failed(self); } Q_EMIT done(self); } void Mp3tunesLoginWorker::completeJob() { DEBUG_BLOCK debug() << "Login Job complete"; emit( finishedLogin( m_sessionId ) ); deleteLater(); } /* ARTIST FETCHER */ Mp3tunesArtistFetcher::Mp3tunesArtistFetcher( Mp3tunesLocker * locker ) { - connect( this, SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(completeJob()) ); + connect( this, &Mp3tunesArtistFetcher::done, this, &Mp3tunesArtistFetcher::completeJob ); m_locker = locker; } Mp3tunesArtistFetcher::~Mp3tunesArtistFetcher() { } void Mp3tunesArtistFetcher::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) { Q_UNUSED(self); Q_UNUSED(thread); DEBUG_BLOCK if(m_locker != 0) { debug() << "Artist Fetch Start"; QList list = m_locker->artists(); debug() << "Artist Fetch End. Total artists: " << list.count(); m_artists = list; } else { debug() << "Locker is NULL"; } } void Mp3tunesArtistFetcher::completeJob() { emit( artistsFetched( m_artists ) ); deleteLater(); } void Mp3tunesArtistFetcher::defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { Q_EMIT started(self); ThreadWeaver::Job::defaultBegin(self, thread); } void Mp3tunesArtistFetcher::defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { ThreadWeaver::Job::defaultEnd(self, thread); if (!self->success()) { Q_EMIT failed(self); } Q_EMIT done(self); } /* ALBUM w/ Artist Id FETCHER */ Mp3tunesAlbumWithArtistIdFetcher::Mp3tunesAlbumWithArtistIdFetcher( Mp3tunesLocker * locker, int artistId ) { - connect( this, SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(completeJob()) ); + connect( this, &Mp3tunesAlbumWithArtistIdFetcher::done, this, &Mp3tunesAlbumWithArtistIdFetcher::completeJob ); m_locker = locker; m_artistId = artistId; } Mp3tunesAlbumWithArtistIdFetcher::~Mp3tunesAlbumWithArtistIdFetcher() { } void Mp3tunesAlbumWithArtistIdFetcher::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) { Q_UNUSED(self); Q_UNUSED(thread); DEBUG_BLOCK if(m_locker != 0) { debug() << "Album Fetch Start"; QList list = m_locker->albumsWithArtistId( m_artistId ); debug() << "Album Fetch End. Total albums: " << list.count(); m_albums = list; } else { debug() << "Locker is NULL"; } } void Mp3tunesAlbumWithArtistIdFetcher::completeJob() { emit( albumsFetched( m_albums ) ); deleteLater(); } void Mp3tunesAlbumWithArtistIdFetcher::defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { Q_EMIT started(self); ThreadWeaver::Job::defaultBegin(self, thread); } void Mp3tunesAlbumWithArtistIdFetcher::defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { ThreadWeaver::Job::defaultEnd(self, thread); if (!self->success()) { Q_EMIT failed(self); } Q_EMIT done(self); } /* TRACK w/ albumId FETCHER */ Mp3tunesTrackWithAlbumIdFetcher::Mp3tunesTrackWithAlbumIdFetcher( Mp3tunesLocker * locker, int albumId ) { DEBUG_BLOCK - connect( this, SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(completeJob()) ); + connect( this, &Mp3tunesTrackWithAlbumIdFetcher::done, this, &Mp3tunesTrackWithAlbumIdFetcher::completeJob ); m_locker = locker; debug() << "Constructor albumId: " << albumId; m_albumId = albumId; } Mp3tunesTrackWithAlbumIdFetcher::~Mp3tunesTrackWithAlbumIdFetcher() { } void Mp3tunesTrackWithAlbumIdFetcher::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) { Q_UNUSED(self); Q_UNUSED(thread); DEBUG_BLOCK if(m_locker != 0) { debug() << "Track Fetch Start for album " << m_albumId; QList list = m_locker->tracksWithAlbumId( m_albumId ); debug() << "Track Fetch End. Total tracks: " << list.count(); m_tracks = list; } else { debug() << "Locker is NULL"; } } void Mp3tunesTrackWithAlbumIdFetcher::completeJob() { DEBUG_BLOCK emit( tracksFetched( m_tracks ) ); deleteLater(); } void Mp3tunesTrackWithAlbumIdFetcher::defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { Q_EMIT started(self); ThreadWeaver::Job::defaultBegin(self, thread); } void Mp3tunesTrackWithAlbumIdFetcher::defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { ThreadWeaver::Job::defaultEnd(self, thread); if (!self->success()) { Q_EMIT failed(self); } Q_EMIT done(self); } /* TRACK w/ artistId FETCHER */ Mp3tunesTrackWithArtistIdFetcher::Mp3tunesTrackWithArtistIdFetcher( Mp3tunesLocker * locker, int artistId ) { DEBUG_BLOCK - connect( this, SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(completeJob()) ); + connect( this, &Mp3tunesTrackWithArtistIdFetcher::done, this, &Mp3tunesTrackWithArtistIdFetcher::completeJob ); m_locker = locker; debug() << "Constructor artistId: " << artistId; m_artistId = artistId; } Mp3tunesTrackWithArtistIdFetcher::~Mp3tunesTrackWithArtistIdFetcher() { } void Mp3tunesTrackWithArtistIdFetcher::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) { Q_UNUSED(self); Q_UNUSED(thread); DEBUG_BLOCK if(m_locker != 0) { debug() << "Track Fetch Start for artist " << m_artistId; QList list = m_locker->tracksWithArtistId( m_artistId ); debug() << "Track Fetch End. Total tracks: " << list.count(); m_tracks = list; } else { debug() << "Locker is NULL"; } } void Mp3tunesTrackWithArtistIdFetcher::completeJob() { DEBUG_BLOCK emit( tracksFetched( m_tracks ) ); deleteLater(); } void Mp3tunesTrackWithArtistIdFetcher::defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { Q_EMIT started(self); ThreadWeaver::Job::defaultBegin(self, thread); } void Mp3tunesTrackWithArtistIdFetcher::defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { ThreadWeaver::Job::defaultEnd(self, thread); if (!self->success()) { Q_EMIT failed(self); } Q_EMIT done(self); } /* SEARCH MONKEY */ Mp3tunesSearchMonkey::Mp3tunesSearchMonkey( Mp3tunesLocker * locker, QString query, int searchFor ) { DEBUG_BLOCK - connect( this, SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(completeJob()) ); + connect( this, &Mp3tunesSearchMonkey::done, this, &Mp3tunesSearchMonkey::completeJob ); m_locker = locker; m_searchFor = searchFor; m_query = query; } Mp3tunesSearchMonkey::~Mp3tunesSearchMonkey() {} void Mp3tunesSearchMonkey::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) { Q_UNUSED(self); Q_UNUSED(thread); DEBUG_BLOCK if(m_locker != 0) { Mp3tunesSearchResult container; debug() << "Searching query: " << m_query << " bitmask: " << m_searchFor; container.searchFor = (Mp3tunesSearchResult::SearchType) m_searchFor; if( !m_locker->search(container, m_query) ) { //TODO proper error handling debug() << "!!!Search Failed query: " << m_query << " bitmask: " << m_searchFor; } m_result = container; } else { debug() << "Locker is NULL"; } } void Mp3tunesSearchMonkey::completeJob() { DEBUG_BLOCK - emit( searchComplete( m_result.artistList ) ); - emit( searchComplete( m_result.albumList ) ); - emit( searchComplete( m_result.trackList ) ); + emit( searchArtistComplete( m_result.artistList ) ); + emit( searchAlbumComplete( m_result.albumList ) ); + emit( searchTrackComplete( m_result.trackList ) ); deleteLater(); } void Mp3tunesSearchMonkey::defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { Q_EMIT started(self); ThreadWeaver::Job::defaultBegin(self, thread); } void Mp3tunesSearchMonkey::defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { ThreadWeaver::Job::defaultEnd(self, thread); if (!self->success()) { Q_EMIT failed(self); } Q_EMIT done(self); } /* SIMPLE UPLOADER */ Mp3tunesSimpleUploader:: Mp3tunesSimpleUploader( Mp3tunesLocker * locker, QStringList tracklist ) { DEBUG_BLOCK - connect( this, SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(completeJob()) ); + connect( this, &Mp3tunesSimpleUploader::done, this, &Mp3tunesSimpleUploader::completeJob ); m_locker = locker; m_tracklist = tracklist; Amarok::Components::logger()->newProgressOperation( this, i18n( "Upload to MP3tunes Initiated" ), m_tracklist.count() ); //TODO: port to Amarok::Logger signals // connect( this, SIGNAL(incrementProgress()), The::statusBar(), SLOT(incrementProgress()), Qt::QueuedConnection ); } Mp3tunesSimpleUploader::~Mp3tunesSimpleUploader() { Q_EMIT endProgressOperation( this ); } void Mp3tunesSimpleUploader::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) { Q_UNUSED(self); Q_UNUSED(thread); DEBUG_BLOCK if(m_locker == 0) return; if(m_tracklist.count() == 0) { debug() << "Track list was empty."; return; } debug() << "Starting upload of " << m_tracklist.count() << " tracks."; int progress = 1; foreach(const QString &track, m_tracklist) { QString msg = i18n( "Uploading Track %1/%2", progress, m_tracklist.count() ); debug() << msg; //TODO: port to Amarok::Logger signals // Amarok::Components::logger()->setProgressStatus( this, msg ); emit ( incrementProgress() ); debug() << "Uploading: " << track; bool result = false; if( track.startsWith( "http" ) ) { debug() << "Remote file."; result = m_locker->lockerLoad( track ); } else { debug() << "Local file."; result = m_locker->uploadTrack( track ); } if(result) { debug() << "Uploaded Succeeded."; } else { debug() << "Uploaded Failed."; debug() << "Error msg: " << m_locker->errorMessage(); } progress++; } debug() << "Upload loop complete"; } void Mp3tunesSimpleUploader::completeJob() { DEBUG_BLOCK emit( uploadComplete() ); deleteLater(); } void Mp3tunesSimpleUploader::defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { Q_EMIT started(self); ThreadWeaver::Job::defaultBegin(self, thread); } void Mp3tunesSimpleUploader::defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { ThreadWeaver::Job::defaultEnd(self, thread); if (!self->success()) { Q_EMIT failed(self); } Q_EMIT done(self); } /* TRACK from filekey FETCHER */ Mp3tunesTrackFromFileKeyFetcher::Mp3tunesTrackFromFileKeyFetcher( Mp3tunesLocker * locker, QString filekey ) { DEBUG_BLOCK - connect( this, SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(completeJob()) ); + connect( this, &Mp3tunesTrackFromFileKeyFetcher::done, this, &Mp3tunesTrackFromFileKeyFetcher::completeJob ); m_locker = locker; debug() << "Constructor filekey: " << filekey; m_filekey = filekey; } Mp3tunesTrackFromFileKeyFetcher::~Mp3tunesTrackFromFileKeyFetcher() { } void Mp3tunesTrackFromFileKeyFetcher::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread) { Q_UNUSED(self); Q_UNUSED(thread); DEBUG_BLOCK if(m_locker != 0) { debug() << "Track Fetch from filekey " << m_filekey; Mp3tunesLockerTrack track = m_locker->trackWithFileKey( m_filekey ); debug() << "Track Fetch from filekey End."; m_track = track; } else { debug() << "Locker is NULL"; } } void Mp3tunesTrackFromFileKeyFetcher::completeJob() { DEBUG_BLOCK emit( trackFetched( m_track ) ); deleteLater(); } void Mp3tunesTrackFromFileKeyFetcher::defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { Q_EMIT started(self); ThreadWeaver::Job::defaultBegin(self, thread); } void Mp3tunesTrackFromFileKeyFetcher::defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) { ThreadWeaver::Job::defaultEnd(self, thread); if (!self->success()) { Q_EMIT failed(self); } Q_EMIT done(self); } diff --git a/src/services/mp3tunes/Mp3tunesWorkers.h b/src/services/mp3tunes/Mp3tunesWorkers.h index 79d231be0b..237c2052d0 100644 --- a/src/services/mp3tunes/Mp3tunesWorkers.h +++ b/src/services/mp3tunes/Mp3tunesWorkers.h @@ -1,313 +1,313 @@ /**************************************************************************************** * Copyright (c) 2007,2008 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef MP3TUNESWORKERS_H #define MP3TUNESWORKERS_H #include "Mp3tunesLocker.h" #include #include #include /** * Allows for threading the logging in process. */ class Mp3tunesLoginWorker : public QObject, public ThreadWeaver::Job { Q_OBJECT public: Mp3tunesLoginWorker( Mp3tunesLocker* locker, const QString &username, const QString &password ); ~Mp3tunesLoginWorker(); void run(ThreadWeaver::JobPointer self = QSharedPointer(), ThreadWeaver::Thread *thread = 0) Q_DECL_OVERRIDE; Q_SIGNALS: /** This signal is emitted when this job is being processed by a thread. */ void started(ThreadWeaver::JobPointer); /** This signal is emitted when the job has been finished (no matter if it succeeded or not). */ void done(ThreadWeaver::JobPointer); /** This job has failed. * This signal is emitted when success() returns false after the job is executed. */ void failed(ThreadWeaver::JobPointer); void finishedLogin( const QString &sessionId ); private Q_SLOTS: void completeJob(); private: Mp3tunesLocker* m_locker; QString m_sessionId; QString m_username; QString m_password; protected: void defaultBegin(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; void defaultEnd(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; }; /** * Allows for threading the artist fetching process */ class Mp3tunesArtistFetcher : public QObject, public ThreadWeaver::Job { Q_OBJECT public: Mp3tunesArtistFetcher( Mp3tunesLocker * locker ); ~Mp3tunesArtistFetcher(); void run(ThreadWeaver::JobPointer self = QSharedPointer(), ThreadWeaver::Thread *thread = 0) Q_DECL_OVERRIDE; Q_SIGNALS: /** This signal is emitted when this job is being processed by a thread. */ void started(ThreadWeaver::JobPointer); /** This signal is emitted when the job has been finished (no matter if it succeeded or not). */ void done(ThreadWeaver::JobPointer); /** This job has failed. * This signal is emitted when success() returns false after the job is executed. */ void failed(ThreadWeaver::JobPointer); void artistsFetched( QList ); private Q_SLOTS: void completeJob(); private: Mp3tunesLocker* m_locker; QList m_artists; protected: void defaultBegin(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; void defaultEnd(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; }; /** * Allows for threading the albumWithArtistId fetching process */ class Mp3tunesAlbumWithArtistIdFetcher : public QObject, public ThreadWeaver::Job { Q_OBJECT public: Mp3tunesAlbumWithArtistIdFetcher( Mp3tunesLocker * locker, int artistId ); ~Mp3tunesAlbumWithArtistIdFetcher(); void run(ThreadWeaver::JobPointer self = QSharedPointer(), ThreadWeaver::Thread *thread = 0) Q_DECL_OVERRIDE; Q_SIGNALS: /** This signal is emitted when this job is being processed by a thread. */ void started(ThreadWeaver::JobPointer); /** This signal is emitted when the job has been finished (no matter if it succeeded or not). */ void done(ThreadWeaver::JobPointer); /** This job has failed. * This signal is emitted when success() returns false after the job is executed. */ void failed(ThreadWeaver::JobPointer); void albumsFetched( QList ); private Q_SLOTS: void completeJob(); private: int m_artistId; Mp3tunesLocker* m_locker; QList m_albums; protected: void defaultBegin(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; void defaultEnd(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; }; /** * Allows for threading the trackWithAlbumId fetching process */ class Mp3tunesTrackWithAlbumIdFetcher : public QObject, public ThreadWeaver::Job { Q_OBJECT public: Mp3tunesTrackWithAlbumIdFetcher( Mp3tunesLocker * locker, int albumId ); ~Mp3tunesTrackWithAlbumIdFetcher(); void run(ThreadWeaver::JobPointer self = QSharedPointer(), ThreadWeaver::Thread *thread = 0) Q_DECL_OVERRIDE; Q_SIGNALS: /** This signal is emitted when this job is being processed by a thread. */ void started(ThreadWeaver::JobPointer); /** This signal is emitted when the job has been finished (no matter if it succeeded or not). */ void done(ThreadWeaver::JobPointer); /** This job has failed. * This signal is emitted when success() returns false after the job is executed. */ void failed(ThreadWeaver::JobPointer); void tracksFetched( QList ); private Q_SLOTS: void completeJob(); private: int m_albumId; Mp3tunesLocker* m_locker; QList m_tracks; protected: void defaultBegin(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; void defaultEnd(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; }; /** * Allows for threading the trackWithArtistId fetching process */ class Mp3tunesTrackWithArtistIdFetcher : public QObject, public ThreadWeaver::Job { Q_OBJECT public: Mp3tunesTrackWithArtistIdFetcher( Mp3tunesLocker * locker, int artistId ); ~Mp3tunesTrackWithArtistIdFetcher(); void run(ThreadWeaver::JobPointer self = QSharedPointer(), ThreadWeaver::Thread *thread = 0) Q_DECL_OVERRIDE; Q_SIGNALS: /** This signal is emitted when this job is being processed by a thread. */ void started(ThreadWeaver::JobPointer); /** This signal is emitted when the job has been finished (no matter if it succeeded or not). */ void done(ThreadWeaver::JobPointer); /** This job has failed. * This signal is emitted when success() returns false after the job is executed. */ void failed(ThreadWeaver::JobPointer); void tracksFetched( QList ); private Q_SLOTS: void completeJob(); private: int m_artistId; Mp3tunesLocker* m_locker; QList m_tracks; protected: void defaultBegin(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; void defaultEnd(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; }; /** * Allows for threading the searching process */ class Mp3tunesSearchMonkey : public QObject, public ThreadWeaver::Job { Q_OBJECT public: Mp3tunesSearchMonkey( Mp3tunesLocker * locker, QString query, int searchFor ); ~Mp3tunesSearchMonkey(); void run(ThreadWeaver::JobPointer self = QSharedPointer(), ThreadWeaver::Thread *thread = 0) Q_DECL_OVERRIDE; Q_SIGNALS: /** This signal is emitted when this job is being processed by a thread. */ void started(ThreadWeaver::JobPointer); /** This signal is emitted when the job has been finished (no matter if it succeeded or not). */ void done(ThreadWeaver::JobPointer); /** This job has failed. * This signal is emitted when success() returns false after the job is executed. */ void failed(ThreadWeaver::JobPointer); - void searchComplete( QList ); - void searchComplete( QList ); - void searchComplete( QList ); + void searchArtistComplete( QList ); + void searchAlbumComplete( QList ); + void searchTrackComplete( QList ); private Q_SLOTS: void completeJob(); private: QString m_query; int m_searchFor; Mp3tunesLocker* m_locker; Mp3tunesSearchResult m_result; protected: void defaultBegin(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; void defaultEnd(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; }; /** * Allows for threading a track list upload */ class Mp3tunesSimpleUploader : public QObject, public ThreadWeaver::Job { Q_OBJECT public: Mp3tunesSimpleUploader( Mp3tunesLocker * locker, QStringList tracklist ); ~Mp3tunesSimpleUploader(); void run(ThreadWeaver::JobPointer self = QSharedPointer(), ThreadWeaver::Thread *thread = 0) Q_DECL_OVERRIDE; Q_SIGNALS: /** This signal is emitted when this job is being processed by a thread. */ void started(ThreadWeaver::JobPointer); /** This signal is emitted when the job has been finished (no matter if it succeeded or not). */ void done(ThreadWeaver::JobPointer); /** This job has failed. * This signal is emitted when success() returns false after the job is executed. */ void failed(ThreadWeaver::JobPointer); void uploadComplete(); void incrementProgress(); void endProgressOperation( QObject * ); private Q_SLOTS: void completeJob(); private: Mp3tunesLocker* m_locker; QStringList m_tracklist; protected: void defaultBegin(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; void defaultEnd(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; }; /** * Allows for threading a track from filekey job. */ class Mp3tunesTrackFromFileKeyFetcher : public QObject, public ThreadWeaver::Job { Q_OBJECT public: Mp3tunesTrackFromFileKeyFetcher( Mp3tunesLocker * locker, QString filekey ); ~Mp3tunesTrackFromFileKeyFetcher(); void run(ThreadWeaver::JobPointer self = QSharedPointer(), ThreadWeaver::Thread *thread = 0) Q_DECL_OVERRIDE; Q_SIGNALS: /** This signal is emitted when this job is being processed by a thread. */ void started(ThreadWeaver::JobPointer); /** This signal is emitted when the job has been finished (no matter if it succeeded or not). */ void done(ThreadWeaver::JobPointer); /** This job has failed. * This signal is emitted when success() returns false after the job is executed. */ void failed(ThreadWeaver::JobPointer); void trackFetched( Mp3tunesLockerTrack &track ); private Q_SLOTS: void completeJob(); private: Mp3tunesLocker* m_locker; Mp3tunesLockerTrack m_track; QString m_filekey; protected: void defaultBegin(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; void defaultEnd(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread) Q_DECL_OVERRIDE; }; #endif diff --git a/src/services/mp3tunes/amarok_service_mp3tunes.desktop b/src/services/mp3tunes/amarok_service_mp3tunes.desktop index 3745bfa246..06656230f4 100644 --- a/src/services/mp3tunes/amarok_service_mp3tunes.desktop +++ b/src/services/mp3tunes/amarok_service_mp3tunes.desktop @@ -1,115 +1,114 @@ [Desktop Entry] Type=Service -ServiceTypes=KPluginInfo Icon=view-services-mp3tunes-amarok Name=MP3tunes Name[bg]=MP3tunes Name[bs]=MP3 melodija Name[ca]=MP3tunes Name[ca@valencia]=MP3tunes Name[cs]=MP3tunes Name[da]=MP3tunes Name[de]=MP3tunes Name[el]=MP3tunes Name[en_GB]=MP3tunes Name[es]=MP3tunes Name[et]=MP3tunes Name[eu]=MP3tunes Name[fi]=MP3tunes Name[fr]=MP3tunes Name[ga]=MP3tunes Name[gl]=MP3tunes Name[hu]=MP3tunes Name[id]=MP3tunes Name[is]=Mp3tunes Name[it]=MP3tunes Name[ja]=MP3tunes Name[km]=MP3tunes Name[ko]=MP3tunes Name[lt]=MP3tunes Name[lv]=Mp3tunes Name[nb]=Mp3tunes Name[nds]=MP3tunes Name[nl]=MP3tunes Name[pa]=MP3tunes Name[pl]=MP3tunes Name[pt]=MP3tunes Name[pt_BR]=MP3tunes Name[ro]=MP3tunes Name[ru]=MP3tunes Name[sk]=MP3tunes Name[sl]=MP3tunes Name[sr]=МП3‑тјунс Name[sr@ijekavian]=МП3‑тјунс Name[sr@ijekavianlatin]=MP3Tunes Name[sr@latin]=MP3Tunes Name[sv]=MP3tunes Name[tr]=MP3tunes Name[uk]=MP3tunes Name[x-test]=xxMP3tunesxx Name[zh_CN]=MP3tunes Name[zh_TW]=Mp3tunes Comment=Browse and listen to the music stored in your MP3tunes account Comment[bg]=Преглед и слушане на музиката от сметката ви в MP3tunes Comment[bs]=Pregledajte i slušajte muziku sa svog naloga na MP3‑tjunsu Comment[ca]=Navegueu i escolteu la música emmagatzemada en el vostre compte de MP3tunes Comment[ca@valencia]=Navegueu i escolteu la música emmagatzemada en el vostre compte de MP3tunes Comment[cs]=Procházejte a poslouchejte hudbu uloženou na vašem mp3tunes účtu Comment[da]=Gennemse og lyt til musik gemt på din MP3tunes-konto Comment[de]=Musik durchsehen und anhören, die in Ihrem MP3tunes-Konto abgelegt ist Comment[el]=Περιηγηθείτε και ακούστε τη μουσική που έχετε αποθηκευμένη στο λογαριασμό σας MP3tunes Comment[en_GB]=Browse and listen to the music stored in your MP3tunes account Comment[es]=Navegar y escuchar la música guardada en su cuenta MP3tunes Comment[et]=Sinu MP3tunes'i kontole salvestatud muusika sirvimine ja kuulamine Comment[eu]=Arakatu eta entzun zure MP3tunes kontuan gordetako musika Comment[fi]=Selaa ja kuuntele MP3tunes-tilillesi tallennettua musiikkia Comment[fr]=Parcourir et écouter la musique stockée sur votre compte « MP3tunes » Comment[ga]=Brabhsáil agus éist leis an gceol i do chuntas MP3tunes Comment[gl]=Navegar e escoitar a música gardada na súa conta MP3tunes Comment[hu]=Az Ön MP3tunes-azonosítóján tárolt zene böngészése és hallgatása Comment[id]=Telusur dan mendengarkan musik yang dibeli di akun MP3tune anda Comment[is]=Skoða og hlusta á tónlistina sem þú geymir í Mp3tunes hólfinu þínu Comment[it]=Sfoglia e ascolta la musica conservata nel tuo account MP3tunes Comment[ja]=Mp3tunes アカウントに保存した音楽をブラウズし聴けます Comment[km]=រក​មើល និង​ស្ដាប់​តន្ត្រី​ដែល​បាន​ផ្ទុក​ក្នុង​គណនី MP3tunes របស់​អ្នក Comment[ko]=MP3tunes 계정에 저장되어 있는 음악을 탐색하고 듣기 Comment[lt]=Naršyti ir klausytis muzikos iš MP3tunes paskyros Comment[lv]=Pārlūkojiet un klausieties mūziku, kura glabājas jūsu MP3tunes lietotāja kontā Comment[nb]=Bla gjennom og lytt til musikk som er lagret på Mp3tunes-kontoen din. Comment[nds]=Musik vun Dien MP3Tunes-Konto dörkieken un anhören Comment[nl]=Blader door en luister naar muziek opgeslagen op uw MP3tunes-account Comment[pl]=Przeglądaj i słuchaj muzyki przechowywanej na twoim koncie MP3tunes Comment[pt]=Navegar e escutar a música gravada na sua conta do MP3tunes Comment[pt_BR]=Navegue e ouça as músicas armazenadas na sua conta do MP3tunes Comment[ro]=Răsfoiți și ascultați muzica stocată în contul dumneavoastră MP3tunes Comment[ru]=Обзор и прослушивание музыки, сохранённой в учётной записи MP3tunes Comment[sk]=Prehliadať a počúvať hudbu uloženú na Vašom MP3tunes účte Comment[sl]=Brskajte po glasbi, ki jo imate shranjeno na svojem računu MP3tunes, in jo poslušajte Comment[sr]=Прегледајте и слушајте музику са свог налога на МП3‑тјунсу Comment[sr@ijekavian]=Прегледајте и слушајте музику са свог налога на МП3‑тјунсу Comment[sr@ijekavianlatin]=Pregledajte i slušajte muziku sa svog naloga na MP3Tunesu Comment[sr@latin]=Pregledajte i slušajte muziku sa svog naloga na MP3Tunesu Comment[sv]=Bläddra igenom och lyssna på musik lagrad på ditt Mp3tunes-konto Comment[tr]=MP3tunes hesabınızdaki müzikleri inceleyin ve dinleyin Comment[uk]=Перегляньте і прослухайте музику з вашого рахунку у MP3tunes Comment[x-test]=xxBrowse and listen to the music stored in your MP3tunes accountxx Comment[zh_CN]=浏览并收听储存在您的 MP3tunes 账户中的音乐 Comment[zh_TW]=瀏覽並聆聽儲存在您 MP3tunes 帳戶中的音樂 ServiceTypes=Amarok/Plugin X-KDE-Amarok-authors=Nikolaj Hald Nielsen X-KDE-Amarok-email=nhnFreespirit@gmail.com X-KDE-Amarok-framework-version=73 X-KDE-Amarok-name=Mp3tunesService X-KDE-Amarok-rank=100 X-KDE-Amarok-version=1 X-KDE-PluginInfo-Author=Nikolaj Hald Nielsen X-KDE-PluginInfo-Email=nhnFreespirit@gmail.com X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Category=Service X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=false X-KDE-Library=amarok_service_mp3tunes X-KDE-PluginInfo-Name=amarok_service_mp3tunes diff --git a/src/services/mp3tunes/harmonydaemon/Mp3tunesHarmonyDaemon.cpp b/src/services/mp3tunes/harmonydaemon/Mp3tunesHarmonyDaemon.cpp index d8ae295d6d..2990e098a4 100644 --- a/src/services/mp3tunes/harmonydaemon/Mp3tunesHarmonyDaemon.cpp +++ b/src/services/mp3tunes/harmonydaemon/Mp3tunesHarmonyDaemon.cpp @@ -1,411 +1,412 @@ /**************************************************************************************** * Copyright (c) 2008 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "Mp3tunesHarmonyDaemon.h" #include "mp3tunesharmonydaemonadaptor.h" #ifndef statfs #define statfs statvfs #endif -#include -#include +#include +#include +#include -Mp3tunesHarmonyDaemon::Mp3tunesHarmonyDaemon( QString identifier ) - : QCoreApplication( KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv() ) +Mp3tunesHarmonyDaemon::Mp3tunesHarmonyDaemon( QString identifier, int argc, char *argv[] ) + : QCoreApplication( argc, argv ) , m_identifier( identifier ) , m_email( QString() ) , m_pin( QString() ) , m_gerr( 0 ) , m_error( QString() ) , m_started( false ) , m_inited( false ) , m_state( Mp3tunesHarmonyDaemon::DISCONNECTED ) { allAboardTheDBus(); init(); } -Mp3tunesHarmonyDaemon::Mp3tunesHarmonyDaemon( QString identifier, QString email, QString pin ) - : QCoreApplication( KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv() ) +Mp3tunesHarmonyDaemon::Mp3tunesHarmonyDaemon( QString identifier, QString email, QString pin, int argc, char *argv[] ) + : QCoreApplication( argc, argv ) , m_identifier( identifier ) , m_email( email ) , m_pin( pin ) , m_gerr( 0 ) , m_error( QString() ) , m_started( false ) , m_inited( false ) , m_state( Mp3tunesHarmonyDaemon::DISCONNECTED ) { allAboardTheDBus(); init(); } Mp3tunesHarmonyDaemon::~Mp3tunesHarmonyDaemon() { breakConnection(); delete m_harmony; delete m_gerr; } void Mp3tunesHarmonyDaemon::setClient( Mp3tunesHarmonyClient *client ) { m_client = client; - connect( this, SIGNAL(disconnected()), - m_client, SLOT(harmonyDisconnected())); - connect( this, SIGNAL(waitingForEmail(QString)), - m_client, SLOT(harmonyWaitingForEmail(QString)) ); - connect( this, SIGNAL(connected()), - m_client, SLOT(harmonyConnected()) ); - connect( this, SIGNAL(errorSignal(QString)), - m_client, SLOT(harmonyError(QString)) ); - connect( this, SIGNAL(downloadReady(Mp3tunesHarmonyDownload)), - m_client, SLOT(harmonyDownloadReady(Mp3tunesHarmonyDownload)) ); - connect( this, SIGNAL(downloadPending(Mp3tunesHarmonyDownload)), - m_client, SLOT(harmonyDownloadPending(Mp3tunesHarmonyDownload)) ); + connect( this, &Mp3tunesHarmonyDaemon::disconnected, + m_client, &Mp3tunesHarmonyClient::harmonyDisconnected ); + connect( this, &Mp3tunesHarmonyDaemon::waitingForEmail, + m_client, &Mp3tunesHarmonyClient::harmonyWaitingForEmail ); + connect( this, &Mp3tunesHarmonyDaemon::connected, + m_client, &Mp3tunesHarmonyClient::harmonyConnected ); + connect( this, &Mp3tunesHarmonyDaemon::errorSignal, + m_client, &Mp3tunesHarmonyClient::harmonyError ); + connect( this, &Mp3tunesHarmonyDaemon::downloadReady, + m_client, &Mp3tunesHarmonyClient::harmonyDownloadReady ); + connect( this, &Mp3tunesHarmonyDaemon::downloadPending, + m_client, &Mp3tunesHarmonyClient::harmonyDownloadPending ); } bool Mp3tunesHarmonyDaemon::allAboardTheDBus() { setOrganizationName( "Amarok" ); setOrganizationDomain( "amarok.kde.org" ); setApplicationName( "Mp3tunes Harmony Daemon" ); QDBusConnectionInterface *bus = 0; bus = QDBusConnection::sessionBus().interface(); if( !bus ) { qFatal("No dbus!!"); ::exit(126); return false; } QStringList parts = this->organizationDomain().split(QLatin1Char('.'), QString::SkipEmptyParts); QString reversedDomain; if (parts.isEmpty()) reversedDomain = QLatin1String("local."); else foreach (const QString& s, parts) { reversedDomain.prepend(QLatin1Char('.')); reversedDomain.prepend(s); } const QString pidSuffix = QString::number( applicationPid() ).prepend( QLatin1String("-") ); const QString serviceName = reversedDomain + this->applicationName().remove( ' ' ) + pidSuffix; if ( bus->registerService(serviceName) == QDBusConnectionInterface::ServiceNotRegistered ) { qDebug() << "FATAL: Couldn't register name '" << serviceName << "' with DBUS - another process owns it already!" << endl; ::exit(126); return false; } qDebug() << "Registered service: " << serviceName; new Mp3tunesHarmonyDaemonAdaptor( this ); if ( QDBusConnection::sessionBus().registerObject( QLatin1String("/Mp3tunesHarmonyDaemon"), this ) ) { qDebug() << "Dbus registered"; return true; } else { qDebug() << "Dbus not registered"; return false; } } bool Mp3tunesHarmonyDaemon::daemonConnected() { return m_started; } bool Mp3tunesHarmonyDaemon::breakConnection() { if( !daemonConnected() ) { qDebug() << "Daemon not connected"; return true; } qDebug() << "Disconnecting Harmony"; GError *err; mp3tunes_harmony_disconnect(m_harmony, &err); if (err) { qDebug() << "Error disconnecting: " << err->message; /* If there is an error disconnecting something has probably gone * very wrong and reconnection should not be attempted till the user * re-initiates it */ } return true; } int Mp3tunesHarmonyDaemon::init() { qDebug() << "Begin initing"; #if !GLIB_CHECK_VERSION(2,36,0) /* g_type_init required for using the GObjects in versions of glib older than 2.36. */ g_type_init (); #endif m_harmony = mp3tunes_harmony_new(); /* Set the error signal handler. */ g_signal_connect( m_harmony, "error", G_CALLBACK( signalErrorHandler ), this ); /* Set the state change signal handler. */ g_signal_connect( m_harmony, "state_change", G_CALLBACK(signalStateChangeHandler ), this ); /* Set the download signal handler. */ g_signal_connect( m_harmony, "download_ready", G_CALLBACK(signalDownloadReadyHandler ), this ); g_signal_connect( m_harmony, "download_pending", G_CALLBACK(signalDownloadPendingHandler ), this ); qDebug() << "Initing 1"; mp3tunes_harmony_set_identifier( m_harmony, convertToChar( m_identifier ) ); if( !m_email.isEmpty() ) mp3tunes_harmony_set_email( m_harmony, convertToChar( m_email ) ); if( !m_pin.isEmpty() ) mp3tunes_harmony_set_pin( m_harmony, convertToChar( m_pin ) ); mp3tunes_harmony_set_device_attribute( m_harmony, "device-description", "Amarok 2 Test Daemon"); /* Linux specific variable for getting total and available sizes for the * file system */ qDebug() << "Initing 2"; struct statfs fsstats; unsigned long long total_bytes; unsigned long long available_bytes; if ( statfs( ".", &fsstats ) != 0 ) { perror( "statfs failed" ); return -1; } total_bytes = fsstats.f_bsize * fsstats.f_blocks; available_bytes = fsstats.f_bsize * fsstats.f_bavail; mp3tunes_harmony_set_device_attribute( m_harmony, "total-bytes", &total_bytes ); mp3tunes_harmony_set_device_attribute( m_harmony, "available-bytes", &available_bytes ); qDebug() << "Done initing"; m_inited = true; return 0; } QString Mp3tunesHarmonyDaemon::makeConnection() { if( !m_inited ) return "Daemon not init()ed yet. Connection not attempted."; m_gerr = 0; mp3tunes_harmony_connect( m_harmony, &m_gerr ); /* Check for errors on the connection */ if ( m_gerr ) { g_print( "Error: %s\n", m_gerr->message ); } if ( m_gerr ) { m_started = false; return "Error: " + QString( m_gerr->message ); } else { m_started = true; return "All good!"; } } QString Mp3tunesHarmonyDaemon::pin() const { return QString( mp3tunes_harmony_get_pin( m_harmony ) ); } QString Mp3tunesHarmonyDaemon::email() const { return QString( mp3tunes_harmony_get_email( m_harmony ) ); } QString Mp3tunesHarmonyDaemon::error() const { return m_error; } void Mp3tunesHarmonyDaemon::setState( HarmonyState state ) { m_state = state; } void Mp3tunesHarmonyDaemon::setError( const QString &error ) { m_error = error; } void Mp3tunesHarmonyDaemon::emitError() { emit( errorSignal( m_error ) ); } void Mp3tunesHarmonyDaemon::emitWaitingForEmail( const QString &pin ) { emit( waitingForEmail( pin ) ); } void Mp3tunesHarmonyDaemon::emitWaitingForPin() { emit( waitingForPin() ); } void Mp3tunesHarmonyDaemon::emitConnected() { emit( connected() ); } void Mp3tunesHarmonyDaemon::emitDisconnected() { emit( disconnected() ); } void Mp3tunesHarmonyDaemon::emitDownloadReady( const Mp3tunesHarmonyDownload &download ) { emit( downloadReady( download ) ); } void Mp3tunesHarmonyDaemon::emitDownloadPending( const Mp3tunesHarmonyDownload &download ) { emit( downloadPending( download ) ); } void Mp3tunesHarmonyDaemon::signalErrorHandler(MP3tunesHarmony* harmony, gpointer null_pointer ) { GError *err = 0; Q_UNUSED( null_pointer ) g_print("Fatal Error: %s\n", harmony->error->message ); theDaemon->setError( QString( harmony->error->message ) ); theDaemon->emitError(); mp3tunes_harmony_disconnect(harmony, &err); if( err ) { g_print( "Error disconnecting: %s\n", err->message ); /* If there is an error disconnecting something has probably gone * very wrong and reconnection should not be attempted till the user * re-initiates it */ return; } } void Mp3tunesHarmonyDaemon::signalStateChangeHandler( MP3tunesHarmony* harmony, guint32 state, gpointer null_pointer ) { qDebug() << "inside signalStateChangeHandler"; Q_UNUSED( null_pointer ) switch ( state ) { case MP3TUNES_HARMONY_STATE_DISCONNECTED: g_print( "Disconnected.\n" ); qDebug() << "Disconnected"; theDaemon->setState( Mp3tunesHarmonyDaemon::DISCONNECTED ); theDaemon->emitDisconnected(); /* Do nothing here */ break; case MP3TUNES_HARMONY_STATE_CONNECTED: g_print( "Connected! Waiting for download requests!\n" ); qDebug() << "Connected! Waiting for download requests!\n"; theDaemon->setState( Mp3tunesHarmonyDaemon::CONNECTED ); theDaemon->emitConnected(); /* At this point, it would be best to store the pin, if you haven't * already, and the email in some somewhat permenant storage for * when reauthenticating. */ break; case MP3TUNES_HARMONY_STATE_WAITING_FOR_PIN: g_print( "Connection in process!\n" ); qDebug() << "Connection in process!\n"; theDaemon->setState( Mp3tunesHarmonyDaemon::WAITING_FOR_PIN ); theDaemon->emitWaitingForPin(); /* At this point, just update the user status. */ break; case MP3TUNES_HARMONY_STATE_WAITING_FOR_EMAIL: g_print( "Please login to mp3tunes.com and add the pin '%s' to your devices.\n", mp3tunes_harmony_get_pin( harmony ) ); qDebug() << "Please login to mp3tunes.com and add the pin '%s' to your devices. " << mp3tunes_harmony_get_pin( harmony ); theDaemon->setState( Mp3tunesHarmonyDaemon::WAITING_FOR_EMAIL ); theDaemon->emitWaitingForEmail( theDaemon->pin() ); /* At this point, it would be best to store the pin in case the * network connection drops. As well, display to the user a status * message to have them perform the website authentication action. */ break; } } void Mp3tunesHarmonyDaemon::signalDownloadReadyHandler( MP3tunesHarmony* harmony, gpointer void_mp3tunes_harmony_download, gpointer null_pointer ) { Q_UNUSED( harmony ) Q_UNUSED( null_pointer ) mp3tunes_harmony_download_t *download = ( mp3tunes_harmony_download_t* ) void_mp3tunes_harmony_download; Mp3tunesHarmonyDownload wrappedDownload( download ); theDaemon->emitDownloadReady( wrappedDownload ); mp3tunes_harmony_download_deinit( &download ); } void Mp3tunesHarmonyDaemon::signalDownloadPendingHandler( MP3tunesHarmony* harmony, gpointer void_mp3tunes_harmony_download, gpointer null_pointer ) { Q_UNUSED( harmony ) Q_UNUSED( null_pointer ) mp3tunes_harmony_download_t *download = ( mp3tunes_harmony_download_t* ) void_mp3tunes_harmony_download; Mp3tunesHarmonyDownload wrappedDownload( download ); theDaemon->emitDownloadPending( wrappedDownload ); } char * Mp3tunesHarmonyDaemon::convertToChar ( const QString &source ) const { - QByteArray b = source.toAscii(); + QByteArray b = source.toLatin1(); const char *c_tok = b.constData(); char * ret = ( char * ) malloc ( strlen ( c_tok ) + 1 ); strcpy ( ret, c_tok ); return ret; } diff --git a/src/services/mp3tunes/harmonydaemon/Mp3tunesHarmonyDaemon.h b/src/services/mp3tunes/harmonydaemon/Mp3tunesHarmonyDaemon.h index 72ea67fd2e..292b633ded 100644 --- a/src/services/mp3tunes/harmonydaemon/Mp3tunesHarmonyDaemon.h +++ b/src/services/mp3tunes/harmonydaemon/Mp3tunesHarmonyDaemon.h @@ -1,256 +1,256 @@ /**************************************************************************************** * Copyright (c) 2008 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef MP3TUNESHARMONYDAEMON_H #define MP3TUNESHARMONYDAEMON_H #include "Mp3tunesHarmonyDownload.h" #include "Mp3tunesHarmonyClient.h" -#include +#include #include #include extern "C" { // Get libmp3tunes declarations #include "../libmp3tunes/harmony.h" #include "../libmp3tunes/locker.h" #include #include #include #include #include } /** * A daemon that receives notfications from mp3tunes' * servers about new/changed tracks that can be synced. * * Because this classes implements libmp3tunes which uses * GLIB's c-style callbacks, all the callbacks must be static. * This requires some black magic on my part to keep the OO * facade intact. The solution is a global variable: theDaemon * delcared after this class. Your code must #define DEFINE_HARMONY * and instantiate a new Mp3tunesHarmonyDaemon for theDaemon. * @author Casey Link */ class Mp3tunesHarmonyDaemon : public QCoreApplication { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.amarok.Mp3tunesHarmonyDaemon") public: /** * For the first time run, before we have an email and pin to authenticate */ - Mp3tunesHarmonyDaemon( QString identifier ); + Mp3tunesHarmonyDaemon( QString identifier, int argc, char *argv[] ); /** * For subsequent logins */ - Mp3tunesHarmonyDaemon( QString identifier, QString email, QString pin ); + Mp3tunesHarmonyDaemon( QString identifier, QString email, QString pin, int argc, char *argv[] ); ~Mp3tunesHarmonyDaemon(); /** * Stats the daemon by intiating the connection Harmony connection. */ int init(); /** * Sets the client type that the daemon will send signals to. */ void setClient( Mp3tunesHarmonyClient *client ); /** * The possible states the daemon can be in. * Before init() it is DISONNECTED */ enum HarmonyState { DISCONNECTED, CONNECTED, WAITING_FOR_PIN, WAITING_FOR_EMAIL }; /* * Used by the static callbacks * DO NOT CALL THESE METHODS */ void setState( HarmonyState state ); void setError( const QString &error ); /* * Used by the static callbacks * DO NOT CALL THESE METHODS */ void emitError(); void emitWaitingForEmail( const QString &pin ); void emitWaitingForPin(); void emitConnected(); void emitDisconnected(); void emitDownloadReady( const Mp3tunesHarmonyDownload &download ); void emitDownloadPending( const Mp3tunesHarmonyDownload &download ); Q_SIGNALS: /* The actual signals that get emitted */ void waitingForEmail( const QString &pin ); void waitingForPin(); void connected(); void disconnected(); void errorSignal( const QString &error ); /* signalDownloadReady * this signal is emitted when a track is ready to be downloaded. */ void downloadReady( const Mp3tunesHarmonyDownload &download ); /* signalDownloadPending * this signal is emitted as soon as a download message is received. * it may or may not be ready. the library sends this signal before * adding the download to its own queue */ void downloadPending( const Mp3tunesHarmonyDownload &download ); public Q_SLOTS: /** * Returns the pin */ QString pin() const; /** * Returns the Harmony Email used for authentication */ QString email() const; /** * Returns the latest error message. */ QString error() const; /** * Determines if the daemon is currently connected to the harmony servers. * @return true if the daemon is connected. * false if the daemon is not connected. */ bool daemonConnected(); /** * Disconnects the daemon if it is connected. * @return true if the daemon is disconnected OR if the daemon was disconnected * false if the breaking the connect failed */ bool breakConnection(); QString makeConnection(); private: /* Error signal handler. * * This signal is emitted whenever there is a user fixable error from inside the * library. Most of these errors are from inside of the Jabber library. * * Whenever this signal handler is called, harmony->error will be set to a valid * GError pointer. The message field of that structure will contain the error * and should be displayed to the user and the connection reset by calling * mp3tunes_harmony_disconnect and a reconnection user initiated. */ static void signalErrorHandler( MP3tunesHarmony* harmony, gpointer null_pointer ); /* State change signal handler. * * This signal is emitted whenever the state of the connection changes during * the Harmony authentication process. The state variable will be set to one of * the values of harmony_state_t with the following meanings. * * MP3TUNES_HARMONY_STATE_DISCONNECTED: * The connection to the server has been disconnected. Occurs a couple of * times during the authentication process. Nothing to act on unless was * already in the CONNECTED state. * * MP3TUNES_HARMONY_STATE_CONNECTED: * The connection completed successfully. All to be done from here is to * associate the download handler if needed and wait for download messages. * * MP3TUNES_HARMONY_STATE_WAITING_FOR_PIN: * The client has authenticated and is waiting for the response to a * harmonyPin message. Unless there is a problem with the Conductor this * state should be left almost immediately and moved back into disconnected * before going to WAITING_FOR_EMAIL. Useful for having a progress message * during authentication. * * MP3TUNES_HARMONY_STATE_WAITING_FOR_EMAIL: * The client has authenticated and is waiting for a response to the * harmonyEmail message. In this state, the action to take is to display to * the user the pin, found by calling mp3tunes_harmony_get_pin, and request * for them to log into mp3tunes.com and have them add the pin to their * devices tab. Upon receiving the reply to this you will be authenticated. */ static void signalStateChangeHandler( MP3tunesHarmony* harmony, guint32 state, gpointer null_pointer ); /* signalDownloadReadyHandler * this signal is emitted when a track is ready to be downloaded. */ static void signalDownloadReadyHandler( MP3tunesHarmony* harmony, gpointer void_mp3tunes_harmony_download, gpointer null_pointer ); /* signalDownloadPendingHandler * this signal is emitted as soon as a download message is received. * it may or may not be ready. the library sends this signal before * adding the download to its own queue */ static void signalDownloadPendingHandler( MP3tunesHarmony* harmony, gpointer void_mp3tunes_harmony_download, gpointer null_pointer ); /** * Converts a QString into a char* */ char* convertToChar( const QString &source ) const; /** * Inits the D-Dbus interface. */ bool allAboardTheDBus(); MP3tunesHarmony * m_harmony; QString m_identifier; // the initial identifier used for authentication QString m_email; //used for repeat authentication QString m_pin; //used for repeat authentication GError *m_gerr; // master GError QString m_error; // error message to display to user bool m_started; // true if the connection has been established bool m_inited; // true if the daemon is ready to connect HarmonyState m_state; //current state of the harmony daemon Mp3tunesHarmonyClient *m_client; // the daemon client }; /* * The global variable used by the static callbacks. * G_CALLBACK() requires a pointer to a member function, * and because this is c++ that member function must be static. * However, since I want to edit non-static members in those * callbacks I created a workaround by defining a global variable, * 'theDaemon', which is an instantiation of a Mp3tunesHarmonyDaemon * and call mutators on it from the static callbacks. */ #ifdef DEFINE_HARMONY #define HARMONY #else #define HARMONY extern #endif HARMONY Mp3tunesHarmonyDaemon* theDaemon; #endif diff --git a/src/services/mp3tunes/harmonydaemon/main.cpp b/src/services/mp3tunes/harmonydaemon/main.cpp index 8086efb588..b37c2c34f2 100644 --- a/src/services/mp3tunes/harmonydaemon/main.cpp +++ b/src/services/mp3tunes/harmonydaemon/main.cpp @@ -1,75 +1,74 @@ /**************************************************************************************** * Copyright (c) 2008 Casey Link * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef DEFINE_HARMONY #define DEFINE_HARMONY #endif #include "Mp3tunesHarmonyDaemon.h" #include "AmarokClient.h" -#include -#include -#include + #include -#include +#include +#include + +#include +#include int main( int argc, char *argv[] ) { - const K4AboutData about( "amarokmp3tunesharmonydaemon", "amarok", - ki18n( "Amarok's MP3tunes Harmony Daemon" ), "0.1", - ki18n( "Handles AutoSync for the MP3tunes service in Amarok." ), K4AboutData::License_GPL, - ki18n( "(C) 2008, Casey Link" ), - ki18n( "IRC:\nserver: irc.freenode.net / channels: #amarok, #amarok.de, #amarok.es, #amarok.fr\n\nFeedback:\namarok@kde.org" ), - I18N_NOOP( "http://amarok.kde.org" ) ); - - KCmdLineArgs::reset(); - KCmdLineArgs::init( argc, argv, &about ); //calls KCmdLineArgs::addStdCmdLineOptions() + QCoreApplication app( argc, argv ); + const KAboutData about( "amarokmp3tunesharmonydaemon", + "amarok", + "0.1", + i18n( "Amarok's MP3tunes Harmony Daemon" ), + KAboutLicense::GPL, + i18n( "(C) 2008, Casey Link" ), + i18n( "Handles AutoSync for the MP3tunes service in Amarok." ), + i18n( "IRC:\nserver: irc.freenode.net / channels: #amarok, #amarok.de, #amarok.es, #amarok.fr\n\nFeedback:\namarok@kde.org" ), + I18N_NOOP( "http://amarok.kde.org" ) ); + KAboutData::setApplicationData( about ); - KCmdLineOptions options; - options.add("+identifier", ki18n( "The identifier the daemon should use." )); - options.add("+[email]", ki18n( "The email to be used for authentication." )); - options.add("+[pin]", ki18n( "The pin to be used for authentication." )); - KCmdLineArgs::addCmdLineOptions( options ); //add our own options + QCommandLineParser parser; + parser.setApplicationDescription( about.shortDescription() ); + parser.addHelpOption(); + parser.addVersionOption(); + parser.addOption( { "identifier", i18n( "The identifier the daemon should use." ) } ); + parser.addOption( { "email", i18n( "The email to be used for authentication." ) } ); + parser.addOption( { "pin", i18n( "The pin to be used for authentication." ) } ); + parser.process( app ); - const KCmdLineArgs* const args = KCmdLineArgs::parsedArgs(); + QString ident = parser.value( "identifier" ); + QString email = parser.value( "email" ); + QString pin = parser.value( "pin" ); - QString ident; - QString email; - QString pin; - if ( args->count() < 1 || args->count() > 3 ) + if( ident.isEmpty() ) return -1; - if( args->count() > 0 ) - ident = args->arg( 0 ); - if( args->count() == 3 ) - { - email = args->arg( 1 ); - pin = args->arg( 2 ); - } if( email.isEmpty() && pin.isEmpty() ) - theDaemon = new Mp3tunesHarmonyDaemon( ident ); + theDaemon = new Mp3tunesHarmonyDaemon( ident, argc, argv ); else - theDaemon = new Mp3tunesHarmonyDaemon( ident, email, pin ); + theDaemon = new Mp3tunesHarmonyDaemon( ident, email, pin, argc, argv ); Mp3tunesAmarokClient *client = new Mp3tunesAmarokClient(); theDaemon->setClient( client ); qDebug() << "Starting main event loop"; - QCoreApplication::exec(); + return QCoreApplication::exec(); } diff --git a/src/services/opmldirectory/AddOpmlWidget.ui b/src/services/opmldirectory/AddOpmlWidget.ui index 6f1c368bad..52d7c92493 100644 --- a/src/services/opmldirectory/AddOpmlWidget.ui +++ b/src/services/opmldirectory/AddOpmlWidget.ui @@ -1,113 +1,113 @@ AddOpmlWidget 0 0 530 117 0 0 530 117 QLayout::SetMinimumSize 10 Add a local or remote OPML file to be included in the list. true Qt::Horizontal QSizePolicy::Fixed 100 20 URL: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Title: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + If left blank the title from the OPML will be used. KUrlRequester QFrame
kurlrequester.h
1
- KLineEdit + QLineEdit QLineEdit -
klineedit.h
+
qlineedit.h
changed() slotChanged()
diff --git a/src/services/opmldirectory/CMakeLists.txt b/src/services/opmldirectory/CMakeLists.txt index ffdd5034db..f7a462b639 100644 --- a/src/services/opmldirectory/CMakeLists.txt +++ b/src/services/opmldirectory/CMakeLists.txt @@ -1,38 +1,37 @@ include_directories( ../ ../.. ../../core-impl/collections ../../statusbar ${CMAKE_CURRENT_BINARY_DIR}/../../.. ) ########### next target ############### set(amarok_service_opmldirectory_PART_SRCS OpmlDirectoryService.cpp OpmlDirectoryMeta.cpp OpmlDirectoryInfoParser.cpp OpmlDirectoryModel.cpp OpmlDirectoryView.cpp ) ki18n_wrap_ui( amarok_service_opmldirectory_PART_SRCS AddOpmlWidget.ui ) add_library(amarok_service_opmldirectory MODULE ${amarok_service_opmldirectory_PART_SRCS}) target_link_libraries(amarok_service_opmldirectory amarokcore amaroklib - KF5::KDELibs4Support - + KF5::IconThemes KF5::KIOCore KF5::ThreadWeaver ) install(TARGETS amarok_service_opmldirectory DESTINATION ${PLUGIN_INSTALL_DIR} ) + kcoreaddons_desktop_to_json(amarok_service_opmldirectory amarok_service_opmldirectory.desktop SERVICE_TYPES ${CMAKE_SOURCE_DIR}/src/amarok-plugin.desktop) ########### install files ############### - install( FILES amarok_service_opmldirectory.desktop DESTINATION ${SERVICES_INSTALL_DIR}) install( FILES podcast_directory.opml DESTINATION ${DATA_INSTALL_DIR}/amarok/data) diff --git a/src/services/opmldirectory/OpmlDirectoryInfoParser.cpp b/src/services/opmldirectory/OpmlDirectoryInfoParser.cpp index ea44956c5d..549b6c3d29 100644 --- a/src/services/opmldirectory/OpmlDirectoryInfoParser.cpp +++ b/src/services/opmldirectory/OpmlDirectoryInfoParser.cpp @@ -1,128 +1,128 @@ /**************************************************************************************** * Copyright (c) 2008 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "OpmlDirectoryInfoParser.h" #include "core/support/Debug.h" #include "core/support/Components.h" #include "core/interfaces/Logger.h" #include "OpmlDirectoryMeta.h" -#include - #include +#include + using namespace Meta; OpmlDirectoryInfoParser::OpmlDirectoryInfoParser() : InfoParserBase() - , m_rssDownloadJob( ) + , m_rssDownloadJob( Q_NULLPTR ) { } OpmlDirectoryInfoParser::~OpmlDirectoryInfoParser() { } void OpmlDirectoryInfoParser::getInfo(ArtistPtr artist) { AMAROK_NOTIMPLEMENTED Q_UNUSED( artist ); } void OpmlDirectoryInfoParser::getInfo(AlbumPtr album) { AMAROK_NOTIMPLEMENTED Q_UNUSED( album ); } void OpmlDirectoryInfoParser::getInfo( TrackPtr track ) { DEBUG_BLOCK showLoading( i18n( "Loading Podcast Info..." ) ); OpmlDirectoryFeed * feed = dynamic_cast( track.data() ); if ( !feed ) return; debug() << "OpmlDirectoryInfoParser: getInfo about feed: " << feed->uidUrl(); - m_rssDownloadJob = KIO::storedGet( feed->uidUrl(), KIO::Reload, KIO::HideProgressInfo ); + m_rssDownloadJob = KIO::storedGet( QUrl( feed->uidUrl() ), KIO::Reload, KIO::HideProgressInfo ); Amarok::Components::logger()->newProgressOperation( m_rssDownloadJob, i18n( "Fetching Podcast Info" ) ); - connect( m_rssDownloadJob, SIGNAL(result(KJob*)), SLOT(rssDownloadComplete(KJob*)) ); + connect( m_rssDownloadJob, &KJob::result, this, &OpmlDirectoryInfoParser::rssDownloadComplete ); } void OpmlDirectoryInfoParser::rssDownloadComplete(KJob * downLoadJob) { if ( downLoadJob->error() ) { //TODO: error handling here return ; } if ( downLoadJob != m_rssDownloadJob ) return ; //not the right job, so let's ignore it QString rssString = ((KIO::StoredTransferJob* ) downLoadJob)->data(); debug() << "rss: " << rssString; QDomDocument doc( "reply" ); if ( !doc.setContent( rssString ) ) { debug() << "could not set reply document to given RSS string"; return; } //there might be an rss node, there might not... QDomElement element = doc.firstChildElement( "rss" ); if ( !element.isNull() ) { element = element.firstChildElement( "channel" ); } else { element = doc.firstChildElement( "channel" ); } QString description = element.firstChildElement( "description" ).text(); QString title = element.firstChildElement( "title" ).text(); QString imageUrl; QDomElement image = element.firstChildElement( "image" ); if ( !image.isNull() ) imageUrl = image.firstChildElement( "url" ).text(); QString infoHtml = ""; infoHtml += "
"; infoHtml += title; infoHtml += "

"; if ( !imageUrl.isEmpty() ) infoHtml += ""; infoHtml += "

" + description; infoHtml += ""; emit ( info( infoHtml ) ); downLoadJob->deleteLater(); } diff --git a/src/services/opmldirectory/OpmlDirectoryInfoParser.h b/src/services/opmldirectory/OpmlDirectoryInfoParser.h index 455d2ab9b6..8fe04f7f54 100644 --- a/src/services/opmldirectory/OpmlDirectoryInfoParser.h +++ b/src/services/opmldirectory/OpmlDirectoryInfoParser.h @@ -1,49 +1,48 @@ /**************************************************************************************** * Copyright (c) 2008 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef OPMLDIRECTORYINFOPARSER_H #define OPMLDIRECTORYINFOPARSER_H #include #include -#include /** * Handles the fetching and processing of Jamendo specific information for meta items */ class OpmlDirectoryInfoParser : public InfoParserBase { Q_OBJECT public: OpmlDirectoryInfoParser(); ~OpmlDirectoryInfoParser(); virtual void getInfo( Meta::ArtistPtr artist ); virtual void getInfo( Meta::AlbumPtr album ); virtual void getInfo( Meta::TrackPtr track ); private: KJob * m_rssDownloadJob; private Q_SLOTS: void rssDownloadComplete( KJob *downLoadJob ); }; #endif diff --git a/src/services/opmldirectory/OpmlDirectoryMeta.h b/src/services/opmldirectory/OpmlDirectoryMeta.h index a13f9a950f..f5785dd59b 100644 --- a/src/services/opmldirectory/OpmlDirectoryMeta.h +++ b/src/services/opmldirectory/OpmlDirectoryMeta.h @@ -1,67 +1,67 @@ /**************************************************************************************** * Copyright (c) 2008 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef OPMLDIRECTORYMETA_H #define OPMLDIRECTORYMETA_H #include "../ServiceMetaBase.h" #include #include #include #include class OpmlDirectoryService; class OpmlDirectoryMetaFactory : public ServiceMetaFactory { public: OpmlDirectoryMetaFactory( const QString &dbPrefix, OpmlDirectoryService *service ); virtual ~OpmlDirectoryMetaFactory() {} virtual Meta::TrackPtr createTrack( const QStringList &rows ); virtual Meta::AlbumPtr createAlbum( const QStringList &rows ); }; namespace Meta { class OpmlDirectoryFeed; class OpmlDirectoryCategory; -typedef KSharedPtr OpmlDirectoryFeedPtr; -typedef KSharedPtr OpmlDirectoryCategoryPtr; +typedef AmarokSharedPointer OpmlDirectoryFeedPtr; +typedef AmarokSharedPointer OpmlDirectoryCategoryPtr; class OpmlDirectoryFeed : public ServiceTrack { public: OpmlDirectoryFeed( const QString &name ); OpmlDirectoryFeed( const QStringList &resultRow ); }; class OpmlDirectoryCategory : public ServiceAlbum { public: OpmlDirectoryCategory( const QString &name ); OpmlDirectoryCategory( const QStringList &resultRow ); }; } #endif diff --git a/src/services/opmldirectory/OpmlDirectoryModel.cpp b/src/services/opmldirectory/OpmlDirectoryModel.cpp index f7f9f06df1..a1f97ac657 100644 --- a/src/services/opmldirectory/OpmlDirectoryModel.cpp +++ b/src/services/opmldirectory/OpmlDirectoryModel.cpp @@ -1,511 +1,515 @@ /**************************************************************************************** * Copyright (c) 2010 Bart Cerneels . * ****************************************************************************************/ #include "OpmlDirectoryModel.h" #include "core/support/Amarok.h" #include "MainWindow.h" #include "OpmlParser.h" #include "OpmlWriter.h" #include "core/support/Debug.h" //included to access defaultPodcasts() #include "playlistmanager/PlaylistManager.h" #include "core/podcasts/PodcastProvider.h" #include "ui_AddOpmlWidget.h" -#include - #include +#include +#include OpmlDirectoryModel::OpmlDirectoryModel( QUrl outlineUrl, QObject *parent ) : QAbstractItemModel( parent ) , m_rootOpmlUrl( outlineUrl ) { //fetchMore will be called by the view m_addOpmlAction = new QAction( QIcon::fromTheme( "list-add" ), i18n( "Add OPML" ), this ); - connect( m_addOpmlAction, SIGNAL(triggered()), SLOT(slotAddOpmlAction()) ); + connect( m_addOpmlAction, &QAction::triggered, this, &OpmlDirectoryModel::slotAddOpmlAction ); m_addFolderAction = new QAction( QIcon::fromTheme( "folder-add" ), i18n( "Add Folder"), this ); - connect( m_addFolderAction, SIGNAL(triggered()), SLOT(slotAddFolderAction()) ); + connect( m_addFolderAction, &QAction::triggered, this, &OpmlDirectoryModel::slotAddFolderAction ); } OpmlDirectoryModel::~OpmlDirectoryModel() { } QModelIndex OpmlDirectoryModel::index( int row, int column, const QModelIndex &parent ) const { if( !parent.isValid() ) { if( m_rootOutlines.isEmpty() || m_rootOutlines.count() <= row ) return QModelIndex(); else return createIndex( row, column, m_rootOutlines[row] ); } OpmlOutline *parentOutline = static_cast( parent.internalPointer() ); if( !parentOutline ) return QModelIndex(); if( !parentOutline->hasChildren() || parentOutline->children().count() <= row ) return QModelIndex(); return createIndex( row, column, parentOutline->children()[row] ); } Qt::ItemFlags OpmlDirectoryModel::flags( const QModelIndex &idx ) const { if( !idx.isValid() ) return Qt::ItemIsDropEnabled; OpmlOutline *outline = static_cast( idx.internalPointer() ); if( outline && !outline->attributes().contains( "type" ) ) //probably a folder return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; return QAbstractItemModel::flags( idx ); } QModelIndex OpmlDirectoryModel::parent( const QModelIndex &idx ) const { if( !idx.isValid() ) return QModelIndex(); - debug() << idx; +// debug() << idx; OpmlOutline *outline = static_cast( idx.internalPointer() ); if( outline->isRootItem() ) return QModelIndex(); OpmlOutline *parentOutline = outline->parent(); int childIndex; if( parentOutline->isRootItem() ) childIndex = m_rootOutlines.indexOf( parentOutline ); else childIndex = parentOutline->parent()->children().indexOf( parentOutline ); return createIndex( childIndex, 0, parentOutline ); } int OpmlDirectoryModel::rowCount( const QModelIndex &parent ) const { if( !parent.isValid() ) return m_rootOutlines.count(); OpmlOutline *outline = static_cast( parent.internalPointer() ); if( !outline || !outline->hasChildren() ) return 0; else return outline->children().count(); } bool OpmlDirectoryModel::hasChildren( const QModelIndex &parent ) const { debug() << parent; if( !parent.isValid() ) return !m_rootOutlines.isEmpty(); OpmlOutline *outline = static_cast( parent.internalPointer() ); if( !outline ) return false; if( outline->hasChildren() ) return true; return outline->attributes().value( "type" ) == "include"; } int OpmlDirectoryModel::columnCount( const QModelIndex &parent ) const { Q_UNUSED(parent) return 1; } QVariant OpmlDirectoryModel::data( const QModelIndex &idx, int role ) const { if( !idx.isValid() ) { if( role == ActionRole ) { QList actions; actions << m_addOpmlAction << m_addFolderAction; return QVariant::fromValue( actions ); } return QVariant(); } OpmlOutline *outline = static_cast( idx.internalPointer() ); if( !outline ) return QVariant(); switch( role ) { case Qt::DisplayRole: return outline->attributes()["text"]; case Qt::DecorationRole: return m_imageMap.contains( outline ) ? m_imageMap.value( outline ) : QVariant(); case ActionRole: if( outline->opmlNodeType() == RegularNode ) //probably a folder { //store the index the new item should get added to m_addOpmlAction->setData( QVariant::fromValue( idx ) ); m_addFolderAction->setData( QVariant::fromValue( idx ) ); return QVariant::fromValue( QActionList() << m_addOpmlAction << m_addFolderAction ); } + debug() << outline->opmlNodeType(); return QVariant(); default: return QVariant(); } return QVariant(); } bool OpmlDirectoryModel::setData( const QModelIndex &idx, const QVariant &value, int role ) { Q_UNUSED(role); if( !idx.isValid() ) return false; OpmlOutline *outline = static_cast( idx.internalPointer() ); if( !outline ) return false; outline->mutableAttributes()["text"] = value.toString(); saveOpml( m_rootOpmlUrl ); return true; } bool OpmlDirectoryModel::removeRows( int row, int count, const QModelIndex &parent ) { if( !parent.isValid() ) { if( m_rootOutlines.count() >= ( row + count ) ) { beginRemoveRows( parent, row, row + count - 1 ); for( int i = 0; i < count; i++ ) m_rootOutlines.removeAt( row ); endRemoveRows(); saveOpml( m_rootOpmlUrl ); return true; } return false; } OpmlOutline *outline = static_cast( parent.internalPointer() ); if( !outline ) return false; if( !outline->hasChildren() || outline->children().count() < ( row + count ) ) return false; beginRemoveRows( parent, row, row + count -1 ); for( int i = 0; i < count - 1; i++ ) outline->mutableChildren().removeAt( row ); endRemoveRows(); saveOpml( m_rootOpmlUrl ); return true; } void OpmlDirectoryModel::saveOpml( const QUrl &saveLocation ) { if( !saveLocation.isLocalFile() ) { //TODO:implement error() << "can not save OPML to remote location"; return; } QFile *opmlFile = new QFile( saveLocation.toLocalFile(), this ); if( !opmlFile->open( QIODevice::WriteOnly | QIODevice::Truncate ) ) { error() << "could not open OPML file for writing " << saveLocation.url(); return; } QMap headerData; //TODO: set header data such as date OpmlWriter *opmlWriter = new OpmlWriter( m_rootOutlines, headerData, opmlFile ); - connect( opmlWriter, SIGNAL(result(int)), SLOT(slotOpmlWriterDone(int)) ); + connect( opmlWriter, &OpmlWriter::result, this, &OpmlDirectoryModel::slotOpmlWriterDone ); opmlWriter->run(); } void OpmlDirectoryModel::slotOpmlWriterDone( int result ) { Q_UNUSED( result ) OpmlWriter *writer = qobject_cast( QObject::sender() ); Q_ASSERT( writer ); writer->device()->close(); delete writer; } OpmlNodeType OpmlDirectoryModel::opmlNodeType( const QModelIndex &idx ) const { OpmlOutline *outline = static_cast( idx.internalPointer() ); return outline->opmlNodeType(); } void OpmlDirectoryModel::slotAddOpmlAction() { QModelIndex parentIdx = QModelIndex(); QAction *action = qobject_cast( sender() ); if( action ) { parentIdx = action->data().value(); } - KDialog *dialog = new KDialog( The::mainWindow() ); - dialog->setCaption( i18nc( "Heading of Add OPML dialog", "Add OPML" ) ); - dialog->setButtons( KDialog::Ok | KDialog::Cancel ); + QDialog *dialog = new QDialog( The::mainWindow() ); + dialog->setLayout( new QVBoxLayout ); + dialog->setWindowTitle( i18nc( "Heading of Add OPML dialog", "Add OPML" ) ); QWidget *opmlAddWidget = new QWidget( dialog ); + dialog->layout()->addWidget( opmlAddWidget ); Ui::AddOpmlWidget widget; widget.setupUi( opmlAddWidget ); widget.urlEdit->setMode( KFile::File ); - dialog->setMainWidget( opmlAddWidget ); + auto buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, dialog ); + dialog->layout()->addWidget( buttonBox ); + connect( buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept ); + connect( buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject ); if( dialog->exec() != QDialog::Accepted ) return; QString url = widget.urlEdit->url().url(); QString title = widget.titleEdit->text(); debug() << QString( "creating a new OPML outline with url = %1 and title \"%2\"." ).arg( url, title ); OpmlOutline *outline = new OpmlOutline(); outline->addAttribute( "type", "include" ); outline->addAttribute( "url", url ); if( !title.isEmpty() ) outline->addAttribute( "text", title ); //Folder icon with down-arrow emblem - m_imageMap.insert( outline, QIcon::fromTheme( "folder", 0, QStringList( "go-down" ) ).pixmap( 24, 24 ) ); + m_imageMap.insert( outline, QIcon::fromTheme( "folder-download", QIcon::fromTheme( "go-down" ) ).pixmap( 24, 24 ) ); QModelIndex newIdx = addOutlineToModel( parentIdx, outline ); //TODO: force the view to expand the folder (parentIdx) so the new node is shown //if the title is missing, start parsing the OPML so we can get it from the feed if( outline->attributes().contains( "text" ) ) saveOpml( m_rootOpmlUrl ); else fetchMore( newIdx ); //saves OPML after receiving the title. delete dialog; } void OpmlDirectoryModel::slotAddFolderAction() { QModelIndex parentIdx = QModelIndex(); QAction *action = qobject_cast( sender() ); if( action ) { parentIdx = action->data().value(); } OpmlOutline *outline = new OpmlOutline(); outline->addAttribute( "text", i18n( "New Folder" ) ); m_imageMap.insert( outline, QIcon::fromTheme( "folder" ).pixmap( 24, 24 ) ); addOutlineToModel( parentIdx, outline ); //TODO: trigger edit of the new folder saveOpml( m_rootOpmlUrl ); } bool OpmlDirectoryModel::canFetchMore( const QModelIndex &parent ) const { debug() << parent; //already fetched or just started? if( rowCount( parent ) || m_currentFetchingMap.values().contains( parent ) ) return false; if( !parent.isValid() ) return m_rootOutlines.isEmpty(); OpmlOutline *outline = static_cast( parent.internalPointer() ); return outline && ( outline->attributes().value( "type" ) == "include" ); } void OpmlDirectoryModel::fetchMore( const QModelIndex &parent ) { debug() << parent; if( m_currentFetchingMap.values().contains( parent ) ) { error() << "trying to start second fetch job for same item"; return; } QUrl urlToFetch; if( !parent.isValid() ) { urlToFetch = m_rootOpmlUrl; } else { OpmlOutline *outline = static_cast( parent.internalPointer() ); if( !outline ) return; if( outline->attributes().value( "type" ) != "include" ) return; urlToFetch = QUrl( outline->attributes()["url"] ); } if( !urlToFetch.isValid() ) return; OpmlParser *parser = new OpmlParser( urlToFetch ); - connect( parser, SIGNAL(headerDone()), SLOT(slotOpmlHeaderDone()) ); - connect( parser, SIGNAL(outlineParsed(OpmlOutline*)), - SLOT(slotOpmlOutlineParsed(OpmlOutline*)) ); - connect( parser, SIGNAL(doneParsing()), SLOT(slotOpmlParsingDone()) ); + connect( parser, &OpmlParser::headerDone, this, &OpmlDirectoryModel::slotOpmlHeaderDone ); + connect( parser, &OpmlParser::outlineParsed, this, &OpmlDirectoryModel::slotOpmlOutlineParsed ); + connect( parser, &OpmlParser::doneParsing, this, &OpmlDirectoryModel::slotOpmlParsingDone ); m_currentFetchingMap.insert( parser, parent ); // ThreadWeaver::Weaver::instance()->enqueue( parser ); parser->run(); } void OpmlDirectoryModel::slotOpmlHeaderDone() { OpmlParser *parser = qobject_cast( QObject::sender() ); QModelIndex idx = m_currentFetchingMap.value( parser ); if( !idx.isValid() ) //header data of the root not required. return; OpmlOutline *outline = static_cast( idx.internalPointer() ); if( !outline->attributes().contains("text") ) { if( parser->headerData().contains( "title" ) ) outline->addAttribute( "text", parser->headerData()["title"] ); else outline->addAttribute( "text", parser->url().fileName() ); //force a view update emit dataChanged( idx, idx ); saveOpml( m_rootOpmlUrl ); } } void OpmlDirectoryModel::slotOpmlOutlineParsed( OpmlOutline *outline ) { OpmlParser *parser = qobject_cast( QObject::sender() ); QModelIndex idx = m_currentFetchingMap.value( parser ); addOutlineToModel( idx, outline ); //TODO: begin image fetch switch( outline->opmlNodeType() ) { case RegularNode: m_imageMap.insert( outline, QIcon::fromTheme( "folder" ).pixmap( 24, 24 ) ); break; case IncludeNode: { m_imageMap.insert( outline, - QIcon::fromTheme( "folder", 0, QStringList( "go-down" ) ).pixmap( 24, 24 ) + QIcon::fromTheme( "folder-download", QIcon::fromTheme( "go-down" ) ).pixmap( 24, 24 ) ); break; } case RssUrlNode: default: break; } } void OpmlDirectoryModel::slotOpmlParsingDone() { OpmlParser *parser = qobject_cast( QObject::sender() ); m_currentFetchingMap.remove( parser ); parser->deleteLater(); } void OpmlDirectoryModel::subscribe( const QModelIndexList &indexes ) const { QList outlines; foreach( const QModelIndex &idx, indexes ) outlines << static_cast( idx.internalPointer() ); foreach( const OpmlOutline *outline, outlines ) { if( !outline ) continue; QUrl url; if( outline->attributes().contains( "xmlUrl" ) ) url = QUrl( outline->attributes()["xmlUrl"] ); else if( outline->attributes().contains( "url" ) ) url = QUrl( outline->attributes()["url"] ); if( url.isEmpty() ) continue; The::playlistManager()->defaultPodcasts()->addPodcast( url ); } } QModelIndex OpmlDirectoryModel::addOutlineToModel( QModelIndex parentIdx, OpmlOutline *outline ) { int newRow = rowCount( parentIdx ); beginInsertRows( parentIdx, newRow, newRow ); //no reparenting required when the item is already parented. if( outline->isRootItem() ) { if( parentIdx.isValid() ) { OpmlOutline * parentOutline = static_cast( parentIdx.internalPointer() ); Q_ASSERT(parentOutline); outline->setParent( parentOutline ); parentOutline->addChild( outline ); parentOutline->setHasChildren( true ); } else { m_rootOutlines << outline; } } endInsertRows(); return index( newRow, 0, parentIdx ); } diff --git a/src/services/opmldirectory/OpmlDirectoryModel.h b/src/services/opmldirectory/OpmlDirectoryModel.h index 6ae3bc8a40..5eb8c568d0 100644 --- a/src/services/opmldirectory/OpmlDirectoryModel.h +++ b/src/services/opmldirectory/OpmlDirectoryModel.h @@ -1,97 +1,94 @@ /**************************************************************************************** * Copyright (c) 2010 Bart Cerneels . * ****************************************************************************************/ #ifndef OPMLDIRECTORYMODEL_H #define OPMLDIRECTORYMODEL_H #include "OpmlOutline.h" #include #include class OpmlParser; class QAction; typedef QList QActionList; class OpmlDirectoryModel : public QAbstractItemModel { Q_OBJECT public: //TODO: make these rols part of a common class in Amarok::. enum { ActionRole = Qt::UserRole, //list of QActions for the index DecorationUriRole, //a URI for the decoration to be fetched by the view. CustomRoleOffset //first role that can be used by sublasses for their own data }; explicit OpmlDirectoryModel( QUrl outlineUrl, QObject *parent = 0 ); ~OpmlDirectoryModel(); // QAbstractItemModel methods virtual QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const; virtual Qt::ItemFlags flags( const QModelIndex &index ) const; virtual QModelIndex parent( const QModelIndex &index ) const; virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const; virtual bool hasChildren( const QModelIndex &parent = QModelIndex() ) const; virtual int columnCount( const QModelIndex &parent = QModelIndex() ) const; virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const; virtual bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ); virtual bool removeRows( int row, int count, const QModelIndex &parent = QModelIndex() ); // OpmlDirectoryModel methods virtual void saveOpml( const QUrl &saveLocation ); virtual OpmlNodeType opmlNodeType( const QModelIndex &idx ) const; //TODO: extract these into OpmlPodcastDirectoryModel subclass void subscribe( const QModelIndexList &indexes ) const; Q_SIGNALS: public Q_SLOTS: void slotAddOpmlAction(); void slotAddFolderAction(); protected: virtual bool canFetchMore( const QModelIndex &parent ) const; virtual void fetchMore( const QModelIndex &parent ); private Q_SLOTS: void slotOpmlHeaderDone(); void slotOpmlOutlineParsed( OpmlOutline * ); void slotOpmlParsingDone(); void slotOpmlWriterDone( int result ); private: QModelIndex addOutlineToModel( QModelIndex parentIdx, OpmlOutline *oultine ); QUrl m_rootOpmlUrl; QList m_rootOutlines; QMap m_currentFetchingMap; QMap m_imageMap; QAction *m_addOpmlAction; QAction *m_addFolderAction; }; -Q_DECLARE_METATYPE(QActionList) -//we store these in a QVariant for the addFolder and addOpml actions -Q_DECLARE_METATYPE( QModelIndex ) #endif // OPMLDIRECTORYMODEL_H diff --git a/src/services/opmldirectory/OpmlDirectoryService.cpp b/src/services/opmldirectory/OpmlDirectoryService.cpp index a7e511bad8..65c6f29800 100644 --- a/src/services/opmldirectory/OpmlDirectoryService.cpp +++ b/src/services/opmldirectory/OpmlDirectoryService.cpp @@ -1,200 +1,195 @@ /**************************************************************************************** * Copyright (c) 2008 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "OpmlDirectoryService.h" #include "amarokurls/AmarokUrlHandler.h" #include "core/support/Debug.h" #include "core/support/Components.h" #include "core/interfaces/Logger.h" #include "browsers/CollectionTreeItem.h" #include "browsers/SingleCollectionTreeItemModel.h" #include "OpmlDirectoryInfoParser.h" #include "OpmlDirectoryModel.h" #include "OpmlDirectoryView.h" #include "playlistmanager/PlaylistManager.h" #include "core/podcasts/PodcastProvider.h" #include "ServiceSqlRegistry.h" #include "widgets/SearchWidget.h" -#include -#include +#include + +#include -#include using namespace Meta; OpmlDirectoryServiceFactory::OpmlDirectoryServiceFactory() : ServiceFactory() {} OpmlDirectoryServiceFactory::~OpmlDirectoryServiceFactory() {} void OpmlDirectoryServiceFactory::init() { ServiceBase* service = new OpmlDirectoryService( this, "OpmlDirectory", i18n( "Podcast Directory" ) ); m_initialized = true; emit newService( service ); } QString OpmlDirectoryServiceFactory::name() { return "OpmlDirectory"; } KConfigGroup OpmlDirectoryServiceFactory::config() { return Amarok::config( "Service_OpmlDirectory" ); } OpmlDirectoryService::OpmlDirectoryService( OpmlDirectoryServiceFactory* parent, const QString &name, const QString &prettyName ) : ServiceBase( name, parent, false, prettyName ) { setShortDescription( i18n( "A large listing of podcasts" ) ); setIcon( QIcon::fromTheme( "view-services-opml-amarok" ) ); setLongDescription( i18n( "A comprehensive list of searchable podcasts that you can subscribe to directly from within Amarok." ) ); KIconLoader loader; setImagePath( loader.iconPath( "view-services-opml-amarok", -128, true ) ); The::amarokUrlHandler()->registerRunner( this, command() ); setServiceReady( true ); } OpmlDirectoryService::~OpmlDirectoryService() { } void OpmlDirectoryService::polish() { generateWidgetInfo(); if ( m_polished ) return; //do not allow this content to get added to the playlist. At least not for now setPlayableTracks( false ); //TODO: implement searching m_searchWidget->setVisible( false ); OpmlDirectoryView* opmlView = new OpmlDirectoryView( this ); opmlView->setHeaderHidden( true ); opmlView->setFrameShape( QFrame::NoFrame ); opmlView->setDragEnabled ( true ); opmlView->setSortingEnabled( false ); opmlView->setSelectionMode( QAbstractItemView::ExtendedSelection ); opmlView->setDragDropMode ( QAbstractItemView::DragOnly ); opmlView->setEditTriggers( QAbstractItemView::SelectedClicked | QAbstractItemView::EditKeyPressed ); setView( opmlView ); - QUrl opmlLocation( Amarok::saveLocation() ); - opmlLocation = opmlLocation.adjusted(QUrl::StripTrailingSlash); - opmlLocation.setPath(opmlLocation.path() + '/' + ( "podcast_directory.opml" )); + QString opmlLocation = Amarok::saveLocation() + "podcast_directory.opml"; - if( !QFile::exists( opmlLocation.toLocalFile() ) ) + if( !QFile::exists( opmlLocation ) ) { //copy from the standard data dir - QUrl schippedOpmlLocation( KStandardDirs::locate( "data", "amarok/data/" ) ); - schippedOpmlLocation = schippedOpmlLocation.adjusted(QUrl::StripTrailingSlash); - schippedOpmlLocation.setPath(schippedOpmlLocation.path() + '/' + ( "podcast_directory.opml" )); - if( !QFile::copy( schippedOpmlLocation.toLocalFile(), opmlLocation.toLocalFile() ) ) + QString schippedOpmlLocation = QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/data/podcast_directory.opml" ); + if( !QFile::copy( schippedOpmlLocation, opmlLocation ) ) { debug() << QString( "Failed to copy from %1 to %2" ) - .arg( schippedOpmlLocation.toLocalFile(), opmlLocation.toLocalFile() ); + .arg( schippedOpmlLocation, opmlLocation ); //TODO: error box drawn in the view's area. return; } } - setModel( new OpmlDirectoryModel( opmlLocation, this ) ); + setModel( new OpmlDirectoryModel( QUrl::fromLocalFile( opmlLocation ), this ) ); m_subscribeButton = new QPushButton( m_bottomPanel ); m_subscribeButton->setText( i18n( "Subscribe" ) ); m_subscribeButton->setObjectName( "subscribeButton" ); m_subscribeButton->setIcon( QIcon::fromTheme( "get-hot-new-stuff-amarok" ) ); m_subscribeButton->setEnabled( false ); - connect( m_subscribeButton, SIGNAL(clicked()), this, SLOT(subscribe()) ); + connect( m_subscribeButton, &QPushButton::clicked, this, &OpmlDirectoryService::subscribe ); m_addOpmlButton = new QPushButton( m_bottomPanel ); m_addOpmlButton->setText( i18n( "Add OPML" ) ); m_addOpmlButton->setObjectName( "addOpmlButton" ); m_addOpmlButton->setIcon( QIcon::fromTheme( "list-add-amarok" ) ); - connect( m_addOpmlButton, SIGNAL(clicked()), model(), SLOT(slotAddOpmlAction()) ); + connect( m_addOpmlButton, &QPushButton::clicked, + dynamic_cast( model() ), &OpmlDirectoryModel::slotAddOpmlAction ); - connect( view()->selectionModel(), - SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - SLOT(slotSelectionChanged(QItemSelection,QItemSelection)) - ); + connect( view()->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &OpmlDirectoryService::slotSelectionChanged ); setInfoParser( new OpmlDirectoryInfoParser() ); m_polished = true; } QString OpmlDirectoryService::command() const { return "service-podcastdirectory"; } QString OpmlDirectoryService::prettyCommand() const { return i18n( "Add an OPML file to the list." ); } bool OpmlDirectoryService::run( AmarokUrl url ) { //make sure this category is shown. AmarokUrl( "amarok://navigate/internet/OpmlDirectory" ).run(); if( url.path() == QLatin1String( "addOpml" ) ) { OpmlDirectoryModel *opmlModel = qobject_cast( model() ); Q_ASSERT_X(opmlModel, "OpmlDirectoryService::run()", "fix if a proxy is used"); opmlModel->slotAddOpmlAction(); return true; } return false; } void OpmlDirectoryService::subscribe() { OpmlDirectoryModel * opmlModel = dynamic_cast( model() ); Q_ASSERT( opmlModel ); opmlModel->subscribe( view()->selectionModel()->selectedIndexes() ); } void OpmlDirectoryService::slotSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected ) { Q_UNUSED(selected) Q_UNUSED(deselected) m_subscribeButton->setEnabled( !view()->selectionModel()->selectedIndexes().isEmpty() ); } diff --git a/src/services/opmldirectory/OpmlDirectoryService.h b/src/services/opmldirectory/OpmlDirectoryService.h index 7178adcf62..8464790d31 100644 --- a/src/services/opmldirectory/OpmlDirectoryService.h +++ b/src/services/opmldirectory/OpmlDirectoryService.h @@ -1,86 +1,85 @@ /**************************************************************************************** * Copyright (c) 2008 Nikolaj Hald Nielsen * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef OPMLDIRECTORYSERVICE_H #define OPMLDIRECTORYSERVICE_H #include "amarokurls/AmarokUrlRunnerBase.h" #include "../ServiceBase.h" #include "OpmlDirectoryDatabaseHandler.h" #include "ServiceSqlCollection.h" #include "core/support/Amarok.h" -#include -#include +#include class OpmlOutline; class OpmlDirectoryServiceFactory: public ServiceFactory { Q_PLUGIN_METADATA(IID AmarokPluginFactory_iid FILE "amarok_service_opmldirectory.json") Q_INTERFACES(Plugins::PluginFactory) Q_OBJECT public: OpmlDirectoryServiceFactory(); virtual ~OpmlDirectoryServiceFactory(); virtual void init(); virtual QString name(); virtual KConfigGroup config(); }; /** A service for displaying, previewing and downloading music from OpmlDirectory.com @author */ class OpmlDirectoryService : public ServiceBase, public AmarokUrlRunnerBase { Q_OBJECT public: OpmlDirectoryService( OpmlDirectoryServiceFactory* parent, const QString &name, const QString &prettyName ); ~OpmlDirectoryService(); void polish(); virtual Collections::Collection * collection() { return 0; } /* UrlRunnerBase methods */ virtual QString command() const; virtual QString prettyCommand() const; virtual bool run( AmarokUrl url ); virtual QIcon icon() const { return QIcon::fromTheme( "view-services-opml-amarok" ); } private Q_SLOTS: void subscribe(); void slotSelectionChanged( const QItemSelection &, const QItemSelection & ); private: QPushButton *m_addOpmlButton; QPushButton *m_subscribeButton; int m_currentCategoryId; int m_numberOfFeeds; int m_numberOfCategories; }; #endif diff --git a/src/services/opmldirectory/OpmlDirectoryView.cpp b/src/services/opmldirectory/OpmlDirectoryView.cpp index f5c1fdbe82..121cb1ddc1 100644 --- a/src/services/opmldirectory/OpmlDirectoryView.cpp +++ b/src/services/opmldirectory/OpmlDirectoryView.cpp @@ -1,87 +1,90 @@ /**************************************************************************************** * Copyright (c) 2010 Bart Cerneels * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #include "OpmlDirectoryView.h" #include "OpmlDirectoryModel.h" #include "core/support/Debug.h" #include #include OpmlDirectoryView::OpmlDirectoryView( QWidget *parent ) : Amarok::PrettyTreeView(parent) { } void OpmlDirectoryView::contextMenuEvent( QContextMenuEvent *event ) { + DEBUG_BLOCK + QModelIndex idx = indexAt( event->pos() ); debug() << idx; event->accept(); QVariant data = model()->data( idx, OpmlDirectoryModel::ActionRole ); QActionList actions = data.value(); if( actions.isEmpty() ) { + warning() << "no actions for index:" << idx; return; } QMenu menu; foreach( QAction *action, actions ) { if( action ) menu.addAction( action ); } menu.exec( mapToGlobal( event->pos() ) ); //We keep the items that the actions need to be applied to in the actions private data. //Clear the data from all actions now that the context menu has executed. foreach( QAction *action, actions ) action->setData( QVariant() ); } void OpmlDirectoryView::keyPressEvent( QKeyEvent *event ) { switch( event->key() ) { case Qt::Key_Delete: { foreach( const QItemSelectionRange &range, selectionModel()->selection() ) model()->removeRows( range.top(), range.height(), range.parent() ); event->accept(); return; } } Amarok::PrettyTreeView::keyPressEvent( event ); } QItemSelectionModel::SelectionFlags OpmlDirectoryView::selectionCommand ( const QModelIndex &index, const QEvent *event ) const { if( model()->hasChildren( index ) ) return QItemSelectionModel::ClearAndSelect; return Amarok::PrettyTreeView::selectionCommand( index, event ); } diff --git a/src/services/opmldirectory/amarok_service_opmldirectory.desktop b/src/services/opmldirectory/amarok_service_opmldirectory.desktop index c1ada58d12..9055cd187f 100644 --- a/src/services/opmldirectory/amarok_service_opmldirectory.desktop +++ b/src/services/opmldirectory/amarok_service_opmldirectory.desktop @@ -1,126 +1,125 @@ [Desktop Entry] Type=Service -ServiceTypes=KPluginInfo Icon=view-services-opml-amarok Name=Podcast Directory Name[bg]=Подкастове Name[bs]=Podcast direktorij Name[ca]=Directori de podcasts Name[ca@valencia]=Directori de podcasts Name[cs]=Adresář podcastů Name[csb]=Katalog pòdcastów Name[da]=Podcast-liste Name[de]=Podcast-Verzeichnis Name[el]=Κατάλογος Podcast Name[en_GB]=Podcast Directory Name[es]=Directorio de podcast Name[et]=Podcastide kataloog Name[eu]=Podcast-en direktorioa Name[fi]=Podcast-kansio Name[fr]=Dossier de podcasts Name[ga]=Eolaire Podchraoltaí Name[gl]=Directorio de Podcast Name[hu]=Podcast Directory Name[id]=Direktori Podcast Name[is]=Podcast mappa Name[it]=Podcast Directory Name[ja]=ポッドキャストディレクトリ Name[km]=ថត​ផតខាស់ Name[ko]=팟캐스트 목록 Name[lt]=Garso prenumeratų aplankas Name[lv]=Podraižu katalogs Name[nb]=Podkastkatalog Name[nds]=Podcast-Orner Name[nl]=Podcastmap Name[nn]=Podkast-katalog Name[pa]=ਪੋਡਕਾਸਟ ਡਾਇਰੈਕਟਰੀ Name[pl]=Katalog podcastów Name[pt]=Pasta de 'Podcasts' Name[pt_BR]=Pasta de Podcast Name[ro]=Director Podcast Name[ru]=Каталог подкастов Name[sk]=Priečinok podcastov Name[sl]=Imenik podcastov Name[sr]=Фасцикла подемисија Name[sr@ijekavian]=Фасцикла подемисија Name[sr@ijekavianlatin]=Fascikla podemisija Name[sr@latin]=Fascikla podemisija Name[sv]=Podsändningskatalog Name[th]=ไดเรกทอรีพ็อดแคสต์ Name[tr]=Podcast Dizini Name[uk]=Каталог трансляцій Name[wa]=Ridant des podcasts Name[x-test]=xxPodcast Directoryxx Name[zh_CN]=播客目录 Name[zh_TW]=Podcast 資料夾 Comment=Browse and subscribe to a huge list of podcasts Comment[bg]=Разглеждане и абониране за голям брой подкастове Comment[bs]=Pregledajte i pretplatite se na ogromnu listu podemisija Comment[ca]=Navegueu i subscriviu-vos a una àmplia llista de podcasts Comment[ca@valencia]=Navegueu i subscriviu-vos a una àmplia llista de podcasts Comment[cs]=Prohlížení a registrace obrovského množství podcastů Comment[da]=Gennemse og abonnér på en enorm liste over podcasts Comment[de]=Eine umfangreiche Liste an Podcasts durchsehen und abonnieren Comment[el]=Περιήγηση και εγγραφή σε μια τεράστια λίστα εκπομπών pod Comment[en_GB]=Browse and subscribe to a huge list of podcasts Comment[es]=Navegar y suscribirse a una enorme lista de podcast Comment[et]=Tohutu hulga podcastide sirvimine ja tellimine Comment[eu]=Arakatu eta harpidetu podcast-en zerrenda izugarrira Comment[fi]=Ota käyttöön kattava valikoima podcasteja Comment[fr]=Parcourir et s'inscrire à un énorme choix de podcasts Comment[ga]=Brabhsáil agus liostáil le podchraoltaí ó liosta ollmhór Comment[gl]=Navegue e subscrébase a unha lista enorme de podcasts Comment[hu]=Böngészés egy óriási podcast-listában, feliratkozás podcastokra Comment[id]=Telusur dan berlangganan daftar besar pada podcast Comment[is]=Skoða og gerast áskrifandi að fjölmörgum podkast hlaðvörpum Comment[it]=Sfoglia e registrati a un enorme elenco di podcast Comment[ja]=広範なポッドキャストのリストをブラウズして購読できます Comment[km]=រកមើល និង​ជាវ​ទៅ​បញ្ជី​ផតខាស់​ធំៗ Comment[ko]=팟캐스트 목록을 보고 구독하기 Comment[ku]=Binihêre û bibe endamê lîsteya mezin yê podcast ê Comment[lt]=Naršyti ir užsisakyti podcast iš didžiulio sąrašo Comment[lv]=Pārlūkojiet un pasūtiniet no milzīga podraižu saraksta Comment[nb]=Bla gjennom og abonner på en veldig stor samling av podcasts Comment[nds]=En groot List vun Podcasts dörkieken un bestellen Comment[nl]=Blader door en luister naar een enorm aantal podcasts Comment[nn]=Sjå gjennom og abonner på ei lang rekkje podkastar Comment[pl]=Przeglądaj i zapisz się do ogromnej listy podcastów Comment[pt]=Navegar e inscrever numa enorme lista de 'podcasts' Comment[pt_BR]=Navegue e inscreva-se em uma lista enorme de podcasts Comment[ro]=Răsfoiți și abonați-vă la o listă uriașă de podcasturi Comment[ru]=Обзор и подписка на огромный список подкастов Comment[sk]=Prehliadanie a registrácia obrovského množstva podcastov Comment[sl]=Brskajte po obsežni zbirki podcastov in se naročite nanje Comment[sr]=Прегледајте и претплатите се на огромну листу подемисија Comment[sr@ijekavian]=Прегледајте и претплатите се на огромну листу подемисија Comment[sr@ijekavianlatin]=Pregledajte i pretplatite se na ogromnu listu podemisija Comment[sr@latin]=Pregledajte i pretplatite se na ogromnu listu podemisija Comment[sv]=Bläddra i och prenumerera på en enorm samling podsändningar Comment[th]=เรียกดูและขอรับข้อมูลรายการเพลงขนาดมหึมาของบริการพ็อดแคสต์ Comment[tr]=Geniş bir podcast listesine gözat ve üye ol Comment[uk]=Перегляньте і підпишіться на трансляції з величезного списку Comment[wa]=Foyter eyet s' abouner a ene djivêye di podcast foû mezeure Comment[x-test]=xxBrowse and subscribe to a huge list of podcastsxx Comment[zh_CN]=浏览并订阅大量播客列表 Comment[zh_TW]=瀏覽並訂閱大量的 podcast 清單 ServiceTypes=Amarok/Plugin X-KDE-Library=amarok_service_opmldirectory X-KDE-Amarok-authors=Nikolaj Hald Nielsen X-KDE-Amarok-email=nhnFreespirit@gmail.com X-KDE-Amarok-framework-version=73 X-KDE-Amarok-name=JamendoService X-KDE-Amarok-rank=100 X-KDE-Amarok-version=1 X-KDE-PluginInfo-Author=Nikolaj Hald Nielsen X-KDE-PluginInfo-Email=nhnFreespirit@gmail.com X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Category=Service X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-KDE-Library=amarok_service_opmldirectory X-KDE-PluginInfo-Name=amarok_service_opmldirectory