1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-09-06 20:20:41 +02:00

Compare commits

...

124 Commits

Author SHA1 Message Date
Christian Muehlhaeuser
a05972e9ce * Fixed merge. 2011-11-24 02:47:15 +01:00
Christian Muehlhaeuser
5157e13ba1 * Updated ChangeLog. 2011-11-24 01:48:58 +01:00
Christian Muehlhaeuser
9f97afc148 * Updated ChangeLog. 2011-11-24 01:47:48 +01:00
Christian Muehlhaeuser
8b1b0091c6 * Updated ChangeLog. 2011-11-24 01:46:42 +01:00
Christian Muehlhaeuser
3e9aee3687 * Use default locale if translation can't be found. 2011-11-24 01:35:10 +01:00
Christian Muehlhaeuser
48bb0a82f9 * Disable UbuntuUnityHack until it's working properly. 2011-11-24 01:35:03 +01:00
Christian Muehlhaeuser
1efdf38085 * Disable german translation for 0.3.2 release. Needs be updated. 2011-11-24 01:20:01 +01:00
Christian Muehlhaeuser
3e4f4d2f38 * Updated verison to 0.3.2. 2011-11-23 23:31:02 +01:00
Jason Herskowitz
a2f870f2b8 Update changelog 2011-11-23 23:26:35 +01:00
Christian Muehlhaeuser
2ac167d438 * Support .oga files (ogg mimetype). 2011-11-23 23:26:21 +01:00
Christian Muehlhaeuser
c218695a3d * Prevent stuck now playing indicator. 2011-11-23 23:26:13 +01:00
Christian Muehlhaeuser
fae5f1dbbe * Should hopefully fix Windows SettingsDialog. 2011-11-23 23:25:57 +01:00
Christian Muehlhaeuser
54d29cc05d * Try fixing SettingsDialog on Windows. 2011-11-23 23:25:45 +01:00
Christian Muehlhaeuser
5aea02def2 * Forgot to rename crash reporter in one more place. 2011-11-23 23:25:35 +01:00
Christian Muehlhaeuser
368aa20b75 * Updated new crash reporter binary name in release scripts. 2011-11-23 23:25:26 +01:00
Christian Muehlhaeuser
d2a07f2b6c * Call ensurePolished before creating buttons in SettingsDialog. 2011-11-23 23:25:08 +01:00
Christian Muehlhaeuser
14253a9c66 * DRY: Cleaned up DropJob / ShortenedLinkParser & handling spoti.fi urls now. 2011-11-23 23:25:02 +01:00
Christian Muehlhaeuser
6881f4218a * Moved charts & spotify parser to port 80. 2011-11-23 23:23:57 +01:00
Christian Muehlhaeuser
17badb972e * Prevented over-zealous assert in PlaylistModel. 2011-11-23 23:23:50 +01:00
Christian Muehlhaeuser
2ea09dd60d * Merged version check. 2011-11-23 23:22:00 +01:00
Christian Muehlhaeuser
87f7b6b2c6 * Made sure we don't duplicate connections in WelcomeWidget. 2011-11-23 23:15:52 +01:00
Christian Muehlhaeuser
01986a4129 * Use the proper color for selected playlist items on the Dashboard. 2011-11-23 23:15:43 +01:00
Christian Muehlhaeuser
2b404b517f * Use the proper highlight color for painting hovered artist names in the AlbumItemDelegate. 2011-11-23 23:15:32 +01:00
Christian Muehlhaeuser
b21b2d10f7 * Update SourceInfoWidget when collection changes. 2011-11-23 23:15:22 +01:00
Christian Muehlhaeuser
04deaba788 * Less furious logging. 2011-11-23 23:15:10 +01:00
Christian Muehlhaeuser
5cabc62733 * Update recent additions on welcome page whenever a collection changes. 2011-11-23 23:15:02 +01:00
Kuba Serafinowski
01c6c3f0b6 moar strings to translate! 2011-11-23 23:14:42 +01:00
Kuba Serafinowski
b1a5cd4dcb remove unneeded strings 2011-11-23 23:14:28 +01:00
Kuba Serafinowski
58f78725db tiny typo fix 2011-11-23 23:14:11 +01:00
Jeff Mitchell
4210e8eab8 Hopefully fix a case where the seek slider travels on when Phonon is
buffering
2011-11-23 23:13:49 +01:00
Thierry Goeckel
115492d1cc Enhance version upgrading to include a third point. 2011-11-23 23:13:21 +01:00
Leo Franchi
edadbb0c95 Fix loading XSPFs with spaces in their name. 2011-11-23 23:13:11 +01:00
Christian Muehlhaeuser
6c67dfcab7 * Revert back to using system locale. 2011-11-23 23:12:50 +01:00
Christian Muehlhaeuser
b9453ab77b * Add a default english translation. Fallback to 'en' if locale is set to 'C'. 2011-11-23 23:12:39 +01:00
Christian Muehlhaeuser
9507ffdd86 * Fixed translation issue in TomahawkUtils. 2011-11-23 23:12:26 +01:00
Christian Muehlhaeuser
ff768954f6 * Fixed window title in AudioControls. 2011-11-23 23:12:17 +01:00
Christian Muehlhaeuser
15729bb66a * Increased size of Get-New-Resolvers dialog. 2011-11-23 23:12:01 +01:00
Christian Muehlhaeuser
41f46aaeea * Cleanup scanner if there were no new and no obsolete tracks found. 2011-11-23 23:11:53 +01:00
Christian Muehlhaeuser
9e0d884f80 * Merge musicscanner changes into stable. 2011-11-23 23:11:16 +01:00
Christian Muehlhaeuser
d34491b85f * Re-enable grouping of dynamic playlist revisions. 2011-11-23 23:10:34 +01:00
Christian Muehlhaeuser
45070fb4e1 * Remove bottom label of steering controls. Changes now take effect on clicking the apply button. 2011-11-23 23:10:27 +01:00
Christian Muehlhaeuser
18612b48d3 * Don't remove current index when restarting a track by activating it directly.
* Don't create new playlist revisions for a list that doesn't belong to you.
2011-11-23 23:10:18 +01:00
Christian Muehlhaeuser
a38c0a544d * Fixed crashing on playing from queue. 2011-11-23 23:10:06 +01:00
Christian Muehlhaeuser
d38c9730fb * Merge into stable. 2011-11-23 23:09:32 +01:00
Christian Muehlhaeuser
d25320833c * Merged into stable. 2011-11-23 23:06:02 +01:00
Christian Muehlhaeuser
d38600dd6a * Don't overwrite friendlyname with an empty one. 2011-11-23 23:05:23 +01:00
Christian Muehlhaeuser
6fa48587e1 * Assert on empty friendlynames. 2011-11-23 23:05:12 +01:00
Leo Franchi
8da0f3e9a3 * Merged into stable. 2011-11-23 23:04:58 +01:00
Kilian Lackhove
ab402c0867 fix xspf import QFileDialog filter 2011-11-23 23:00:43 +01:00
Kuba Serafinowski
545a915439 fix TWK-583 for jabber too 2011-11-23 23:00:31 +01:00
Kuba Serafinowski
d026902d91 fix for TWK-583 2011-11-23 23:00:25 +01:00
Hugo Lindström
ff72157c5f Fix for broken compile 2011-11-23 23:00:05 +01:00
Christian Muehlhaeuser
faab306dbe * Merged friendlyname fixes. 2011-11-23 22:59:57 +01:00
Christian Muehlhaeuser
57fef9f3f2 * Fixed WelcomeWidget. 2011-11-23 22:57:44 +01:00
Leo Franchi
89fac77951 Fix one compile error 2011-11-23 22:57:36 +01:00
Christian Muehlhaeuser
ef02e9b4ac * Refactored Track-, Collection-, PlaylistModel. 2011-11-23 22:57:24 +01:00
Leo Franchi
99b09c1faa Increment job count when creating a new xspf job 2011-11-23 22:55:17 +01:00
Kuba Serafinowski
d66e86f5c6 make the string extract with lupdate 2011-11-23 22:54:59 +01:00
Kuba Serafinowski
9bd46984e0 match the text with buttons in the dialog 2011-11-23 22:54:51 +01:00
Hugo Lindström
3992392245 * Merge appending XSPFs. 2011-11-23 22:54:32 +01:00
Hugo Lindström
198e92bc2c Fix for local xspf dropjob, also allow remotes 2011-11-23 22:53:22 +01:00
Kuba Serafinowski
2f899eed2a fix misleading cmake message 2011-11-23 22:52:49 +01:00
Jeff Mitchell
8cdd4bdea3 Do the connection name fixing in a different way; always end up with a
QHostAddress.
2011-11-23 22:52:39 +01:00
Jeff Mitchell
3d53b0c251 Remove some debugging 2011-11-23 22:52:23 +01:00
Jeff Mitchell
0ac9512eb9 Lots of debug and half the fixes for fixing hostnames 2011-11-23 22:52:15 +01:00
Jeff Mitchell
3f0ac7c666 * Merge application proxy factory. 2011-11-23 22:52:13 +01:00
Christian Muehlhaeuser
faf7d00fdd * Fixed TWK-579: Proper default width for the sidebar. 2011-11-23 22:50:32 +01:00
Christian Muehlhaeuser
b31abb0979 * This should fix sources being stuck in 'Parsing'. 2011-11-23 22:50:25 +01:00
Thierry Göckel
f2009cf99b Fix returning of external IP address for some routers. 2011-11-23 22:49:29 +01:00
Hugo Lindström
0d97a47cc5 Add cache for all charts. Later on move spotify and hypem to charts api. 2011-11-23 22:49:17 +01:00
Hugo Lindström
7db6beb896 Adding cache for charts 2011-11-23 22:48:54 +01:00
Christian Muehlhaeuser
e56a629ad7 * Fixed negative time values showing up in GUI. 2011-11-23 22:48:28 +01:00
Christian Muehlhaeuser
ef918bbbcf * Make SetPlaylistRevision DBCmds groupable. 2011-11-23 22:48:13 +01:00
Christian Muehlhaeuser
60911ab887 * Set stats for the right collection. 2011-11-23 22:47:49 +01:00
Christian Muehlhaeuser
e10a96ecd5 * Merged groupable db-commands. 2011-11-23 22:47:17 +01:00
Christian Muehlhaeuser
e99973925a * Merged improved collection syncing. 2011-11-23 22:45:52 +01:00
Dominik Schmidt
949c6f6341 Unbreak FindCLucene for Fedora 16 and openSUSE Factory. 2011-11-23 06:35:39 +01:00
Dominik Schmidt
e041723ffc Install CrashReporter 2011-11-23 06:07:54 +01:00
Christian Muehlhaeuser
89919928da * Look for CrashReporter in local and global dirs. 2011-11-23 05:49:34 +01:00
Leo Franchi
d4fc504f3d Add a 0.3.2 changelog 2011-11-20 22:52:11 -05:00
Leo Franchi
05924b8deb TWK-531: Show (-) on new item after deleting an item 2011-11-20 22:43:20 -05:00
Leo Franchi
6d4aa50596 Clear upcoming station tracks when steering, and re-fetch two 2011-11-20 22:43:08 -05:00
Leo Franchi
7ee2755617 use QFileSystemModel instead of deprecated QDirModel, and show /Volumes on mac 2011-11-20 22:43:00 -05:00
Leo Franchi
35406ceb5a Use plugins/ instead of PlugIns/ as vlc hard-codes it internally. 2011-11-20 22:42:52 -05:00
Leo Franchi
9efb536e55 Show tooltip with full text in job view 2011-11-20 22:42:30 -05:00
Leo Franchi
b8d27f1811 Make clicking back to current track work on dashboard, charts, and artist/album pages 2011-11-20 22:42:23 -05:00
Leo Franchi
0939ba0705 Don't show speaker next to a playlist if not paused or playing 2011-11-20 22:42:03 -05:00
Leo Franchi
3788bc7cbf Always clear spinner when no albums found to load in albumview 2011-11-20 22:41:52 -05:00
Leo Franchi
54723abc07 Only show JS errors in tomahawk debug builds 2011-11-20 22:41:36 -05:00
Leo Franchi
900f57437f Put delcaration in ifdef 2011-11-20 22:40:51 -05:00
Leo Franchi
300c9d5d68 Automatically upgrade resolvers 2011-11-20 22:40:44 -05:00
Leo Franchi
96c9996284 Set the timer with track duration calculated in 2011-11-17 09:49:43 -05:00
Leo Franchi
8a15bcaa66 fix rdio track loading 2011-11-17 09:49:16 -05:00
Leo Franchi
d17d67869c Add an "apply steering" button to make it clearer how to steer
Remove some super spammy debug
2011-11-17 09:49:11 -05:00
Leo Franchi
c491f33ce4 Manually replace + with " " as QUrl::queryItems doesn't do it for us... 2011-11-17 09:49:06 -05:00
Leo Franchi
1dee5c809c Don't hook up signals twice for loading xspf 2011-11-17 09:49:00 -05:00
Leo Franchi
924ede61b4 Make surer to show playlist once loaded 2011-11-17 09:48:52 -05:00
Leo Franchi
0d64d9c42d Don't show drop indicator for playlists in sidebar and always create new ones 2011-11-17 09:48:44 -05:00
Leo Franchi
6b6fb909cd Make source visible in audiocontrols on os x 2011-11-17 09:48:35 -05:00
Leo Franchi
b51ff46085 Don't create QNRs for null urls, let audioengine handle it properly 2011-11-17 09:48:07 -05:00
Leo Franchi
46ec23990f Revert "Skip to next track if we have a QNR and it emits a failed signal"
This reverts commit 9418fd0fb8.
2011-11-17 09:47:59 -05:00
Jeff Mitchell
eb46feac2c Handle the worker/cache threads better in InfoSystem, and re-enable
deletion on shutdown, so we can test in windows nightlies.
2011-11-16 20:19:13 -05:00
Leo Franchi
2c206f4193 Shrink font for playlist author 2011-11-14 18:07:56 -05:00
Leo Franchi
c1bd19b35b Don't accept button press events as well as release when handling latches 2011-11-14 18:06:58 -05:00
Jeff Mitchell
370154cc46 hacky fix for Jreen needing the application proxy set. Really we need to
get them to accept their own QNAM
2011-11-14 18:06:50 -05:00
Leo Franchi
9a81ce8b89 Add 0.3.1 fixes to Changelog 2011-11-13 14:07:37 -05:00
Christian Muehlhaeuser
c805d283d4 * Updated ChangeLog and bumped version to 0.3.1. 2011-11-13 19:25:53 +01:00
Christian Muehlhaeuser
03379f361c * Force an index update after 0.3.1 upgrade. 2011-11-13 19:19:36 +01:00
Christian Muehlhaeuser
c671c0e898 * 'Fixed' Windows shutdown crash. 2011-11-13 19:19:25 +01:00
Christian Muehlhaeuser
edaf49b4c5 * Fixed creating index when there is only a local source. 2011-11-13 19:19:02 +01:00
Christian Muehlhaeuser
682903b0cb * Prevent watch for changes interfering with running scan. 2011-11-13 19:18:49 +01:00
Dominik Schmidt
53f7f599d5 Update ChangeLog 2011-11-13 17:31:16 +01:00
Dominik Schmidt
cc669fc20f Remove unneccessary toString conversion 2011-11-13 17:26:43 +01:00
Dominik Schmidt
4e7496a38a Search for *_tomahawkresolver in CMAKE_INSTALL_LIBEXECDIR 2011-11-13 15:40:39 +01:00
Leo Franchi
5842cdceee Only do setenv on linux, that's theonly time we'll have unity anyway
(cherry picked from commit 09a7d790ad)
2011-11-12 10:04:31 -05:00
Leo Franchi
b7e0be8d9d Hide red latch headphones on local source if the source you are latched on to goes offline
(cherry picked from commit d49a43709d)
2011-11-12 10:04:25 -05:00
Leo Franchi
a203b60cbb Add Unity systray hack from Clementine.
(cherry picked from commit 96fe10cc9f)
2011-11-12 10:04:20 -05:00
Leo Franchi
2be08af321 Draw the author name as well as avatar in recent playlists
(cherry picked from commit 2a27cbde0d)
2011-11-12 10:04:12 -05:00
Leo Franchi
ad4c229a6b Don't show in global menu in unity. Thanks davidsansome :)
(cherry picked from commit 1f10444d5c)
2011-11-12 10:04:06 -05:00
Leo Franchi
b2e0206e34 Be a bit more resilient in setting the playlist to rename after creating
(cherry picked from commit 1bb8ff97d4)
2011-11-12 10:04:00 -05:00
Leo Franchi
743ae013ea Don't select a temp item when deleting it
(cherry picked from commit 518546b525)
2011-11-12 10:03:54 -05:00
Leo Franchi
767cf7c1b9 Hide warning
(cherry picked from commit 17e1003714)
2011-11-12 10:03:47 -05:00
Leo Franchi
6be94ab81c Stop the timer on new track starting
(cherry picked from commit 3c024ab7ac)
2011-11-12 10:02:03 -05:00
Leo Franchi
b1ed304a61 Fix crash on Sparkle update when binary is replaced.
(cherry picked from commit f9b5bda925)
2011-11-11 11:04:30 -05:00
161 changed files with 5564 additions and 1352 deletions

View File

@@ -16,7 +16,7 @@ SET( TOMAHAWK_DESCRIPTION_SUMMARY "The social media player" )
SET( TOMAHAWK_VERSION_MAJOR 0 )
SET( TOMAHAWK_VERSION_MINOR 3 )
SET( TOMAHAWK_VERSION_PATCH 0 )
SET( TOMAHAWK_VERSION_PATCH 2 )
#SET( TOMAHAWK_VERSION_RC 0 )
@@ -108,7 +108,7 @@ macro_optional_find_package(LibAttica)
macro_log_feature(LIBATTICA_FOUND "libattica" "Provides support for automatic fetching and managing of resolvers from the tomahawk website" "https://projects.kde.org/projects/kdesupport/attica" FALSE "" "")
macro_optional_find_package(QuaZip)
macro_log_feature(QuaZip_FOUND "QuaZip" "Provides support for extracting downloaded resolvers autmatically. Will build internal copy instead." "http://quazip.sourceforge.net/" FALSE "" "")
macro_log_feature(QuaZip_FOUND "QuaZip" "Provides support for extracting downloaded resolvers automatically." "http://quazip.sourceforge.net/" FALSE "" "")
IF( NOT QuaZip_FOUND )
add_subdirectory( ${CMAKE_SOURCE_DIR}/src/libtomahawk/thirdparty/quazip )
@@ -116,6 +116,7 @@ IF( NOT QuaZip_FOUND )
SET( QuaZip_LIBRARY quazip )
SET( QuaZip_LIBRARIES ${QuaZip_LIBRARY} )
SET( QuaZip_FOUND true )
macro_log_feature(QuaZip_FOUND "QuaZip" "Provides support for extracting downloaded resolvers automatically. Building internal copy" "http://quazip.sourceforge.net/" FALSE "" "")
# copy headers to build/quazip so we can use proper includes inside the code
FILE( COPY ${CMAKE_SOURCE_DIR}/src/libtomahawk/thirdparty/quazip/quazip/ DESTINATION ${CMAKE_BINARY_DIR}/libtomahawk/thirdparty/quazip )
@@ -211,4 +212,7 @@ ADD_SUBDIRECTORY( src/libtomahawk )
SET( TOMAHAWK_LIBRARIES tomahawklib )
ADD_SUBDIRECTORY( src )
ADD_SUBDIRECTORY( admin )
ADD_SUBDIRECTORY( src/breakpad/CrashReporter )
IF( NOT DISABLE_CRASHREPORTER )
ADD_SUBDIRECTORY( src/breakpad/CrashReporter )
ENDIF()

View File

@@ -2,6 +2,7 @@
# This module looks for clucene (http://clucene.sf.net) support
# It will define the following values
#
# CLUCENE_INCLUDE_DIRS = CLUCENE_INCLUDE_DIR + CLUCENE_LIBRARY_DIR
# CLUCENE_INCLUDE_DIR = where CLucene/StdHeader.h can be found
# CLUCENE_LIBRARY_DIR = where CLucene/clucene-config.h can be found
# CLUCENE_LIBRARIES = the libraries to link against CLucene
@@ -17,6 +18,7 @@ FIND_PACKAGE(CLuceneUnstable)
IF(CLUCENEUNSTABLE_FOUND)
SET(CLucene_FOUND TRUE)
SET(CLUCENE_INCLUDE_DIR ${CLUCENE_UNSTABLE_INCLUDE_DIRS})
SET(CLUCENE_INCLUDE_DIRS ${CLUCENE_INCLUDE_DIR})
SET(CLUCENE_LIBRARIES ${CLUCENE_UNSTABLE_LIBS})
#MESSAGE(FATAL_ERROR NARF)
@@ -106,7 +108,9 @@ ENDIF (CLUCENE_LIBRARY_DIR)
IF(CLUCENE_INCLUDE_DIR AND CLUCENE_LIBRARIES AND CLUCENE_LIBRARY_DIR AND CLUCENE_GOOD_VERSION)
SET(CLucene_FOUND TRUE)
SET(CLUCENE_INCLUDE_DIRS ${CLUCENE_LIBRARY_DIR} ${CLUCENE_INCLUDE_DIR})
ENDIF(CLUCENE_INCLUDE_DIR AND CLUCENE_LIBRARIES AND CLUCENE_LIBRARY_DIR AND CLUCENE_GOOD_VERSION)
ENDIF(CLUCENEUNSTABLE_FOUND)
IF(CLucene_FOUND)
@@ -120,7 +124,8 @@ ELSE(CLucene_FOUND)
ENDIF(CLucene_FOUND)
MARK_AS_ADVANCED(
CLUCENE_INCLUDE_DIR
CLUCENE_LIBRARY_DIR
CLUCENE_INCLUDE_DIRS
CLUCENE_INCLUDE_DIR
CLUCENE_LIBRARY_DIR
CLUCENE_LIBRARIES
)

View File

@@ -0,0 +1,182 @@
# - Define GNU standard installation directories
# Provides install directory variables as defined for GNU software:
# http://www.gnu.org/prep/standards/html_node/Directory-Variables.html
# Inclusion of this module defines the following variables:
# CMAKE_INSTALL_<dir> - destination for files of a given type
# CMAKE_INSTALL_FULL_<dir> - corresponding absolute path
# where <dir> is one of:
# BINDIR - user executables (bin)
# SBINDIR - system admin executables (sbin)
# LIBEXECDIR - program executables (libexec)
# SYSCONFDIR - read-only single-machine data (etc)
# SHAREDSTATEDIR - modifiable architecture-independent data (com)
# LOCALSTATEDIR - modifiable single-machine data (var)
# LIBDIR - object code libraries (lib or lib64)
# INCLUDEDIR - C header files (include)
# OLDINCLUDEDIR - C header files for non-gcc (/usr/include)
# DATAROOTDIR - read-only architecture-independent data root (share)
# DATADIR - read-only architecture-independent data (DATAROOTDIR)
# INFODIR - info documentation (DATAROOTDIR/info)
# LOCALEDIR - locale-dependent data (DATAROOTDIR/locale)
# MANDIR - man documentation (DATAROOTDIR/man)
# DOCDIR - documentation root (DATAROOTDIR/doc/PROJECT_NAME)
# Each CMAKE_INSTALL_<dir> value may be passed to the DESTINATION options of
# install() commands for the corresponding file type. If the includer does
# not define a value the above-shown default will be used and the value will
# appear in the cache for editing by the user.
# Each CMAKE_INSTALL_FULL_<dir> value contains an absolute path constructed
# from the corresponding destination by prepending (if necessary) the value
# of CMAKE_INSTALL_PREFIX.
#=============================================================================
# Copyright 2011 Nikita Krupen'ko <krnekit@gmail.com>
# Copyright 2011 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
# Installation directories
#
if(NOT DEFINED CMAKE_INSTALL_BINDIR)
set(CMAKE_INSTALL_BINDIR "bin" CACHE PATH "user executables (bin)")
endif()
if(NOT DEFINED CMAKE_INSTALL_SBINDIR)
set(CMAKE_INSTALL_SBINDIR "sbin" CACHE PATH "system admin executables (sbin)")
endif()
if(NOT DEFINED CMAKE_INSTALL_LIBEXECDIR)
set(CMAKE_INSTALL_LIBEXECDIR "libexec" CACHE PATH "program executables (libexec)")
endif()
if(NOT DEFINED CMAKE_INSTALL_SYSCONFDIR)
set(CMAKE_INSTALL_SYSCONFDIR "etc" CACHE PATH "read-only single-machine data (etc)")
endif()
if(NOT DEFINED CMAKE_INSTALL_SHAREDSTATEDIR)
set(CMAKE_INSTALL_SHAREDSTATEDIR "com" CACHE PATH "modifiable architecture-independent data (com)")
endif()
if(NOT DEFINED CMAKE_INSTALL_LOCALSTATEDIR)
set(CMAKE_INSTALL_LOCALSTATEDIR "var" CACHE PATH "modifiable single-machine data (var)")
endif()
if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
set(_LIBDIR_DEFAULT "lib")
# Override this default 'lib' with 'lib64' iff:
# - we are on Linux system but NOT cross-compiling
# - we are NOT on debian
# - we are on a 64 bits system
# reason is: amd64 ABI: http://www.x86-64.org/documentation/abi.pdf
# Note that the future of multi-arch handling may be even
# more complicated than that: http://wiki.debian.org/Multiarch
if(CMAKE_SYSTEM_NAME MATCHES "Linux"
AND NOT CMAKE_CROSSCOMPILING
AND NOT EXISTS "/etc/debian_version")
if(NOT DEFINED CMAKE_SIZEOF_VOID_P)
message(AUTHOR_WARNING
"Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. "
"Please enable at least one language before including GNUInstallDirs.")
else()
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(_LIBDIR_DEFAULT "lib64")
endif()
endif()
endif()
set(CMAKE_INSTALL_LIBDIR "${_LIBDIR_DEFAULT}" CACHE PATH "object code libraries (${_LIBDIR_DEFAULT})")
endif()
if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR)
set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "C header files (include)")
endif()
if(NOT DEFINED CMAKE_INSTALL_OLDINCLUDEDIR)
set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "C header files for non-gcc (/usr/include)")
endif()
if(NOT DEFINED CMAKE_INSTALL_DATAROOTDIR)
set(CMAKE_INSTALL_DATAROOTDIR "share" CACHE PATH "read-only architecture-independent data root (share)")
endif()
#-----------------------------------------------------------------------------
# Values whose defaults are relative to DATAROOTDIR. Store empty values in
# the cache and store the defaults in local variables if the cache values are
# not set explicitly. This auto-updates the defaults as DATAROOTDIR changes.
if(NOT CMAKE_INSTALL_DATADIR)
set(CMAKE_INSTALL_DATADIR "" CACHE PATH "read-only architecture-independent data (DATAROOTDIR)")
set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}")
endif()
if(NOT CMAKE_INSTALL_INFODIR)
set(CMAKE_INSTALL_INFODIR "" CACHE PATH "info documentation (DATAROOTDIR/info)")
set(CMAKE_INSTALL_INFODIR "${CMAKE_INSTALL_DATAROOTDIR}/info")
endif()
if(NOT CMAKE_INSTALL_LOCALEDIR)
set(CMAKE_INSTALL_LOCALEDIR "" CACHE PATH "locale-dependent data (DATAROOTDIR/locale)")
set(CMAKE_INSTALL_LOCALEDIR "${CMAKE_INSTALL_DATAROOTDIR}/locale")
endif()
if(NOT CMAKE_INSTALL_MANDIR)
set(CMAKE_INSTALL_MANDIR "" CACHE PATH "man documentation (DATAROOTDIR/man)")
set(CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_DATAROOTDIR}/man")
endif()
if(NOT CMAKE_INSTALL_DOCDIR)
set(CMAKE_INSTALL_DOCDIR "" CACHE PATH "documentation root (DATAROOTDIR/doc/PROJECT_NAME)")
set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}")
endif()
#-----------------------------------------------------------------------------
mark_as_advanced(
CMAKE_INSTALL_BINDIR
CMAKE_INSTALL_SBINDIR
CMAKE_INSTALL_LIBEXECDIR
CMAKE_INSTALL_SYSCONFDIR
CMAKE_INSTALL_SHAREDSTATEDIR
CMAKE_INSTALL_LOCALSTATEDIR
CMAKE_INSTALL_LIBDIR
CMAKE_INSTALL_INCLUDEDIR
CMAKE_INSTALL_OLDINCLUDEDIR
CMAKE_INSTALL_DATAROOTDIR
CMAKE_INSTALL_DATADIR
CMAKE_INSTALL_INFODIR
CMAKE_INSTALL_LOCALEDIR
CMAKE_INSTALL_MANDIR
CMAKE_INSTALL_DOCDIR
)
# Result directories
#
foreach(dir
BINDIR
SBINDIR
LIBEXECDIR
SYSCONFDIR
SHAREDSTATEDIR
LOCALSTATEDIR
LIBDIR
INCLUDEDIR
OLDINCLUDEDIR
DATAROOTDIR
DATADIR
INFODIR
LOCALEDIR
MANDIR
DOCDIR
)
if(NOT IS_ABSOLUTE ${CMAKE_INSTALL_${dir}})
set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_${dir}}")
else()
set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_${dir}}")
endif()
endforeach()

View File

@@ -273,7 +273,7 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
;Main executable.
File "${INSTALL_PATH}\bin\tomahawk.exe"
File "${INSTALL_PATH}\bin\CrashReporter.exe"
File "${INSTALL_PATH}\bin\tomahawk_crash_reporter.exe"
File "${INSTALL_PATH}\bin\libtomahawk_breakpad.dll"
File "${INSTALL_PATH}\bin\libqxtweb-standalone.dll"
@@ -287,7 +287,7 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
;Main executable.
File "${BUILD_PATH}\tomahawk.exe"
File "${BUILD_PATH}\CrashReporter.exe"
File "${BUILD_PATH}\tomahawk_crash_reporter.exe"
File "${BUILD_PATH}\libtomahawk_breakpad.dll"
File "${BUILD_PATH}\libtomahawklib.dll"

View File

@@ -1,3 +1,43 @@
Version 0.3.2:
* Improved syncing process, it's faster and more reliable now.
* Fixed UPnP issues.
* Fixed not updating collections and views after a collection changes.
* Fixed not showing a source's proper name at all times.
* Improved music scanner reliability.
* Fixed various labels not being painted in the right color when selected.
* Support .oga (ogg mimetype) files.
* Fixed stuck Now Playing indicator icon.
* Fixed sidebar width of settings dialog on Windows.
* Fixed not always showing delete icon on hovered temporary pages.
* Fixed inability to select externally mounted drives.
* Fixed bug where speaker would be stuck next to a playlist.
* Fixed bug where first startup would show a loading spinner in the
Dashboard's "Recent Additions" forever.
* Automatically upgrade resolvers when an update is found.
* Fixed last played track of a friend never expiring.
* Fixed parsing of rdio tracks.
* Changed steering mechanism in stations to be user-friendlier.
* Fixed loading biographies of artist names with special characters.
* Fixed behaviour of dropping Spotify playlists on the sidebar.
* Fixed hard to read source-name in the audio control area on OS X.
* Fixed crashes when playing Grooveshark tracks.
* Fixed bug where clicking the latch button would flicker and unlatch.
* Fixed Jabber accounts not connecting properly when proxy is in use.
Version 0.3.1:
* Fixed not resolving to local files.
* Fixed shutdown crash on Windows.
* Support more widget types in Javascript resolver configs.
* Fixed bug where red headphone would be visible on My Collection
when listened along to source went offline.
* Draw author name as well as avatar in recently played playlists.
* Fixed Tomahawk integration in Unity.
* Fixed incorrect item selected in source tree when deleting temporary
pages.
* Fixed currently played label disappearing after 10 minutes regardless
of length of track.
* Fixed crash on Sparkle upgrade on OS X.
Version 0.3.0:
* Make artist names in the album view clickable.
* Don't start playing if a tomahawk:// link was clicked while Tomahawk
@@ -12,7 +52,7 @@ Version 0.3.0:
* Added YouTube resolver.
* Fixed bug where going offline then online would not re-connect to many
peers.
* Added support for auto-updating live XSPF playlists.
* Added support for auto-updating live XSPF playlists.
* Don't show an age of 41 years for tracks that have no age information.
* Show config UI for resolvers that have them as soon as you add them.
* Add support for Echo Nest Personal Catalogs and User Radio. Synchronize

View File

@@ -250,7 +250,7 @@ frameworks_dir = os.path.join(bundle_dir, 'Contents', 'Frameworks')
commands.append(['mkdir', '-p', frameworks_dir])
resources_dir = os.path.join(bundle_dir, 'Contents', 'Resources')
commands.append(['mkdir', '-p', resources_dir])
plugins_dir = os.path.join(bundle_dir, 'Contents', 'PlugIns')
plugins_dir = os.path.join(bundle_dir, 'Contents', 'plugins')
binary = os.path.join(bundle_dir, 'Contents', 'MacOS', bundle_name)
fixed_libraries = []
@@ -502,9 +502,9 @@ except:
print 'Failed to find spotify resolver'
try:
FixPlugin('CrashReporter', '../MacOS')
FixPlugin('tomahawk_crash_reporter', '../MacOS')
except:
print 'Failed to find CrashReporter'
print 'Failed to find tomahawk_crash_reporter'
for plugin in QT_PLUGINS:
FixPlugin(FindQtPlugin(plugin), os.path.dirname(plugin))

BIN
data/images/apply-check.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

3028
lang/tomahawk_en.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/lang">
<file>tomahawk_de.qm</file>
<file>tomahawk_en.qm</file>
</qresource>
</RCC>

View File

@@ -98,6 +98,7 @@
<file>data/images/track-icon-22x22.png</file>
<file>data/images/track-icon-32x32.png</file>
<file>data/images/track-icon-16x16.png</file>
<file>data/images/apply-check.png</file>
<file>data/stylesheets/topbar-radiobuttons.css</file>
<file>data/icons/tomahawk-icon-16x16.png</file>
<file>data/icons/tomahawk-icon-32x32.png</file>

View File

@@ -40,6 +40,7 @@ SET( tomahawkSources ${tomahawkSources}
musicscanner.cpp
shortcuthandler.cpp
scanmanager.cpp
ubuntuunityhack.cpp
tomahawkapp.cpp
main.cpp
)
@@ -85,6 +86,7 @@ SET( tomahawkHeaders ${tomahawkHeaders}
musicscanner.h
scanmanager.h
ubuntuunityhack.h
shortcuthandler.h
)

View File

@@ -22,7 +22,7 @@
#include "GetNewStuffDelegate.h"
#include "GetNewStuffModel.h"
GetNewStuffDialog::GetNewStuffDialog( QWidget *parent, Qt::WindowFlags f )
GetNewStuffDialog::GetNewStuffDialog( QWidget* parent, Qt::WindowFlags f )
: QDialog( parent, f )
, ui( new Ui::GetNewStuffDialog )
, m_model( new GetNewStuffModel( this ) )
@@ -37,16 +37,17 @@ GetNewStuffDialog::GetNewStuffDialog( QWidget *parent, Qt::WindowFlags f )
ui->listView->setMouseTracking( true );
setMinimumSize( 560, 350 );
#ifdef Q_WS_MAC
setMinimumSize( 510, 350 );
setMaximumSize( 510, 350 );
setMaximumSize( 560, 350 );
setSizeGripEnabled( false );
ui->listView->setAttribute( Qt::WA_MacShowFocusRect, false );
#endif
}
GetNewStuffDialog::~GetNewStuffDialog()
{
delete ui;

View File

@@ -34,9 +34,6 @@ LoadXSPFDialog::LoadXSPFDialog( QWidget* parent, Qt::WindowFlags f )
m_ui->verticalLayout->setSpacing( 0 );
#endif
connect( m_ui->buttonBox, SIGNAL( accepted() ), SLOT( accept() ) );
connect( m_ui->buttonBox, SIGNAL( rejected() ), SLOT( reject() ) );
connect( m_ui->navigateButton, SIGNAL( clicked( bool ) ), this, SLOT( getLocalFile() ) );
}
@@ -47,7 +44,7 @@ LoadXSPFDialog::~LoadXSPFDialog()
void
LoadXSPFDialog::getLocalFile()
{
QString url = QFileDialog::getOpenFileName( this, tr( "Load XSPF File" ), QDir::homePath(), ".xspf" );
QString url = QFileDialog::getOpenFileName( this, tr( "Load XSPF File" ), QDir::homePath(), tr( "XSPF Files (*.xspf)" ) );
m_ui->lineEdit->setText( url );
}

View File

@@ -19,9 +19,9 @@
#include "audiocontrols.h"
#include "ui_audiocontrols.h"
#include <QNetworkReply>
#include <QDropEvent>
#include <QMouseEvent>
#include <QtNetwork/QNetworkReply>
#include <QtGui/QDropEvent>
#include <QtGui/QMouseEvent>
#include "audio/audioengine.h"
#include "playlist/playlistview.h"
@@ -86,7 +86,11 @@ AudioControls::AudioControls( QWidget* parent )
ui->loveButton->setPixmap( RESPATH "images/not-loved.png" );
ui->loveButton->setCheckable( true );
#ifdef Q_WS_MAC
ui->ownerLabel->setForegroundRole( QPalette::Text );
#else
ui->ownerLabel->setForegroundRole( QPalette::Dark );
#endif
ui->metaDataArea->setStyleSheet( "QWidget#metaDataArea {\nborder-width: 4px;\nborder-image: url(" RESPATH "images/now-playing-panel.png) 4 4 4 4 stretch stretch; }" );
ui->seekSlider->setEnabled( true );
@@ -204,9 +208,11 @@ AudioControls::onPlaybackStarted( const Tomahawk::result_ptr& result )
m_sliderTimeLine.setFrameRange( 0, duration );
m_sliderTimeLine.setCurrentTime( 0 );
m_seekMsecs = -1;
ui->seekSlider->setVisible( true );
m_noTimeChange = false;
Tomahawk::InfoSystem::InfoStringHash trackInfo;
trackInfo["artist"] = result->artist()->name();
trackInfo["album"] = result->album()->name();
@@ -373,9 +379,22 @@ AudioControls::onPlaybackTimer( qint64 msElapsed )
ui->timeLabel->setText( TomahawkUtils::timeToString( seconds ) );
ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( m_currentTrack->duration() - seconds ) );
if ( m_sliderTimeLine.currentTime() > msElapsed || m_seekMsecs != -1 )
if ( m_noTimeChange )
{
if ( m_sliderTimeLine.currentTime() != msElapsed )
{
m_noTimeChange = false;
m_sliderTimeLine.resume();
}
}
else if ( m_sliderTimeLine.currentTime() >= msElapsed || m_seekMsecs != -1 )
{
m_sliderTimeLine.setPaused( true );
m_noTimeChange = false;
if ( m_sliderTimeLine.currentTime() == msElapsed )
m_noTimeChange = true;
m_sliderTimeLine.setCurrentTime( msElapsed );
m_seekMsecs = -1;
if ( AudioEngine::instance()->state() != AudioEngine::Paused )

View File

@@ -19,8 +19,8 @@
#ifndef AUDIOCONTROLS_H
#define AUDIOCONTROLS_H
#include <QWidget>
#include <QTimeLine>
#include <QtGui/QWidget>
#include <QtCore/QTimeLine>
#include "result.h"
#include "playlistinterface.h"
@@ -95,6 +95,7 @@ private:
QTimeLine m_sliderTimeLine;
qint64 m_seekMsecs;
bool m_noTimeChange;
};
#endif // AUDIOCONTROLS_H

View File

@@ -28,9 +28,6 @@
<height>80</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="topMargin">
<number>0</number>
@@ -164,9 +161,6 @@
<height>58</height>
</size>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>

View File

@@ -18,11 +18,17 @@
#include "BreakPad.h"
#include "config.h"
#include "utils/logger.h"
#include <QCoreApplication>
#include <QString>
#include <QFileInfo>
#include <string.h>
#define CRASH_REPORTER_BINARY "CrashReporter"
#define CRASH_REPORTER_BINARY "tomahawk_crash_reporter"
bool s_active = true;
#ifndef WIN32
#include <unistd.h>
@@ -38,8 +44,10 @@ LaunchUploader( const char* dump_dir, const char* minidump_id, void* that, bool
return false;
const char* crashReporter = static_cast<BreakPad*>(that)->crashReporter();
pid_t pid = fork();
if ( !s_active || strlen( crashReporter ) == 0 )
return false;
pid_t pid = fork();
if ( pid == -1 ) // fork failed
return false;
if ( pid == 0 )
@@ -63,14 +71,25 @@ LaunchUploader( const char* dump_dir, const char* minidump_id, void* that, bool
}
BreakPad::BreakPad( const QString& path )
BreakPad::BreakPad( const QString& path, bool active )
#ifdef Q_OS_LINUX
: google_breakpad::ExceptionHandler( path.toStdString(), 0, LaunchUploader, this, true )
#else
: google_breakpad::ExceptionHandler( path.toStdString(), 0, LaunchUploader, this, true, 0 )
#endif
{
QString reporter = QString( "%1/%2" ).arg( qApp->applicationDirPath() ).arg( CRASH_REPORTER_BINARY );
s_active = active;
QString reporter;
QString localReporter = QString( "%1/%2" ).arg( qApp->applicationDirPath() ).arg( CRASH_REPORTER_BINARY );
QString globalReporter = QString( "%1/%2" ).arg( CMAKE_INSTALL_PREFIX "/" CMAKE_INSTALL_LIBEXECDIR ).arg( CRASH_REPORTER_BINARY );
if ( QFileInfo( localReporter ).exists() )
reporter = localReporter;
else if ( QFileInfo( globalReporter ).exists() )
reporter = globalReporter;
else
tLog() << "Could not find \"" CRASH_REPORTER_BINARY "\" in \"" CMAKE_INSTALL_PREFIX "/" CMAKE_INSTALL_LIBEXECDIR "\" or application path";
char* creporter;
std::string sreporter = reporter.toStdString();
@@ -126,7 +145,7 @@ LaunchUploader( const wchar_t* dump_dir, const wchar_t* minidump_id, void* that,
si.wShowWindow = SW_SHOWNORMAL;
ZeroMemory( &pi, sizeof(pi) );
if (CreateProcess( NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
if ( CreateProcess( NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ) )
{
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
@@ -137,9 +156,24 @@ LaunchUploader( const wchar_t* dump_dir, const wchar_t* minidump_id, void* that,
}
BreakPad::BreakPad( const QString& path )
BreakPad::BreakPad( const QString& path, bool active )
: google_breakpad::ExceptionHandler( path.toStdWString(), 0, LaunchUploader, this, true )
{
s_active = active;
}
#endif // WIN32
void
BreakPad::setActive( bool enabled )
{
s_active = enabled;
}
bool
BreakPad::isActive()
{
return s_active;
}

View File

@@ -32,11 +32,14 @@ class BreakPad : public google_breakpad::ExceptionHandler
const char* m_crashReporter; // again, const char[]
public:
BreakPad( const QString &dump_write_dirpath );
BreakPad( const QString& dump_write_dirpath, bool active );
~BreakPad()
{}
static void setActive( bool enabled );
static bool isActive();
void setProductName( const char* s ) { m_productName = s; };
const char* productName() const { return m_productName; }

View File

@@ -1,8 +1,10 @@
PROJECT( CrashReporter )
FIND_PACKAGE( Qt4 REQUIRED )
SET( QT_USE_QTNETWORK TRUE )
SET( crashreporter_SOURCES main.cpp CrashReporter.cpp )
SET( crashreporter_HEADERS CrashReporter.h )
SET( crashreporter_UI CrashReporter.ui )
@@ -16,5 +18,8 @@ INCLUDE( ${QT_USE_FILE} )
INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/src ../../libtomahawk )
ADD_DEFINITIONS( ${QT_DEFINITIONS} )
ADD_EXECUTABLE( CrashReporter WIN32 ${crashreporter_SOURCES} ${crashreporter_HEADERS_MOC} ${crashreporter_UI_HEADERS} ${crashreporter_RC_RCC} )
TARGET_LINK_LIBRARIES( CrashReporter ${QT_LIBRARIES} tomahawklib )
ADD_EXECUTABLE( tomahawk_crash_reporter WIN32 ${crashreporter_SOURCES} ${crashreporter_HEADERS_MOC} ${crashreporter_UI_HEADERS} ${crashreporter_RC_RCC} )
TARGET_LINK_LIBRARIES( tomahawk_crash_reporter ${QT_LIBRARIES} tomahawklib )
INCLUDE(GNUInstallDirs)
install(TARGETS tomahawk_crash_reporter RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR})

View File

@@ -9,6 +9,7 @@
#cmakedefine DEBUG_BUILD
#define CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}"
#define CMAKE_INSTALL_LIBEXECDIR "${CMAKE_INSTALL_LIBEXECDIR}"
#define CMAKE_SYSTEM "${CMAKE_SYSTEM}"
#cmakedefine LION

View File

@@ -45,6 +45,8 @@ AtticaManager::AtticaManager( QObject* parent )
// resolvers
m_manager.addProviderFile( QUrl( "http://bakery.tomahawk-player.org:10480/resolvers/providers.xml" ) );
qRegisterMetaType< Attica::Content >( "Attica::Content" );
}
@@ -149,6 +151,7 @@ AtticaManager::pathFromId( const QString& resolverId ) const
return m_resolverStates.value( resolverId ).scriptPath;
}
void
AtticaManager::uploadRating( const Content& c )
{
@@ -175,6 +178,7 @@ AtticaManager::uploadRating( const Content& c )
emit resolverStateChanged( c.id() );
}
bool
AtticaManager::userHasRated( const Content& c ) const
{
@@ -245,6 +249,7 @@ AtticaManager::resolverIconFetched()
m_resolverStates[ resolverId ].pixmap = icon;
}
void
AtticaManager::syncServerData()
{
@@ -271,62 +276,16 @@ AtticaManager::syncServerData()
if ( ( r.state == Installed || r.state == NeedsUpgrade ) &&
!upstream.version().isEmpty() )
{
if ( newerVersion( r.version, upstream.version() ) )
if ( TomahawkUtils::newerVersion( r.version, upstream.version() ) )
{
m_resolverStates[ id ].state = NeedsUpgrade;
QMetaObject::invokeMethod( this, "upgradeResolver", Qt::QueuedConnection, Q_ARG( Attica::Content, upstream ) );
}
}
}
}
}
bool
AtticaManager::newerVersion( const QString& older, const QString& newer ) const
{
// Dumb version comparison. Expects two strings, X.Y and Z.V. Returns true if Z > v || Z == V && V > Y
// DOES NOT support X.Y.Z version strings
if ( older.isEmpty() || newer.isEmpty() )
return false;
QPair<int, int> oldVer, newVer;
QStringList parts = older.split( "." );
if ( parts.size() == 1 )
{
oldVer.first = parts[ 0 ].toInt();
oldVer.second = 0;
}
else if ( parts.size() == 2 )
{
oldVer.first = parts[ 0 ].toInt();
oldVer.second = parts[ 1 ].toInt();;
}
else
return false;
parts = newer.split( "." );
if ( parts.size() == 1 )
{
newVer.first = parts[ 0 ].toInt();
newVer.second = 0;
}
else if ( parts.size() == 2 )
{
newVer.first = parts[ 0 ].toInt();
newVer.second = parts[ 1 ].toInt();;
}
else
return false;
// Do the comparison
if ( newVer.first > oldVer.first )
return true;
if ( newVer.first == oldVer.first &&
newVer.second > oldVer.second )
return true;
return false;
}
void
AtticaManager::installResolver( const Content& resolver )
@@ -347,6 +306,7 @@ AtticaManager::installResolver( const Content& resolver )
job->start();
}
void
AtticaManager::upgradeResolver( const Content& resolver )
{

View File

@@ -83,8 +83,6 @@ public:
ResolverState resolverState( const Attica::Content& resolver ) const;
QPixmap iconForResolver( const Attica::Content& id ); // Looks up in icon cache
void installResolver( const Attica::Content& resolver );
void upgradeResolver( const Attica::Content& resolver );
void uninstallResolver( const Attica::Content& resolver );
void uninstallResolver( const QString& pathToResolver );
QString pathFromId( const QString& resolverId ) const;
@@ -92,6 +90,9 @@ public:
void uploadRating( const Attica::Content& c );
bool userHasRated( const Attica::Content& c ) const;
public slots:
void installResolver( const Attica::Content& resolver );
void upgradeResolver( const Attica::Content& resolver );
signals:
void resolversReloaded( const Attica::Content::List& resolvers );
@@ -111,7 +112,6 @@ private slots:
void resolverIconFetched();
void syncServerData();
bool newerVersion( const QString& older, const QString& newer ) const;
private:
QString extractPayload( const QString& filename, const QString& resolverId ) const;
@@ -127,4 +127,8 @@ private:
static AtticaManager* s_instance;
};
#ifdef LIBATTICA_FOUND
Q_DECLARE_METATYPE( Attica::Content );
#endif
#endif // ATTICAMANAGER_H

View File

@@ -440,6 +440,7 @@ set( libHeaders
widgets/playlisttypeselectordlg.h
widgets/welcomewidget.h
widgets/whatshotwidget.h
widgets/whatshotwidget_p.h
widgets/RecentlyPlayedPlaylistsModel.h
widgets/RecentPlaylistsModel.h
widgets/OverlayButton.h
@@ -501,7 +502,7 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/.
${QJSON_INCLUDE_DIR}
${LIBECHONEST_INCLUDE_DIR}
${LIBECHONEST_INCLUDE_DIR}/..
${CLUCENE_INCLUDE_DIR}
${CLUCENE_INCLUDE_DIRS}
${PHONON_INCLUDES}
${CMAKE_BINARY_DIR}/thirdparty/liblastfm2/src

View File

@@ -42,7 +42,7 @@ ActionCollection::initActions()
m_actionCollection[ "latchOff" ] = new QAction( tr( "&Stop Listening Along" ), this );
bool isPublic = TomahawkSettings::instance()->privateListeningMode() == TomahawkSettings::PublicListening;
QAction *privacyToggle = new QAction( tr( QString( isPublic ? "&Listen Privately" : "&Listen Publicly" ).toAscii().constData() ), this );
QAction *privacyToggle = new QAction( ( isPublic ? tr( "&Listen Privately" ) : tr( "&Listen Publicly" ) ), this );
privacyToggle->setIcon( QIcon( RESPATH "images/private-listening.png" ) );
privacyToggle->setIconVisibleInMenu( isPublic );
m_actionCollection[ "togglePrivacy" ] = privacyToggle;

View File

@@ -413,7 +413,7 @@ AudioEngine::togglePrivateListeningMode()
#ifndef TOMAHAWK_HEADLESS
QAction *privacyToggle = ActionCollection::instance()->getAction( "togglePrivacy" );
bool isPublic = TomahawkSettings::instance()->privateListeningMode() == TomahawkSettings::PublicListening;
privacyToggle->setText( tr( QString( isPublic ? "&Listen Privately" : "&Listen Publicly" ).toAscii().constData() ) );
privacyToggle->setText( ( isPublic ? tr( "&Listen Privately" ) : tr( "&Listen Publicly" ) ) );
privacyToggle->setIconVisibleInMenu( isPublic );
#endif
}
@@ -450,9 +450,6 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
tLog() << "Starting new song:" << m_currentTrack->url();
emit loading( m_currentTrack );
if ( QNetworkReply* qnr_io = qobject_cast< QNetworkReply* >( io.data() ) )
connect( qnr_io, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( ioStreamError( QNetworkReply::NetworkError ) ) );
if ( !isHttpResult( m_currentTrack->url() ) && !isLocalResult( m_currentTrack->url() ) )
{
if ( QNetworkReply* qnr_io = qobject_cast< QNetworkReply* >( io.data() ) )
@@ -605,16 +602,6 @@ AudioEngine::playItem( Tomahawk::PlaylistInterface* playlist, const Tomahawk::re
}
}
void
AudioEngine::ioStreamError( QNetworkReply::NetworkError error )
{
if ( error != QNetworkReply::NoError )
{
if ( canGoNext() )
loadNextTrack();
}
}
void
AudioEngine::playlistNextTrackReady()

View File

@@ -21,7 +21,6 @@
#include <QObject>
#include <QTimer>
#include <QNetworkReply>
#include <phonon/MediaObject>
#include <phonon/AudioOutput>
@@ -137,7 +136,6 @@ private slots:
void setCurrentTrack( const Tomahawk::result_ptr& result );
void ioStreamError( QNetworkReply::NetworkError );
private:
void setState( AudioState state );

View File

@@ -33,10 +33,12 @@ Collection::Collection( const source_ptr& source, const QString& name, QObject*
: QObject( parent )
, m_name( name )
, m_lastmodified( 0 )
, m_isLoaded( false )
, m_changed( false )
, m_source( source )
{
qDebug() << Q_FUNC_INFO << name << source->friendlyName();
connect( source.data(), SIGNAL( synced() ), SLOT( onSynced() ) );
}
@@ -211,8 +213,8 @@ Collection::setTracks( const QList<unsigned int>& ids )
{
qDebug() << Q_FUNC_INFO << ids.count() << name();
m_changed = true;
emit tracksAdded( ids );
emit changed();
}
@@ -221,8 +223,19 @@ Collection::delTracks( const QList<unsigned int>& ids )
{
qDebug() << Q_FUNC_INFO << ids.count() << name();
m_changed = true;
emit tracksRemoved( ids );
emit changed();
}
void
Collection::onSynced()
{
if ( m_changed )
{
m_changed = false;
emit changed();
}
}

View File

@@ -49,8 +49,6 @@ public:
Collection( const source_ptr& source, const QString& name, QObject* parent = 0 );
virtual ~Collection();
virtual void setLoaded() { m_isLoaded = true; }
virtual bool isLoaded() const { return m_isLoaded; }
virtual QString name() const;
virtual void loadPlaylists() { qDebug() << Q_FUNC_INFO; }
@@ -107,8 +105,11 @@ protected:
QString m_name;
unsigned int m_lastmodified; // unix time of last change to collection
private slots:
void onSynced();
private:
bool m_isLoaded;
bool m_changed;
source_ptr m_source;
QHash< QString, Tomahawk::playlist_ptr > m_playlists;

View File

@@ -73,7 +73,15 @@ Database::loadIndex()
void
Database::enqueue( QSharedPointer<DatabaseCommand> lc )
Database::enqueue( const QList< QSharedPointer<DatabaseCommand> >& lc )
{
qDebug() << "Enqueueing" << lc.count() << "commands to rw thread";
m_workerRW->enqueue( lc );
}
void
Database::enqueue( const QSharedPointer<DatabaseCommand>& lc )
{
if ( lc->doesMutates() )
{

View File

@@ -66,7 +66,8 @@ signals:
void newJobRW( QSharedPointer<DatabaseCommand> );
public slots:
void enqueue( QSharedPointer<DatabaseCommand> lc );
void enqueue( const QSharedPointer<DatabaseCommand>& lc );
void enqueue( const QList< QSharedPointer<DatabaseCommand> >& lc );
private slots:
void setIsReadyTrue() { m_ready = true; }

View File

@@ -69,22 +69,8 @@ DatabaseCollection::loadStations()
connect( cmd, SIGNAL( stationLoaded( Tomahawk::source_ptr, QVariantList ) ),
SLOT( stationCreated( const Tomahawk::source_ptr&, const QVariantList& ) ) );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );}
/*void
DatabaseCollection::loadTracks()
{
qDebug() << Q_FUNC_INFO << source()->userName();
setLoaded();
DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( source()->collection() );
connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr>, QVariant ) ),
SLOT( setTracks( QList<Tomahawk::query_ptr> ) ) );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}*/
}
void
@@ -143,19 +129,8 @@ DatabaseCollection::stations()
}
/*QList< Tomahawk::query_ptr >
DatabaseCollection::tracks()
{
if ( !isLoaded() )
{
loadTracks();
}
return Collection::tracks();
}*/
void DatabaseCollection::autoPlaylistCreated( const source_ptr& source, const QVariantList& data )
void
DatabaseCollection::autoPlaylistCreated( const source_ptr& source, const QVariantList& data )
{
dynplaylist_ptr p( new DynamicPlaylist( source, //src
data[0].toString(), //current rev
@@ -172,7 +147,8 @@ void DatabaseCollection::autoPlaylistCreated( const source_ptr& source, const QV
}
void DatabaseCollection::stationCreated( const source_ptr& source, const QVariantList& data )
void
DatabaseCollection::stationCreated( const source_ptr& source, const QVariantList& data )
{
dynplaylist_ptr p( new DynamicPlaylist( source, //src
data[0].toString(), //current rev

View File

@@ -80,12 +80,14 @@ DatabaseCommand::setSource( const Tomahawk::source_ptr& s )
m_source = s;
}
const Tomahawk::source_ptr&
DatabaseCommand::source() const
{
return m_source;
}
DatabaseCommand*
DatabaseCommand::factory( const QVariant& op, const source_ptr& source )
{
@@ -139,7 +141,8 @@ DatabaseCommand::factory( const QVariant& op, const source_ptr& source )
cmd->setSource( source );
QJson::QObjectHelper::qvariant2qobject( op.toMap(), cmd );
return cmd;
} else if( name == "createdynamicplaylist" )
}
else if( name == "createdynamicplaylist" )
{
DatabaseCommand_CreateDynamicPlaylist * cmd = new DatabaseCommand_CreateDynamicPlaylist;
cmd->setSource( source );
@@ -183,7 +186,7 @@ DatabaseCommand::factory( const QVariant& op, const source_ptr& source )
return cmd;
}
qDebug() << "ERROR in" << Q_FUNC_INFO << name;
qDebug() << "Unknown database command" << name;
// Q_ASSERT( false );
return NULL;
}

View File

@@ -70,6 +70,7 @@ public:
const Tomahawk::source_ptr& source() const;
virtual bool loggable() const { return false; }
virtual bool groupable() const { return false; }
virtual bool singletonCmd() const { return false; }
virtual bool localOnly() const { return false; }

View File

@@ -24,7 +24,6 @@
#include "album.h"
#include "collection.h"
#include "database/database.h"
#include "databasecommand_collectionstats.h"
#include "databaseimpl.h"
#include "network/dbsyncconnection.h"
#include "network/servent.h"
@@ -73,15 +72,7 @@ DatabaseCommand_AddFiles::postCommitHook()
emit notify( m_ids );
if ( source()->isLocal() )
{
Servent::instance()->triggerDBSync();
// Re-calculate local db stats
DatabaseCommand_CollectionStats* cmd = new DatabaseCommand_CollectionStats( SourceList::instance()->getLocal() );
connect( cmd, SIGNAL( done( QVariantMap ) ),
SourceList::instance()->getLocal().data(), SLOT( setStats( QVariantMap ) ), Qt::QueuedConnection );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}
}

View File

@@ -35,20 +35,21 @@ DatabaseCommand_addSource::DatabaseCommand_addSource( const QString& username, c
void
DatabaseCommand_addSource::exec( DatabaseImpl* dbi )
{
Q_ASSERT( !m_fname.isEmpty() );
TomahawkSqlQuery query = dbi->newquery();
query.prepare( "SELECT id, friendlyname FROM source WHERE name = ?" );
query.prepare( "SELECT id FROM source WHERE name = ?" );
query.addBindValue( m_username );
query.exec();
if ( query.next() )
{
unsigned int id = query.value( 0 ).toInt();
QString fname = query.value( 1 ).toString();
query.prepare( "UPDATE source SET isonline = 'true', friendlyname = ? WHERE id = ?" );
query.addBindValue( m_fname );
query.addBindValue( id );
query.exec();
emit done( id, fname );
emit done( id, m_fname );
return;
}

View File

@@ -25,7 +25,6 @@
#include "collection.h"
#include "source.h"
#include "database/database.h"
#include "databasecommand_collectionstats.h"
#include "databaseimpl.h"
#include "network/servent.h"
#include "utils/logger.h"
@@ -54,7 +53,7 @@ DatabaseCommand_DeleteFiles::postCommitHook()
ids << id.toUInt();
emit notify( ids );
if( source()->isLocal() )
if ( source()->isLocal() )
Servent::instance()->triggerDBSync();
}

View File

@@ -64,6 +64,7 @@ public:
virtual void exec( DatabaseImpl* );
virtual bool doesMutates() const { return true; }
virtual bool localOnly() const { return false; }
virtual bool groupable() const { return true; }
virtual void postCommitHook();
QVariantList ids() const { return m_ids; }

View File

@@ -94,23 +94,13 @@ DatabaseCommand_LoadDynamicPlaylistEntries::exec( DatabaseImpl* dbi )
}
else
{
// No controls, lets load the info we need directly from the playlist table
TomahawkSqlQuery info = dbi->newquery();
info.prepare( QString( "SELECT dynamic_playlist.pltype, dynamic_playlist.plmode FROM playlist, dynamic_playlist WHERE playlist.guid = \"%1\" AND playlist.guid = dynamic_playlist.guid" ).arg( playlist_guid ) );
if( !info.exec() ) {
qWarning() << "Failed to load dynplaylist info..";
return;
} else if( !info.first() ) {
qWarning() << "Noo results for queryL:" << info.lastQuery();
return;
}
type = info.value( 0 ).toString();
mode = static_cast<GeneratorMode>( info.value( 1 ).toInt() );
// No controls or plguid is null, but that's okay. We'll get a setdynrevision command with a proper revision some point later
return;
}
if( mode == OnDemand )
{
Q_ASSERT( m_entrymap.isEmpty() ); // ondemand should have no entry
// Q_ASSERT( m_entrymap.isEmpty() ); // ondemand should have no entry
emit done( revisionGuid(), m_islatest, type, controls, true );
}

View File

@@ -36,8 +36,8 @@ using namespace Tomahawk;
void
DatabaseCommand_LogPlayback::postCommitHook()
{
connect( this, SIGNAL( trackPlaying( Tomahawk::query_ptr ) ),
source().data(), SLOT( onPlaybackStarted( Tomahawk::query_ptr ) ), Qt::QueuedConnection );
connect( this, SIGNAL( trackPlaying( Tomahawk::query_ptr, unsigned int ) ),
source().data(), SLOT( onPlaybackStarted( Tomahawk::query_ptr, unsigned int ) ), Qt::QueuedConnection );
connect( this, SIGNAL( trackPlayed( Tomahawk::query_ptr ) ),
source().data(), SLOT( onPlaybackFinished( Tomahawk::query_ptr ) ), Qt::QueuedConnection );
@@ -60,7 +60,7 @@ DatabaseCommand_LogPlayback::postCommitHook()
// if the play time is more than 10 minutes in the past, ignore
else if ( m_action == Started && QDateTime::fromTime_t( playtime() ).secsTo( QDateTime::currentDateTime() ) < STARTED_THRESHOLD )
{
emit trackPlaying( q );
emit trackPlaying( q, m_trackDuration );
}
if ( source()->isLocal() )

View File

@@ -37,6 +37,7 @@ Q_PROPERTY( QString artist READ artist WRITE setArtist )
Q_PROPERTY( QString track READ track WRITE setTrack )
Q_PROPERTY( unsigned int playtime READ playtime WRITE setPlaytime )
Q_PROPERTY( unsigned int secsPlayed READ secsPlayed WRITE setSecsPlayed )
Q_PROPERTY( unsigned int trackDuration READ trackDuration WRITE setTrackDuration )
Q_PROPERTY( int action READ action WRITE setAction )
public:
@@ -47,13 +48,14 @@ public:
};
explicit DatabaseCommand_LogPlayback( QObject* parent = 0 )
: DatabaseCommandLoggable( parent )
: DatabaseCommandLoggable( parent ), m_playtime( 0 ), m_secsPlayed( 0 ), m_trackDuration( 0 )
{}
explicit DatabaseCommand_LogPlayback( const Tomahawk::result_ptr& result, Action action, unsigned int secsPlayed = 0, QObject* parent = 0 )
: DatabaseCommandLoggable( parent ), m_result( result ), m_secsPlayed( secsPlayed ), m_action( action )
{
m_playtime = QDateTime::currentDateTimeUtc().toTime_t();
m_trackDuration = result->duration();
setSource( SourceList::instance()->getLocal() );
setArtist( result->artist()->name() );
@@ -68,6 +70,7 @@ public:
virtual bool doesMutates() const { return true; }
virtual bool singletonCmd() const { return ( m_action == Started ); }
virtual bool localOnly() const;
virtual bool groupable() const { return true; }
QString artist() const { return m_artist; }
void setArtist( const QString& s ) { m_artist = s; }
@@ -81,11 +84,14 @@ public:
unsigned int secsPlayed() const { return m_secsPlayed; }
void setSecsPlayed( unsigned int i ) { m_secsPlayed = i; }
unsigned int trackDuration() const { return m_trackDuration; }
void setTrackDuration( unsigned int trackDuration ) { m_trackDuration = trackDuration; }
int action() const { return m_action; }
void setAction( int a ) { m_action = (Action)a; }
signals:
void trackPlaying( const Tomahawk::query_ptr& query );
void trackPlaying( const Tomahawk::query_ptr& query, unsigned int duration );
void trackPlayed( const Tomahawk::query_ptr& query );
private:
@@ -95,6 +101,7 @@ private:
QString m_track;
unsigned int m_playtime;
unsigned int m_secsPlayed;
unsigned int m_trackDuration;
Action m_action;
};

View File

@@ -157,7 +157,6 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
result->setRID( uuid() );
result->setAlbumPos( files_query.value( 14 ).toUInt() );
result->setTrackId( files_query.value( 9 ).toUInt() );
result->setYear( files_query.value( 17 ).toUInt() );
TomahawkSqlQuery attrQuery = lib->newquery();
QVariantMap attr;

View File

@@ -249,8 +249,8 @@ DatabaseCommand_SetDynamicPlaylistRevision::exec( DatabaseImpl* lib )
controlsQuery.exec();
}
}
if ( m_applied )
{
tLog() << "updating dynamic playlist, optimistic locking okay";

View File

@@ -60,6 +60,7 @@ public:
virtual void exec( DatabaseImpl* lib );
virtual void postCommitHook();
virtual bool doesMutates() const { return true; }
virtual bool groupable() const { return true; }
void setControlsV( const QVariantList& vlist )
{

View File

@@ -104,6 +104,7 @@ DatabaseCommand_SetPlaylistRevision::exec( DatabaseImpl* lib )
}
else
{
tDebug() << "Playlist:" << m_playlistguid << m_currentRevision << source()->friendlyName() << source()->id();
throw "No such playlist, WTF?";
return;
}
@@ -212,6 +213,6 @@ DatabaseCommand_SetPlaylistRevision::exec( DatabaseImpl* lib )
else if ( !m_oldrev.isEmpty() )
{
tDebug() << "Not updating current revision, optimistic locking fail";
Q_ASSERT( false );
// Q_ASSERT( false );
}
}

View File

@@ -58,6 +58,7 @@ public:
virtual bool doesMutates() const { return true; }
virtual bool localOnly() const { return m_localOnly; }
virtual bool groupable() const { return true; }
void setAddedentriesV( const QVariantList& vlist )
{

View File

@@ -170,6 +170,7 @@ public:
void setTimestamp( const int ts ) { m_timestamp = ts; }
virtual bool doesMutates() const { return true; }
virtual bool groupable() const { return true; }
private:
Tomahawk::result_ptr m_result;

View File

@@ -26,6 +26,7 @@
DatabaseCommand_UpdateSearchIndex::DatabaseCommand_UpdateSearchIndex()
: DatabaseCommand()
{
tLog() << Q_FUNC_INFO << "Updating index.";
}

View File

@@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -72,16 +72,21 @@ DatabaseWorker::run()
}
void
DatabaseWorker::enqueue( const QList< QSharedPointer<DatabaseCommand> >& cmds )
{
QMutexLocker lock( &m_mut );
m_outstanding += cmds.count();
m_commands << cmds;
if ( m_outstanding == cmds.count() )
QTimer::singleShot( 0, this, SLOT( doWork() ) );
}
void
DatabaseWorker::enqueue( const QSharedPointer<DatabaseCommand>& cmd )
{
if ( QThread::currentThread() != thread() )
{
// qDebug() << Q_FUNC_INFO << "Reinvoking in correct thread.";
QMetaObject::invokeMethod( this, "enqueue", Qt::QueuedConnection, Q_ARG( QSharedPointer<DatabaseCommand>, cmd ) );
return;
}
QMutexLocker lock( &m_mut );
m_outstanding++;
m_commands << cmd;
@@ -107,6 +112,7 @@ DatabaseWorker::doWork()
timer.start();
#endif
QList< QSharedPointer<DatabaseCommand> > cmdGroup;
QSharedPointer<DatabaseCommand> cmd;
{
QMutexLocker lock( &m_mut );
@@ -119,47 +125,66 @@ DatabaseWorker::doWork()
Q_ASSERT( transok );
Q_UNUSED( transok );
}
unsigned int completed = 0;
try
{
bool finished = false;
{
// tDebug() << "Executing cmd:" << cmd->guid();
cmd->_exec( m_dbimpl ); // runs actual SQL stuff
if ( cmd->loggable() )
while ( !finished )
{
// We only save our own ops to the oplog, since incoming ops from peers
// are applied immediately.
//
// Crazy idea: if peers had keypairs and could sign ops/msgs, in theory it
// would be safe to sync ops for friend A from friend B's cache, if he saved them,
// which would mean you could get updates even if a peer was offline.
if ( cmd->source()->isLocal() && !cmd->localOnly() )
completed++;
cmd->_exec( m_dbimpl ); // runs actual SQL stuff
if ( cmd->loggable() )
{
// save to op-log
DatabaseCommandLoggable* command = (DatabaseCommandLoggable*)cmd.data();
logOp( command );
}
else
{
// Make a note of the last guid we applied for this source
// so we can always request just the newer ops in future.
// We only save our own ops to the oplog, since incoming ops from peers
// are applied immediately.
//
if ( !cmd->singletonCmd() )
// Crazy idea: if peers had keypairs and could sign ops/msgs, in theory it
// would be safe to sync ops for friend A from friend B's cache, if he saved them,
// which would mean you could get updates even if a peer was offline.
if ( cmd->source()->isLocal() && !cmd->localOnly() )
{
// qDebug() << "Setting lastop for source" << cmd->source()->id() << "to" << cmd->guid();
TomahawkSqlQuery query = m_dbimpl->newquery();
query.prepare( "UPDATE source SET lastop = ? WHERE id = ?" );
query.addBindValue( cmd->guid() );
query.addBindValue( cmd->source()->id() );
if ( !query.exec() )
// save to op-log
DatabaseCommandLoggable* command = (DatabaseCommandLoggable*)cmd.data();
logOp( command );
}
else
{
// Make a note of the last guid we applied for this source
// so we can always request just the newer ops in future.
//
if ( !cmd->singletonCmd() )
{
qDebug() << "Failed to set lastop";
throw "Failed to set lastop";
TomahawkSqlQuery query = m_dbimpl->newquery();
query.prepare( "UPDATE source SET lastop = ? WHERE id = ?" );
query.addBindValue( cmd->guid() );
query.addBindValue( cmd->source()->id() );
if ( !query.exec() )
{
throw "Failed to set lastop";
}
}
}
}
cmdGroup << cmd;
if ( cmd->groupable() && !m_commands.isEmpty() )
{
QMutexLocker lock( &m_mut );
if ( m_commands.first()->groupable() )
{
cmd = m_commands.takeFirst();
}
else
{
finished = true;
}
}
else
finished = true;
}
if ( cmd->doesMutates() )
@@ -167,8 +192,7 @@ DatabaseWorker::doWork()
qDebug() << "Committing" << cmd->commandname() << cmd->guid();
if ( !m_dbimpl->database().commit() )
{
qDebug() << "*FAILED TO COMMIT TRANSACTION*";
tDebug() << "FAILED TO COMMIT TRANSACTION*";
throw "commit failed";
}
}
@@ -178,7 +202,8 @@ DatabaseWorker::doWork()
tDebug() << "DBCmd Duration:" << duration << "ms, now running postcommit for" << cmd->commandname();
#endif
cmd->postCommit();
foreach ( QSharedPointer<DatabaseCommand> c, cmdGroup )
c->postCommit();
#ifdef DEBUG_TIMING
tDebug() << "Post commit finished in" << timer.elapsed() - duration << "ms for" << cmd->commandname();
@@ -195,7 +220,7 @@ DatabaseWorker::doWork()
<< m_dbimpl->database().lastError().driverText()
<< endl;
if( cmd->doesMutates() )
if ( cmd->doesMutates() )
m_dbimpl->database().rollback();
Q_ASSERT( false );
@@ -203,17 +228,19 @@ DatabaseWorker::doWork()
catch(...)
{
qDebug() << "Uncaught exception processing dbcmd";
if( cmd->doesMutates() )
if ( cmd->doesMutates() )
m_dbimpl->database().rollback();
Q_ASSERT( false );
throw;
}
cmd->emitFinished();
foreach ( QSharedPointer<DatabaseCommand> c, cmdGroup )
c->emitFinished();
QMutexLocker lock( &m_mut );
if ( --m_outstanding > 0 )
m_outstanding -= completed;
if ( m_outstanding > 0 )
QTimer::singleShot( 0, this, SLOT( doWork() ) );
}

View File

@@ -47,6 +47,7 @@ public:
public slots:
void enqueue( const QSharedPointer<DatabaseCommand>& );
void enqueue( const QList< QSharedPointer<DatabaseCommand> >& );
protected:
void run();

View File

@@ -266,6 +266,7 @@ CREATE TABLE IF NOT EXISTS playback_log (
playtime INTEGER NOT NULL, -- when playback finished (timestamp)
secs_played INTEGER NOT NULL
);
CREATE INDEX playback_log_source ON playback_log(source);
CREATE INDEX playback_log_track ON playback_log(track);

View File

@@ -1,5 +1,5 @@
/*
This file was automatically generated from schema.sql on Thu Sep 29 17:28:17 EDT 2011.
This file was automatically generated from schema.sql on Wed Nov 16 22:47:16 EST 2011.
*/
static const char * tomahawk_schema_sql =

View File

@@ -34,10 +34,12 @@
#include "utils/xspfloader.h"
#include "jobview/JobStatusView.h"
#include "jobview/JobStatusModel.h"
using namespace Tomahawk;
bool DropJob::s_canParseSpotifyPlaylists = false;
DropJob::DropJob( QObject *parent )
: QObject( parent )
, m_queryCount( 0 )
@@ -50,11 +52,13 @@ DropJob::DropJob( QObject *parent )
{
}
DropJob::~DropJob()
{
qDebug() << "destryong DropJob";
}
/// QMIMEDATA HANDLING
QStringList
@@ -68,15 +72,18 @@ DropJob::mimeTypes()
<< "application/tomahawk.metadata.artist"
<< "application/tomahawk.metadata.album"
<< "application/tomahawk.mixed"
<< "text/plain";
<< "text/plain"
<< "text/uri-list";
return mimeTypes;
}
bool
DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType, DropJob::DropAction acceptedAction )
{
Q_UNUSED( acceptedAction );
if ( data->hasFormat( "application/tomahawk.query.list" )
|| data->hasFormat( "application/tomahawk.plentry.list" )
|| data->hasFormat( "application/tomahawk.result.list" )
@@ -90,7 +97,8 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
// check plain text url types
if ( !data->hasFormat( "text/plain" ) )
return false;
if ( !data->hasFormat( "text/uri-list" ) )
return false;
const QString url = data->data( "text/plain" );
@@ -99,6 +107,9 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
if( url.contains( "xspf" ) )
return true;
if( data->data( "text/uri-list" ).contains( "xspf" ) )
return true;
// Not the most elegant
if ( url.contains( "spotify" ) && url.contains( "playlist" ) && s_canParseSpotifyPlaylists )
return true;
@@ -137,21 +148,22 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
return true;
}
// We whitelist t.co and bit.ly (and j.mp) since they do some link checking. Often playable (e.g. spotify..) links hide behind them,
// so we do an extra level of lookup
if ( url.contains( "bit.ly" ) || url.contains( "j.mp" ) || url.contains( "t.co" ) || url.contains( "rd.io" ) )
// We whitelist certain url-shorteners since they do some link checking. Often playable (e.g. spotify) links hide behind them,
// so we do an extra level of lookup
if ( ShortenedLinkParser::handlesUrl( url ) )
return true;
return false;
}
bool
DropJob::isDropType( DropJob::DropType desired, const QMimeData* data )
{
const QString url = data->data( "text/plain" );
if ( desired == Playlist )
{
if( url.contains( "xspf" ) )
if( url.contains( "xspf" ) || data->data( "text/uri-list").contains( "xspf" ) )
return true;
// Not the most elegant
@@ -161,11 +173,7 @@ DropJob::isDropType( DropJob::DropType desired, const QMimeData* data )
if ( url.contains( "rdio.com" ) && url.contains( "people" ) && url.contains( "playlist" ) )
return true;
// we don't know about these.. gotta say yes for now
if ( url.contains( "bit.ly" ) ||
url.contains( "j.mp" ) ||
url.contains( "t.co" ) ||
url.contains( "rd.io" ) )
if ( ShortenedLinkParser::handlesUrl( url ) )
return true;
}
@@ -179,6 +187,7 @@ DropJob::setGetWholeArtists( bool getWholeArtists )
m_getWholeArtists = getWholeArtists;
}
void
DropJob::setGetWholeAlbums( bool getWholeAlbums )
{
@@ -208,6 +217,7 @@ DropJob::tracksFromMimeData( const QMimeData* data, bool allowDuplicates, bool o
}
}
void
DropJob::parseMimeData( const QMimeData *data )
{
@@ -222,15 +232,21 @@ DropJob::parseMimeData( const QMimeData *data )
results = tracksFromArtistMetaData( data );
else if ( data->hasFormat( "application/tomahawk.mixed" ) )
tracksFromMixedData( data );
else if ( data->hasFormat( "text/plain" ) )
else if ( data->hasFormat( "text/plain" ) && !data->data( "text/plain" ).isEmpty() )
{
const QString plainData = QString::fromUtf8( data->data( "text/plain" ) );
handleAllUrls( plainData );
}else if ( data->hasFormat( "text/uri-list" ) )
{
const QString plainData = QString::fromUtf8( data->data( "text/uri-list" ).trimmed() );
handleAllUrls( plainData );
}
m_resultList.append( results );
}
QList< query_ptr >
DropJob::tracksFromQueryList( const QMimeData* data )
{
@@ -270,6 +286,7 @@ DropJob::tracksFromQueryList( const QMimeData* data )
return queries;
}
QList< query_ptr >
DropJob::tracksFromResultList( const QMimeData* data )
{
@@ -311,6 +328,7 @@ DropJob::tracksFromResultList( const QMimeData* data )
return queries;
}
QList< query_ptr >
DropJob::tracksFromAlbumMetaData( const QMimeData *data )
{
@@ -335,6 +353,7 @@ DropJob::tracksFromAlbumMetaData( const QMimeData *data )
return queries;
}
QList< query_ptr >
DropJob::tracksFromArtistMetaData( const QMimeData *data )
{
@@ -359,6 +378,7 @@ DropJob::tracksFromArtistMetaData( const QMimeData *data )
return queries;
}
QList< query_ptr >
DropJob::tracksFromMixedData( const QMimeData *data )
{
@@ -407,32 +427,50 @@ DropJob::tracksFromMixedData( const QMimeData *data )
return queries;
}
void
DropJob::handleXspfs( const QString& fileUrls )
{
tDebug() << Q_FUNC_INFO << "Got xspf playlist!!" << fileUrls;
QStringList urls = fileUrls.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
bool error = false;
QStringList urls = fileUrls.split( QRegExp( "\n" ), QString::SkipEmptyParts );
if ( dropAction() == Default )
setDropAction( Create );
foreach ( const QString& url, urls )
{
XSPFLoader* l = 0;
QFile xspfFile( QUrl::fromUserInput( url ).toLocalFile() );
if ( xspfFile.exists() )
{
XSPFLoader* l = new XSPFLoader( true, this );
l = new XSPFLoader( dropAction() == Create, this );
tDebug( LOGINFO ) << "Loading local xspf " << xspfFile.fileName();
l->load( xspfFile );
}
else if ( QUrl( url ).isValid() )
{
l = new XSPFLoader( dropAction() == Create, this );
tDebug( LOGINFO ) << "Loading remote xspf " << url;
l->load( QUrl( url ) );
}
else
tLog( LOGINFO ) << "Error Loading local xspf " << xspfFile.fileName();
}
{
error = true;
tLog() << "Failed to load or parse dropped XSPF";
}
if ( dropAction() == Append && !error && l )
{
qDebug() << Q_FUNC_INFO << "Trying to append xspf";
connect( l, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) );
m_queryCount++;
}
}
}
void
DropJob::handleSpotifyUrls( const QString& urlsRaw )
{
@@ -458,6 +496,7 @@ DropJob::handleSpotifyUrls( const QString& urlsRaw )
m_queryCount++;
}
void
DropJob::handleRdioUrls( const QString& urlsRaw )
{
@@ -470,10 +509,10 @@ DropJob::handleRdioUrls( const QString& urlsRaw )
RdioParser* rdio = new RdioParser( this );
connect( rdio, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) );
m_queryCount++;
rdio->setCreatePlaylist( dropAction() == Create );
rdio->parse( urls );
m_queryCount++;
}
@@ -524,10 +563,8 @@ DropJob::handleTrackUrls( const QString& urls )
m_queryCount++;
rdio->parse( tracks );
} else if ( urls.contains( "bit.ly" ) ||
urls.contains( "j.mp" ) ||
urls.contains( "t.co" ) ||
urls.contains( "rd.io" ) )
}
else if ( ShortenedLinkParser::handlesUrl( urls ) )
{
QStringList tracks = urls.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
@@ -538,6 +575,7 @@ DropJob::handleTrackUrls( const QString& urls )
}
}
void
DropJob::expandedUrls( QStringList urls )
{
@@ -545,6 +583,7 @@ DropJob::expandedUrls( QStringList urls )
handleAllUrls( urls.join( "\n" ) );
}
void
DropJob::onTracksAdded( const QList<Tomahawk::query_ptr>& tracksList )
{
@@ -569,6 +608,7 @@ DropJob::onTracksAdded( const QList<Tomahawk::query_ptr>& tracksList )
}
}
void
DropJob::removeDuplicates()
{
@@ -587,6 +627,7 @@ DropJob::removeDuplicates()
m_resultList = list;
}
void
DropJob::removeRemoteSources()
{
@@ -605,6 +646,7 @@ DropJob::removeRemoteSources()
m_resultList = list;
}
void
DropJob::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
{
@@ -634,6 +676,7 @@ DropJob::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVar
}
}
QList< query_ptr >
DropJob::getArtist( const QString &artist )
{
@@ -649,6 +692,7 @@ DropJob::getArtist( const QString &artist )
return artistPtr->tracks();
}
QList< query_ptr >
DropJob::getAlbum(const QString &artist, const QString &album)
{
@@ -672,6 +716,7 @@ DropJob::getAlbum(const QString &artist, const QString &album)
return albumPtr->tracks();
}
void
DropJob::getTopTen( const QString &artist )
{

View File

@@ -108,6 +108,7 @@ public:
static bool canParseSpotifyPlaylists() { return s_canParseSpotifyPlaylists; }
static void setCanParseSpotifyPlaylists( bool parseable ) { s_canParseSpotifyPlaylists = parseable; }
signals:
/// QMimeData parsing results
void tracks( const QList< Tomahawk::query_ptr >& tracks );

View File

@@ -297,12 +297,12 @@ GlobalActionManager::handlePlaylistCommand( const QUrl& url )
tDebug() << "No xspf to load...";
return false;
}
QUrl xspf = QUrl( url.queryItemValue( "xspf" ) );
QUrl xspf = QUrl::fromUserInput( url.queryItemValue( "xspf" ) );
QString title = url.hasQueryItem( "title" ) ? url.queryItemValue( "title" ) : QString();
XSPFLoader* l= new XSPFLoader( true, this );
l->setOverrideTitle( title );
l->load( xspf );
connect( l, SIGNAL( ok( Tomahawk::playlist_ptr ) ), ViewManager::instance(), SLOT( show( Tomahawk::playlist_ptr ) ) );
connect( l, SIGNAL( ok( Tomahawk::playlist_ptr ) ), this, SLOT( playlistCreatedToShow( Tomahawk::playlist_ptr) ) );
} else if( parts [ 0 ] == "new" ) {
if( !url.hasQueryItem( "title" ) ) {
@@ -323,6 +323,22 @@ GlobalActionManager::handlePlaylistCommand( const QUrl& url )
return false;
}
void
GlobalActionManager::playlistCreatedToShow( const playlist_ptr& pl )
{
connect( pl.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistReadyToShow() ) );
pl->setProperty( "sharedptr", QVariant::fromValue<Tomahawk::playlist_ptr>( pl ) );
}
void GlobalActionManager::playlistReadyToShow()
{
playlist_ptr pl = sender()->property( "sharedptr" ).value<Tomahawk::playlist_ptr>();
if ( !pl.isNull() )
ViewManager::instance()->show( pl );
disconnect( sender(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistReadyToShow() ) );
}
bool
GlobalActionManager::handleCollectionCommand( const QUrl& url )
@@ -408,6 +424,7 @@ GlobalActionManager::doQueueAdd( const QStringList& parts, const QList< QPair< Q
QString title, artist, album, urlStr;
foreach( pair, queryItems ) {
pair.second = pair.second.replace( "+", " " ); // QUrl::queryItems doesn't decode + to a space :(
if( pair.first == "title" )
title = pair.second;
else if( pair.first == "artist" )
@@ -431,7 +448,7 @@ GlobalActionManager::doQueueAdd( const QStringList& parts, const QList< QPair< Q
foreach( pair, queryItems ) {
if( pair.first != "url" )
continue;
QUrl track = QUrl::fromUserInput( pair.second );
QUrl track = QUrl::fromUserInput( pair.second );
//FIXME: isLocalFile is Qt 4.8
if( track.toString().startsWith( "file://" ) ) { // it's local, so we see if it's in the DB and load it if so
// TODO

View File

@@ -81,6 +81,8 @@ private slots:
void playOrQueueNow( const Tomahawk::query_ptr& );
void playNow( const Tomahawk::query_ptr& );
void playlistCreatedToShow( const Tomahawk::playlist_ptr& pl );
void playlistReadyToShow();
private:
explicit GlobalActionManager( QObject* parent = 0 );
void doBookmark( const Tomahawk::playlist_ptr& pl, const Tomahawk::query_ptr& q );

View File

@@ -39,9 +39,6 @@
<height>64</height>
</size>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
@@ -76,9 +73,6 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
@@ -92,9 +86,6 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
@@ -143,9 +134,6 @@
<height>62</height>
</size>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>

View File

@@ -19,12 +19,10 @@
#include "chartsplugin.h"
#include <QDir>
#include <QSettings>
#include <QCryptographicHash>
#include <QNetworkConfiguration>
#include <QNetworkReply>
#include <QDomElement>
#include <QtCore/QDir>
#include <QtCore/QSettings>
#include <QtNetwork/QNetworkConfiguration>
#include <QtNetwork/QNetworkReply>
#include "album.h"
#include "chartsplugin_data_p.h"
@@ -34,7 +32,7 @@
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#define CHART_URL "http://charts.tomahawk-player.org:10080/"
#define CHART_URL "http://charts.tomahawk-player.org/"
//#define CHART_URL "http://localhost:8080/"
#include <qjson/parser.h>
#include <qjson/serializer.h>
@@ -50,27 +48,12 @@ ChartsPlugin::ChartsPlugin()
m_chartResources << "billboard" << "itunes" << "rdio" << "wearehunted" << "ex.fm";
m_supportedGetTypes << InfoChart << InfoChartCapabilities;
/// Then get each chart from resource
/// We want to prepopulate the breadcrumb to fetch them before they are asked for
if ( !m_chartResources.isEmpty() && m_allChartsMap.isEmpty() )
{
tDebug() << "ChartsPlugin: InfoChart fetching possible resources";
foreach ( QVariant resource, m_chartResources )
{
QUrl url = QUrl( QString( CHART_URL "source/%1" ).arg(resource.toString() ) );
QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
tDebug() << "fetching:" << url;
connect( reply, SIGNAL( finished() ), SLOT( chartTypes() ) );
m_chartsFetchJobs++;
}
}
}
ChartsPlugin::~ChartsPlugin()
{
qDebug() << Q_FUNC_INFO;
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
}
@@ -85,27 +68,27 @@ ChartsPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData )
void
ChartsPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
{
qDebug() << Q_FUNC_INFO << requestData.caller;
qDebug() << Q_FUNC_INFO << requestData.customData;
//qDebug() << Q_FUNC_INFO << requestData.caller;
//qDebug() << Q_FUNC_INFO << requestData.customData;
InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
bool foundSource = false;
switch ( requestData.type )
{
case InfoChart:
/// We need something to check if the request is actually ment to go to this plugin
if ( !hash.contains( "chart_source" ) )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Hash did not contain required param!";
dataError( requestData );
break;
}
else
{
foreach( QVariant resource, m_chartResources )
foreach( QString resource, m_chartResources )
{
if( resource.toString() == hash["chart_source"] )
if( resource == hash["chart_source"] )
{
foundSource = true;
}
@@ -130,15 +113,6 @@ ChartsPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
}
void
ChartsPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input )
{
Q_UNUSED( caller )
Q_UNUSED( type)
Q_UNUSED( input )
}
void
ChartsPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
{
@@ -155,6 +129,7 @@ ChartsPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
/// Each request needs to contain both a id and source
if ( !hash.contains( "chart_id" ) && !hash.contains( "chart_source" ) )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Hash did not contain required params!";
dataError( requestData );
return;
@@ -163,7 +138,7 @@ ChartsPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
criteria["chart_id"] = hash["chart_id"];
criteria["chart_source"] = hash["chart_source"];
emit getCachedInfo( criteria, 0, requestData );
emit getCachedInfo( criteria, 86400000, requestData );
}
void
@@ -171,13 +146,14 @@ ChartsPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requ
{
if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Could not convert requestData to InfoStringHash!";
dataError( requestData );
return;
}
Tomahawk::InfoSystem::InfoStringHash criteria;
emit getCachedInfo( criteria, 0, requestData );
criteria[ "InfoChartCapabilities" ] = "chartsplugin";
emit getCachedInfo( criteria, 604800000, requestData );
}
void
@@ -187,9 +163,11 @@ ChartsPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSy
{
case InfoChart:
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChart not in cache! Fetching...";
/// Fetch the chart, we need source and id
QUrl url = QUrl( QString( CHART_URL "source/%1/chart/%2" ).arg( criteria["chart_source"] ).arg( criteria["chart_id"] ) );
qDebug() << Q_FUNC_INFO << "Getting chart url" << url;
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Getting chart url" << url;
QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
@@ -201,9 +179,33 @@ ChartsPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSy
case InfoChartCapabilities:
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChartCapabilities not in cache! Fetching...";
// we never need to re-fetch
if ( !m_allChartsMap.isEmpty() )
return;
/// Then get each chart from resource
if ( !m_chartResources.isEmpty() && m_allChartsMap.isEmpty() )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChart fetching possible resources";
foreach ( QString resource, m_chartResources )
{
QUrl url = QUrl( QString( CHART_URL "source/%1" ).arg( resource ) );
QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
reply->setProperty( "chart_resource", resource);
tDebug() << "fetching:" << url;
connect( reply, SIGNAL( finished() ), SLOT( chartTypes() ) );
m_chartsFetchJobs++;
}
}
if ( m_chartsFetchJobs > 0 )
{
qDebug() << Q_FUNC_INFO << "InfoChartCapabilities still fetching!";
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChartCapabilities still fetching!";
m_cachedRequests.append( requestData );
return;
}
@@ -226,9 +228,10 @@ void
ChartsPlugin::chartTypes()
{
/// Get possible chart type for specificChartsPlugin: InfoChart types returned chart source
tDebug() << "Got chart type result";
tDebug( LOGVERBOSE ) << "Got chart type result";
QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
if ( reply->error() == QNetworkReply::NoError )
{
QJson::Parser p;
@@ -262,7 +265,8 @@ ChartsPlugin::chartTypes()
QHash< QString, QVariantMap > extraType;
foreach( const QVariant& chartObj, chartObjs.values() )
{
if( !chartObj.toMap().isEmpty() ){
if( !chartObj.toMap().isEmpty() )
{
const QVariantMap chart = chartObj.toMap();
const QString id = chart.value( "id" ).toString();
const QString geo = chart.value( "geo" ).toString();
@@ -271,7 +275,8 @@ ChartsPlugin::chartTypes()
const bool isDefault = ( chart.contains( "default" ) && chart[ "default" ].toInt() == 1 );
QString extra;
if( !geo.isEmpty() ){
if( !geo.isEmpty() )
{
if ( !m_cachedCountries.contains( geo ) )
{
@@ -289,10 +294,10 @@ ChartsPlugin::chartTypes()
m_cachedCountries[ geo ] = extra;
}
else
{
extra = m_cachedCountries[ geo ];
}
}else extra = chart.value( "extra" ).toString();
}
else
extra = chart.value( "extra" ).toString();
if ( name.isEmpty() ) // not a specific chart, an all chart
name = tr( "Top Overall" );
@@ -304,10 +309,10 @@ ChartsPlugin::chartTypes()
if ( isDefault )
c[ "default" ] = "true";
QList<InfoStringHash> extraTypeData = extraType[ extra ][ type ].value< QList< InfoStringHash > >();
QList< Tomahawk::InfoSystem::InfoStringHash > extraTypeData = extraType[ extra ][ type ].value< QList< Tomahawk::InfoSystem::InfoStringHash > >();
extraTypeData.append( c );
extraType[ extra ][ type ] = QVariant::fromValue< QList< Tomahawk::InfoSystem::InfoStringHash > >( extraTypeData );
extraType[ extra ].insert( type, QVariant::fromValue< QList< InfoStringHash > >( extraTypeData ) );
if ( isDefault )
{
defaultChain.clear();
@@ -319,7 +324,7 @@ ChartsPlugin::chartTypes()
foreach( const QString& c, extraType.keys() )
{
charts[ c ] = extraType[ c ];
// qDebug() << "extraType has types:" << c;
// tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "extraType has types:" << c;
}
if( source == "itunes" ){
chartName = "iTunes";
@@ -330,7 +335,8 @@ ChartsPlugin::chartTypes()
}
}
}else
}
else
{
// We'll just build:
// [Source] - Album - Chart Type
@@ -380,11 +386,11 @@ ChartsPlugin::chartTypes()
}
}
if( !artistCharts.isEmpty() )
charts.insert( tr( "Artists" ), QVariant::fromValue< QList< InfoStringHash > >( artistCharts ) );
charts.insert( tr( "Artists" ), QVariant::fromValue< QList< Tomahawk::InfoSystem::InfoStringHash > >( artistCharts ) );
if( !albumCharts.isEmpty() )
charts.insert( tr( "Albums" ), QVariant::fromValue< QList< InfoStringHash > >( albumCharts ) );
charts.insert( tr( "Albums" ), QVariant::fromValue< QList< Tomahawk::InfoSystem::InfoStringHash > >( albumCharts ) );
if( !trackCharts.isEmpty() )
charts.insert( tr( "Tracks" ), QVariant::fromValue< QList< InfoStringHash > >( trackCharts ) );
charts.insert( tr( "Tracks" ), QVariant::fromValue< QList< Tomahawk::InfoSystem::InfoStringHash > >( trackCharts ) );
/// @note For displaying purposes, upper the first letter
/// @note Remeber to lower it when fetching this!
@@ -394,7 +400,7 @@ ChartsPlugin::chartTypes()
}
/// Add the possible charts and its types to breadcrumb
// qDebug() << "ADDING CHART TYPE TO CHARTS:" << chartName;
// tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "ADDING CHART TYPE TO CHARTS:" << chartName;
QVariantMap defaultMap = m_allChartsMap.value( "defaults" ).value< QVariantMap >();
defaultMap[ source ] = defaultChain;
m_allChartsMap[ "defaults" ] = defaultMap;
@@ -413,6 +419,10 @@ ChartsPlugin::chartTypes()
foreach ( InfoRequestData request, m_cachedRequests )
{
emit info( request, m_allChartsMap );
// update cache
Tomahawk::InfoSystem::InfoStringHash criteria;
criteria[ "InfoChartCapabilities" ] = "chartsplugin";
emit updateCache( criteria, 604800000, request.type, m_allChartsMap );
}
m_cachedRequests.clear();
}
@@ -441,8 +451,8 @@ ChartsPlugin::chartReturned()
/// SO we have a result, parse it!
QVariantList chartResponse = res.value( "list" ).toList();
QList< InfoStringHash > top_tracks;
QList< InfoStringHash > top_albums;
QList< Tomahawk::InfoSystem::InfoStringHash > top_tracks;
QList< Tomahawk::InfoSystem::InfoStringHash > top_albums;
QStringList top_artists;
/// Deside what type, we need to handle it differently
@@ -478,16 +488,16 @@ ChartsPlugin::chartReturned()
if ( album.isEmpty() && artist.isEmpty() ) // don't have enough...
{
tLog() << "Didn't get an artist and album name from chart, not enough to build a query on. Aborting" << title << album << artist;
tDebug( LOGVERBOSE ) << "Didn't get an artist and album name from chart, not enough to build a query on. Aborting" << title << album << artist;
}
else
{
qDebug() << Q_FUNC_INFO << album << artist;
InfoStringHash pair;
Tomahawk::InfoSystem::InfoStringHash pair;
pair["artist"] = artist;
pair["album"] = album;
top_albums << pair;
top_albums.append( pair );
}
}
@@ -497,28 +507,28 @@ ChartsPlugin::chartReturned()
if ( title.isEmpty() && artist.isEmpty() ) // don't have enough...
{
tLog() << "Didn't get an artist and track name from charts, not enough to build a query on. Aborting" << title << artist << album;
tDebug( LOGVERBOSE ) << "Didn't get an artist and track name from charts, not enough to build a query on. Aborting" << title << artist << album;
}
else
{
InfoStringHash pair;
Tomahawk::InfoSystem::InfoStringHash pair;
pair["artist"] = artist;
pair["track"] = title;
top_tracks << pair;
top_tracks.append( pair );
}
}else if( chartType() == Artist )
{
if ( artist.isEmpty() ) // don't have enough...
{
tLog() << "Didn't get an artist from charts, not enough to build a query on. Aborting" << artist;
tDebug( LOGVERBOSE ) << "Didn't get an artist from charts, not enough to build a query on. Aborting" << artist;
}
else
{
top_artists << artist;
top_artists.append( artist );
}
}
@@ -528,31 +538,35 @@ ChartsPlugin::chartReturned()
if( chartType() == Artist )
{
tDebug() << "ChartsPlugin:" << "\tgot " << top_artists.size() << " artists";
returnedData["artists"] = QVariant::fromValue( top_artists );
returnedData["type"] = "artists";
returnedData[ "artists" ] = QVariant::fromValue< QStringList >( top_artists );
returnedData[ "type" ] = "artists";
}
if( chartType() == Track )
{
tDebug() << "ChartsPlugin:" << "\tgot " << top_tracks.size() << " tracks";
returnedData["tracks"] = QVariant::fromValue( top_tracks );
returnedData["type"] = "tracks";
returnedData[ "tracks" ] = QVariant::fromValue< QList< Tomahawk::InfoSystem::InfoStringHash > >( top_tracks );
returnedData[ "type" ] = "tracks";
}
if( chartType() == Album )
{
tDebug() << "ChartsPlugin:" << "\tgot " << top_albums.size() << " albums";
returnedData["albums"] = QVariant::fromValue( top_albums );
returnedData["type"] = "albums";
returnedData[ "albums" ] = QVariant::fromValue< QList< Tomahawk::InfoSystem::InfoStringHash > >( top_albums );
returnedData[ "type" ] = "albums";
}
Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
emit info( requestData, returnedData );
// TODO update cache
// update cache
Tomahawk::InfoSystem::InfoStringHash criteria;
Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
criteria[ "chart_id" ] = origData[ "chart_id" ];
criteria[ "chart_source" ] = origData[ "chart_source" ];
emit updateCache( criteria, 86400000, requestData.type, returnedData );
}
else
qDebug() << "Network error in fetching chart:" << reply->url().toString();
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Network error in fetching chart:" << reply->url().toString();
}

View File

@@ -21,8 +21,8 @@
#include "infosystem/infosystem.h"
#include "infosystem/infosystemworker.h"
#include <QNetworkReply>
#include <QObject>
#include <QtNetwork/QNetworkReply>
#include <QtCore/QObject>
class QNetworkReply;
@@ -47,8 +47,9 @@ public:
Artist = 0x04
};
void setChartType( ChartType type ) { m_chartType = type; }
ChartType chartType() const { return m_chartType; }
void setChartType( ChartType type ) { m_chartType = type; }
ChartType chartType() const { return m_chartType; }
public slots:
void chartReturned();
@@ -58,15 +59,20 @@ protected slots:
virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData );
virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data );
virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data )
{
Q_UNUSED( caller )
Q_UNUSED( type )
Q_UNUSED( data )
}
private:
void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData );
void fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData );
void dataError( Tomahawk::InfoSystem::InfoRequestData requestData );
QVariantList m_chartResources;
QList<InfoStringHash> m_charts;
QStringList m_chartResources;
QList< InfoStringHash > m_charts;
ChartType m_chartType;
QVariantMap m_allChartsMap;

View File

@@ -23,11 +23,9 @@
#include <QCryptographicHash>
#include <QNetworkConfiguration>
#include <QNetworkReply>
#include <QDomElement>
#include "album.h"
#include "typedefs.h"
#include "audio/audioengine.h"
#include "tomahawksettings.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
@@ -140,16 +138,6 @@ hypemPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
}
}
void
hypemPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input )
{
Q_UNUSED( caller )
Q_UNUSED( type)
Q_UNUSED( input )
}
void
hypemPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
{
@@ -173,8 +161,9 @@ hypemPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
/// Set the criterias for current chart
criteria["chart_id"] = hash["chart_id"];
criteria["chart_source"] = hash["chart_source"];
emit getCachedInfo( criteria, 0, requestData );
/// @todo
/// set cache time based on wether requested type is 3day, lastweek or recent.
emit getCachedInfo( criteria, 86400000, requestData );
}
void
@@ -198,7 +187,7 @@ hypemPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSys
case InfoChart:
{
/// Fetch the chart, we need source and id
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChart not in cache! Fetching...";
QUrl url = QUrl( QString( HYPEM_URL "%1/%2" ).arg( criteria["chart_id"].toLower() ).arg(HYPEM_END_URL) );
qDebug() << Q_FUNC_INFO << "Getting chart url" << url;
@@ -212,6 +201,7 @@ hypemPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSys
case InfoChartCapabilities:
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChartCapabilities not in cache! Fetching...";
if ( m_chartsFetchJobs > 0 )
{
qDebug() << Q_FUNC_INFO << "InfoChartCapabilities still fetching!";
@@ -390,7 +380,14 @@ hypemPlugin::chartReturned()
emit info( requestData, returnedData );
// TODO update cache
// update cache
Tomahawk::InfoSystem::InfoStringHash criteria;
Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
criteria[ "chart_id" ] = origData[ "chart_id" ];
criteria[ "chart_source" ] = origData[ "chart_source" ];
/// @todo
/// set cache time based on wether requested type is 3day, lastweek or recent.
emit updateCache( criteria, 86400000, requestData.type, returnedData );
}
else
qDebug() << "Network error in fetching chart:" << reply->url().toString();

View File

@@ -57,8 +57,13 @@ public slots:
protected slots:
virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData );
virtual void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input )
{
Q_UNUSED( caller )
Q_UNUSED( type)
Q_UNUSED( input )
}
virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data );
private:
void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData );

View File

@@ -23,7 +23,6 @@
#include <QCryptographicHash>
#include <QNetworkConfiguration>
#include <QNetworkReply>
#include <QDomElement>
#include "album.h"
#include "typedefs.h"
@@ -33,7 +32,7 @@
#include "utils/logger.h"
#include "chartsplugin_data_p.h"
#define SPOTIFY_API_URL "http://spotikea.tomahawk-player.org:10380/"
#define SPOTIFY_API_URL "http://spotikea.tomahawk-player.org/"
#include <qjson/parser.h>
#include <qjson/serializer.h>
@@ -47,19 +46,6 @@ SpotifyPlugin::SpotifyPlugin()
m_supportedGetTypes << InfoChart << InfoChartCapabilities;
// we never need to re-fetch
if ( !m_allChartsMap.isEmpty() )
return;
/// We need to fetch possible types before they are asked for
tDebug() << "SpotifyPlugin: InfoChart fetching possible resources";
QUrl url = QUrl( QString( SPOTIFY_API_URL "toplist/charts" ) );
QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
tDebug() << Q_FUNC_INFO << "fetching:" << url;
connect( reply, SIGNAL( finished() ), SLOT( chartTypes() ) );
m_chartsFetchJobs++;
}
@@ -107,14 +93,6 @@ SpotifyPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
}
void
SpotifyPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input )
{
Q_UNUSED( caller )
Q_UNUSED( type)
Q_UNUSED( input )
}
void
SpotifyPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
{
@@ -123,17 +101,22 @@ SpotifyPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
dataError( requestData );
return;
}
InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
Tomahawk::InfoSystem::InfoStringHash criteria;
if ( !hash.contains( "chart_id" ) )
/// Each request needs to contain both a id and source
if ( !hash.contains( "chart_id" ) && !hash.contains( "chart_source" ) )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Hash did not contain required params!";
dataError( requestData );
return;
} else {
criteria["chart_id"] = hash["chart_id"];
}
emit getCachedInfo( criteria, 604800000 /* Expire chart cache in 1 week */, requestData );
}
/// Set the criterias for current chart
criteria["chart_id"] = hash["chart_id"];
criteria["chart_source"] = hash["chart_source"];
emit getCachedInfo( criteria, 86400000 /* Expire chart cache in 1 day */, requestData );
}
void
SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData )
@@ -145,7 +128,8 @@ SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData req
}
Tomahawk::InfoSystem::InfoStringHash criteria;
emit getCachedInfo( criteria, 0, requestData );
criteria[ "InfoChartCapabilities" ] = "spotifyplugin";
emit getCachedInfo( criteria, 604800000, requestData );
}
void
@@ -157,6 +141,7 @@ SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, To
case InfoChart:
{
/// Fetch the chart, we need source and id
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChart not in cache! Fetching...";
QUrl url = QUrl( QString( SPOTIFY_API_URL "toplist/%1/" ).arg( criteria["chart_id"] ) );
qDebug() << Q_FUNC_INFO << "Getting chart url" << url;
@@ -169,6 +154,21 @@ SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, To
}
case InfoChartCapabilities:
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChartCapabilities not in cache! Fetching...";
// we never need to re-fetch
if ( !m_allChartsMap.isEmpty() )
return;
/// We need to fetch possible types before they are asked for
tDebug() << "SpotifyPlugin: InfoChart fetching possible resources";
QUrl url = QUrl( QString( SPOTIFY_API_URL "toplist/charts" ) );
QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
tDebug() << Q_FUNC_INFO << "fetching:" << url;
connect( reply, SIGNAL( finished() ), SLOT( chartTypes() ) );
m_chartsFetchJobs++;
if ( m_chartsFetchJobs > 0 )
{
qDebug() << Q_FUNC_INFO << "InfoChartCapabilities still fetching!";
@@ -275,6 +275,9 @@ SpotifyPlugin::chartTypes()
foreach ( InfoRequestData request, m_cachedRequests )
{
emit info( request, m_allChartsMap );
Tomahawk::InfoSystem::InfoStringHash criteria;
criteria[ "InfoChartCapabilities" ] = "spotifyplugin";
emit updateCache( criteria,604800000, request.type, m_allChartsMap );
}
m_cachedRequests.clear();
}
@@ -379,9 +382,14 @@ SpotifyPlugin::chartReturned()
Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
emit info( requestData, returnedData );
// update cache
Tomahawk::InfoSystem::InfoStringHash criteria;
Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
criteria[ "chart_id" ] = origData[ "chart_id" ];
criteria[ "chart_source" ] = origData[ "chart_source" ];
emit updateCache( criteria, 86400000, requestData.type, returnedData );
}
else
qDebug() << "Network error in fetching chart:" << reply->url().toString();

View File

@@ -57,7 +57,12 @@ public slots:
protected slots:
virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData );
virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data );
virtual void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input )
{
Q_UNUSED( caller )
Q_UNUSED( type)
Q_UNUSED( input )
}
private:
void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData );

View File

@@ -51,6 +51,7 @@ InfoSystem::instance()
InfoSystem::InfoSystem( QObject *parent )
: QObject( parent )
, m_inited( false )
, m_infoSystemCacheThreadController( 0 )
, m_infoSystemWorkerThreadController( 0 )
{
@@ -59,71 +60,94 @@ InfoSystem::InfoSystem( QObject *parent )
qDebug() << Q_FUNC_INFO;
m_infoSystemCacheThreadController = new InfoSystemCacheThread( this );
m_cache = QWeakPointer< InfoSystemCache >( new InfoSystemCache() );
m_cache.data()->moveToThread( m_infoSystemCacheThreadController );
m_infoSystemCacheThreadController->setCache( m_cache );
m_infoSystemCacheThreadController->start( QThread::IdlePriority );
m_infoSystemWorkerThreadController = new InfoSystemWorkerThread( this );
m_worker = QWeakPointer< InfoSystemWorker >( new InfoSystemWorker() );
m_worker.data()->moveToThread( m_infoSystemWorkerThreadController );
m_infoSystemWorkerThreadController->setWorker( m_worker );
m_infoSystemWorkerThreadController->start();
QMetaObject::invokeMethod( m_worker.data(), "init", Qt::QueuedConnection, Q_ARG( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache >, m_cache ) );
connect( m_cache.data(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
m_worker.data(), SLOT( infoSlot( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection );
connect( m_worker.data(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
this, SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection );
connect( m_worker.data(), SIGNAL( finished( QString ) ), this, SIGNAL( finished( QString ) ), Qt::UniqueConnection );
connect( m_worker.data(), SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ),
this, SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ), Qt::UniqueConnection );
QTimer::singleShot( 0, this, SLOT( init() ) );
}
InfoSystem::~InfoSystem()
{
qDebug() << Q_FUNC_INFO << " beginning";
tDebug() << Q_FUNC_INFO << " beginning";
if ( !m_worker.isNull() )
if ( m_infoSystemWorkerThreadController->worker() )
{
m_infoSystemWorkerThreadController->quit();
m_infoSystemWorkerThreadController->wait( 60000 );
//delete m_worker.data();
delete m_infoSystemWorkerThreadController;
m_infoSystemWorkerThreadController = 0;
}
qDebug() << Q_FUNC_INFO << " done deleting worker";
tDebug() << Q_FUNC_INFO << " done deleting worker";
if( m_infoSystemCacheThreadController )
if( m_infoSystemCacheThreadController->cache() )
{
m_infoSystemCacheThreadController->quit();
m_infoSystemCacheThreadController->wait( 60000 );
//delete m_cache.data();
delete m_infoSystemCacheThreadController;
m_infoSystemCacheThreadController = 0;
}
qDebug() << Q_FUNC_INFO << " done deleting cache";
tDebug() << Q_FUNC_INFO << " done deleting cache";
}
void
InfoSystem::init()
{
tDebug() << Q_FUNC_INFO;
if ( !m_infoSystemCacheThreadController->cache() || !m_infoSystemWorkerThreadController->worker() )
{
QTimer::singleShot( 0, this, SLOT( init() ) );
return;
}
Tomahawk::InfoSystem::InfoSystemCache* cache = m_infoSystemCacheThreadController->cache();
Tomahawk::InfoSystem::InfoSystemWorker* worker = m_infoSystemWorkerThreadController->worker();
connect( cache, SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
worker, SLOT( infoSlot( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection );
connect( worker, SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
this, SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection );
connect( worker, SIGNAL( finished( QString ) ), this, SIGNAL( finished( QString ) ), Qt::UniqueConnection );
connect( worker, SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ),
this, SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ), Qt::UniqueConnection );
QMetaObject::invokeMethod( worker, "init", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoSystemCache*, cache ) );
m_inited = true;
}
bool
InfoSystem::getInfo( const InfoRequestData &requestData )
{
qDebug() << Q_FUNC_INFO;
QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) );
if ( !m_inited || !m_infoSystemWorkerThreadController->worker() )
{
init();
return false;
}
QMetaObject::invokeMethod( m_infoSystemWorkerThreadController->worker(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) );
return true;
}
void
bool
InfoSystem::getInfo( const QString &caller, const QVariantMap &customData, const InfoTypeMap &inputMap, const InfoTimeoutMap &timeoutMap, bool allSources )
{
if ( !m_inited || !m_infoSystemWorkerThreadController->worker() )
{
init();
return false;
}
InfoRequestData requestData;
requestData.caller = caller;
requestData.customData = customData;
@@ -133,82 +157,102 @@ InfoSystem::getInfo( const QString &caller, const QVariantMap &customData, const
requestData.type = type;
requestData.input = inputMap[ type ];
requestData.timeoutMillis = timeoutMap.contains( type ) ? timeoutMap[ type ] : 10000;
QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) );
QMetaObject::invokeMethod( m_infoSystemWorkerThreadController->worker(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) );
}
return false;
}
void
bool
InfoSystem::pushInfo( const QString &caller, const InfoType type, const QVariant& input )
{
qDebug() << Q_FUNC_INFO;
QMetaObject::invokeMethod( m_worker.data(), "pushInfo", Qt::QueuedConnection, Q_ARG( QString, caller ), Q_ARG( Tomahawk::InfoSystem::InfoType, type ), Q_ARG( QVariant, input ) );
tDebug() << Q_FUNC_INFO;
if ( !m_inited || !m_infoSystemWorkerThreadController->worker() )
{
init();
return false;
}
QMetaObject::invokeMethod( m_infoSystemWorkerThreadController->worker(), "pushInfo", Qt::QueuedConnection, Q_ARG( QString, caller ), Q_ARG( Tomahawk::InfoSystem::InfoType, type ), Q_ARG( QVariant, input ) );
return true;
}
void
bool
InfoSystem::pushInfo( const QString &caller, const InfoTypeMap &input )
{
if ( !m_inited || !m_infoSystemWorkerThreadController->worker() )
{
init();
return false;
}
Q_FOREACH( InfoType type, input.keys() )
QMetaObject::invokeMethod( m_worker.data(), "pushInfo", Qt::QueuedConnection, Q_ARG( QString, caller ), Q_ARG( Tomahawk::InfoSystem::InfoType, type ), Q_ARG( QVariant, input[ type ] ) );
QMetaObject::invokeMethod( m_infoSystemWorkerThreadController->worker(), "pushInfo", Qt::QueuedConnection, Q_ARG( QString, caller ), Q_ARG( Tomahawk::InfoSystem::InfoType, type ), Q_ARG( QVariant, input[ type ] ) );
return true;
}
InfoSystemCacheThread::InfoSystemCacheThread( QObject *parent )
: QThread( parent )
{
tDebug() << Q_FUNC_INFO;
}
InfoSystemCacheThread::~InfoSystemCacheThread()
{
delete m_cache.data();
tDebug() << Q_FUNC_INFO;
}
void
InfoSystemCacheThread::InfoSystemCacheThread::run()
{
m_cache = QWeakPointer< InfoSystemCache >( new InfoSystemCache() );
exec();
if ( !m_cache.isNull() )
delete m_cache.data();
}
QWeakPointer< InfoSystemCache >
InfoSystemCache*
InfoSystemCacheThread::cache() const
{
return m_cache;
if ( m_cache.isNull() )
return 0;
return m_cache.data();
}
void
InfoSystemCacheThread::setCache( QWeakPointer< InfoSystemCache > cache )
{
m_cache = cache;
}
InfoSystemWorkerThread::InfoSystemWorkerThread( QObject *parent )
: QThread( parent )
{
tDebug() << Q_FUNC_INFO;
}
InfoSystemWorkerThread::~InfoSystemWorkerThread()
{
tDebug() << Q_FUNC_INFO;
}
void
InfoSystemWorkerThread::InfoSystemWorkerThread::run()
{
m_worker = QWeakPointer< InfoSystemWorker >( new InfoSystemWorker() );
exec();
if( m_worker )
if( !m_worker.isNull() )
delete m_worker.data();
}
QWeakPointer< InfoSystemWorker >
InfoSystemWorker*
InfoSystemWorkerThread::worker() const
{
return m_worker;
}
void
InfoSystemWorkerThread::setWorker( QWeakPointer< InfoSystemWorker > worker )
{
m_worker = worker;
if ( m_worker.isNull() )
return 0;
return m_worker.data();
}

View File

@@ -205,8 +205,7 @@ public:
virtual ~InfoSystemCacheThread();
void run();
QWeakPointer< InfoSystemCache > cache() const;
void setCache( QWeakPointer< InfoSystemCache > cache );
InfoSystemCache* cache() const;
private:
QWeakPointer< InfoSystemCache > m_cache;
@@ -221,8 +220,7 @@ public:
virtual ~InfoSystemWorkerThread();
void run();
QWeakPointer< InfoSystemWorker > worker() const;
void setWorker( QWeakPointer< InfoSystemWorker > worker );
InfoSystemWorker* worker() const;
private:
QWeakPointer< InfoSystemWorker > m_worker;
@@ -238,20 +236,22 @@ public:
InfoSystem( QObject *parent );
~InfoSystem();
void getInfo( const InfoRequestData &requestData );
bool getInfo( const InfoRequestData &requestData );
//WARNING: if changing timeoutMillis above, also change in below function in .cpp file
void getInfo( const QString &caller, const QVariantMap &customData, const InfoTypeMap &inputMap, const InfoTimeoutMap &timeoutMap = InfoTimeoutMap(), bool allSources = false );
void pushInfo( const QString &caller, const InfoType type, const QVariant &input );
void pushInfo( const QString &caller, const InfoTypeMap &input );
bool getInfo( const QString &caller, const QVariantMap &customData, const InfoTypeMap &inputMap, const InfoTimeoutMap &timeoutMap = InfoTimeoutMap(), bool allSources = false );
bool pushInfo( const QString &caller, const InfoType type, const QVariant &input );
bool pushInfo( const QString &caller, const InfoTypeMap &input );
signals:
void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
void finished( QString target );
void finished( QString target, Tomahawk::InfoSystem::InfoType type );
private slots:
void init();
private:
QWeakPointer< InfoSystemCache > m_cache;
QWeakPointer< InfoSystemWorker > m_worker;
bool m_inited;
InfoSystemCacheThread* m_infoSystemCacheThreadController;
InfoSystemWorkerThread* m_infoSystemWorkerThreadController;
@@ -287,7 +287,7 @@ inline uint qHash( Tomahawk::InfoSystem::InfoStringHash hash )
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoRequestData );
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoStringHash );
Q_DECLARE_METATYPE( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache > );
Q_DECLARE_METATYPE( QList<Tomahawk::InfoSystem::InfoStringHash> );
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoSystemCache* );
Q_DECLARE_METATYPE( QList< Tomahawk::InfoSystem::InfoStringHash > );
#endif // TOMAHAWK_INFOSYSTEM_H

View File

@@ -39,6 +39,7 @@ InfoSystemCache::InfoSystemCache( QObject* parent )
, m_cacheBaseDir( QDesktopServices::storageLocation( QDesktopServices::CacheLocation ) + "/InfoSystemCache/" )
, m_cacheVersion( 2 )
{
tDebug() << Q_FUNC_INFO;
TomahawkSettings *s = TomahawkSettings::instance();
if ( s->infoSystemCacheVersion() != m_cacheVersion )
{
@@ -64,6 +65,7 @@ InfoSystemCache::InfoSystemCache( QObject* parent )
InfoSystemCache::~InfoSystemCache()
{
tDebug() << Q_FUNC_INFO;
}
void

View File

@@ -53,7 +53,7 @@ namespace InfoSystem
InfoSystemWorker::InfoSystemWorker()
: QObject()
{
// qDebug() << Q_FUNC_INFO;
tDebug() << Q_FUNC_INFO;
m_checkTimeoutsTimer.setInterval( 1000 );
m_checkTimeoutsTimer.setSingleShot( false );
@@ -64,20 +64,20 @@ InfoSystemWorker::InfoSystemWorker()
InfoSystemWorker::~InfoSystemWorker()
{
// qDebug() << Q_FUNC_INFO << " beginning";
tDebug() << Q_FUNC_INFO << " beginning";
Q_FOREACH( InfoPluginPtr plugin, m_plugins )
{
if( plugin )
delete plugin.data();
}
// qDebug() << Q_FUNC_INFO << " finished";
tDebug() << Q_FUNC_INFO << " finished";
}
void
InfoSystemWorker::init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache> cache )
InfoSystemWorker::init( Tomahawk::InfoSystem::InfoSystemCache* cache )
{
// qDebug() << Q_FUNC_INFO << "and cache is" << cache.data();
tDebug() << Q_FUNC_INFO;
InfoPluginPtr enptr( new EchoNestPlugin() );
m_plugins.append( enptr );
@@ -132,13 +132,13 @@ InfoSystemWorker::init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache> cac
connect(
plugin.data(),
SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ),
cache.data(),
cache,
SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) )
);
connect(
plugin.data(),
SIGNAL( updateCache( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ),
cache.data(),
cache,
SLOT( updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) )
);
}

View File

@@ -55,7 +55,7 @@ signals:
void finished( QString target, Tomahawk::InfoSystem::InfoType type );
public slots:
void init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache > cache );
void init( Tomahawk::InfoSystem::InfoSystemCache* cache );
void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input );

View File

@@ -81,6 +81,7 @@ JobStatusModel::data( const QModelIndex& index, int role ) const
{
case Qt::DecorationRole:
return item->icon();
case Qt::ToolTipRole:
case Qt::DisplayRole:
{
if ( m_collapseCount.contains( item->type() ) )
@@ -114,7 +115,7 @@ JobStatusModel::itemFinished()
JobStatusItem* item = qobject_cast< JobStatusItem* >( sender() );
Q_ASSERT( item );
tDebug() << "Got item finished:" << item->type() << item->mainText() << item;
// tDebug() << "Got item finished:" << item->type() << item->mainText() << item;
if ( !m_items.contains( item ) && !m_collapseCount.contains( item->type() ) )
return;
@@ -140,7 +141,7 @@ JobStatusModel::itemFinished()
// qDebug() << "Replaced" << m_collapseCount[ item->type() ].first() << "with:" << m_collapseCount[ item->type() ][ 1 ] << m_items;
}
m_collapseCount[ item->type() ].removeAll( item );
tDebug() << "New collapse count list:" << m_collapseCount[ item->type() ];
// tDebug() << "New collapse count list:" << m_collapseCount[ item->type() ];
if ( m_collapseCount[ item->type() ].isEmpty() )
m_collapseCount.remove( item->type() );
else

View File

@@ -18,8 +18,8 @@
#include "connection.h"
#include <QTime>
#include <QThread>
#include <QtCore/QTime>
#include <QtCore/QThread>
#include "network/servent.h"
#include "utils/logger.h"
@@ -63,7 +63,7 @@ Connection::Connection( Servent* parent )
Connection::~Connection()
{
qDebug() << "DTOR connection (super)" << id() << thread() << m_sock.isNull();
tDebug() << "DTOR connection (super)" << id() << thread() << m_sock.isNull();
if( !m_sock.isNull() )
{
// qDebug() << "deleteLatering sock" << m_sock;
@@ -82,7 +82,7 @@ Connection::handleIncomingQueueEmpty()
// << "m_peer_disconnected" << m_peer_disconnected
// << "bytes rx" << bytesReceived();
if( m_sock->bytesAvailable() == 0 && m_peer_disconnected )
if( !m_sock.isNull() && m_sock->bytesAvailable() == 0 && m_peer_disconnected )
{
qDebug() << "No more data to read, peer disconnected. shutting down connection."
<< "bytesavail" << m_sock->bytesAvailable()
@@ -144,13 +144,13 @@ void
Connection::actualShutdown()
{
qDebug() << Q_FUNC_INFO << m_actually_shutting_down << id();
if( m_actually_shutting_down )
if ( m_actually_shutting_down )
{
return;
}
m_actually_shutting_down = true;
if( !m_sock.isNull() && m_sock->isOpen() )
if ( !m_sock.isNull() && m_sock->isOpen() )
{
m_sock->disconnectFromHost();
}
@@ -261,7 +261,7 @@ Connection::doSetup()
void
Connection::socketDisconnected()
{
qDebug() << "SOCKET DISCONNECTED" << this->name() << id()
tDebug() << "SOCKET DISCONNECTED" << this->name() << id()
<< "shutdown will happen after incoming queue empties."
<< "bytesavail:" << m_sock->bytesAvailable()
<< "bytesRecvd" << bytesReceived();
@@ -276,14 +276,15 @@ Connection::socketDisconnected()
}
}
void
Connection::socketDisconnectedError(QAbstractSocket::SocketError e)
Connection::socketDisconnectedError( QAbstractSocket::SocketError e )
{
qDebug() << "SOCKET ERROR CODE" << e << this->name() << "CALLING Connection::shutdown(false)";
if ( e == QAbstractSocket::RemoteHostClosedError )
return;
qDebug() << "SOCKET ERROR CODE" << e << this->name() << "CALLING Connection::shutdown(false)";
m_peer_disconnected = true;
emit socketErrored(e);
@@ -423,16 +424,16 @@ Connection::sendMsg_now( msg_ptr msg )
{
//qDebug() << Q_FUNC_INFO << thread() << QThread::currentThread();
Q_ASSERT( QThread::currentThread() == thread() );
Q_ASSERT( this->isRunning() );
// Q_ASSERT( this->isRunning() );
if( m_sock.isNull() || !m_sock->isOpen() || !m_sock->isWritable() )
if ( m_sock.isNull() || !m_sock->isOpen() || !m_sock->isWritable() )
{
qDebug() << "***** Socket problem, whilst in sendMsg(). Cleaning up. *****";
shutdown( false );
return;
}
if( ! msg->write( m_sock.data() ) )
if ( !msg->write( m_sock.data() ) )
{
//qDebug() << "Error writing to socket in sendMsg() *************";
shutdown( false );

View File

@@ -19,17 +19,17 @@
#ifndef CONNECTION_H
#define CONNECTION_H
#include <QSharedPointer>
#include <QTcpSocket>
#include <QHostAddress>
#include <QVariant>
#include <QVariantMap>
#include <QString>
#include <QDataStream>
#include <QtEndian>
#include <QTimer>
#include <QTime>
#include <QPointer>
#include <QtCore/QSharedPointer>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QHostAddress>
#include <QtCore/QVariant>
#include <QtCore/QVariantMap>
#include <QtCore/QString>
#include <QtCore/QDataStream>
#include <QtCore/QtEndian>
#include <QtCore/QTimer>
#include <QtCore/QTime>
#include <QtCore/QPointer>
#include <qjson/parser.h>
#include <qjson/serializer.h>
@@ -87,6 +87,8 @@ public:
void setMsgProcessorModeOut( quint32 m ) { m_msgprocessor_out.setMode( m ); }
void setMsgProcessorModeIn( quint32 m ) { m_msgprocessor_in.setMode( m ); }
const QHostAddress peerIpAddress() const { return m_peerIpAddress; }
signals:
void ready();
void failed();
@@ -128,6 +130,7 @@ protected:
bool m_outbound, m_ready, m_onceonly;
msg_ptr m_firstmsg;
QString m_name;
QHostAddress m_peerIpAddress;
private:
void handleReadMsg();

View File

@@ -33,7 +33,7 @@
using namespace Tomahawk;
ControlConnection::ControlConnection( Servent* parent )
ControlConnection::ControlConnection( Servent* parent, const QHostAddress &ha )
: Connection( parent )
, m_dbsyncconn( 0 )
, m_registered( false )
@@ -47,6 +47,38 @@ ControlConnection::ControlConnection( Servent* parent )
this->setMsgProcessorModeIn( MsgProcessor::UNCOMPRESS_ALL | MsgProcessor::PARSE_JSON );
this->setMsgProcessorModeOut( MsgProcessor::COMPRESS_IF_LARGE );
m_peerIpAddress = ha;
}
ControlConnection::ControlConnection( Servent* parent, const QString &ha )
: Connection( parent )
, m_dbsyncconn( 0 )
, m_registered( false )
, m_pingtimer( 0 )
{
qDebug() << "CTOR controlconnection";
setId("ControlConnection()");
// auto delete when connection closes:
connect( this, SIGNAL( finished() ), SLOT( deleteLater() ) );
this->setMsgProcessorModeIn( MsgProcessor::UNCOMPRESS_ALL | MsgProcessor::PARSE_JSON );
this->setMsgProcessorModeOut( MsgProcessor::COMPRESS_IF_LARGE );
if ( !ha.isEmpty() )
{
QHostAddress qha( ha );
if ( !qha.isNull() )
m_peerIpAddress = qha;
else
{
QHostInfo qhi = QHostInfo::fromName( ha );
if ( !qhi.addresses().isEmpty() )
m_peerIpAddress = qhi.addresses().first();
}
}
}
@@ -72,7 +104,7 @@ ControlConnection::source() const
Connection*
ControlConnection::clone()
{
ControlConnection* clone = new ControlConnection( servent() );
ControlConnection* clone = new ControlConnection( servent(), m_peerIpAddress.toString() );
clone->setOnceOnly( onceOnly() );
clone->setName( name() );
return clone;
@@ -102,6 +134,8 @@ ControlConnection::setup()
else
friendlyName = name();
tDebug() << "Detected name:" << name() << friendlyName << m_sock->peerAddress();
// setup source and remote collection for this peer
m_source = SourceList::instance()->get( id(), friendlyName );
m_source->setControlConnection( this );

View File

@@ -39,7 +39,8 @@ class DLLEXPORT ControlConnection : public Connection
Q_OBJECT
public:
explicit ControlConnection( Servent* parent = 0 );
explicit ControlConnection( Servent* parent = 0, const QHostAddress &ha = QHostAddress() );
explicit ControlConnection( Servent* parent = 0, const QString &ha = QString() );
~ControlConnection();
Connection* clone();

View File

@@ -39,10 +39,6 @@
#include "sourcelist.h"
#include "utils/logger.h"
// close the dbsync connection after this much inactivity.
// it's automatically reestablished as needed.
#define IDLE_TIMEOUT 300000
using namespace Tomahawk;
@@ -52,11 +48,11 @@ DBSyncConnection::DBSyncConnection( Servent* s, const source_ptr& src )
, m_state( UNKNOWN )
{
qDebug() << Q_FUNC_INFO << src->id() << thread();
connect( this, SIGNAL( stateChanged( DBSyncConnection::State, DBSyncConnection::State, QString ) ),
m_source.data(), SLOT( onStateChanged( DBSyncConnection::State, DBSyncConnection::State, QString ) ) );
m_timer.setInterval( IDLE_TIMEOUT );
connect( &m_timer, SIGNAL( timeout() ), SLOT( idleTimeout() ) );
connect( m_source.data(), SIGNAL( commandsFinished() ),
this, SLOT( lastOpApplied() ) );
this->setMsgProcessorModeIn( MsgProcessor::PARSE_JSON | MsgProcessor::UNCOMPRESS_ALL );
@@ -67,19 +63,11 @@ DBSyncConnection::DBSyncConnection( Servent* s, const source_ptr& src )
DBSyncConnection::~DBSyncConnection()
{
qDebug() << "DTOR" << Q_FUNC_INFO;
tDebug() << "DTOR" << Q_FUNC_INFO << m_source->id() << m_source->friendlyName();
m_state = SHUTDOWN;
}
void
DBSyncConnection::idleTimeout()
{
qDebug() << Q_FUNC_INFO;
shutdown( true );
}
void
DBSyncConnection::changeState( State newstate )
{
@@ -96,7 +84,6 @@ DBSyncConnection::changeState( State newstate )
void
DBSyncConnection::setup()
{
// qDebug() << Q_FUNC_INFO;
setId( QString( "DBSyncConnection/%1" ).arg( socket()->peerAddress().toString() ) );
check();
}
@@ -105,23 +92,19 @@ DBSyncConnection::setup()
void
DBSyncConnection::trigger()
{
// qDebug() << Q_FUNC_INFO;
// if we're still setting up the connection, do nothing - we sync on first connect anyway:
if ( !isRunning() )
return;
QMetaObject::invokeMethod( this, "sendMsg", Qt::QueuedConnection,
Q_ARG( msg_ptr,
Msg::factory( "{\"method\":\"trigger\"}", Msg::JSON ) )
);
Q_ARG( msg_ptr, Msg::factory( "{\"method\":\"trigger\"}", Msg::JSON ) ) );
}
void
DBSyncConnection::check()
{
qDebug() << Q_FUNC_INFO << m_source->id();
qDebug() << Q_FUNC_INFO << this << m_source->id();
if ( m_state != UNKNOWN && m_state != SYNCED )
{
qDebug() << "Syncing in progress already.";
@@ -133,10 +116,7 @@ DBSyncConnection::check()
return;
}
Q_ASSERT( m_cmds.isEmpty() );
m_uscache.clear();
m_us.clear();
changeState( CHECKING );
// load last-modified etc data for our collection and theirs from our DB:
@@ -154,9 +134,6 @@ DBSyncConnection::check()
{
fetchOpsData( m_source->lastCmdGuid() );
}
// restarts idle countdown
m_timer.start();
}
@@ -165,7 +142,7 @@ DBSyncConnection::check()
void
DBSyncConnection::gotUs( const QVariantMap& m )
{
m_us = m;
Q_UNUSED( m )
if ( !m_uscache.empty() )
sendOps();
}
@@ -231,19 +208,16 @@ DBSyncConnection::handleMsg( msg_ptr msg )
if ( msg->is( Msg::DBOP ) )
{
DatabaseCommand* cmd = DatabaseCommand::factory( m, m_source );
if ( !cmd )
if ( cmd )
{
qDebug() << "UNKNOWN DBOP CMD";
return;
QSharedPointer<DatabaseCommand> cmdsp = QSharedPointer<DatabaseCommand>(cmd);
m_source->addCommand( cmdsp );
}
QSharedPointer<DatabaseCommand> cmdsp = QSharedPointer<DatabaseCommand>(cmd);
m_cmds << cmdsp;
if ( !msg->is( Msg::FRAGMENT ) ) // last msg in this batch
{
changeState( SAVING ); // just DB work left to complete
executeCommands();
m_source->executeCommands();
}
return;
}
@@ -251,8 +225,7 @@ DBSyncConnection::handleMsg( msg_ptr msg )
if ( m.value( "method" ).toString() == "fetchops" )
{
m_uscache = m;
if ( !m_us.empty() )
sendOps();
sendOps();
return;
}
@@ -269,23 +242,11 @@ DBSyncConnection::handleMsg( msg_ptr msg )
void
DBSyncConnection::executeCommands()
DBSyncConnection::lastOpApplied()
{
if ( !m_cmds.isEmpty() )
{
QSharedPointer<DatabaseCommand> cmd = m_cmds.takeFirst();
if ( !cmd->singletonCmd() )
m_source->setLastCmdGuid( cmd->guid() );
connect( cmd.data(), SIGNAL( finished() ), SLOT( executeCommands() ) );
Database::instance()->enqueue( cmd );
}
else
{
changeState( SYNCED );
// check again, until peer responds we have no new ops to process
check();
}
changeState( SYNCED );
// check again, until peer responds we have no new ops to process
check();
}
@@ -298,8 +259,8 @@ DBSyncConnection::sendOps()
source_ptr src = SourceList::instance()->getLocal();
DatabaseCommand_loadOps* cmd = new DatabaseCommand_loadOps( src, m_uscache.value( "lastop" ).toString() );
connect( cmd, SIGNAL( done( QString, QString, QList< dbop_ptr > ) ),
SLOT( sendOpsData( QString, QString, QList< dbop_ptr > ) ) );
connect( cmd, SIGNAL( done( QString, QString, QList< dbop_ptr > ) ),
SLOT( sendOpsData( QString, QString, QList< dbop_ptr > ) ) );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}
@@ -314,6 +275,7 @@ DBSyncConnection::sendOpsData( QString sinceguid, QString lastguid, QList< dbop_
m_lastSentOp = lastguid;
if ( ops.length() == 0 )
{
tLog( LOGVERBOSE ) << "Sending ok" << m_source->id() << m_source->friendlyName();
sendMsg( Msg::factory( "ok", Msg::DBOP ) );
return;
}

View File

@@ -70,23 +70,20 @@ private slots:
void fetchOpsData( const QString& sinceguid );
void sendOpsData( QString sinceguid, QString lastguid, QList< dbop_ptr > ops );
void executeCommands();
void lastOpApplied();
void check();
void idleTimeout();
private:
void synced();
void changeState( State newstate );
Tomahawk::source_ptr m_source;
QVariantMap m_us, m_uscache;
QVariantMap m_uscache;
QList< QSharedPointer<DatabaseCommand> > m_cmds;
QString m_lastSentOp;
State m_state;
QTimer m_timer;
};
#endif // DBSYNCCONNECTION_H

View File

@@ -161,7 +161,7 @@ Servent::createConnectionKey( const QString& name, const QString &nodeid, const
Q_ASSERT( this->thread() == QThread::currentThread() );
QString _key = ( key.isEmpty() ? uuid() : key );
ControlConnection* cc = new ControlConnection( this );
ControlConnection* cc = new ControlConnection( this, name );
cc->setName( name.isEmpty() ? QString( "KEY(%1)" ).arg( key ) : name );
if ( !nodeid.isEmpty() )
cc->setId( nodeid );
@@ -260,6 +260,7 @@ Servent::unregisterControlConnection( ControlConnection* conn )
if( c!=conn )
n.append( c );
m_connectedNodes.removeAll( conn->id() );
m_controlconnections = n;
}
@@ -327,7 +328,6 @@ Servent::readyRead()
ControlConnection* cc = 0;
bool ok;
// int pport; //FIXME?
QString key, conntype, nodeid, controlid;
QVariantMap m = parser.parse( sock->_msg->payload(), &ok ).toMap();
if( !ok )
@@ -335,9 +335,9 @@ Servent::readyRead()
tDebug() << "Invalid JSON on new connection, aborting";
goto closeconnection;
}
conntype = m.value( "conntype" ).toString();
key = m.value( "key" ).toString();
// pport = m.value( "port" ).toInt();
nodeid = m.value( "nodeid" ).toString();
controlid = m.value( "controlid" ).toString();
@@ -345,14 +345,25 @@ Servent::readyRead()
if( !nodeid.isEmpty() ) // only control connections send nodeid
{
bool dupe = false;
if ( m_connectedNodes.contains( nodeid ) )
dupe = true;
foreach( ControlConnection* con, m_controlconnections )
{
tLog( LOGVERBOSE ) << "known connection:" << con->id() << con->source()->friendlyName();
if( con->id() == nodeid )
{
tLog() << "Duplicate control connection detected, dropping:" << nodeid << conntype;
goto closeconnection;
dupe = true;
break;
}
}
if ( dupe )
{
tLog() << "Duplicate control connection detected, dropping:" << nodeid << conntype;
goto closeconnection;
}
}
foreach( ControlConnection* con, m_controlconnections )
@@ -368,14 +379,16 @@ Servent::readyRead()
if( conntype == "accept-offer" || "push-offer" )
{
sock->_msg.clear();
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << key << nodeid << "socket peer address = " << sock->peerAddress() << "socket peer name = " << sock->peerName();
Connection* conn = claimOffer( cc, nodeid, key, sock->peerAddress() );
if( !conn )
{
tLog() << "claimOffer FAILED, key:" << key;
tLog() << "claimOffer FAILED, key:" << key << nodeid;
goto closeconnection;
}
tDebug( LOGVERBOSE ) << "claimOffer OK:" << key;
tDebug( LOGVERBOSE ) << "claimOffer OK:" << key << nodeid;
m_connectedNodes << nodeid;
if( !nodeid.isEmpty() )
conn->setId( nodeid );
@@ -400,7 +413,7 @@ closeconnection:
void
Servent::createParallelConnection( Connection* orig_conn, Connection* new_conn, const QString& key )
{
tDebug( LOGVERBOSE ) << "Servent::createParallelConnection, key:" << key << thread() << orig_conn;
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << ", key:" << key << thread() << orig_conn;
// if we can connect to them directly:
if( orig_conn && orig_conn->outbound() )
{
@@ -433,7 +446,7 @@ Servent::socketConnected()
{
QTcpSocketExtra* sock = (QTcpSocketExtra*)sender();
tDebug( LOGVERBOSE ) << "Servent::SocketConnected" << thread() << "socket:" << sock;
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << thread() << "socket: " << sock << ", hostaddr: " << sock->peerAddress() << ", hostname: " << sock->peerName();
Connection* conn = sock->_conn.data();
handoverSocket( conn, sock );
@@ -497,7 +510,7 @@ Servent::connectToPeer( const QString& ha, int port, const QString &key, const Q
{
Q_ASSERT( this->thread() == QThread::currentThread() );
ControlConnection* conn = new ControlConnection( this );
ControlConnection* conn = new ControlConnection( this, ha );
QVariantMap m;
m["conntype"] = "accept-offer";
m["key"] = key;
@@ -549,7 +562,10 @@ Servent::connectToPeer( const QString& ha, int port, const QString &key, Connect
connect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ),
SLOT( socketError( QAbstractSocket::SocketError ) ) );
sock->connectToHost( ha, port, QTcpSocket::ReadWrite );
if ( !conn->peerIpAddress().isNull() )
sock->connectToHost( conn->peerIpAddress(), port, QTcpSocket::ReadWrite );
else
sock->connectToHost( ha, port, QTcpSocket::ReadWrite );
sock->moveToThread( thread() );
}
@@ -616,7 +632,7 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString
if( isIPWhitelisted( peer ) )
{
tDebug() << "Connection is from whitelisted IP range (LAN)";
Connection* conn = new ControlConnection( this );
Connection* conn = new ControlConnection( this, peer.toString() );
conn->setName( peer.toString() );
return conn;
}
@@ -662,7 +678,7 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString
else if ( noauth )
{
Connection* conn;
conn = new ControlConnection( this );
conn = new ControlConnection( this, peer );
conn->setName( key );
return conn;
}
@@ -799,11 +815,11 @@ Servent::isIPWhitelisted( QHostAddress ip )
static QList<range> whitelist;
if( whitelist.isEmpty() )
{
whitelist << range( QHostAddress( "10.0.0.0" ), 8 )
<< range( QHostAddress( "172.16.0.0" ), 12 )
<< range( QHostAddress( "192.168.0.0" ), 16 )
<< range( QHostAddress( "169.254.0.0" ), 16 )
<< range( QHostAddress( "127.0.0.0" ), 24 );
whitelist << range( QHostAddress( "10.0.0.0" ), 8 )
<< range( QHostAddress( "172.16.0.0" ), 12 )
<< range( QHostAddress( "192.168.0.0" ), 16 )
<< range( QHostAddress( "169.254.0.0" ), 16 )
<< range( QHostAddress( "127.0.0.0" ), 24 );
// tDebug( LOGVERBOSE ) << "Loaded whitelist IP range:" << whitelist;
}

View File

@@ -22,15 +22,15 @@
// time before new connection terminates if no auth received
#define AUTH_TIMEOUT 180000
#include <QObject>
#include <QTcpServer>
#include <QHostInfo>
#include <QMap>
#include <QMutex>
#include <QSharedPointer>
#include <QTcpSocket>
#include <QTimer>
#include <QPointer>
#include <QtCore/QObject>
#include <QtCore/QMap>
#include <QtCore/QMutex>
#include <QtCore/QSharedPointer>
#include <QtCore/QTimer>
#include <QtCore/QPointer>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QHostInfo>
#include <qjson/parser.h>
#include <qjson/serializer.h>
@@ -159,6 +159,8 @@ private:
QJson::Parser parser;
QList< ControlConnection* > m_controlconnections; // canonical list of authed peers
QMap< QString, QWeakPointer<Connection> > m_offers;
QStringList m_connectedNodes;
int m_port, m_externalPort;
QHostAddress m_externalAddress;
QString m_externalHostname;

View File

@@ -293,7 +293,8 @@ Playlist::loadRevision( const QString& rev )
void
Playlist::createNewRevision( const QString& newrev, const QString& oldrev, const QList< plentry_ptr >& entries )
{
qDebug() << Q_FUNC_INFO << newrev << oldrev << entries.count();
tDebug() << Q_FUNC_INFO << newrev << oldrev << entries.count();
Q_ASSERT( m_source->isLocal() || newrev == oldrev );
if ( busy() )
{
@@ -387,34 +388,32 @@ Playlist::setNewRevision( const QString& rev,
bool is_newest_rev,
const QMap< QString, Tomahawk::plentry_ptr >& addedmap )
{
// qDebug() << Q_FUNC_INFO << rev << is_newest_rev << m_title << addedmap.count() << neworderedguids.count() << oldorderedguids.count();
// build up correctly ordered new list of plentry_ptrs from
// existing ones, and the ones that have been added
QMap<QString, plentry_ptr> entriesmap;
foreach( const plentry_ptr& p, m_entries )
foreach ( const plentry_ptr& p, m_entries )
entriesmap.insert( p->guid(), p );
QList<plentry_ptr> entries;
foreach( const QString& id, neworderedguids )
foreach ( const QString& id, neworderedguids )
{
if( entriesmap.contains( id ) )
if ( entriesmap.contains( id ) )
{
entries.append( entriesmap.value( id ) );
}
else if( addedmap.contains( id ) )
else if ( addedmap.contains( id ) )
{
entries.append( addedmap.value( id ) );
if( is_newest_rev )
if ( is_newest_rev )
m_entries.append( addedmap.value( id ) );
}
else
{
/* qDebug() << "id:" << id;
* qDebug() << "newordered:" << neworderedguids.count() << neworderedguids;
* qDebug() << "entriesmap:" << entriesmap.count() << entriesmap;
* qDebug() << "addedmap:" << addedmap.count() << addedmap;
* qDebug() << "m_entries" << m_entries; */
tDebug() << "id:" << id;
tDebug() << "newordered:" << neworderedguids.count() << neworderedguids;
tDebug() << "entriesmap:" << entriesmap.count() << entriesmap;
tDebug() << "addedmap:" << addedmap.count() << addedmap;
tDebug() << "m_entries" << m_entries;
tLog() << "Playlist error for playlist with guid" << guid() << "from source" << author()->friendlyName();
Q_ASSERT( false ); // XXX
@@ -428,19 +427,19 @@ Playlist::setNewRevision( const QString& rev,
// entries that have been removed:
QSet<QString> removedguids = oldorderedguids.toSet().subtract( neworderedguids.toSet() );
//qDebug() << "Removedguids:" << removedguids << "oldorederedguids" << oldorderedguids << "newog" << neworderedguids;
foreach( QString remid, removedguids )
foreach ( QString remid, removedguids )
{
// NB: entriesmap will contain old/removed entries only if the removal was done
// in the same session - after a restart, history is not in memory.
if( entriesmap.contains( remid ) )
if ( entriesmap.contains( remid ) )
{
pr.removed << entriesmap.value( remid );
if( is_newest_rev )
if ( is_newest_rev )
{
//qDebug() << "Removing from m_entries" << remid;
for( int k = 0 ; k < m_entries.length(); ++k )
for ( int k = 0 ; k < m_entries.length(); ++k )
{
if( m_entries.at( k )->guid() == remid )
if ( m_entries.at( k )->guid() == remid )
{
//qDebug() << "removed at" << k;
m_entries.removeAt( k );
@@ -452,8 +451,8 @@ Playlist::setNewRevision( const QString& rev,
}
pr.added = addedmap.values();
pr.newlist = entries;
return pr;
}

View File

@@ -159,13 +159,19 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
r.setTop( r.bottom() - painter->fontMetrics().height() );
r.adjust( 4, 0, -4, -1 );
if ( m_hoveringOver == index )
{
TomahawkUtils::drawQueryBackground( painter, opt.palette, r, 1.5 );
painter->setPen( opt.palette.color( QPalette::HighlightedText ) );
}
else
{
#ifdef Q_WS_MAC
painter->setPen( opt.palette.color( QPalette::Dark ).darker( 200 ) );
painter->setPen( opt.palette.color( QPalette::Dark ).darker( 200 ) );
#else
painter->setPen( opt.palette.color( QPalette::Dark ) );
painter->setPen( opt.palette.color( QPalette::Dark ) );
#endif
}
to.setAlignment( Qt::AlignHCenter | Qt::AlignBottom );
text = painter->fontMetrics().elidedText( item->album()->artist()->name(), Qt::ElideRight, textRect.width() - 3 );
painter->drawText( textRect, text, to );
@@ -177,9 +183,11 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
painter->restore();
}
bool
AlbumItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index )
{
Q_UNUSED( model );
Q_UNUSED( option );
if ( event->type() != QEvent::MouseButtonRelease &&
@@ -217,7 +225,8 @@ AlbumItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const
event->accept();
return true;
} else if ( event->type() == QEvent::MouseButtonPress )
}
else if ( event->type() == QEvent::MouseButtonPress )
{
// Stop the whole album from having a down click action as we just want the artist name to be clicked
event->accept();
@@ -231,6 +240,7 @@ AlbumItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const
return false;
}
void
AlbumItemDelegate::whitespaceMouseEvent()
{

View File

@@ -25,6 +25,7 @@
#include "artist.h"
#include "albumitem.h"
#include "source.h"
#include "sourcelist.h"
#include "database/database.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
@@ -247,6 +248,7 @@ AlbumModel::addCollection( const collection_ptr& collection, bool overwrite )
DatabaseCommand_AllAlbums* cmd = new DatabaseCommand_AllAlbums( collection );
m_overwriteOnAdd = overwrite;
m_collection = collection;
connect( cmd, SIGNAL( albums( QList<Tomahawk::album_ptr>, QVariant ) ),
SLOT( addAlbums( QList<Tomahawk::album_ptr> ) ) );
@@ -255,6 +257,21 @@ AlbumModel::addCollection( const collection_ptr& collection, bool overwrite )
m_title = tr( "All albums from %1" ).arg( collection->source()->friendlyName() );
if ( collection.isNull() )
{
connect( SourceList::instance(), SIGNAL( sourceAdded( Tomahawk::source_ptr ) ), SLOT( onSourceAdded( Tomahawk::source_ptr ) ) );
QList<Tomahawk::source_ptr> sources = SourceList::instance()->sources();
foreach ( const source_ptr& source, sources )
{
connect( source->collection().data(), SIGNAL( changed() ), SLOT( onCollectionChanged() ), Qt::UniqueConnection );
}
}
else
{
connect( collection.data(), SIGNAL( changed() ), SLOT( onCollectionChanged() ), Qt::UniqueConnection );
}
emit loadingStarted();
}
@@ -290,14 +307,14 @@ AlbumModel::addFilteredCollection( const collection_ptr& collection, unsigned in
void
AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums )
{
emit loadingFinished();
if ( !albums.count() )
return;
if ( m_overwriteOnAdd )
clear();
emit loadingFinished();
int c = rowCount( QModelIndex() );
QPair< int, int > crows;
crows.first = c;
@@ -318,6 +335,23 @@ AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums )
}
void
AlbumModel::onSourceAdded( const Tomahawk::source_ptr& source )
{
connect( source->collection().data(), SIGNAL( changed() ), SLOT( onCollectionChanged() ), Qt::UniqueConnection );
}
void
AlbumModel::onCollectionChanged()
{
if ( m_collection )
addCollection( m_collection, true );
else
addCollection( m_collection, true );
}
void
AlbumModel::clear()
{

View File

@@ -96,9 +96,13 @@ signals:
void loadingStarted();
void loadingFinished();
private slots:
void onDataChanged();
void onSourceAdded( const Tomahawk::source_ptr& source );
void onCollectionChanged();
void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
void infoSystemFinished( QString target );
@@ -110,6 +114,8 @@ private:
QString m_description;
bool m_overwriteOnAdd;
Tomahawk::collection_ptr m_collection;
QHash<qlonglong, QPersistentModelIndex> m_coverHash;
};

View File

@@ -369,6 +369,9 @@ ArtistView::onMenuTriggered( int action )
bool
ArtistView::jumpToCurrentTrack()
{
if ( !m_proxyModel )
return false;
scrollTo( m_proxyModel->currentIndex(), QAbstractItemView::PositionAtCenter );
return true;
}

View File

@@ -18,9 +18,6 @@
#include "collectionflatmodel.h"
#include <QMimeData>
#include <QTreeView>
#include "database/database.h"
#include "sourcelist.h"
#include "utils/logger.h"
@@ -31,9 +28,6 @@ using namespace Tomahawk;
CollectionFlatModel::CollectionFlatModel( QObject* parent )
: TrackModel( parent )
{
qDebug() << Q_FUNC_INFO;
connect( SourceList::instance(), SIGNAL( sourceRemoved( Tomahawk::source_ptr ) ), SLOT( onSourceOffline( Tomahawk::source_ptr ) ) );
}
@@ -45,7 +39,6 @@ CollectionFlatModel::~CollectionFlatModel()
void
CollectionFlatModel::addCollections( const QList< collection_ptr >& collections )
{
qDebug() << Q_FUNC_INFO << "Adding collections!";
foreach( const collection_ptr& col, collections )
{
addCollection( col );
@@ -64,12 +57,7 @@ CollectionFlatModel::addCollection( const collection_ptr& collection, bool sendN
<< collection->source()->id()
<< collection->source()->userName();
connect( collection.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
connect( collection.data(), SIGNAL( tracksRemoved( QList<Tomahawk::query_ptr> ) ),
SLOT( onTracksRemoved( QList<Tomahawk::query_ptr> ) ) );
if( sendNotifications )
if ( sendNotifications )
emit loadingStarted();
DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( collection );
@@ -107,143 +95,32 @@ CollectionFlatModel::addFilteredCollection( const collection_ptr& collection, un
}
void
CollectionFlatModel::removeCollection( const collection_ptr& collection )
{
return; // FIXME
disconnect( collection.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
this, SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
QTime timer;
timer.start();
// QList<TrackModelItem*> plitems = m_collectionIndex.values( collection );
QList< QPair< int, int > > rows;
QList< QPair< int, int > > sortrows;
QPair< int, int > row;
QPair< int, int > rowf;
rows = m_collectionRows.values( collection );
while ( rows.count() )
{
int x = -1;
int j = 0;
foreach( row, rows )
{
if ( x < 0 || row.first > rows.at( x ).first )
x = j;
j++;
}
sortrows.append( rows.at( x ) );
rows.removeAt( x );
}
foreach( row, sortrows )
{
QMap< Tomahawk::collection_ptr, QPair< int, int > > newrows;
foreach ( const collection_ptr& col, m_collectionRows.uniqueKeys() )
{
if ( col.data() == collection.data() )
continue;
foreach ( rowf, m_collectionRows.values( col ) )
{
if ( rowf.first > row.first )
{
rowf.first -= ( row.second - row.first ) + 1;
rowf.second -= ( row.second - row.first ) + 1;
}
newrows.insertMulti( col, rowf );
}
}
m_collectionRows = newrows;
qDebug() << "Removing rows:" << row.first << row.second;
emit beginRemoveRows( QModelIndex(), row.first, row.second );
for ( int i = row.second; i >= row.first; i-- )
{
TrackModelItem* item = itemFromIndex( index( i, 0, QModelIndex() ) );
delete item;
}
emit endRemoveRows();
}
qDebug() << "Collection removed, time elapsed:" << timer.elapsed();
// emit trackCountChanged( rowCount( QModelIndex() ) );
}
void
CollectionFlatModel::onTracksAdded( const QList<Tomahawk::query_ptr>& tracks )
{
qDebug() << Q_FUNC_INFO << tracks.count() << rowCount( QModelIndex() );
if( !m_loadingCollections.isEmpty() && sender() && qobject_cast< Collection* >( sender() ) )
if ( !m_loadingCollections.isEmpty() && sender() && qobject_cast< Collection* >( sender() ) )
{
// we are keeping track and are called as a slot
m_loadingCollections.removeAll( qobject_cast< Collection* >( sender() ) );
}
bool kickOff = m_tracksToAdd.isEmpty();
m_tracksToAdd << tracks;
append( tracks );
emit trackCountChanged( trackCount() );
if ( m_tracksToAdd.count() && kickOff )
processTracksToAdd();
else if ( m_tracksToAdd.isEmpty() && m_loadingCollections.isEmpty() )
if ( m_loadingCollections.isEmpty() )
emit loadingFinished();
}
void
CollectionFlatModel::processTracksToAdd()
{
int chunkSize = 5000;
int maxc = qMin( chunkSize, m_tracksToAdd.count() );
int c = rowCount( QModelIndex() );
emit beginInsertRows( QModelIndex(), c, c + maxc - 1 );
//beginResetModel();
TrackModelItem* plitem;
QList< Tomahawk::query_ptr >::iterator iter = m_tracksToAdd.begin();
for( int i = 0; i < maxc; ++i )
{
plitem = new TrackModelItem( *iter, m_rootItem );
plitem->index = createIndex( m_rootItem->children.count() - 1, 0, plitem );
connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) );
++iter;
}
m_tracksToAdd.erase( m_tracksToAdd.begin(), iter );
if ( m_tracksToAdd.isEmpty() && m_loadingCollections.isEmpty() )
emit loadingFinished();
//endResetModel();
emit endInsertRows();
qDebug() << Q_FUNC_INFO << rowCount( QModelIndex() );
if ( m_tracksToAdd.count() )
QTimer::singleShot( 250, this, SLOT( processTracksToAdd() ) );
}
void
CollectionFlatModel::onTracksRemoved( const QList<Tomahawk::query_ptr>& tracks )
{
QList<Tomahawk::query_ptr> t = tracks;
for ( int i = rowCount( QModelIndex() ); i >= 0 && t.count(); i-- )
{
TrackModelItem* item = itemFromIndex( index( i, 0, QModelIndex() ) );
QModelIndex idx = index( i, 0, QModelIndex() );
TrackModelItem* item = itemFromIndex( idx );
if ( !item )
continue;
@@ -252,11 +129,7 @@ CollectionFlatModel::onTracksRemoved( const QList<Tomahawk::query_ptr>& tracks )
{
if ( item->query().data() == query.data() )
{
qDebug() << "Removing row:" << i << query->toString();
emit beginRemoveRows( QModelIndex(), i, i );
delete item;
emit endRemoveRows();
remove( idx );
t.removeAt( j );
break;
}
@@ -264,9 +137,6 @@ CollectionFlatModel::onTracksRemoved( const QList<Tomahawk::query_ptr>& tracks )
j++;
}
}
// emit trackCountChanged( rowCount( QModelIndex() ) );
qDebug() << Q_FUNC_INFO << rowCount( QModelIndex() );
}
@@ -274,20 +144,7 @@ void
CollectionFlatModel::onDataChanged()
{
TrackModelItem* p = (TrackModelItem*)sender();
// emit itemSizeChanged( p->index );
if ( p )
emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount( QModelIndex() ) - 1 ) );
}
void
CollectionFlatModel::onSourceOffline( const Tomahawk::source_ptr& src )
{
qDebug() << Q_FUNC_INFO;
if ( m_collectionRows.contains( src->collection() ) )
{
removeCollection( src->collection() );
}
}

View File

@@ -46,16 +46,9 @@ public:
virtual int trackCount() const { return rowCount( QModelIndex() ) + m_tracksToAdd.count(); }
void addCollections( const QList< Tomahawk::collection_ptr >& collections );
void addCollection( const Tomahawk::collection_ptr& collection, bool sendNotifications = true );
void removeCollection( const Tomahawk::collection_ptr& collection );
void addFilteredCollection( const Tomahawk::collection_ptr& collection, unsigned int amount, DatabaseCommand_AllTracks::SortOrder order );
virtual void append( const Tomahawk::query_ptr& /*query*/ ) {}
virtual void append( const Tomahawk::artist_ptr& /*artist*/ ) {}
virtual void append( const Tomahawk::album_ptr& /*album*/ ) {}
signals:
void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode );
void shuffleModeChanged( bool enabled );
@@ -70,10 +63,6 @@ private slots:
void onTracksAdded( const QList<Tomahawk::query_ptr>& tracks );
void onTracksRemoved( const QList<Tomahawk::query_ptr>& tracks );
void onSourceOffline( const Tomahawk::source_ptr& src );
void processTracksToAdd();
private:
QMap< Tomahawk::collection_ptr, QPair< int, int > > m_collectionRows;
QList<Tomahawk::query_ptr> m_tracksToAdd;

View File

@@ -313,9 +313,9 @@ DynamicModel::removeIndex(const QModelIndex& idx, bool moreToCome)
if( !moreToCome && idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) ) { // if the user is manually removing the last one, re-add as we're a station
newTrackLoading();
}
TrackModel::removeIndex( idx );
TrackModel::remove( idx );
} else
PlaylistModel::removeIndex( idx, moreToCome );
PlaylistModel::remove( idx, moreToCome );
// don't call onPlaylistChanged.
if( !moreToCome )

View File

@@ -172,7 +172,7 @@ DynamicPlaylist::createNewRevision( const QString& newUuid )
}
else if( mode() == OnDemand )
{
createNewRevision( newUuid.isEmpty() ? uuid() : newUuid, currentrevision(), type(), generator()->controls());
createNewRevision( newUuid.isEmpty() ? uuid() : newUuid, currentrevision(), type(), generator()->controls() );
}
}
@@ -185,6 +185,8 @@ DynamicPlaylist::createNewRevision( const QString& newrev,
const QList< dyncontrol_ptr>& controls,
const QList< plentry_ptr >& entries )
{
Q_ASSERT( m_source->isLocal() || newrev == oldrev );
if ( busy() )
{
m_revisionQueue.enqueue( DynQueueItem( newrev, oldrev, type, controls, (int)Static, entries, oldrev == currentrevision() ) );
@@ -230,6 +232,8 @@ DynamicPlaylist::createNewRevision( const QString& newrev,
const QString& type,
const QList< dyncontrol_ptr>& controls )
{
Q_ASSERT( m_source->isLocal() || newrev == oldrev );
if ( busy() )
{
m_revisionQueue.enqueue( DynQueueItem( newrev, oldrev, type, controls, (int)OnDemand, QList< plentry_ptr >(), oldrev == currentrevision() ) );
@@ -533,8 +537,15 @@ DynamicPlaylist::checkRevisionQueue()
if ( item.oldRev != currentrevision() && item.applyToTip )
{
// this was applied to the then-latest, but the already-running operation changed it so it's out of date now. fix it
if ( item.oldRev == item.newRev )
{
checkRevisionQueue();
return;
}
item.oldRev = currentrevision();
}
if( item.mode == Static )
createNewRevision( item.newRev, item.oldRev, item.type, item.controls, item.entries );
else

View File

@@ -264,7 +264,7 @@ DynamicView::collapseEntries( int startRow, int num, int numToKeep )
todel << proxyModel()->index( startRow + i, k );
}
}
proxyModel()->removeIndexes( todel );
proxyModel()->remove( todel );
}

View File

@@ -102,6 +102,10 @@ public:
* If this generator doesn't support this (and returns false for
* \c onDemandSteerable) this will be null. The generator is responsible
* for reacting to changes in the widget.
*
* Steering widgets may emit a \c steeringChanged() signal, which will cause the model to toss any
* upcoming tracks and re-fetch them.
*
*/
virtual QWidget* steeringWidget() { return 0; }

View File

@@ -389,9 +389,6 @@ EchonestGenerator::dynamicFetched()
resetSteering();
if( !m_steerer.isNull() )
m_steerer.data()->resetSteering( true );
try
{
Echonest::Song song = m_dynPlaylist->parseNextSong( reply );

View File

@@ -60,10 +60,10 @@ EchonestSteerer::EchonestSteerer( QWidget* parent )
f.setBold( true );
m_steerTop->setFont( f );
m_textL->addWidget( m_steerTop );
m_steerBottom = new QLabel( tr( "Takes effect on track change" ), this );
f.setPointSize( f.pointSize() - 3 );
m_steerBottom->setFont( f );
m_textL->addWidget( m_steerBottom );
// m_steerBottom = new QLabel( tr( "Takes effect on track change" ), this );
// f.setPointSize( f.pointSize() - 3 );
// m_steerBottom->setFont( f );
// m_textL->addWidget( m_steerBottom );
m_layout->addLayout( m_textL, 1 );
@@ -97,6 +97,12 @@ EchonestSteerer::EchonestSteerer( QWidget* parent )
connect( m_description, SIGNAL( textChanged( QString ) ), this, SLOT( changed() ) );
m_apply = initButton( this );
m_apply->setIcon( QIcon( RESPATH "images/apply-check.png" ) );
m_apply->setToolTip( tr( "Apply steering command" ) );
m_layout->addWidget( m_apply );
connect( m_apply, SIGNAL( clicked( bool ) ), this, SLOT( applySteering() ) );
m_reset = initButton( this );
m_reset->setIcon( QIcon( RESPATH "images/view-refresh.png" ) );
m_reset->setToolTip( tr( "Reset all steering commands" ) );
@@ -165,19 +171,7 @@ EchonestSteerer::fadeOut()
void
EchonestSteerer::changed()
{
bool keep = false;
if( m_amplifier->itemData( m_amplifier->currentIndex() ).toString().isEmpty() ) { // Keep Current
keep = true;
emit reset();
}
if( m_field->itemData( m_field->currentIndex() ).toString() != "desc" ) {
if( !keep ) {
QString steer = m_field->itemData( m_field->currentIndex() ).toString() + m_amplifier->itemData( m_amplifier->currentIndex() ).toString();
emit steerField( steer );
}
// if description was shown, animate to shrink
if( m_layout->indexOf( m_description ) > 0 ) {
m_expanding = false;
@@ -190,15 +184,8 @@ EchonestSteerer::changed()
m_resizeAnim.setFrameRange( start, end );
m_resizeAnim.start();
qDebug() << "COLLAPSING FROM" << start << "TO" << end;
}
} else { // description, so put in the description field
if( !m_description->text().isEmpty() && !keep ) {
QString steer = m_description->text() + m_amplifier->itemData( m_amplifier->currentIndex() ).toString();
emit steerDescription( steer );
}
if( m_layout->indexOf( m_description ) == -1 ) {
// animate to expand
m_layout->insertWidget( m_layout->count() - 1, m_description, 1 );
@@ -210,12 +197,32 @@ EchonestSteerer::changed()
int end = start + m_layout->spacing() + m_description->sizeHint().width();
m_resizeAnim.setFrameRange( start, end );
m_resizeAnim.start();
qDebug() << "EXPANDING FROM" << start << "TO" << end;
}
}
}
void
EchonestSteerer::applySteering()
{
if ( m_field->itemData( m_field->currentIndex() ).toString() != "desc" )
{
QString steer = m_field->itemData( m_field->currentIndex() ).toString() + m_amplifier->itemData( m_amplifier->currentIndex() ).toString();
emit steerField( steer );
}
else
{
if ( !m_description->text().isEmpty() )
{
QString steer = m_description->text() + m_amplifier->itemData( m_amplifier->currentIndex() ).toString();
emit steerDescription( steer );
}
}
emit steeringChanged();
resetSteering( true );
}
void
EchonestSteerer::resizeFrame( int width )

View File

@@ -44,6 +44,7 @@ public:
virtual void paintEvent(QPaintEvent* );
public slots:
void applySteering();
void resetSteering( bool automatic = false );
void fadeIn();
@@ -56,6 +57,9 @@ signals:
void reset();
void resized();
// interface to DynamicWidget
void steeringChanged();
private slots:
void changed();
@@ -77,6 +81,7 @@ private:
QLabel* m_steerBottom;
// icons on the right
QToolButton* m_apply;
QToolButton* m_reset;
// animations

View File

@@ -324,6 +324,8 @@ DynamicWidget::startStation()
m_steering = m_playlist->generator()->steeringWidget();
Q_ASSERT( m_steering );
connect( m_steering, SIGNAL( steeringChanged() ), this, SLOT( steeringChanged() ) );
int x = ( width() / 2 ) - ( m_steering->size().width() / 2 );
int y = height() - m_steering->size().height() - 40; // padding
@@ -394,6 +396,25 @@ DynamicWidget::controlChanged( const Tomahawk::dyncontrol_ptr& control )
emit descriptionChanged( m_playlist->generator()->sentenceSummary() );
}
void
DynamicWidget::steeringChanged()
{
// When steering changes, toss all the tracks that are upcoming, and re-fetch.
QModelIndex cur = m_view->currentIndex();
const int upcoming = m_view->proxyModel()->rowCount( QModelIndex() ) - 1 - cur.row();
tDebug() << "Removing tracks after current in station, found" << upcoming;
QModelIndexList toRemove;
for ( int i = cur.row() + 1; i < m_view->proxyModel()->rowCount( QModelIndex() ); i++ )
{
toRemove << m_view->proxyModel()->index( i, 0, QModelIndex() );
}
m_view->proxyModel()->remove( toRemove );
m_playlist->generator()->fetchNext();
}
void
DynamicWidget::showPreview()

View File

@@ -103,6 +103,7 @@ private slots:
void controlsChanged( bool added );
void controlChanged( const Tomahawk::dyncontrol_ptr& control );
void steeringChanged();
void showPreview();
void layoutFloatingWidgets();

View File

@@ -182,12 +182,12 @@ PlaylistItemDelegate::paintShort( QPainter* painter, const QStyleOptionViewItem&
else
{
upperText = QString( "%1 - %2" ).arg( artist ).arg( track );
QString playtime = TomahawkUtils::ageToString( QDateTime::fromTime_t( item->query()->playedBy().second ) );
QString playtime = TomahawkUtils::ageToString( QDateTime::fromTime_t( item->query()->playedBy().second ), true );
if ( source == SourceList::instance()->getLocal() )
lowerText = QString( "played %1 ago by you" ).arg( playtime );
lowerText = QString( tr( "played %1 by you" ) ).arg( playtime );
else
lowerText = QString( "played %1 ago by %2" ).arg( playtime ).arg( source->friendlyName() );
lowerText = QString( tr( "played %1 by %2" ) ).arg( playtime ).arg( source->friendlyName() );
if ( useAvatars )
pixmap = source->avatar( Source::FancyStyle );

View File

@@ -39,9 +39,8 @@ using namespace Tomahawk;
PlaylistModel::PlaylistModel( QObject* parent )
: TrackModel( parent )
, m_isTemporary( false )
, m_changesOngoing( false )
{
qDebug() << Q_FUNC_INFO;
m_dropStorage.parent = QPersistentModelIndex();
m_dropStorage.row = -10;
@@ -54,34 +53,9 @@ PlaylistModel::~PlaylistModel()
}
int
PlaylistModel::columnCount( const QModelIndex& parent ) const
{
return TrackModel::columnCount( parent );
}
QVariant
PlaylistModel::data( const QModelIndex& index, int role ) const
{
return TrackModel::data( index, role );
}
QVariant
PlaylistModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
return TrackModel::headerData( section, orientation, role );
}
void
PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEntries )
{
QString currentuuid;
if ( currentItem().isValid() )
currentuuid = itemFromIndex( currentItem() )->query()->id();
if ( !m_playlist.isNull() )
{
disconnect( m_playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::PlaylistRevision ) ) );
@@ -89,10 +63,8 @@ PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEn
disconnect( m_playlist.data(), SIGNAL( changed() ), this, SIGNAL( playlistChanged() ) );
}
if ( rowCount( QModelIndex() ) && loadEntries )
{
if ( loadEntries )
clear();
}
m_playlist = playlist;
connect( playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), SLOT( onRevisionLoaded( Tomahawk::PlaylistRevision ) ) );
@@ -101,53 +73,16 @@ PlaylistModel::loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEn
setReadOnly( !m_playlist->author()->isLocal() );
setTitle( playlist->title() );
setDescription( tr( "A playlist by %1, created %2 ago" )
setDescription( tr( "A playlist by %1, created %2" )
.arg( playlist->author()->isLocal() ? tr( "you" ) : playlist->author()->friendlyName() )
.arg( TomahawkUtils::ageToString( QDateTime::fromTime_t( playlist->createdOn() ) ) ) );
.arg( TomahawkUtils::ageToString( QDateTime::fromTime_t( playlist->createdOn() ), true ) ) );
m_isTemporary = false;
if ( !loadEntries )
return;
TrackModelItem* plitem;
QList<plentry_ptr> entries = playlist->entries();
if ( entries.count() )
{
int c = rowCount( QModelIndex() );
qDebug() << "Starting loading" << playlist->title();
emit beginInsertRows( QModelIndex(), c, c + entries.count() - 1 );
m_waitingForResolved.clear();
foreach( const plentry_ptr& entry, entries )
{
// qDebug() << entry->query()->toString();
plitem = new TrackModelItem( entry, m_rootItem );
plitem->index = createIndex( m_rootItem->children.count() - 1, 0, plitem );
connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) );
if ( entry->query()->id() == currentuuid )
setCurrentItem( plitem->index );
if ( !entry->query()->resolvingFinished() && !entry->query()->playable() )
{
m_waitingForResolved.append( entry->query().data() );
connect( entry->query().data(), SIGNAL( resolvingFinished( bool ) ), SLOT( trackResolved( bool ) ) );
}
}
emit endInsertRows();
}
else
qDebug() << "Playlist seems empty:" << playlist->title();
if ( !m_waitingForResolved.isEmpty() )
{
emit loadingStarted();
}
emit trackCountChanged( rowCount( QModelIndex() ) );
append( entries );
}
@@ -165,7 +100,7 @@ PlaylistModel::loadHistory( const Tomahawk::source_ptr& source, unsigned int amo
cmd->setLimit( amount );
connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ), Qt::QueuedConnection );
SLOT( append( QList<Tomahawk::query_ptr> ) ), Qt::QueuedConnection );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}
@@ -174,22 +109,23 @@ PlaylistModel::loadHistory( const Tomahawk::source_ptr& source, unsigned int amo
void
PlaylistModel::clear()
{
if ( rowCount( QModelIndex() ) )
{
emit loadingFinished();
TrackModel::clear();
emit beginResetModel();
delete m_rootItem;
m_rootItem = 0;
m_rootItem = new TrackModelItem( 0, this );
emit endResetModel();
}
m_waitingForResolved.clear();
}
void
PlaylistModel::append( const QList< plentry_ptr >& entries )
{
insert( entries, rowCount( QModelIndex() ) );
}
void
PlaylistModel::append( const QList< query_ptr >& queries )
{
onTracksAdded( queries );
insert( queries, rowCount( QModelIndex() ) );
}
@@ -199,13 +135,10 @@ PlaylistModel::append( const Tomahawk::query_ptr& query )
if ( query.isNull() )
return;
QList< Tomahawk::query_ptr > ql;
ql << query;
if ( !query->resolvingFinished() )
Pipeline::instance()->resolve( query );
onTracksAdded( ql );
TrackModel::append( query );
}
@@ -225,7 +158,7 @@ PlaylistModel::append( const Tomahawk::album_ptr& album )
m_isTemporary = true;
}
onTracksAdded( album->tracks() );
append( album->tracks() );
}
@@ -245,20 +178,83 @@ PlaylistModel::append( const Tomahawk::artist_ptr& artist )
m_isTemporary = true;
}
onTracksAdded( artist->tracks() );
append( artist->tracks() );
}
void
PlaylistModel::insert( unsigned int row, const Tomahawk::query_ptr& query )
PlaylistModel::insert( const Tomahawk::query_ptr& query, int row )
{
if ( query.isNull() )
TrackModel::insert( query, row );
}
void
PlaylistModel::insert( const QList< Tomahawk::query_ptr >& queries, int row )
{
if ( !queries.count() )
return;
QList< Tomahawk::query_ptr > ql;
ql << query;
QList< Tomahawk::plentry_ptr > entries;
foreach( const query_ptr& query, queries )
{
plentry_ptr entry = plentry_ptr( new PlaylistEntry() );
onTracksInserted( row, ql );
if ( query->results().count() )
entry->setDuration( query->results().at( 0 )->duration() );
else
entry->setDuration( 0 );
entry->setLastmodified( 0 );
entry->setAnnotation( "" ); // FIXME
entry->setQuery( query );
entry->setGuid( uuid() );
entries << entry;
}
insert( entries, row );
}
void
PlaylistModel::insert( const QList< Tomahawk::plentry_ptr >& entries, int row )
{
if ( !entries.count() )
return;
int c = row;
QPair< int, int > crows;
crows.first = c;
crows.second = c + entries.count() - 1;
emit beginInsertRows( QModelIndex(), crows.first, crows.second );
int i = 0;
TrackModelItem* plitem;
foreach( const plentry_ptr& entry, entries )
{
plitem = new TrackModelItem( entry, m_rootItem, row + i );
plitem->index = createIndex( row + i, 0, plitem );
i++;
if ( entry->query()->id() == currentItemUuid() )
setCurrentItem( plitem->index );
if ( !entry->query()->resolvingFinished() && !entry->query()->playable() )
{
m_waitingForResolved.append( entry->query().data() );
connect( entry->query().data(), SIGNAL( resolvingFinished( bool ) ), SLOT( trackResolved( bool ) ) );
}
connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) );
}
if ( !m_waitingForResolved.isEmpty() )
emit loadingStarted();
emit endInsertRows();
emit trackCountChanged( rowCount( QModelIndex() ) );
}
@@ -282,53 +278,9 @@ PlaylistModel::trackResolved( bool )
}
void
PlaylistModel::onTracksAdded( const QList<Tomahawk::query_ptr>& tracks )
{
onTracksInserted( rowCount( QModelIndex() ), tracks );
}
void
PlaylistModel::onTracksInserted( unsigned int row, const QList<Tomahawk::query_ptr>& tracks )
{
if ( !tracks.count() )
{
emit trackCountChanged( rowCount( QModelIndex() ) );
return;
}
int c = row;
QPair< int, int > crows;
crows.first = c;
crows.second = c + tracks.count() - 1;
emit beginInsertRows( QModelIndex(), crows.first, crows.second );
int i = 0;
TrackModelItem* plitem;
foreach( const query_ptr& query, tracks )
{
plentry_ptr entry = plentry_ptr( new PlaylistEntry() );
entry->setQuery( query );
plitem = new TrackModelItem( entry, m_rootItem, row + i );
plitem->index = createIndex( row + i, 0, plitem );
i++;
connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) );
}
emit endInsertRows();
emit trackCountChanged( rowCount( QModelIndex() ) );
}
void
PlaylistModel::onDataChanged()
{
qDebug() << Q_FUNC_INFO;
TrackModelItem* p = (TrackModelItem*)sender();
if ( p && p->index.isValid() )
emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount() - 1 ) );
@@ -384,14 +336,16 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r
// so check if the drag originated in this playlist to determine whether or not to copy
#ifdef Q_WS_MAC
if ( !data->hasFormat( "application/tomahawk.playlist.id" ) ||
( !m_playlist.isNull() && data->data( "application/tomahawk.playlist.id" ) != m_playlist->guid() ) )
( !m_playlist.isNull() && data->data( "application/tomahawk.playlist.id" ) != m_playlist->guid() ) )
{
dj->setDropAction( DropJob::Append );
}
#else
if ( action & Qt::CopyAction )
dj->setDropAction( DropJob::Append );
#endif
connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
dj->tracksFromMimeData( data );
return true;
@@ -412,44 +366,13 @@ PlaylistModel::parsedDroppedTracks( QList< query_ptr > tracks )
else
beginRow = rowCount( QModelIndex() );
// qDebug() << data->formats();
QString currentuuid;
if ( currentItem().isValid() )
currentuuid = itemFromIndex( currentItem() )->query()->id();
if ( tracks.count() )
{
emit beginInsertRows( QModelIndex(), beginRow, beginRow + tracks.count() - 1 );
foreach( const Tomahawk::query_ptr& query, tracks )
{
plentry_ptr e( new PlaylistEntry() );
e->setGuid( uuid() );
if ( query->results().count() )
e->setDuration( query->results().at( 0 )->duration() );
else
e->setDuration( 0 );
e->setLastmodified( 0 );
e->setAnnotation( "" ); // FIXME
e->setQuery( query );
TrackModelItem* plitem = new TrackModelItem( e, m_rootItem, beginRow );
plitem->index = createIndex( beginRow++, 0, plitem );
if ( query->id() == currentuuid )
setCurrentItem( plitem->index );
connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) );
}
emit endInsertRows();
if ( m_dropStorage.action & Qt::CopyAction || m_dropStorage.action & Qt::MoveAction )
{
onPlaylistChanged();
emit trackCountChanged( rowCount( QModelIndex() ) );
}
bool update = ( m_dropStorage.action & Qt::CopyAction || m_dropStorage.action & Qt::MoveAction );
if ( update )
beginPlaylistChanges();
insert( tracks, beginRow );
}
m_dropStorage.parent = QPersistentModelIndex();
@@ -458,11 +381,32 @@ PlaylistModel::parsedDroppedTracks( QList< query_ptr > tracks )
void
PlaylistModel::onPlaylistChanged()
PlaylistModel::beginPlaylistChanges()
{
if ( m_playlist.isNull() )
if ( m_playlist.isNull() || !m_playlist->author()->isLocal() )
return;
Q_ASSERT( !m_changesOngoing );
m_changesOngoing = true;
}
void
PlaylistModel::endPlaylistChanges()
{
if ( m_playlist.isNull() || !m_playlist->author()->isLocal() )
return;
if ( m_changesOngoing )
{
m_changesOngoing = false;
}
else
{
tDebug() << "Called" << Q_FUNC_INFO << "unexpectedly!";
Q_ASSERT( false );
}
QList<plentry_ptr> l = playlistEntries();
QString newrev = uuid();
m_waitForRevision << newrev;
@@ -505,16 +449,17 @@ PlaylistModel::playlistEntries() const
void
PlaylistModel::remove( unsigned int row, bool moreToCome )
PlaylistModel::remove( int row, bool moreToCome )
{
removeIndex( index( row, 0, QModelIndex() ), moreToCome );
TrackModel::remove( row, moreToCome );
}
void
PlaylistModel::removeIndex( const QModelIndex& index, bool moreToCome )
PlaylistModel::remove( const QModelIndex& index, bool moreToCome )
{
TrackModelItem* item = itemFromIndex( index );
if ( item && m_waitingForResolved.contains( item->query().data() ) )
{
m_waitingForResolved.removeAll( item->query().data() );
@@ -522,12 +467,27 @@ PlaylistModel::removeIndex( const QModelIndex& index, bool moreToCome )
emit loadingFinished();
}
TrackModel::removeIndex( index );
if ( !m_changesOngoing )
beginPlaylistChanges();
if ( !moreToCome && !m_playlist.isNull() )
{
onPlaylistChanged();
}
TrackModel::remove( index, moreToCome );
if ( !moreToCome )
endPlaylistChanges();
}
void
PlaylistModel::remove( const QList<QModelIndex>& indexes )
{
TrackModel::remove( indexes );
}
void
PlaylistModel::remove( const QList<QPersistentModelIndex>& indexes )
{
TrackModel::remove( indexes );
}

View File

@@ -47,11 +47,6 @@ public:
explicit PlaylistModel( QObject* parent = 0 );
~PlaylistModel();
int columnCount( const QModelIndex& parent = QModelIndex() ) const;
QVariant data( const QModelIndex& index, int role ) const;
QVariant headerData( int section, Qt::Orientation orientation, int role ) const;
virtual QMimeData* mimeData ( const QModelIndexList& indexes ) const;
virtual bool dropMimeData( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent );
@@ -60,44 +55,47 @@ public:
virtual void loadPlaylist( const Tomahawk::playlist_ptr& playlist, bool loadEntries = true );
void loadHistory( const Tomahawk::source_ptr& source, unsigned int amount = 50 );
void clear();
void append( const Tomahawk::query_ptr& query );
void append( const Tomahawk::album_ptr& album );
void append( const Tomahawk::artist_ptr& artist );
void append( const QList< Tomahawk::query_ptr >& queries );
void insert( unsigned int row, const Tomahawk::query_ptr& query );
void remove( unsigned int row, bool moreToCome = false );
virtual void removeIndex( const QModelIndex& index, bool moreToCome = false );
bool isTemporary() const;
public slots:
virtual void clear();
virtual void append( const Tomahawk::query_ptr& query );
virtual void append( const Tomahawk::album_ptr& album );
virtual void append( const Tomahawk::artist_ptr& artist );
virtual void append( const QList< Tomahawk::query_ptr >& queries );
virtual void append( const QList< Tomahawk::plentry_ptr >& entries );
virtual void insert( const Tomahawk::query_ptr& query, int row = 0 );
virtual void insert( const QList< Tomahawk::query_ptr >& queries, int row = 0 );
virtual void insert( const QList< Tomahawk::plentry_ptr >& entries, int row = 0 );
virtual void remove( int row, bool moreToCome = false );
virtual void remove( const QModelIndex& index, bool moreToCome = false );
virtual void remove( const QList<QModelIndex>& indexes );
virtual void remove( const QList<QPersistentModelIndex>& indexes );
signals:
void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode );
void shuffleModeChanged( bool enabled );
void itemSizeChanged( const QModelIndex& index );
void playlistDeleted();
void playlistChanged();
private slots:
void onDataChanged();
void onRevisionLoaded( Tomahawk::PlaylistRevision revision );
void onPlaylistChanged();
void onTracksAdded( const QList<Tomahawk::query_ptr>& tracks );
void onTracksInserted( unsigned int row, const QList<Tomahawk::query_ptr>& tracks );
void parsedDroppedTracks( QList<Tomahawk::query_ptr> );
void trackResolved( bool );
private:
void beginPlaylistChanges();
void endPlaylistChanges();
QList<Tomahawk::plentry_ptr> playlistEntries() const;
Tomahawk::playlist_ptr m_playlist;
bool m_isTemporary;
bool m_changesOngoing;
QList< Tomahawk::Query* > m_waitingForResolved;
QStringList m_waitForRevision;

View File

@@ -94,7 +94,7 @@ PlaylistView::keyPressEvent( QKeyEvent* event )
if ( ( event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace ) && !model()->isReadOnly() )
{
qDebug() << "Removing selected items";
proxyModel()->removeIndexes( selectedIndexes() );
proxyModel()->remove( selectedIndexes() );
}
}
@@ -102,9 +102,10 @@ PlaylistView::keyPressEvent( QKeyEvent* event )
void
PlaylistView::deleteItems()
{
proxyModel()->removeIndexes( selectedIndexes() );
proxyModel()->remove( selectedIndexes() );
}
bool
PlaylistView::canAutoUpdate() const
{
@@ -114,6 +115,7 @@ PlaylistView::canAutoUpdate() const
return false;
}
bool
PlaylistView::autoUpdate() const
{

View File

@@ -44,7 +44,7 @@ void
QueueProxyModel::onIndexActivated( const QModelIndex& index )
{
setCurrentIndex( QModelIndex() );
removeIndex( index );
remove( index );
}
@@ -54,7 +54,7 @@ QueueProxyModel::siblingItem( int itemsAway )
setCurrentIndex( QModelIndex() );
Tomahawk::result_ptr res = PlaylistProxyModel::siblingItem( itemsAway );
removeIndex( currentIndex() );
remove( currentIndex() );
return res;
}

View File

@@ -39,16 +39,13 @@ TrackModel::TrackModel( QObject* parent )
, m_readOnly( true )
, m_style( Detailed )
{
qDebug() << Q_FUNC_INFO;
connect( AudioEngine::instance(), SIGNAL( finished( Tomahawk::result_ptr ) ), SLOT( onPlaybackFinished( Tomahawk::result_ptr ) ), Qt::DirectConnection );
connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( onPlaybackStarted( Tomahawk::result_ptr ) ), Qt::DirectConnection );
connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onPlaybackStopped() ), Qt::DirectConnection );
}
TrackModel::~TrackModel()
{
// delete m_rootItem;
}
@@ -230,6 +227,7 @@ QVariant
TrackModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
Q_UNUSED( orientation );
QStringList headers;
headers << tr( "Artist" ) << tr( "Title" ) << tr( "Album" ) << tr( "Track" ) << tr( "Duration" ) << tr( "Bitrate" ) << tr( "Age" ) << tr( "Year" ) << tr( "Size" ) << tr( "Origin" ) << tr( "Score" );
if ( role == Qt::DisplayRole && section >= 0 )
@@ -244,7 +242,6 @@ TrackModel::headerData( int section, Qt::Orientation orientation, int role ) con
void
TrackModel::setCurrentItem( const QModelIndex& index )
{
qDebug() << Q_FUNC_INFO;
TrackModelItem* oldEntry = itemFromIndex( m_currentIndex );
if ( oldEntry )
{
@@ -252,14 +249,16 @@ TrackModel::setCurrentItem( const QModelIndex& index )
}
TrackModelItem* entry = itemFromIndex( index );
if ( entry )
if ( index.isValid() && entry && !entry->query().isNull() )
{
m_currentIndex = index;
m_currentUuid = entry->query()->id();
entry->setIsPlaying( true );
}
else
{
m_currentIndex = QModelIndex();
m_currentUuid = QString();
}
}
@@ -322,16 +321,96 @@ TrackModel::mimeData( const QModelIndexList &indexes ) const
void
TrackModel::removeIndex( const QModelIndex& index, bool moreToCome )
TrackModel::clear()
{
if ( rowCount( QModelIndex() ) )
{
emit loadingFinished();
emit beginResetModel();
delete m_rootItem;
m_rootItem = 0;
m_rootItem = new TrackModelItem( 0, this );
emit endResetModel();
}
}
void
TrackModel::append( const Tomahawk::query_ptr& query )
{
insert( query, rowCount( QModelIndex() ) );
}
void
TrackModel::append( const QList< Tomahawk::query_ptr >& queries )
{
insert( queries, rowCount( QModelIndex() ) );
}
void
TrackModel::insert( const Tomahawk::query_ptr& query, int row )
{
if ( query.isNull() )
return;
QList< Tomahawk::query_ptr > ql;
ql << query;
insert( ql, row );
}
void
TrackModel::insert( const QList< Tomahawk::query_ptr >& queries, int row )
{
if ( !queries.count() )
return;
int c = row;
QPair< int, int > crows;
crows.first = c;
crows.second = c + queries.count() - 1;
emit beginInsertRows( QModelIndex(), crows.first, crows.second );
int i = 0;
TrackModelItem* plitem;
foreach( const query_ptr& query, queries )
{
plitem = new TrackModelItem( query, m_rootItem, row + i );
plitem->index = createIndex( row + i, 0, plitem );
i++;
if ( query->id() == currentItemUuid() )
setCurrentItem( plitem->index );
connect( plitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) );
}
emit endInsertRows();
emit trackCountChanged( rowCount( QModelIndex() ) );
}
void
TrackModel::remove( int row, bool moreToCome )
{
remove( index( row, 0, QModelIndex() ), moreToCome );
}
void
TrackModel::remove( const QModelIndex& index, bool moreToCome )
{
if ( QThread::currentThread() != thread() )
{
// qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO;
QMetaObject::invokeMethod( this, "removeIndex",
QMetaObject::invokeMethod( this, "remove",
Qt::QueuedConnection,
Q_ARG(const QModelIndex, index),
Q_ARG(bool, moreToCome)
);
Q_ARG(bool, moreToCome) );
return;
}
@@ -346,24 +425,49 @@ TrackModel::removeIndex( const QModelIndex& index, bool moreToCome )
emit endRemoveRows();
}
emit trackCountChanged( rowCount( QModelIndex() ) );
if ( !moreToCome )
emit trackCountChanged( rowCount( QModelIndex() ) );
}
void
TrackModel::removeIndexes( const QList<QModelIndex>& indexes )
TrackModel::remove( const QList<QModelIndex>& indexes )
{
foreach( const QModelIndex& idx, indexes )
QList<QPersistentModelIndex> pil;
foreach ( const QModelIndex& idx, indexes )
{
removeIndex( idx );
pil << idx;
}
remove( pil );
}
void
TrackModel::remove( const QList<QPersistentModelIndex>& indexes )
{
QList<QPersistentModelIndex> finalIndexes;
foreach ( const QPersistentModelIndex index, indexes )
{
if ( index.column() > 0 )
continue;
finalIndexes << index;
}
for ( int i = 0; i < finalIndexes.count(); i++ )
{
remove( finalIndexes.at( i ), i + 1 != finalIndexes.count() );
}
}
TrackModelItem*
TrackModel::itemFromIndex( const QModelIndex& index ) const
{
if ( index.isValid() )
{
return static_cast<TrackModelItem*>( index.internalPointer() );
}
else
{
return m_rootItem;
@@ -372,10 +476,10 @@ TrackModel::itemFromIndex( const QModelIndex& index ) const
void
TrackModel::onPlaybackFinished( const Tomahawk::result_ptr& result )
TrackModel::onPlaybackStarted( const Tomahawk::result_ptr& result )
{
TrackModelItem* oldEntry = itemFromIndex( m_currentIndex );
if ( oldEntry && !oldEntry->query().isNull() && oldEntry->query()->results().contains( result ) )
if ( oldEntry && ( oldEntry->query().isNull() || !oldEntry->query()->numResults() || oldEntry->query()->results().first().data() != result.data() ) )
{
oldEntry->setIsPlaying( false );
}

View File

@@ -23,6 +23,7 @@
#include "playlistinterface.h"
#include "trackmodelitem.h"
#include "typedefs.h"
#include "dllmacro.h"
@@ -84,16 +85,13 @@ public:
virtual Qt::ItemFlags flags( const QModelIndex& index ) const;
virtual QPersistentModelIndex currentItem() { return m_currentIndex; }
virtual Tomahawk::QID currentItemUuid() { return m_currentUuid; }
virtual Tomahawk::PlaylistInterface::RepeatMode repeatMode() const { return Tomahawk::PlaylistInterface::NoRepeat; }
virtual bool shuffled() const { return false; }
virtual void ensureResolved();
virtual void append( const Tomahawk::query_ptr& query ) = 0;
virtual void append( const Tomahawk::artist_ptr& artist ) = 0;
virtual void append( const Tomahawk::album_ptr& album ) = 0;
TrackModelItem* itemFromIndex( const QModelIndex& index ) const;
TrackModelItem* m_rootItem;
@@ -109,18 +107,32 @@ signals:
public slots:
virtual void setCurrentItem( const QModelIndex& index );
virtual void removeIndex( const QModelIndex& index, bool moreToCome = false );
virtual void removeIndexes( const QList<QModelIndex>& indexes );
virtual void clear();
virtual void append( const QList< Tomahawk::query_ptr >& queries );
virtual void append( const Tomahawk::query_ptr& query );
virtual void append( const Tomahawk::artist_ptr& artist ) { Q_UNUSED( artist ); }
virtual void append( const Tomahawk::album_ptr& album ) { Q_UNUSED( album ); }
virtual void insert( const QList< Tomahawk::query_ptr >& queries, int row = 0 );
virtual void insert( const Tomahawk::query_ptr& query, int row = 0 );
virtual void remove( int row, bool moreToCome = false );
virtual void remove( const QModelIndex& index, bool moreToCome = false );
virtual void remove( const QList<QModelIndex>& indexes );
virtual void remove( const QList<QPersistentModelIndex>& indexes );
virtual void setRepeatMode( Tomahawk::PlaylistInterface::RepeatMode /*mode*/ ) {}
virtual void setShuffled( bool /*shuffled*/ ) {}
private slots:
void onPlaybackFinished( const Tomahawk::result_ptr& result );
void onPlaybackStarted( const Tomahawk::result_ptr& result );
void onPlaybackStopped();
private:
QPersistentModelIndex m_currentIndex;
Tomahawk::QID m_currentUuid;
bool m_readOnly;
QString m_title;

Some files were not shown because too many files have changed in this diff Show More