1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-09-14 07:52:05 +02:00

Compare commits

...

167 Commits
vsxu ... 0.4.2

Author SHA1 Message Date
Christian Muehlhaeuser
a104e92471 * Merged ChangeLog. 2012-04-07 10:10:52 +02:00
Christian Muehlhaeuser
1929804541 * Merged audio backend fixes from master. 2012-04-07 10:10:10 +02:00
Christian Muehlhaeuser
a564b3b272 * Don't update seek slider too often, use less cpu. 2012-04-07 10:07:05 +02:00
Christian Muehlhaeuser
d083528ad5 * Updated ChangeLog. 2012-04-07 08:10:31 +02:00
Christian Muehlhaeuser
4d63a9462d * Updated translations. 2012-04-07 08:09:37 +02:00
Christian Muehlhaeuser
5a64886b7c * Bumped to 0.4.2 and updated ChangeLog. 2012-04-07 06:46:13 +02:00
Leo Franchi
76986e8908 Backport case-insensitivity fix from master 2012-04-06 22:07:38 -03:00
Christian Muehlhaeuser
9a9a7148af * Properly fix time display in stable. 2012-04-05 06:17:40 +02:00
Christian Muehlhaeuser
aec7a0b140 * Fixed crash in AudioControls. (merge) 2012-04-05 05:39:50 +02:00
Christian Muehlhaeuser
eff42af593 * Updated ChangeLog. 2012-04-04 02:44:18 +02:00
Christian Muehlhaeuser
ad2b54ad90 * Bump version to 0.4.1. 2012-04-04 01:55:00 +02:00
Christian Muehlhaeuser
4e316a48ea * Fixed non debug builds. 2012-04-04 01:54:14 +02:00
Christian Muehlhaeuser
87863ae7e2 * Fixed about dialog for non debug releases. 2012-04-04 01:54:05 +02:00
Jeff Mitchell
5e439b990f See if this fixes Chris' time-updating problem 2012-03-31 13:06:50 -04:00
Leo Franchi
5dcf426cdf TWK-770: Fix echonest sentence summary grammar 2012-03-31 10:03:53 -04:00
Leo Franchi
5b6c4560ae TWK-785: Rename playlist after revision is created so plitem is selectable 2012-03-31 09:53:47 -04:00
Leo Franchi
fec670f27c TWK-793: Let the sourcetreeview sort before we scroll otherwise we might scroll the wrong amount 2012-03-31 09:26:30 -04:00
Leo Franchi
fb13ccd285 Oops, fix signals/slot params 2012-03-31 09:26:07 -04:00
Leo Franchi
b3b70cea82 TWK-795: Filter dups from last.fm top artists 2012-03-31 09:05:14 -04:00
Leo Franchi
f84d08e011 Don't sort related artists in footnotes 2012-03-31 08:49:18 -04:00
Leo Franchi
9c5966000c TWK-799: Don't set autoupdate when loading playlist view 2012-03-30 17:47:28 -04:00
Leo Franchi
79bfdec895 TWK-798: Start playing first resolvable track when double-clicking on a playlist item 2012-03-30 17:14:08 -04:00
Leo Franchi
9f5215302c various updater fixes 2012-03-30 14:32:01 -04:00
Leo Franchi
73d7ba03f5 TWK-815: Try using foreground text color when drawing grey bg, white on grey is hard to read 2012-03-30 13:34:48 -04:00
Leo Franchi
d623bbefc2 Fix crash on exit 2012-03-30 13:27:36 -04:00
Leo Franchi
907dad95e1 TWK-721: Don't flicker from officialtracks to supercollection tracks.
If the second infosystem albumtracks request comes back empty but the first one
came back with tracks, this is not a failure and so keep the official tracks
2012-03-30 12:09:59 -04:00
Leo Franchi
565217a53a TWK-725: Some extra pointer safety 2012-03-30 10:42:11 -04:00
Christian Muehlhaeuser
bee6485475 * Fixed crash situation in AudioControls.
(cherry picked from commit a2bfd73d55)

Conflicts:

	src/audiocontrols.cpp
2012-03-30 10:31:07 -04:00
Christian Muehlhaeuser
ace18dfa1f * Prevent race condition.
(cherry picked from commit ebbedb2b99)
2012-03-30 10:30:29 -04:00
Christian Muehlhaeuser
95f1162b6e * Fixed cached resolving.
(cherry picked from commit bd098e3ff2)
2012-03-30 10:30:09 -04:00
Christian Muehlhaeuser
e5bdd2242f * Fixed race condition during resolving.
(cherry picked from commit b70114a225)
2012-03-30 10:30:01 -04:00
Christian Muehlhaeuser
deb0eb819c * Fetch square covers from Last.fm.
(cherry picked from commit d5aed7b6df)
2012-03-30 10:29:48 -04:00
Jeff Mitchell
04864c7d79 Set scanning threads idle priority
(cherry picked from commit 5a57e285ec)
2012-03-30 10:28:04 -04:00
Jeff Mitchell
43c4daa4e3 Change wording 2012-03-29 10:41:16 -04:00
Jeff Mitchell
fc95cee6ce Dynamically enable/disable the web API, and change the wording for the option to make it more enticing 2012-03-29 10:41:14 -04:00
Dominik Schmidt
fff8fbfe80 win: fix qca2 usage/grooveshark 2012-03-28 17:23:27 +02:00
Leo Franchi
eec8b76de0 fix xspf auto-updating
(cherry picked from commit c54ca78769)
2012-03-27 11:27:07 -04:00
Leo Franchi
68f03dbd13 autoupdate fixes: init on creation, and every 10 not 100 mins
(cherry picked from commit ff04ab3b92)
2012-03-27 11:27:03 -04:00
Leo Franchi
753e1b3c90 aFix crash in dragging artist, don't assume AlbumModel only contains albums as it's hacked to also contain artists.
(cherry picked from commit 68d541d2e8)
2012-03-20 11:29:30 -04:00
Leo Franchi
e828dadec8 All QSharedPointers used in different threads need deleteLater as custom deleter. This is some of them.
(cherry picked from commit e3f5605c4e)
2012-03-19 22:24:41 -04:00
Leo Franchi
6e929986cb A bit of extra safety
(cherry picked from commit 2a58d53145)
2012-03-19 22:22:28 -04:00
mack-t
e1b086e3a4 Add albumpos, discnumber and score information from tomahawk resolver
(cherry picked from commit 21eeab61d8)
2012-03-19 22:20:32 -04:00
Leo Franchi
27147a0140 Guard against null sharedptr that shouldn't be null anyway -.- 2012-03-19 22:20:07 -04:00
Christian Muehlhaeuser
66836ae0bd * Something is wonky about CLucene scoring. Rely on our internal scoring. 2012-03-15 09:05:56 +01:00
Christian Muehlhaeuser
6342e6a9f8 * Fixed TWK-758: Sorting playlist by album position. 2012-03-15 09:05:47 +01:00
Jeff Mitchell
042ccbe730 Fix merge issue 2012-03-14 16:31:20 -04:00
Jeff Mitchell
6c6411561b Bump dependencies 2012-03-14 16:20:24 -04:00
Jeff Mitchell
18e3669911 Fix XMPP proxy. Make no proxy hosts static so changing it works across
all threads; make a duplication method so jreen doesn't scopepointer us
to death on shutdown; pass in proxyfactory to jreen.

Conflicts:

	src/settingsdialog.cpp
	src/sip/jabber/jabber.cpp
2012-03-14 16:19:32 -04:00
Dominik Schmidt
240517b0cd Bump the required QTweetLib version 2012-03-11 18:46:00 +01:00
Jeff Mitchell
fab36b2262 Use new QTweetLib 0.5 API for PIN values so leading zeros are respected
Conflicts:

	src/accounts/twitter/tomahawkoauthtwitter.h
2012-03-11 18:44:43 +01:00
Christian Muehlhaeuser
dd74ce04e6 Moved QuaZip from "shipped" to "recommended". 2012-03-07 17:51:20 +01:00
Christian Muehlhaeuser
e3a43e8ca6 * Bumped version to 0.4.0. 2012-03-07 05:07:47 +01:00
Christian Muehlhaeuser
c401ea0d91 * Update ChangeLog. 2012-03-07 05:07:23 +01:00
Christian Muehlhaeuser
2d03b8aea0 * Didn't mean to commit that. 2012-03-07 04:49:26 +01:00
Christian Muehlhaeuser
178aed9174 * Fixed TWK-474: proper fulltext search. 2012-03-07 04:47:24 +01:00
Christian Muehlhaeuser
6552deca61 * Fixed Source method being run in the wrong thread. 2012-03-07 04:45:32 +01:00
Leo Franchi
89009c09be Add a small guard to Artist::get and Album::get as they are called all over, and might be called after destruction of DB on shutdown 2012-03-06 18:47:55 -05:00
Leo Franchi
34672c7bed TWK-722: Fix quitting right after startup by ensuring chart data loaders are deleted before database 2012-03-06 18:46:48 -05:00
Leo Franchi
3caeb4642a Attempt at fixing TWK-723. 2012-03-06 18:23:17 -05:00
Christian Muehlhaeuser
80b0eacfed * Revert macdeploy back to use Qt 4.7.4. 2012-03-06 00:42:12 +01:00
Leo Franchi
aa078e86e5 Merge pull request #77 from anselmolsm/master
Play/Pause when system tray receives a mouse middle click
2012-03-05 12:20:13 -08:00
Anselmo L. S. Melo
9daf0a6089 Play/Pause when system tray receives a mouse middle click 2012-03-05 15:00:28 -03:00
Jason Herskowitz
6dead5e7e1 Fix all Super Collection text strings to SuperCollection 2012-03-04 22:46:10 -05:00
Jason Herskowitz
335868ea95 Merge branch 'master' of github.com:tomahawk-player/tomahawk 2012-03-04 22:28:56 -05:00
Jason Herskowitz
fc45646205 Update SuperCollection header text 2012-03-04 22:28:35 -05:00
Leo Franchi
dbd466d3ed Changelog++ 2012-03-04 22:21:04 -05:00
Leo Franchi
292d8c9530 Don't update playlist if no tracks have changed 2012-03-04 21:54:55 -05:00
Leo Franchi
6b68598d1d Show job status for indexing 2012-03-04 21:54:55 -05:00
Dominik Schmidt
0d9e248dac Fix lastfm export macro after my last commit. Why did it work before that?! 2012-03-05 01:09:31 +01:00
Leo Franchi
46a86fe6a5 Install vlc plugins to Frameworks/vlc/plugins instead of plugins/ 2012-03-04 16:55:57 -05:00
Dominik Schmidt
a56ca4ddfc Improve liblastfm2 export macro 2012-03-04 21:14:59 +01:00
Leo Franchi
6483c55749 Revert "Disable breakpad for an osx nightly"
This reverts commit 5a56a3e060.
2012-03-04 13:21:41 -05:00
Leo Franchi
5a56a3e060 Disable breakpad for an osx nightly 2012-03-04 12:55:05 -05:00
Dominik Schmidt
585f32c4d8 osx: fix undefined reference to bringToFront() with visibility=hidden 2012-03-04 18:44:23 +01:00
Jason Herskowitz
9ea311526c Make audio error icon match set 2012-03-03 18:45:07 -05:00
Leo Franchi
1342fdb9a7 Revert "disable break for a sec"
This reverts commit 2cc4fccd66.
2012-03-03 11:40:16 -05:00
Leo Franchi
2cc4fccd66 disable break for a sec 2012-03-03 11:39:49 -05:00
Leo Franchi
a2d0899285 Add grooveshark track parsing, and add a job notification for shortened url unwrapping 2012-03-02 22:41:44 -05:00
Dominik Schmidt
06cc52744d Enforce proper symbol exporting on all platforms 2012-03-03 01:14:21 +01:00
Leo Franchi
df758aa3f8 TWK-710: Don't expand items when clicking on headphones or lock icon 2012-03-02 00:15:16 -05:00
Leo Franchi
f79ed86b57 TWK-711: Some attica resolver icon fixes and optimizations. Don't write too much to disk 2012-03-01 23:33:27 -05:00
Leo Franchi
c9b0c92450 Bump up time for SetFile to finish 2012-03-01 23:25:49 -05:00
Leo Franchi
57d1c29d35 TWK-533: Show instructive error message if stations run out of tracks 2012-03-01 23:05:09 -05:00
Christian Muehlhaeuser
cdf7c11b29 * Fixed TWK-703: Auto expand sidebar's groups per default. 2012-03-02 04:42:32 +01:00
Leo Franchi
be7b5babe9 Show filter errors when loading a dynamic playlist for the first time too 2012-03-01 22:34:55 -05:00
Leo Franchi
f7f2c51d4e TWK-712: Don't emit track number changed when they really haven't, thus masking other errors 2012-03-01 22:11:02 -05:00
Leo Franchi
4889ed6a33 TWK-633: Don't use useless-to-us QUrl.addQueryItem, does not encode '+' 2012-03-01 21:53:13 -05:00
Leo Franchi
3557686681 Include in the right place 2012-03-01 21:53:11 -05:00
Christian Muehlhaeuser
58dfc54e00 * Fixed TWK-708: Resolve album page top-to-bottom. 2012-03-02 03:32:30 +01:00
Christian Muehlhaeuser
bddbc7a194 * Fixed TWK-642: Update Footnotes when selecting an album in AlbumView. 2012-03-02 02:45:56 +01:00
Christian Muehlhaeuser
33e0116e4b * Fixed TWK-700: Add description for SuperCollection. 2012-03-02 02:28:27 +01:00
Christian Muehlhaeuser
b07f498516 * Fixed TWK-699: Don't paint now playing icon over item decoration. 2012-03-02 02:21:08 +01:00
Christian Muehlhaeuser
c77be81e5b * Fixed TWK-641: don't write empty album covers in InfoSystem. 2012-03-02 01:55:28 +01:00
Christian Muehlhaeuser
11cf2c78d8 * Fixed ArtistPlaylistInterface. 2012-03-02 01:30:31 +01:00
Leo Franchi
77bcc7c7aa TWK-709: Fetch albums from infosystem if no tracks are found in the DB when dropping an album 2012-03-01 18:14:45 -05:00
Leo Franchi
5b97802104 Revert "Add debug for jason". Debug did its job :)
This reverts commit 25ec5cb432.
2012-03-01 09:20:41 -05:00
Leo Franchi
25ec5cb432 Add debug for jason 2012-03-01 09:12:27 -05:00
Leo Franchi
8b4b4a8f0c Add documentation 2012-03-01 00:19:59 -05:00
Leo Franchi
6448cebec6 Mostly fix TWK-701. A few bugs lined up here.
1) Tracks fetched from an album or artist in DropJob would only fetch from the db
   and never resolve w/ resolvers
2) artist* and album* objects were getting deleted too early if they didn't already exist before
   DropJob asked for them (results from db thread are queued, sharedpointer deletion happened first).
   Keep ahold of them until we're done with them.

For albums we don't know about, we still need to look up metadata track listings when nothing is found in the DB.
2012-03-01 00:14:16 -05:00
Leo Franchi
191ee259d4 TWK-671: Fix filter text being 'behind' by one step 2012-02-29 22:28:54 -05:00
Leo Franchi
dc364726c0 TWK-706: Don't auto-enable resolvers when upgrading them. 2012-02-29 18:46:58 -05:00
Hugo Lindström
6ac0f68224 Silly misstake in naming 2012-03-01 00:38:32 +01:00
Leo Franchi
3c3078f9f8 Clean up from previous test 2012-02-29 17:45:20 -05:00
Leo Franchi
b994befc58 osx /volumes tweak 2012-02-29 17:12:59 -05:00
Hugo Lindström
56cf0239da Fix soundcloudwall and pump version 2012-02-29 08:06:56 +01:00
Christian Muehlhaeuser
69d75362b3 * This should speed up resolving... a lot. 2012-02-29 02:36:33 +01:00
Leo Franchi
8db9c79e57 Add some SetFile/GetFileInfo debug 2012-02-28 18:54:22 -05:00
Hugo Lindström
e9ee617d94 Better name for soundcloudwall 2012-02-28 21:36:53 +01:00
Leo Franchi
c9c6534af5 Wait a bit longer for portfwd thread to exit to avoid crashes on exit 2012-02-28 10:37:37 -05:00
Leo Franchi
1b9767995b Make stations item drop enabled. How did this ever work!? 2012-02-28 10:37:37 -05:00
Christian Muehlhaeuser
1c561ba7a9 * Prepare macdeploy for Qt 4.8. 2012-02-28 15:49:21 +01:00
Leo Franchi
52baebc899 Delete pipeline after database, as some dbcmds create queries which access Pipeline 2012-02-26 17:35:14 -05:00
Leo Franchi
63c029554a Set default type of echonest control so an empty control is valid (and ignored) 2012-02-26 17:34:11 -05:00
Leo Franchi
3900dd27d2 TWK-686: Implement playlistinterface and jump to current track 2012-02-26 13:06:10 -05:00
Leo Franchi
e9fb17dadb TWK-604: Update Show/Hide menu entry if Tomahawk is hidden with Cmd-H 2012-02-26 12:44:19 -05:00
Leo Franchi
56c2f86381 Use SetFile/GetFileInfo from bundle 2012-02-26 10:57:00 -05:00
Leo Franchi
a8440c72e4 Fix build for gcc on mac. Hudson should use clang :D 2012-02-25 23:52:34 -05:00
Leo Franchi
cbb5bac075 TWK-624: When resolving dups in dropjob, prefer playable ones 2012-02-25 23:20:31 -05:00
Leo Franchi
b7f1f56f77 TWK-633: Don't hand-make URLs, that is just asking for trouble 2012-02-25 21:53:08 -05:00
Leo Franchi
9fe17cf6f5 Upgrade SPMediaKeyTap 2012-02-25 21:25:57 -05:00
Leo Franchi
d641f06e6d cleanups 2012-02-25 20:54:54 -05:00
Leo Franchi
7668f04c0c Cache height as sizehint is called plenty often 2012-02-25 20:13:12 -05:00
Leo Franchi
a73d1cd342 Don't calculate height by assuming rows are same height anymore, as they are not. 2012-02-25 20:07:33 -05:00
Leo Franchi
35c4a29cbe center icon in delegate for multi-line jobs 2012-02-25 20:03:01 -05:00
Leo Franchi
f6e00fbb86 Show error message if phonon spits an error, but don't block with modal dialog/audio stop 2012-02-25 20:02:38 -05:00
Leo Franchi
3f9503364c Return null qstring 2012-02-25 19:48:00 -05:00
Leo Franchi
36b2a585f9 Show errors when fetching information from the network. Fixes TWK-515 2012-02-25 19:39:35 -05:00
Leo Franchi
42220a8c95 Delete InfoSystem after pipeline as various constructors connect to InfoSystem. Fixes crash on exit. 2012-02-25 16:24:57 -05:00
Christian Muehlhaeuser
1ac4efa88a * Prepare for 0.4.0. 2012-02-25 10:18:00 +01:00
Christian Muehlhaeuser
cfe42d10ea * Minor cleanups. 2012-02-24 06:29:32 +01:00
Christian Muehlhaeuser
6ad9e820e3 * Only show top 50 loved tracks for the SuperCollection. 2012-02-24 05:52:02 +01:00
Christian Muehlhaeuser
47a403a9c8 * Update social actions everytime you open the contextual menu of a track. 2012-02-24 05:40:00 +01:00
Christian Muehlhaeuser
b2fc0935a4 * Show now playing speaker next to current playlist again. 2012-02-24 05:18:55 +01:00
Christian Muehlhaeuser
a04d384ac4 * Show default album, not artist cover on AlbumInfoWidget. 2012-02-24 04:15:28 +01:00
Christian Muehlhaeuser
325bb0bf8f * Added global common image cache. 2012-02-24 04:13:37 +01:00
Christian Muehlhaeuser
c2a79d4cbf * Don't wordwrap track names in sidebar. 2012-02-23 21:06:17 +01:00
Christian Muehlhaeuser
b17b8356ad * Fixed a few issues with controlling playback via CLI args. 2012-02-23 20:34:23 +01:00
Christian Muehlhaeuser
a41b174851 Merge pull request #76 from ubertaco/master
Initial support for TWK-595
2012-02-23 11:23:43 -08:00
ubertaco
458d32ed7c removed duplicate --stop 2012-02-23 14:05:44 -05:00
ubertaco
a0383b6664 fixed helpstrings and inconsistent spacing 2012-02-23 14:04:24 -05:00
ubertaco
9b1fc4cd24 added helpstrings 2012-02-23 13:52:11 -05:00
ubertaco
2a27ef88f6 Added initial support for TWK-595 ( command-line audio controls ) 2012-02-23 13:48:51 -05:00
Christian Muehlhaeuser
d1ecf6d748 * More work on sidebar reorganization. 2012-02-23 06:22:47 +01:00
Christian Muehlhaeuser
396b5cd6e0 * Modify Tomahawk's config file to be only accessible by the owner. 2012-02-23 04:36:30 +01:00
Christian Muehlhaeuser
55e7d6383b * Unbreak headless more. 2012-02-23 04:08:14 +01:00
Christian Muehlhaeuser
dbcbffb6c4 * Auto load covers after expanding an artist. 2012-02-23 04:05:06 +01:00
Christian Muehlhaeuser
6b51872c3e * Unbreak headless. 2012-02-23 03:30:22 +01:00
Christian Muehlhaeuser
79d8b081d7 * Speed up cover loading in TreeView. 2012-02-23 03:21:21 +01:00
Christian Muehlhaeuser
165276912f * Fixed Windows compiling. 2012-02-22 02:42:42 +01:00
Christian Muehlhaeuser
413052bf8e * Destroy Pipeline properly and deactivate it earlier during shutdown. 2012-02-22 02:09:24 +01:00
Leo Franchi
46fd72920c Remove duplicate signal/slot collection. All SourceTreeItem* signals are already hooked up for each item 2012-02-19 15:36:48 -05:00
Christian Muehlhaeuser
399b835436 * SourcePlaylistInterface shouldn't use temporary queries. 2012-02-19 11:29:03 +01:00
Alejandro Wainzinger
9f76cdf486 Fix typo. 2012-02-18 00:49:08 -08:00
Alejandro Wainzinger
881bf5dd9d Check for OS X 10.7 as well as 10.6 for delegate protocol. 2012-02-18 00:47:23 -08:00
Leo Franchi
8bb9661960 Add an index on the file mtime. For future users for now 2012-02-17 19:08:59 -05:00
Leo Franchi
a1c69b0b43 rework some startup order to load servent before UI components 2012-02-17 18:37:59 -05:00
Dominik Schmidt
5083849514 Merge branch 'master' of git://github.com/tomahawk-player/tomahawk 2012-02-14 05:28:57 +01:00
Dominik Schmidt
8d75ba4d64 Add console output on windows 2012-02-14 05:28:35 +01:00
Leo Franchi
b54b7f6455 Up webapi timeouts so resolving can finish 2012-02-12 17:15:02 -05:00
Dominik Schmidt
488eb387cb Use my new pvlc build in update-vlc.sh 2012-02-12 20:49:55 +01:00
Dominik Schmidt
99143f6148 Fix windows for qt 4.8 2012-02-12 20:23:20 +01:00
Dominik Schmidt
1d4320afea Bump year ;-) 2012-02-11 03:55:23 +01:00
Dominik Schmidt
d9c3162146 Remove now unneccesary workaround in mingw toolchain file 2012-02-10 19:36:10 +01:00
Dominik Schmidt
4a1b021753 Fix FindTaglib.cmake for windows 2012-02-10 19:33:48 +01:00
Leo Franchi
3418d5295b Merge pull request #75 from crabmanX/master
suggest playlist filename on export
2012-02-10 06:35:51 -08:00
Kilian Lackhove
bf30dc37a2 suggest playlist filename on export 2012-02-10 15:30:07 +01:00
Dominik Schmidt
6ed4902e00 Check for QtUiTools 2012-02-06 14:56:32 +01:00
181 changed files with 15472 additions and 7767 deletions

View File

@@ -15,11 +15,13 @@ SET( TOMAHAWK_APPLICATION_NAME "Tomahawk" )
SET( TOMAHAWK_DESCRIPTION_SUMMARY "The social media player" )
SET( TOMAHAWK_VERSION_MAJOR 0 )
SET( TOMAHAWK_VERSION_MINOR 3 )
SET( TOMAHAWK_VERSION_PATCH 99 )
SET( TOMAHAWK_VERSION_MINOR 4 )
SET( TOMAHAWK_VERSION_PATCH 2 )
#SET( TOMAHAWK_VERSION_RC 0 )
# enforce proper symbol exporting on all platforms
add_definitions( "-fvisibility=hidden" )
# build options
option(BUILD_GUI "Build Tomahawk with GUI" ON)
@@ -72,7 +74,7 @@ IF( NOT BUILD_GUI )
MESSAGE( STATUS "Building Tomahawk ${TOMAHAWK_VERSION} in HEADLESS mode ***" )
ELSE()
MESSAGE( STATUS "Building Tomahawk ${TOMAHAWK_VERSION} full GUI version ***" )
LIST(APPEND NEEDED_QT4_COMPONENTS "QtGui" "QtWebkit" )
LIST(APPEND NEEDED_QT4_COMPONENTS "QtGui" "QtWebkit" "QtUiTools" )
ENDIF()
IF( BUILD_GUI AND UNIX AND NOT APPLE )
@@ -80,7 +82,7 @@ IF( BUILD_GUI AND UNIX AND NOT APPLE )
ENDIF()
macro_optional_find_package(Qt4 4.7.0 COMPONENTS ${NEEDED_QT4_COMPONENTS} )
macro_log_feature(QT4_FOUND "Qt" "A cross-platform application and UI framework" "http://qt.nokia.com" TRUE "" "If you see this, although libqt4-devel is installed, check whether \n the qtwebkit-devel package is installed as well")
macro_log_feature(QT4_FOUND "Qt" "A cross-platform application and UI framework" "http://qt.nokia.com" TRUE "" "If you see this, although libqt4-devel is installed, check whether the \n qtwebkit-devel package and whatever contains QtUiTools is installed too")
macro_optional_find_package(Phonon 4.5.0)
macro_log_feature(PHONON_FOUND "Phonon" "The Phonon multimedia library" "http://phonon.kde.org" TRUE "" "")

View File

@@ -56,7 +56,7 @@ ELSE()
include(FindLibraryWithDebug)
include(FindPackageHandleStandardArgs)
find_path(TAGLIB_CFLAGS
find_path(TAGLIB_INCLUDES
NAMES
tag.h
PATH_SUFFIXES taglib

View File

@@ -28,8 +28,8 @@
; We use official release plugins
; mingw32-vlc from obs misses a lot and has even broken ones probably
!define VLC_PATH "${SOURCE_PATH}\admin\win\vlc\prefix" ; SIC! ^
!define VLC_BIN "${VLC_PATH}\bin"
!define VLC_PATH "${SOURCE_PATH}\admin\win\vlc\" ; SIC! ^
!define VLC_BIN "${VLC_PATH}"
!define VLC_PLUGIN_PATH "${VLC_BIN}\plugins"
!define NSI_PATH "${SOURCE_PATH}/admin/win/nsi"
@@ -279,7 +279,6 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
File "${INSTALL_PATH}\bin\libqxtweb-standalone.dll"
File "${INSTALL_PATH}\bin\libtomahawk_portfwd.dll"
File "${INSTALL_PATH}\bin\libtomahawk_lastfm2.dll"
File "${INSTALL_PATH}\bin\libquazip.dll"
File "${INSTALL_PATH}\bin\libtomahawklib.dll"
File "${INSTALL_PATH}\lib\libtomahawk_sip*.dll"
!endif
@@ -294,7 +293,6 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
File "${BUILD_PATH}\libqxtweb-standalone.dll"
File "${BUILD_PATH}\libtomahawk_portfwd.dll"
File "${BUILD_PATH}\libtomahawk_lastfm2.dll"
File "${BUILD_PATH}\libquazip.dll"
File "${BUILD_PATH}\libtomahawk_sip*.dll"
!endif
@@ -307,8 +305,10 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
File "${QT_DLL_PATH}\QtGui4.dll"
File "${QT_DLL_PATH}\QtNetwork4.dll"
File "${QT_DLL_PATH}\QtSql4.dll"
File "${QT_DLL_PATH}\QtXml4.dll"
File "${QT_DLL_PATH}\QtScript4.dll"
File "${QT_DLL_PATH}\QtUiTools4.dll"
File "${QT_DLL_PATH}\QtWebKit4.dll"
File "${QT_DLL_PATH}\QtXml4.dll"
;SQLite driver
SetOutPath "$INSTDIR\sqldrivers"
@@ -329,13 +329,12 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
File "${MING_BIN}\libstdc++-6.dll"
;Phonon stuff
File "${VLC_BIN}\libphonon.dll"
File "${MING_BIN}\libphonon.dll"
SetOutPath "$INSTDIR\phonon_backend"
File "${VLC_BIN}\phonon_backend\phonon_vlc.dll"
SetOutPath "$INSTDIR"
;VLC
;SetOutPath "$INSTDIR\phonon_backend"
File "${VLC_BIN}\libvlc.dll"
File "${VLC_BIN}\libvlccore.dll"
SetOutPath "$INSTDIR\plugins"
@@ -351,6 +350,7 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER
File "${MING_BIN}\libechonest.dll"
File "${MING_BIN}\libQTweetLib.dll"
File "${MING_BIN}\libquazip.dll"
; Jabber
File "${MING_BIN}\libjreen.dll"

View File

@@ -1,3 +1,56 @@
Version 0.4.2:
* Updated translations for various languages.
* Resuming playback restores correct volume settings.
* Reduced CPU usage during playback.
* Fixed not starting up due to case sensitivity issue on OS X.
* Fixed volume issue (too quiet) on Windows.
Version 0.4.1:
* Fixed various crashes.
* Fixed issues with auto-updating XSPF playlists.
* Double-clicking a playlist starts playing it.
* Resolvers can now return disc number and album position for results.
* Fixed sorting playlists by track number.
* Fixed issues with changing proxy.
* Fixed Twitter authentication issues.
* Fixed Grooveshark support on Windows.
Version 0.4.0:
* Added visual notification for database indexing job.
* Fixed icons not appearing in resolvers list.
* Fixed various UI glitches and stray error messages in stations.
* Fixed bug where album page would resolve bottom-to-top.
* Fixed bug where Footnotes would not update when changing selected album in Album View.
* Fixed dragging albums and artists from charts, album, and artist views.
* Fixed bug where filter text would be one step behind filter value.
* Fixed bug where resolvers would enable themselves after auto-updating.
* Fixed occasional crash when dropping tracks onto New Station item.
* Added jump-to-current-track support for search results page.
* Fixed non-resolving tracks when dragging from album view.
* Fixed fetching album covers for albums with special characters.
* Show errors and continue gracefully when resolved audio is not available.
* Fixed various crashes on exit.
* Added basic command-line options for playback control.
* Bumped up web api timeouts to allow web clients to finish resolving.
* Added filename suggestion when exporting a playlist.
* Cleaned up highlighting of artist names in album view.
* Cleaned up alignment of playlist items.
* Fixed potential crash when searching.
* Added support for disc number.
* Added SoundCloudWall.com charts.
* Added ability to "lock on" to a user when listening along, to skip along.
* Fixed bug where loved tracks would be refreshed much too often.
* Fixed some font size issues.
* Sped up Tomahawk startup by moving chart loading into a separate thread.
* Added support for parsing Grooveshark and Tinysong tracks and playlists.
* Reorganized sidebar to follow more logical item groupings.
* Added artist and album results to global searches.
* Fixed style and contrast issues when using GTK styles.
* Fixed paths to artwork when using MPRIS2 interface.
* Fixed out of sync Show/Hide menu items on OS X when hidden with cmd-h.
* Fixed /Volumes directory not showing up on OS X.
* Fixed startup crash on OS X.
Version 0.3.3:
* Automatically load Super Collection tracks when no official release
information is available.

6
README
View File

@@ -39,13 +39,13 @@ Dependencies
The following dependencies are optional, but recommended:
Attica 0.2.0 - ftp://ftp.kde.org/pub/kde/stable/attica/
Jreen 1.0.1 - https://github.com/euroelessar/jreen
QTweetLib 0.3.0 - https://github.com/minimoog/QTweetLib
QuaZip 0.4.3 - http://quazip.sourceforge.net/
Jreen 1.0.3 - https://github.com/euroelessar/jreen
QTweetLib 0.5.0 - https://github.com/minimoog/QTweetLib
Third party libraries that we ship with our source:
MiniUPnP 1.6 - http://miniupnp.free.fr/
liblastfm 0.4.0 - http://github.com/jonocole/liblastfm/
QuaZip 0.4.3 - http://quazip.sourceforge.net/
Enjoy!

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 = []
@@ -491,7 +491,7 @@ def FindVLCPlugin(name):
FixBinary(binary)
for plugin in VLC_PLUGINS:
FixVLCPlugin(FindVLCPlugin(plugin), '.')
FixVLCPlugin(FindVLCPlugin(plugin), '../Frameworks/vlc/plugins')
for plugin in TOMAHAWK_PLUGINS:
FixPlugin(plugin, '../MacOS')

View File

@@ -3,9 +3,12 @@ SET(MINGW_PREFIX "i686-w64-mingw32")
# this one is important
SET(CMAKE_SYSTEM_NAME Windows)
# specify the cross compiler
SET(CMAKE_C_COMPILER ccache ${MINGW_PREFIX}-gcc)
SET(CMAKE_C_FLAGS "-fno-keep-inline-dllexport")
SET(CMAKE_CXX_COMPILER ccache ${MINGW_PREFIX}-g++)
SET(CMAKE_CXX_FLAGS ${CMAKE_C_FLAGS})
SET(CMAKE_RC_COMPILER /usr/bin/${MINGW_PREFIX}-windres)
# where is the target environment containing libraries
@@ -15,13 +18,6 @@ SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# libs with broken find modules
SET(TAGLIB_FOUND true)
SET(TAGLIB_LIBRARIES ${CMAKE_FIND_ROOT_PATH}/lib/libtag.dll.a)
SET(TAGLIB_INCLUDES ${CMAKE_FIND_ROOT_PATH}/include/taglib)
# configure qt variables
SET(QT_LIBRARY_DIR /usr/${MINGW_PREFIX}/bin)
SET(QT_PLUGINS_DIR ${CMAKE_FIND_ROOT_PATH}/lib/qt4/plugins/)
SET(QT_QTUITOOLS_LIBRARY_RELEASE ${CMAKE_FIND_ROOT_PATH}/lib/libQtUiTools.a)
SET(QT_QTUITOOLS_LIBRARY_DEBUG ${CMAKE_FIND_ROOT_PATH}/lib/libQtUiToolsd.a)
SET(QT_QTUITOOLS_LIBRARY ${QT_QTUITOOLS_LIBRARY_RELEASE})

View File

@@ -1,51 +1,52 @@
#!/bin/bash
mkdir -p vlc/
if [ "$1" = "-c" ] ; then
echo "Continuing last download.."
rm -rvf vlc/prefix/
rm -rvf vlc/
else
echo "Remove old vlc dir..."
rm -rvf vlc/*
echo "Update archive..."
fi
cd vlc/
rm -rvf vlc/
echo "Download phonon archive..."
#wget -c "http://downloads.sourceforge.net/project/vlc/1.1.9/win32/vlc-1.1.9-win32.7z?r=http%3A%2F%2Fwww.videolan.org%2Fvlc%2Fdownload-windows.html&ts=1306272584&use_mirror=leaseweb"
#wget -c "http://download.tomahawk-player.org/tomahawk-vlc-0.1.zip"
#wget -c http://people.videolan.org/~jb/phonon/phonon-vlc-last.7z
wget -c http://people.videolan.org/~jb/phonon/phonon_phonon-vlc_20111128.7z
# wget -c "http://downloads.sourceforge.net/project/vlc/1.1.9/win32/vlc-1.1.9-win32.7z?r=http%3A%2F%2Fwww.videolan.org%2Fvlc%2Fdownload-windows.html&ts=1306272584&use_mirror=leaseweb"
# wget -c "http://download.tomahawk-player.org/tomahawk-vlc-0.1.zip"
# wget -c http://people.videolan.org/~jb/phonon/phonon-vlc-last.7z
# wget -c http://people.videolan.org/~jb/phonon/phonon_phonon-vlc_20111128.7z
wget -c http://download.tomahawk-player.org/test/pvlc.tar.bz2
echo "Extract binary..."
7z x phonon*.7z
#mv -v vlc-*/ vlc/
#unzip tomahawk-vlc-0.1.zip
# 7z x phonon*.7z
# mv -v vlc-*/ vlc/
# unzip tomahawk-vlc-0.1.zip
tar xvjf pvlc.tar.bz2
echo "Download phonon_vlc_no_video.dll..."
wget -c http://people.videolan.org/~jb/phonon/phonon_vlc_no_video.dll
cp -v phonon_vlc_no_video.dll prefix/bin/phonon_backend/phonon_vlc.dll
# echo "Download phonon_vlc_no_video.dll..."
# wget -c http://people.videolan.org/~jb/phonon/phonon_vlc_no_video.dll
# cp -v phonon_vlc_no_video.dll prefix/bin/phonon_backend/phonon_vlc.dll
echo "Strip unneeded plugins from vlc/plugins..."
cd prefix/bin/plugins
rm -rvf libold* libvcd* libdvd* liblibass* libx264* libschroe* liblibmpeg2* \
libstream_out_* libmjpeg_plugin* libh264_plugin* libzvbi_plugin* lib*sub* \
*qt4* *skins2* libaccess_bd_plugin.dll \
libaudiobargraph_* libball_plugin.dll \
libdirac_plugin.dll \
libgnutls_plugin.dll \
libcaca_plugin.dll \
libfreetype_plugin.dll \
libaccess_output_shout_plugin.dll \
libremoteosd_plugin.dll \
libsdl_image_plugin.dll \
libvout_sdl_plugin.dll \
libpng_plugin.dll \
libgoom_plugin.dll \
libatmo_plugin.dll \
libmux_ts_plugin.dll \
libkate_plugin.dll \
libtaglib_plugin.dll
# echo "Strip unneeded plugins from vlc/plugins..."
# cd prefix/bin/plugins
# rm -rvf libold* libvcd* libdvd* liblibass* libx264* libschroe* liblibmpeg2* \
# libstream_out_* libmjpeg_plugin* libh264_plugin* libzvbi_plugin* lib*sub* \
# *qt4* *skins2* libaccess_bd_plugin.dll \
# libaudiobargraph_* libball_plugin.dll \
# libdirac_plugin.dll \
# libgnutls_plugin.dll \
# libcaca_plugin.dll \
# libfreetype_plugin.dll \
# libaccess_output_shout_plugin.dll \
# libremoteosd_plugin.dll \
# libsdl_image_plugin.dll \
# libvout_sdl_plugin.dll \
# libpng_plugin.dll \
# libgoom_plugin.dll \
# libatmo_plugin.dll \
# libmux_ts_plugin.dll \
# libkate_plugin.dll \
# libtaglib_plugin.dll
# this is for vlc-1.2

BIN
data/images/grooveshark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

3541
lang/tomahawk_bg.ts Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,8 @@
<file>tomahawk_de.qm</file>
<file>tomahawk_sv.qm</file>
<file>tomahawk_es.qm</file>
<file>tomahawk_bg.qm</file>
<file>tomahawk_pl.qm</file>
<file>tomahawk_pt_BR.qm</file>
</qresource>
</RCC>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -133,6 +133,8 @@
<file>data/images/headphones-bigger.png</file>
<file>data/images/no-album-no-case.png</file>
<file>data/images/rdio.png</file>
<file>data/images/grooveshark.png</file>
<file>data/sql/dbmigrate-27_to_28.sql</file>
<file>data/images/process-stop.png</file>
</qresource>
</RCC>

View File

@@ -58,4 +58,7 @@ if (APPLE)
FILE(COPY ${CMAKE_SOURCE_DIR}/admin/mac/sparkle_pub.pem
DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/Resources")
FILE(COPY /usr/bin/SetFile DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/MacOS")
FILE(COPY /usr/bin/GetFileInfo DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/MacOS")
endif (APPLE)

View File

@@ -92,16 +92,12 @@ AudioControls::AudioControls( QWidget* parent )
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 );
ui->seekSlider->setTimeLine( &m_sliderTimeLine );
ui->volumeSlider->setRange( 0, 100 );
ui->volumeSlider->setValue( AudioEngine::instance()->volume() );
m_phononTickCheckTimer.setSingleShot( true );
m_sliderTimeLine.setCurveShape( QTimeLine::LinearCurve );
ui->seekSlider->setTimeLine( &m_sliderTimeLine );
m_defaultCover = QPixmap( RESPATH "images/no-album-no-case.png" ).scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
connect( &m_phononTickCheckTimer, SIGNAL( timeout() ), SLOT( phononTickCheckTimeout() ) );
connect( &m_sliderTimeLine, SIGNAL( frameChanged( int ) ), ui->seekSlider, SLOT( setValue( int ) ) );
@@ -201,6 +197,7 @@ AudioControls::onPlaybackStarted( const Tomahawk::result_ptr& result )
ui->seekSlider->setRange( 0, duration );
ui->seekSlider->setValue( 0 );
ui->seekSlider->setEnabled( AudioEngine::instance()->canSeek() );
m_phononTickCheckTimer.stop();
@@ -209,9 +206,12 @@ AudioControls::onPlaybackStarted( const Tomahawk::result_ptr& result )
m_sliderTimeLine.setFrameRange( 0, duration );
m_sliderTimeLine.setCurrentTime( 0 );
m_seekMsecs = -1;
ui->seekSlider->setVisible( true );
int updateRate = (double)1000 / ( (double)ui->seekSlider->contentsRect().width() / (double)( duration / 1000 ) );
m_sliderTimeLine.setUpdateInterval( qBound( 40, updateRate, 500 ) );
m_noTimeChange = false;
m_lastSliderCheck = 0;
}
@@ -239,6 +239,7 @@ AudioControls::onPlaybackLoading( const Tomahawk::result_ptr& result )
ui->timeLabel->setText( TomahawkUtils::timeToString( 0 ) );
ui->timeLeftLabel->setFixedWidth( ui->timeLeftLabel->fontMetrics().width( QString( duration.length() + 1, QChar( '0' ) ) ) );
ui->timeLeftLabel->setText( "-" + duration );
m_lastTextSecondShown = 0;
ui->stackedLayout->setCurrentWidget( ui->pauseButton );
@@ -264,14 +265,14 @@ AudioControls::onAlbumCoverUpdated()
void
AudioControls::setAlbumCover()
{
if ( !m_currentTrack->album()->cover().isNull() )
if ( !m_currentTrack->album()->cover( ui->coverImage->size() ).isNull() )
{
QPixmap cover;
cover.loadFromData( m_currentTrack->album()->cover() );
ui->coverImage->setPixmap( cover.scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) );
cover = m_currentTrack->album()->cover( ui->coverImage->size() );
ui->coverImage->setPixmap( cover );
}
else
ui->coverImage->setPixmap( m_defaultCover );
ui->coverImage->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::ScaledCover, ui->coverImage->size() ) );
}
@@ -279,10 +280,16 @@ void
AudioControls::onSocialActionsLoaded()
{
Query* query = qobject_cast< Query* >( sender() );
if ( !query || query != m_currentTrack->toQuery().data() )
if ( !query || !m_currentTrack || query != m_currentTrack->toQuery().data() )
return;
setSocialActions();
query_ptr currentQuery = m_currentTrack->toQuery();
if ( query->artist() == currentQuery->artist() &&
query->track() == currentQuery->track() &&
query->album() == currentQuery->album() )
{
setSocialActions();
}
}
@@ -357,6 +364,14 @@ AudioControls::onPlaybackStopped()
void
AudioControls::onPlaybackTimer( qint64 msElapsed )
{
const int seconds = msElapsed / 1000;
if ( seconds != m_lastTextSecondShown && !m_currentTrack.isNull() )
{
ui->timeLabel->setText( TomahawkUtils::timeToString( seconds ) );
ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( m_currentTrack->duration() - seconds ) );
m_lastTextSecondShown = seconds;
}
//tDebug( LOGEXTRA ) << Q_FUNC_INFO << "msElapsed =" << msElapsed << "and timer current time =" << m_sliderTimeLine.currentTime() << "and m_seekMsecs =" << m_seekMsecs;
if ( msElapsed > 0 && msElapsed != m_lastSliderCheck && m_seekMsecs == -1 && msElapsed - 500 < m_lastSliderCheck )
return;
@@ -372,14 +387,11 @@ AudioControls::onPlaybackTimer( qint64 msElapsed )
if ( sender() != &m_phononTickCheckTimer )
m_phononTickCheckTimer.start( 1000 );
const int seconds = msElapsed / 1000;
ui->timeLabel->setText( TomahawkUtils::timeToString( seconds ) );
ui->timeLeftLabel->setText( "-" + TomahawkUtils::timeToString( m_currentTrack->duration() - seconds ) );
int currentTime = m_sliderTimeLine.currentTime();
if ( m_noTimeChange )
{
if ( m_sliderTimeLine.currentTime() != msElapsed )
if ( currentTime != msElapsed )
{
m_sliderTimeLine.setPaused( true );
m_noTimeChange = false;
@@ -388,12 +400,12 @@ AudioControls::onPlaybackTimer( qint64 msElapsed )
m_sliderTimeLine.resume();
}
}
else if ( m_sliderTimeLine.currentTime() >= msElapsed || m_seekMsecs != -1 )
else if ( currentTime >= msElapsed || m_seekMsecs != -1 )
{
m_sliderTimeLine.setPaused( true );
m_noTimeChange = false;
if ( m_sliderTimeLine.currentTime() == msElapsed )
if ( currentTime == msElapsed )
m_noTimeChange = true;
m_sliderTimeLine.setCurrentTime( msElapsed );
@@ -403,12 +415,10 @@ AudioControls::onPlaybackTimer( qint64 msElapsed )
}
else if ( m_sliderTimeLine.duration() > msElapsed && m_sliderTimeLine.state() == QTimeLine::NotRunning && AudioEngine::instance()->state() == AudioEngine::Playing )
{
ui->seekSlider->setEnabled( AudioEngine::instance()->canSeek() );
m_sliderTimeLine.start();
}
else if ( m_sliderTimeLine.state() == QTimeLine::Paused && AudioEngine::instance()->state() != AudioEngine::Paused )
{
ui->seekSlider->setEnabled( AudioEngine::instance()->canSeek() );
m_sliderTimeLine.resume();
}

View File

@@ -91,8 +91,6 @@ private:
Ui::AudioControls *ui;
QPixmap m_defaultCover;
Tomahawk::result_ptr m_currentTrack;
Tomahawk::PlaylistInterface::RepeatMode m_repeatMode;
bool m_shuffled;
@@ -102,6 +100,7 @@ private:
qint64 m_seekMsecs;
qint64 m_lastSliderCheck;
bool m_noTimeChange;
qint64 m_lastTextSecondShown;
};
#endif // AUDIOCONTROLS_H

View File

@@ -21,5 +21,4 @@ ADD_DEFINITIONS( ${QT_DEFINITIONS} )
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_FULL_LIBEXECDIR})
install(TARGETS tomahawk_crash_reporter RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR})

View File

@@ -76,7 +76,10 @@ AtticaManager::loadPixmapsFromCache()
}
QPixmap* icon = new QPixmap( cacheDir.absoluteFilePath( file ) );
m_resolverStates[ info.baseName() ].pixmap = icon;
if ( !icon->isNull() )
{
m_resolverStates[ info.baseName() ].pixmap = icon;
}
}
}
@@ -93,14 +96,21 @@ AtticaManager::savePixmapsToCache()
foreach( const QString& id, m_resolverStates.keys() )
{
if ( !m_resolverStates[ id ].pixmap )
if ( !m_resolverStates[ id ].pixmap || !m_resolverStates[ id ].pixmapDirty )
continue;
const QString filename = cacheDir.absoluteFilePath( QString( "%1.png" ).arg( id ) );
if ( !m_resolverStates[ id ].pixmap->save( filename ) )
QFile f( filename );
if ( !f.open( QIODevice::WriteOnly ) )
{
tLog() << "Failed to open cache file for writing:" << filename;
continue;
}
else
{
if ( !m_resolverStates[ id ].pixmap->save( &f ) )
{
tLog() << "Failed to save pixmap into opened file for writing:" << filename;
}
}
}
}
@@ -247,6 +257,7 @@ AtticaManager::resolverIconFetched()
QPixmap* icon = new QPixmap;
icon->loadFromData( data );
m_resolverStates[ resolverId ].pixmap = icon;
m_resolverStates[ resolverId ].pixmapDirty = true;
}
@@ -288,7 +299,7 @@ AtticaManager::syncServerData()
void
AtticaManager::installResolver( const Content& resolver )
AtticaManager::installResolver( const Content& resolver, bool autoEnable )
{
Q_ASSERT( !resolver.id().isNull() );
@@ -302,6 +313,7 @@ AtticaManager::installResolver( const Content& resolver )
ItemJob< DownloadItem >* job = m_resolverProvider.downloadLink( resolver.id() );
connect( job, SIGNAL( finished( Attica::BaseJob* ) ), this, SLOT( resolverDownloadFinished( Attica::BaseJob* ) ) );
job->setProperty( "resolverId", resolver.id() );
job->setProperty( "autoEnable", autoEnable );
job->start();
}
@@ -316,11 +328,12 @@ AtticaManager::upgradeResolver( const Content& resolver )
if ( !m_resolverStates.contains( resolver.id() ) || m_resolverStates[ resolver.id() ].state != NeedsUpgrade )
return;
const bool enabled = TomahawkSettings::instance()->enabledScriptResolvers().contains( m_resolverStates[ resolver.id() ].scriptPath );
m_resolverStates[ resolver.id() ].state = Upgrading;
emit resolverStateChanged( resolver.id() );
uninstallResolver( resolver );
installResolver( resolver );
installResolver( resolver, enabled );
}
@@ -337,6 +350,7 @@ AtticaManager::resolverDownloadFinished ( BaseJob* j )
QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
connect( reply, SIGNAL( finished() ), this, SLOT( payloadFetched() ) );
reply->setProperty( "resolverId", job->property( "resolverId" ) );
reply->setProperty( "autoEnable", job->property( "autoEnable" ) );
}
else
{
@@ -372,8 +386,10 @@ AtticaManager::payloadFetched()
// update with absolute, not relative, path
m_resolverStates[ resolverId ].scriptPath = resolverPath;
const bool autoEnable = reply->property( "autoEnable" ).toBool();
// Do the install / add to tomahawk
Tomahawk::Pipeline::instance()->addScriptResolver( resolverPath, true );
Tomahawk::Pipeline::instance()->addScriptResolver( resolverPath, autoEnable );
m_resolverStates[ resolverId ].state = Installed;
TomahawkSettingsGui::instanceGui()->setAtticaResolverStates( m_resolverStates );
emit resolverInstalled( resolverId );

View File

@@ -52,9 +52,12 @@ public:
ResolverState state;
QPixmap* pixmap;
// internal
bool pixmapDirty;
Resolver( const QString& v, const QString& path, int userR, ResolverState s )
: version( v ), scriptPath( path ), userRating( userR ), state( s ), pixmap( 0 ) {}
Resolver() : userRating( -1 ), state( Uninstalled ), pixmap( 0 ) {}
: version( v ), scriptPath( path ), userRating( userR ), state( s ), pixmap( 0 ), pixmapDirty( false ) {}
Resolver() : userRating( -1 ), state( Uninstalled ), pixmap( 0 ), pixmapDirty( false ) {}
};
typedef QHash< QString, AtticaManager::Resolver > StateHash;
@@ -91,7 +94,7 @@ public:
bool userHasRated( const Attica::Content& c ) const;
public slots:
void installResolver( const Attica::Content& resolver );
void installResolver( const Attica::Content& resolver, bool autoEnable = true );
void upgradeResolver( const Attica::Content& resolver );
signals:

View File

@@ -38,6 +38,8 @@ set( libGuiSources
jobview/PipelineStatusItem.cpp
jobview/TransferStatusItem.cpp
jobview/LatchedStatusItem.cpp
jobview/ErrorStatusMessage.cpp
jobview/IndexingJobItem.cpp
infobar/infobar.cpp
@@ -114,6 +116,7 @@ set( libGuiSources
utils/dropjobnotifier.cpp
utils/proxystyle.cpp
utils/tomahawkutilsgui.cpp
utils/closure.cpp
widgets/checkdirtree.cpp
widgets/querylabel.cpp
@@ -238,6 +241,7 @@ set( libGuiHeaders
utils/rdioparser.h
utils/shortenedlinkparser.h
utils/dropjobnotifier.h
utils/closure.h
widgets/checkdirtree.h
widgets/querylabel.h
@@ -276,6 +280,8 @@ set( libGuiHeaders
jobview/PipelineStatusItem.h
jobview/TransferStatusItem.h
jobview/LatchedStatusItem.h
jobview/ErrorStatusMessage.h
jobview/IndexingJobItem.h
thirdparty/Qocoa/qsearchfield.h
)
@@ -648,6 +654,8 @@ IF( APPLE )
utils/tomahawkutils_mac.mm
thirdparty/Qocoa/qsearchfield_mac.mm )
SET_SOURCE_FILES_PROPERTIES(utils/tomahawkutils_mac.mm PROPERTIES COMPILE_FLAGS "-fvisibility=default")
SET( libHeaders ${libHeaders}
infosystem/infoplugins/mac/adium.h
infosystem/infoplugins/mac/adiumplugin.h )
@@ -703,6 +711,7 @@ TARGET_LINK_LIBRARIES( tomahawklib
${QT_QTSQL_LIBRARY}
${QT_QTUITOOLS_LIBRARY}
${QT_QTGUI_LIBRARY}
${QT_QTSCRIPT_LIBRARY}
${OS_SPECIFIC_LINK_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
${LINK_LIBRARIES}

View File

@@ -22,7 +22,6 @@
#include "albumplaylistinterface.h"
#include "database/database.h"
#include "database/databaseimpl.h"
#include "database/databasecommand_alltracks.h"
#include "query.h"
#include "utils/logger.h"
@@ -32,12 +31,16 @@ using namespace Tomahawk;
Album::~Album()
{
delete m_cover;
}
album_ptr
Album::get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCreate )
{
if ( !Database::instance() || !Database::instance()->impl() )
return album_ptr();
int albid = Database::instance()->impl()->albumId( artist->id(), name, autoCreate );
if ( albid < 1 && autoCreate )
return album_ptr();
@@ -58,7 +61,7 @@ Album::get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& ar
return s_albums.value( id );
}
album_ptr a = album_ptr( new Album( id, name, artist ) );
album_ptr a = album_ptr( new Album( id, name, artist ), &QObject::deleteLater );
if ( id > 0 )
s_albums.insert( id, a );
@@ -71,6 +74,7 @@ Album::Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr&
, m_id( id )
, m_name( name )
, m_artist( artist )
, m_cover( 0 )
, m_infoLoaded( false )
{
connect( Tomahawk::InfoSystem::InfoSystem::instance(),
@@ -97,11 +101,14 @@ Album::artist() const
}
QByteArray
Album::cover() const
#ifndef ENABLE_HEADLESS
QPixmap
Album::cover( const QSize& size, bool forceLoad ) const
{
if ( !m_infoLoaded )
{
if ( !forceLoad )
return QPixmap();
m_uuid = uuid();
Tomahawk::InfoSystem::InfoStringHash trackInfo;
@@ -117,8 +124,31 @@ Album::cover() const
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
}
return m_cover;
if ( !m_cover && !m_coverBuffer.isEmpty() )
{
m_cover = new QPixmap();
m_cover->loadFromData( m_coverBuffer );
}
if ( m_cover && !m_cover->isNull() && !size.isEmpty() )
{
if ( m_coverCache.contains( size.width() ) )
{
return m_coverCache.value( size.width() );
}
QPixmap scaledCover;
scaledCover = m_cover->scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_coverCache.insert( size.width(), scaledCover );
return scaledCover;
}
if ( m_cover )
return *m_cover;
else
return QPixmap();
}
#endif
void
@@ -137,7 +167,7 @@ Album::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVaria
const QByteArray ba = returnedData["imgbytes"].toByteArray();
if ( ba.length() )
{
m_cover = ba;
m_coverBuffer = ba;
}
}

View File

@@ -19,8 +19,13 @@
#ifndef TOMAHAWKALBUM_H
#define TOMAHAWKALBUM_H
#include "config.h"
#include <QtCore/QObject>
#include <QtCore/QSharedPointer>
#ifndef ENABLE_HEADLESS
#include <QtGui/QPixmap>
#endif
#include "typedefs.h"
#include "playlistinterface.h"
@@ -44,7 +49,9 @@ public:
unsigned int id() const { return m_id; }
QString name() const { return m_name; }
artist_ptr artist() const;
QByteArray cover() const;
#ifndef ENABLE_HEADLESS
QPixmap cover( const QSize& size, bool forceLoad = true ) const;
#endif
bool infoLoaded() const { return m_infoLoaded; }
Tomahawk::playlistinterface_ptr playlistInterface();
@@ -64,10 +71,13 @@ private:
unsigned int m_id;
QString m_name;
artist_ptr m_artist;
QByteArray m_cover;
QByteArray m_coverBuffer;
mutable QPixmap* m_cover;
bool m_infoLoaded;
mutable QString m_uuid;
mutable QHash< int, QPixmap > m_coverCache;
Tomahawk::playlistinterface_ptr m_playlistInterface;
};

View File

@@ -68,7 +68,6 @@ signals:
void nextTrackReady();
private:
Q_DISABLE_COPY( AlbumPlaylistInterface )
AlbumPlaylistInterface();
QList< Tomahawk::query_ptr > m_queries;

View File

@@ -31,12 +31,16 @@ using namespace Tomahawk;
Artist::~Artist()
{
delete m_cover;
}
artist_ptr
Artist::get( const QString& name, bool autoCreate )
{
if ( !Database::instance() || !Database::instance()->impl() )
return artist_ptr();
int artid = Database::instance()->impl()->artistId( name, autoCreate );
if ( artid < 1 && autoCreate )
return artist_ptr();
@@ -57,7 +61,7 @@ Artist::get( unsigned int id, const QString& name )
return s_artists.value( id );
}
artist_ptr a = artist_ptr( new Artist( id, name ) );
artist_ptr a = artist_ptr( new Artist( id, name ), &QObject::deleteLater );
if ( id > 0 )
s_artists.insert( id, a );
@@ -69,6 +73,7 @@ Artist::Artist( unsigned int id, const QString& name )
: QObject()
, m_id( id )
, m_name( name )
, m_cover( 0 )
, m_infoLoaded( false )
{
m_sortname = DatabaseImpl::sortname( name, true );
@@ -89,11 +94,14 @@ Artist::onTracksAdded( const QList<Tomahawk::query_ptr>& tracks )
}
QByteArray
Artist::cover() const
#ifndef ENABLE_HEADLESS
QPixmap
Artist::cover( const QSize& size, bool forceLoad ) const
{
if ( !m_infoLoaded )
{
if ( !forceLoad )
return QPixmap();
m_uuid = uuid();
Tomahawk::InfoSystem::InfoStringHash trackInfo;
@@ -108,8 +116,31 @@ Artist::cover() const
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
}
return m_cover;
if ( !m_cover && !m_coverBuffer.isEmpty() )
{
m_cover = new QPixmap();
m_cover->loadFromData( m_coverBuffer );
}
if ( m_cover && !m_cover->isNull() && !size.isEmpty() )
{
if ( m_coverCache.contains( size.width() ) )
{
return m_coverCache.value( size.width() );
}
QPixmap scaledCover;
scaledCover = m_cover->scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_coverCache.insert( size.width(), scaledCover );
return scaledCover;
}
if ( m_cover )
return *m_cover;
else
return QPixmap();
}
#endif
void
@@ -128,7 +159,7 @@ Artist::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVari
const QByteArray ba = returnedData["imgbytes"].toByteArray();
if ( ba.length() )
{
m_cover = ba;
m_coverBuffer = ba;
}
}

View File

@@ -19,8 +19,13 @@
#ifndef TOMAHAWKARTIST_H
#define TOMAHAWKARTIST_H
#include "config.h"
#include <QtCore/QObject>
#include <QtCore/QSharedPointer>
#ifndef ENABLE_HEADLESS
#include <QtGui/QPixmap>
#endif
#include "typedefs.h"
#include "dllmacro.h"
@@ -43,7 +48,9 @@ public:
unsigned int id() const { return m_id; }
QString name() const { return m_name; }
QString sortname() const { return m_sortname; }
QByteArray cover() const;
#ifndef ENABLE_HEADLESS
QPixmap cover( const QSize& size, bool forceLoad = true ) const;
#endif
bool infoLoaded() const { return m_infoLoaded; }
Tomahawk::playlistinterface_ptr playlistInterface();
@@ -63,10 +70,13 @@ private:
unsigned int m_id;
QString m_name;
QString m_sortname;
QByteArray m_cover;
QByteArray m_coverBuffer;
mutable QPixmap* m_cover;
bool m_infoLoaded;
mutable QString m_uuid;
mutable QHash< int, QPixmap > m_coverCache;
Tomahawk::playlistinterface_ptr m_playlistInterface;
};

View File

@@ -89,8 +89,8 @@ ArtistPlaylistInterface::tracks()
cmd->setArtist( m_artist );
cmd->setSortOrder( DatabaseCommand_AllTracks::Album );
connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr>, QVariant ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr>, QVariant ) ),
m_artist.data(), SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) );
}

View File

@@ -18,6 +18,8 @@
#include "audioengine.h"
#include "config.h"
#include <QtCore/QUrl>
#include <QtNetwork/QNetworkReply>
@@ -129,8 +131,11 @@ AudioEngine::play()
if ( isPaused() )
{
setVolume( m_volume );
m_mediaObject->play();
setVolume( m_volume );
emit resumed();
Tomahawk::InfoSystem::InfoStringHash trackInfo;
trackInfo["title"] = m_currentTrack->track();
@@ -150,6 +155,7 @@ AudioEngine::pause()
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO;
m_volume = volume();
m_mediaObject->pause();
emit paused();
@@ -328,13 +334,15 @@ AudioEngine::sendWaitingNotificationSlot() const
void
AudioEngine::sendNowPlayingNotification()
{
#ifndef ENABLE_HEADLESS
if ( m_currentTrack->album().isNull() || m_currentTrack->album()->infoLoaded() )
onNowPlayingInfoReady();
else
{
connect( m_currentTrack->album().data(), SIGNAL( updated() ), SLOT( onNowPlayingInfoReady() ), Qt::UniqueConnection );
m_currentTrack->album()->cover();
m_currentTrack->album()->cover( QSize( 0, 0 ) );
}
#endif
}
@@ -357,9 +365,11 @@ AudioEngine::onNowPlayingInfoReady()
if ( !m_currentTrack->album().isNull() )
{
#ifndef ENABLE_HEADLESS
QImage cover;
cover.loadFromData( m_currentTrack->album()->cover() );
cover = m_currentTrack->album()->cover( QSize( 0, 0 ) ).toImage();
playInfo["image"] = QVariant( cover );
#endif
}
Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo(
@@ -584,10 +594,10 @@ AudioEngine::onStateChanged( Phonon::State newState, Phonon::State oldState )
if ( newState == Phonon::ErrorState )
{
stop();
tLog() << "Phonon Error:" << m_mediaObject->errorString() << m_mediaObject->errorType();
emit error( UnknownError );
stop();
return;
}
if ( newState == Phonon::PlayingState )

View File

@@ -155,6 +155,7 @@ private:
mutable QStringList m_supportedMimeTypes;
AudioState m_state;
unsigned int m_volume;
static AudioEngine* s_instance;
};

View File

@@ -37,6 +37,8 @@ RelatedArtistsContext::RelatedArtistsContext()
m_relatedModel->setColumnStyle( TreeModel::TrackOnly );
m_relatedView->setTreeModel( m_relatedModel );
m_relatedView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
m_relatedView->setSortingEnabled( false );
m_relatedView->proxyModel()->sort( -1 );
QPalette pal = m_relatedView->palette();
pal.setColor( QPalette::Window, QColor( 0, 0, 0, 0 ) );

View File

@@ -88,6 +88,7 @@ ContextMenu::setQueries( const QList<Tomahawk::query_ptr>& queries )
m_sigmap->setMapping( m_loveAction, ActionLove );
connect( queries.first().data(), SIGNAL( socialActionsLoaded() ), SLOT( onSocialActionsLoaded() ) );
m_queries.first()->loadSocialActions();
onSocialActionsLoaded();
}

View File

@@ -142,7 +142,7 @@ DatabaseCollection::autoPlaylistCreated( const source_ptr& source, const QVarian
static_cast<GeneratorMode>(data[6].toInt()), // dynamic mode
data[7].toBool(), //shared
data[8].toInt(), //lastmod
data[9].toString() ) ); //GUID
data[9].toString() ), &QObject::deleteLater ); //GUID
addAutoPlaylist( p );
}
@@ -160,7 +160,7 @@ DatabaseCollection::stationCreated( const source_ptr& source, const QVariantList
static_cast<GeneratorMode>(data[6].toInt()), // dynamic mode
data[7].toBool(), //shared
data[8].toInt(), //lastmod
data[9].toString() ) ); //GUID
data[9].toString() ), &QObject::deleteLater ); //GUID
addStation( p );
}

View File

@@ -71,7 +71,7 @@ DatabaseCommand_LoadAllPlaylists::exec( DatabaseImpl* dbi )
query.value(5).toBool(), //shared
query.value(4).toInt(), //lastmod
query.value(0).toString() //GUID
) );
), &QObject::deleteLater );
plists.append( p );
}

View File

@@ -20,6 +20,7 @@
#include "artist.h"
#include "album.h"
#include "pipeline.h"
#include "sourcelist.h"
#include "utils/logger.h"
@@ -30,10 +31,14 @@ DatabaseCommand_Resolve::DatabaseCommand_Resolve( const query_ptr& query )
: DatabaseCommand()
, m_query( query )
{
Q_ASSERT( Pipeline::instance()->isRunning() );
}
DatabaseCommand_Resolve::~DatabaseCommand_Resolve()
{}
{
}
void
DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
@@ -50,9 +55,6 @@ DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
qDebug() << "Using result-hint to speed up resolving:" << m_query->resultHint();
Tomahawk::result_ptr result = lib->resultFromHint( m_query );
/* qDebug() << "Result null:" << result.isNull();
* qDebug() << "Collection null:" << result->collection().isNull();
* qDebug() << "Source null:" << result->collection()->source().isNull();*/
if ( !result.isNull() && !result->collection().isNull() && result->collection()->source()->isOnline() )
{
QList<Tomahawk::result_ptr> res;
@@ -76,11 +78,9 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
typedef QPair<int, float> scorepair_t;
// STEP 1
QList< QPair<int, float> > artists = lib->searchTable( "artist", m_query->artist() );
QList< QPair<int, float> > tracks = lib->searchTable( "track", m_query->track() );
QList< QPair<int, float> > albums = lib->searchTable( "album", m_query->album() );
QList< QPair<int, float> > tracks = lib->search( m_query );
if ( artists.length() == 0 || tracks.length() == 0 )
if ( tracks.length() == 0 )
{
qDebug() << "No candidates found in first pass, aborting resolve" << m_query->artist() << m_query->track();
emit results( m_query->id(), res );
@@ -90,13 +90,10 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
// STEP 2
TomahawkSqlQuery files_query = lib->newquery();
QStringList artsl, trksl;
for ( int k = 0; k < artists.count(); k++ )
artsl.append( QString::number( artists.at( k ).first ) );
QStringList trksl;
for ( int k = 0; k < tracks.count(); k++ )
trksl.append( QString::number( tracks.at( k ).first ) );
QString artsToken = QString( "file_join.artist IN (%1)" ).arg( artsl.join( "," ) );
QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) );
QString sql = QString( "SELECT "
@@ -119,8 +116,7 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
"artist.id = file_join.artist AND "
"track.id = file_join.track AND "
"file.id = file_join.file AND "
"(%1 AND %2)" )
.arg( artsToken )
"(%1)" )
.arg( trksToken );
files_query.prepare( sql );
@@ -138,7 +134,7 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
else
{
s = SourceList::instance()->get( files_query.value( 16 ).toUInt() );
if( s.isNull() )
if ( s.isNull() )
{
qDebug() << "Could not find source" << files_query.value( 16 ).toUInt();
continue;
@@ -147,13 +143,18 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
url = QString( "servent://%1\t%2" ).arg( s->userName() ).arg( url );
}
bool cached = Tomahawk::Result::isCached( url );
Tomahawk::result_ptr result = Tomahawk::Result::get( url );
Tomahawk::artist_ptr artist =
Tomahawk::Artist::get( files_query.value( 18 ).toUInt(), files_query.value( 12 ).toString() );
Tomahawk::album_ptr album =
Tomahawk::Album::get( files_query.value( 19 ).toUInt(), files_query.value( 13 ).toString(), artist );
Tomahawk::artist_ptr composer =
Tomahawk::Artist::get( files_query.value( 20 ).toUInt(), files_query.value( 15 ).toString() );
if ( cached )
{
qDebug() << "Result already cached:" << result->toString();
res << result;
continue;
}
Tomahawk::artist_ptr artist = Tomahawk::Artist::get( files_query.value( 18 ).toUInt(), files_query.value( 12 ).toString() );
Tomahawk::album_ptr album = Tomahawk::Album::get( files_query.value( 19 ).toUInt(), files_query.value( 13 ).toString(), artist );
Tomahawk::artist_ptr composer = Tomahawk::Artist::get( files_query.value( 20 ).toUInt(), files_query.value( 15 ).toString() );
result->setModificationTime( files_query.value( 1 ).toUInt() );
result->setSize( files_query.value( 2 ).toUInt() );
@@ -182,6 +183,7 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
result->setAttributes( attr );
result->setCollection( s->collection() );
res << result;
}
@@ -196,27 +198,9 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
typedef QPair<int, float> scorepair_t;
// STEP 1
QList< QPair<int, float> > artistPairs = lib->searchTable( "artist", m_query->fullTextQuery(), 20 );
QList< QPair<int, float> > trackPairs = lib->searchTable( "track", m_query->fullTextQuery(), 20 );
QList< QPair<int, float> > albumPairs = lib->searchTable( "album", m_query->fullTextQuery(), 20 );
QList< QPair<int, float> > trackPairs = lib->search( m_query );
QList< QPair<int, float> > albumPairs = lib->searchAlbum( m_query, 20 );
foreach ( const scorepair_t& artistPair, artistPairs )
{
TomahawkSqlQuery query = lib->newquery();
QString sql = QString( "SELECT name FROM artist WHERE id = %1" ).arg( artistPair.first );
query.prepare( sql );
query.exec();
QList<Tomahawk::artist_ptr> artistList;
while ( query.next() )
{
Tomahawk::artist_ptr artist = Tomahawk::Artist::get( artistPair.first, query.value( 0 ).toString() );
artistList << artist;
}
emit artists( m_query->id(), artistList );
}
foreach ( const scorepair_t& albumPair, albumPairs )
{
TomahawkSqlQuery query = lib->newquery();
@@ -235,10 +219,10 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
emit albums( m_query->id(), albumList );
}
if ( artistPairs.length() == 0 && trackPairs.length() == 0 && albumPairs.length() == 0 )
if ( trackPairs.length() == 0 )
{
qDebug() << "No candidates found in first pass, aborting resolve" << m_query->artist() << m_query->track();
qDebug() << "No candidates found in first pass, aborting resolve" << m_query->fullTextQuery();
emit results( m_query->id(), res );
return;
}
@@ -246,18 +230,11 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
// STEP 2
TomahawkSqlQuery files_query = lib->newquery();
QStringList artsl, trksl, albsl;
for ( int k = 0; k < artistPairs.count(); k++ )
artsl.append( QString::number( artistPairs.at( k ).first ) );
QStringList trksl;
for ( int k = 0; k < trackPairs.count(); k++ )
trksl.append( QString::number( trackPairs.at( k ).first ) );
for ( int k = 0; k < albumPairs.count(); k++ )
albsl.append( QString::number( albumPairs.at( k ).first ) );
QString artsToken = QString( "file_join.artist IN (%1)" ).arg( artsl.join( "," ) );
QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) );
QString albsToken = QString( "file_join.album IN (%1)" ).arg( albsl.join( "," ) );
QString sql = QString( "SELECT "
"url, mtime, size, md5, mimetype, duration, bitrate, " //0
"file_join.artist, file_join.album, file_join.track, " //7
@@ -279,7 +256,7 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
"track.id = file_join.track AND "
"file.id = file_join.file AND "
"%1" )
.arg( trackPairs.length() > 0 ? trksToken : QString( "0" ) );
.arg( trksl.length() > 0 ? trksToken : QString( "0" ) );
files_query.prepare( sql );
files_query.exec();
@@ -296,7 +273,7 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
else
{
s = SourceList::instance()->get( files_query.value( 16 ).toUInt() );
if( s.isNull() )
if ( s.isNull() )
{
qDebug() << "Could not find source" << files_query.value( 16 ).toUInt();
continue;
@@ -305,13 +282,18 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
url = QString( "servent://%1\t%2" ).arg( s->userName() ).arg( url );
}
bool cached = Tomahawk::Result::isCached( url );
Tomahawk::result_ptr result = Tomahawk::Result::get( url );
Tomahawk::artist_ptr artist =
Tomahawk::Artist::get( files_query.value( 18 ).toUInt(), files_query.value( 12 ).toString() );
Tomahawk::album_ptr album =
Tomahawk::Album::get( files_query.value( 19 ).toUInt(), files_query.value( 13 ).toString(), artist );
Tomahawk::artist_ptr composer =
Tomahawk::Artist::get( files_query.value( 20 ).toUInt(), files_query.value( 15 ).toString() );
if ( cached )
{
qDebug() << "Result already cached:" << result->toString();
res << result;
continue;
}
Tomahawk::artist_ptr artist = Tomahawk::Artist::get( files_query.value( 18 ).toUInt(), files_query.value( 12 ).toString() );
Tomahawk::album_ptr album = Tomahawk::Album::get( files_query.value( 19 ).toUInt(), files_query.value( 13 ).toString(), artist );
Tomahawk::artist_ptr composer = Tomahawk::Artist::get( files_query.value( 20 ).toUInt(), files_query.value( 15 ).toString() );
result->setModificationTime( files_query.value( 1 ).toUInt() );
result->setSize( files_query.value( 2 ).toUInt() );
@@ -348,8 +330,8 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
}
result->setAttributes( attr );
result->setCollection( s->collection() );
res << result;
}

View File

@@ -21,32 +21,26 @@
#include "databaseimpl.h"
#include "tomahawksqlquery.h"
#include "utils/logger.h"
#include "jobview/IndexingJobItem.h"
#include "jobview/JobStatusView.h"
#include "jobview/JobStatusModel.h"
#include <QSqlRecord>
DatabaseCommand_UpdateSearchIndex::DatabaseCommand_UpdateSearchIndex()
: DatabaseCommand()
, m_statusJob( new IndexingJobItem )
{
tLog() << Q_FUNC_INFO << "Updating index.";
JobStatusView::instance()->model()->addJob( m_statusJob );
}
void
DatabaseCommand_UpdateSearchIndex::indexTable( DatabaseImpl* db, const QString& table )
DatabaseCommand_UpdateSearchIndex::~DatabaseCommand_UpdateSearchIndex()
{
qDebug() << Q_FUNC_INFO;
TomahawkSqlQuery query = db->newquery();
qDebug() << "Building index for" << table;
query.exec( QString( "SELECT id, name FROM %1" ).arg( table ) );
QMap< unsigned int, QString > fields;
while ( query.next() )
{
fields.insert( query.value( 0 ).toUInt(), query.value( 1 ).toString() );
}
db->m_fuzzyIndex->appendFields( table, fields );
qDebug() << "Building index for" << table << "finished.";
m_statusJob->done();
}
@@ -55,9 +49,35 @@ DatabaseCommand_UpdateSearchIndex::exec( DatabaseImpl* db )
{
db->m_fuzzyIndex->beginIndexing();
indexTable( db, "artist" );
indexTable( db, "album" );
indexTable( db, "track" );
QMap< unsigned int, QMap< QString, QString > > data;
TomahawkSqlQuery q = db->newquery();
q.exec( "SELECT track.id, track.name, artist.name, artist.id FROM track, artist WHERE artist.id = track.artist" );
while ( q.next() )
{
QMap< QString, QString > track;
track.insert( "track", q.value( 1 ).toString() );
track.insert( "artist", q.value( 2 ).toString() );
track.insert( "artistid", q.value( 3 ).toString() );
data.insert( q.value( 0 ).toUInt(), track );
}
db->m_fuzzyIndex->appendFields( data );
data.clear();
q.exec( "SELECT album.id, album.name FROM album" );
while ( q.next() )
{
QMap< QString, QString > album;
album.insert( "album", q.value( 1 ).toString() );
data.insert( q.value( 0 ).toUInt(), album );
}
db->m_fuzzyIndex->appendFields( data );
qDebug() << "Building index finished.";
db->m_fuzzyIndex->endIndexing();
}

View File

@@ -22,23 +22,21 @@
#include "databasecommand.h"
#include "dllmacro.h"
class IndexingJobItem;
class DLLEXPORT DatabaseCommand_UpdateSearchIndex : public DatabaseCommand
{
Q_OBJECT
public:
explicit DatabaseCommand_UpdateSearchIndex();
virtual ~DatabaseCommand_UpdateSearchIndex();
virtual QString commandname() const { return "updatesearchindex"; }
virtual bool doesMutates() const { return true; }
virtual void exec( DatabaseImpl* db );
signals:
void indexUpdated();
private:
void indexTable( DatabaseImpl* db, const QString& table );
QString table;
IndexingJobItem* m_statusJob;
};
#endif // DATABASECOMMAND_UPDATESEARCHINDEX_H

View File

@@ -79,6 +79,9 @@ DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent )
query.exec( "UPDATE source SET isonline = 'false'" );
m_fuzzyIndex = new FuzzyIndex( *this, schemaUpdated );
if ( schemaUpdated )
QTimer::singleShot( 0, this, SLOT( updateIndex() ) );
tDebug( LOGVERBOSE ) << "Loaded index:" << t.elapsed();
if ( qApp->arguments().contains( "--dumpdb" ) )
@@ -405,13 +408,36 @@ DatabaseImpl::albumId( int artistid, const QString& name_orig, bool autoCreate )
QList< QPair<int, float> >
DatabaseImpl::searchTable( const QString& table, const QString& name, uint limit )
DatabaseImpl::search( const Tomahawk::query_ptr& query, uint limit )
{
QList< QPair<int, float> > resultslist;
if ( table != "artist" && table != "track" && table != "album" )
QMap< int, float > resultsmap = m_fuzzyIndex->search( query );
foreach ( int i, resultsmap.keys() )
{
resultslist << QPair<int, float>( i, (float)resultsmap.value( i ) );
}
qSort( resultslist.begin(), resultslist.end(), DatabaseImpl::scorepairSorter );
if ( !limit )
return resultslist;
QMap< int, float > resultsmap = m_fuzzyIndex->search( table, name );
QList< QPair<int, float> > resultscapped;
for ( int i = 0; i < (int)limit && i < resultsmap.count(); i++ )
{
resultscapped << resultslist.at( i );
}
return resultscapped;
}
QList< QPair<int, float> >
DatabaseImpl::searchAlbum( const Tomahawk::query_ptr& query, uint limit )
{
QList< QPair<int, float> > resultslist;
QMap< int, float > resultsmap = m_fuzzyIndex->searchAlbum( query );
foreach ( int i, resultsmap.keys() )
{
resultslist << QPair<int, float>( i, (float)resultsmap.value( i ) );
@@ -696,3 +722,11 @@ DatabaseImpl::openDatabase( const QString& dbname )
return schemaUpdated;
}
void
DatabaseImpl::updateIndex()
{
DatabaseCommand* cmd = new DatabaseCommand_UpdateSearchIndex();
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}

View File

@@ -56,7 +56,8 @@ public:
int trackId( int artistid, const QString& name_orig, bool autoCreate );
int albumId( int artistid, const QString& name_orig, bool autoCreate );
QList< QPair<int, float> > searchTable( const QString& table, const QString& name, uint limit = 0 );
QList< QPair<int, float> > search( const Tomahawk::query_ptr& query, uint limit = 0 );
QList< QPair<int, float> > searchAlbum( const Tomahawk::query_ptr& query, uint limit = 0 );
QList< int > getTrackFids( int tid );
static QString sortname( const QString& str, bool replaceArticle = false );
@@ -79,7 +80,8 @@ public:
signals:
void indexReady();
public slots:
private slots:
void updateIndex();
private:
QString cleanSql( const QString& sql );

View File

@@ -22,12 +22,14 @@
#include <QTime>
#include <CLucene.h>
#include <CLucene/queryParser/MultiFieldQueryParser.h>
#include "databaseimpl.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
using namespace lucene::analysis;
using namespace lucene::analysis::standard;
using namespace lucene::document;
using namespace lucene::store;
using namespace lucene::index;
@@ -83,7 +85,7 @@ FuzzyIndex::beginIndexing()
}
qDebug() << "Creating new index writer.";
IndexWriter luceneWriter = IndexWriter( m_luceneDir, m_analyzer, true );
IndexWriter luceneWriter( m_luceneDir, m_analyzer, true );
}
catch( CLuceneError& error )
{
@@ -102,38 +104,55 @@ FuzzyIndex::endIndexing()
void
FuzzyIndex::appendFields( const QString& table, const QMap< unsigned int, QString >& fields )
FuzzyIndex::appendFields( const QMap< unsigned int, QMap< QString, QString > >& trackData )
{
try
{
qDebug() << "Appending to index:" << fields.count();
tDebug() << "Appending to index:" << trackData.count();
bool create = !IndexReader::indexExists( TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ).toStdString().c_str() );
IndexWriter luceneWriter = IndexWriter( m_luceneDir, m_analyzer, create );
IndexWriter luceneWriter( m_luceneDir, m_analyzer, create );
Document doc;
QMapIterator< unsigned int, QString > it( fields );
QMapIterator< unsigned int, QMap< QString, QString > > it( trackData );
while ( it.hasNext() )
{
it.next();
unsigned int id = it.key();
QString name = it.value();
QMap< QString, QString > values = it.value();
if ( values.contains( "track" ) )
{
Field* field = _CLNEW Field( table.toStdWString().c_str(), DatabaseImpl::sortname( name ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_UNTOKENIZED );
doc.add( *field );
}
doc.add( *( _CLNEW Field( _T( "fulltext" ), DatabaseImpl::sortname( QString( "%1 %2" ).arg( values.value( "artist" ) ).arg( values.value( "track" ) ) ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
{
Field* field = _CLNEW Field( _T( "id" ), QString::number( id ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO );
doc.add( *field );
doc.add( *( _CLNEW Field( _T( "track" ), DatabaseImpl::sortname( values.value( "track" ) ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
doc.add( *( _CLNEW Field( _T( "artist" ), DatabaseImpl::sortname( values.value( "artist" ) ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
doc.add( *( _CLNEW Field( _T( "artistid" ), values.value( "artistid" ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO ) ) );
doc.add( *( _CLNEW Field( _T( "trackid" ), QString::number( id ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO ) ) );
}
else if ( values.contains( "album" ) )
{
doc.add( *( _CLNEW Field( _T( "album" ), DatabaseImpl::sortname( values.value( "album" ) ).toStdWString().c_str(),
Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
doc.add( *( _CLNEW Field( _T( "albumid" ), QString::number( id ).toStdWString().c_str(),
Field::STORE_YES | Field::INDEX_NO ) ) );
}
else
Q_ASSERT( false );
luceneWriter.addDocument( &doc );
doc.clear();
}
luceneWriter.optimize();
luceneWriter.close();
}
catch( CLuceneError& error )
@@ -152,7 +171,7 @@ FuzzyIndex::loadLuceneIndex()
QMap< int, float >
FuzzyIndex::search( const QString& table, const QString& name )
FuzzyIndex::search( const Tomahawk::query_ptr& query )
{
QMutexLocker lock( &m_mutex );
@@ -171,33 +190,112 @@ FuzzyIndex::search( const QString& table, const QString& name )
m_luceneSearcher = _CLNEW IndexSearcher( m_luceneReader );
}
if ( name.isEmpty() )
return resultsmap;
float minScore;
const TCHAR** fields = 0;
MultiFieldQueryParser parser( fields, m_analyzer );
BooleanQuery* qry = _CLNEW BooleanQuery();
SimpleAnalyzer analyzer;
QueryParser parser( table.toStdWString().c_str(), m_analyzer );
Hits* hits = 0;
if ( query->isFullTextQuery() )
{
QString escapedQuery = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->fullTextQuery() ).toStdWString().c_str() ) );
Term* term = _CLNEW Term( _T( "track" ), escapedQuery.toStdWString().c_str() );
Query* fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::SHOULD );
FuzzyQuery* qry = _CLNEW FuzzyQuery( _CLNEW Term( table.toStdWString().c_str(), DatabaseImpl::sortname( name ).toStdWString().c_str() ) );
hits = m_luceneSearcher->search( qry );
term = _CLNEW Term( _T( "artist" ), escapedQuery.toStdWString().c_str() );
fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::SHOULD );
term = _CLNEW Term( _T( "fulltext" ), escapedQuery.toStdWString().c_str() );
fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::SHOULD );
minScore = 0.00;
}
else
{
QString track = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->track() ).toStdWString().c_str() ) );
QString artist = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->artist() ).toStdWString().c_str() ) );
// QString album = QString::fromWCharArray( parser.escape( query->album().toStdWString().c_str() ) );
Term* term = _CLNEW Term( _T( "track" ), track.toStdWString().c_str() );
Query* fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::MUST );
term = _CLNEW Term( _T( "artist" ), artist.toStdWString().c_str() );
fqry = _CLNEW FuzzyQuery( term );
qry->add( fqry, true, BooleanClause::MUST );
minScore = 0.00;
}
Hits* hits = m_luceneSearcher->search( qry );
for ( uint i = 0; i < hits->length(); i++ )
{
Document* d = &hits->doc( i );
float score = hits->score( i );
int id = QString::fromWCharArray( d->get( _T( "id" ) ) ).toInt();
QString result = QString::fromWCharArray( d->get( table.toStdWString().c_str() ) );
int id = QString::fromWCharArray( d->get( _T( "trackid" ) ) ).toInt();
if ( DatabaseImpl::sortname( result ) == DatabaseImpl::sortname( name ) )
score = 1.0;
else
score = qMin( score, (float)0.99 );
if ( score > 0.05 )
if ( score > minScore )
{
resultsmap.insert( id, score );
// qDebug() << "Hitres:" << result << id << score << table << name;
// tDebug() << "Index hit:" << id << score << QString::fromWCharArray( ((Query*)qry)->toString() );
}
}
delete hits;
delete qry;
}
catch( CLuceneError& error )
{
tDebug() << "Caught CLucene error:" << error.what();
Q_ASSERT( false );
}
return resultsmap;
}
QMap< int, float >
FuzzyIndex::searchAlbum( const Tomahawk::query_ptr& query )
{
Q_ASSERT( query->isFullTextQuery() );
QMutexLocker lock( &m_mutex );
QMap< int, float > resultsmap;
try
{
if ( !m_luceneReader )
{
if ( !IndexReader::indexExists( TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ).toStdString().c_str() ) )
{
qDebug() << Q_FUNC_INFO << "index didn't exist.";
return resultsmap;
}
m_luceneReader = IndexReader::open( m_luceneDir );
m_luceneSearcher = _CLNEW IndexSearcher( m_luceneReader );
}
QueryParser parser( _T( "album" ), m_analyzer );
QString escapedName = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->fullTextQuery() ).toStdWString().c_str() ) );
Query* qry = _CLNEW FuzzyQuery( _CLNEW Term( _T( "album" ), escapedName.toStdWString().c_str() ) );
Hits* hits = m_luceneSearcher->search( qry );
for ( uint i = 0; i < hits->length(); i++ )
{
Document* d = &hits->doc( i );
float score = hits->score( i );
int id = QString::fromWCharArray( d->get( _T( "albumid" ) ) ).toInt();
if ( score > 0.30 )
{
resultsmap.insert( id, score );
// tDebug() << "Index hit:" << id << score;
}
}

View File

@@ -25,6 +25,8 @@
#include <QString>
#include <QMutex>
#include "query.h"
namespace lucene
{
namespace analysis
@@ -58,7 +60,7 @@ public:
void beginIndexing();
void endIndexing();
void appendFields( const QString& table, const QMap< unsigned int, QString >& fields );
void appendFields( const QMap< unsigned int, QMap< QString, QString > >& trackData );
signals:
void indexReady();
@@ -66,7 +68,8 @@ signals:
public slots:
void loadLuceneIndex();
QMap< int, float > search( const QString& table, const QString& name );
QMap< int, float > search( const Tomahawk::query_ptr& query );
QMap< int, float > searchAlbum( const Tomahawk::query_ptr& query );
private:
DatabaseImpl& m_db;

View File

@@ -162,6 +162,7 @@ CREATE TABLE IF NOT EXISTS file (
);
CREATE UNIQUE INDEX file_url_src_uniq ON file(source, url);
CREATE INDEX file_source ON file(source);
CREATE INDEX file_mtime ON file(mtime);
-- mtime of dir when last scanned.
-- load into memory when rescanning, skip stuff that's unchanged

View File

@@ -35,6 +35,9 @@
#include "utils/xspfloader.h"
#include "jobview/JobStatusView.h"
#include "jobview/JobStatusModel.h"
#include "jobview/ErrorStatusMessage.h"
#include "pipeline.h"
#ifdef QCA2_FOUND
#include "utils/groovesharkparser.h"
#endif //QCA2_FOUND
@@ -43,7 +46,7 @@
using namespace Tomahawk;
bool DropJob::s_canParseSpotifyPlaylists = false;
static QString s_dropJobInfoId = "dropjob";
DropJob::DropJob( QObject *parent )
: QObject( parent )
@@ -124,7 +127,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
// Not the most elegant
if ( url.contains( "spotify" ) && url.contains( "playlist" ) && s_canParseSpotifyPlaylists )
return true;
if ( url.contains( "grooveshark.com" ) && url.contains( "playlist" ) )
return true;
}
@@ -571,7 +574,7 @@ DropJob::handleGroovesharkUrls ( const QString& urlsRaw )
#ifdef QCA2_FOUND
QStringList urls = urlsRaw.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
tDebug() << "Got Grooveshark urls!" << urls;
if ( dropAction() == Default )
setDropAction( Create );
@@ -691,6 +694,67 @@ DropJob::onTracksAdded( const QList<Tomahawk::query_ptr>& tracksList )
}
void
DropJob::tracksFromDB( const QList< query_ptr >& tracks )
{
// Tracks that we get from databasecommand_alltracks are resolved only against the database and explicitly marked
// as finished. if the source they resolve to is offline they will not resolve against any resolver.
// explicitly resolve them if they fall in that case first
foreach( const query_ptr& track, tracks )
{
if ( !track->playable() && !track->solved() && track->results().size() ) // we have offline results
{
track->setResolveFinished( false );
Pipeline::instance()->resolve( track );
}
}
album_ptr albumPtr;
artist_ptr artistPtr;
if ( Tomahawk::Album* album = qobject_cast< Tomahawk::Album* >( sender() ) )
{
foreach ( const album_ptr& ptr, m_albumsToKeep )
if ( ptr.data() == album )
{
albumPtr = ptr;
m_albumsToKeep.remove( ptr );
}
}
else if ( Tomahawk::Artist* artist = qobject_cast< Tomahawk::Artist* >( sender() ) )
{
foreach ( const artist_ptr& ptr, m_artistsToKeep )
if ( ptr.data() == artist )
{
artistPtr = ptr;
m_artistsToKeep.remove( ptr );
}
}
// If we have no tracks, this means no sources in our network have the give request (artist or album)
// Since we really do want to try to drop them, we ask the infosystem as well.
if ( tracks.isEmpty() )
{
if ( !albumPtr.isNull() && !albumPtr->artist().isNull() )
{
Q_ASSERT( artistPtr.isNull() );
--m_queryCount; // This query is done. New query is infosystem query
getAlbumFromInfoystem( albumPtr->artist()->name(), albumPtr->name() );
}
else if ( !artistPtr.isNull() )
{
Q_ASSERT( albumPtr.isNull() );
--m_queryCount;
getTopTen( artistPtr->name() );
}
}
else
{
onTracksAdded( tracks );
}
}
void
DropJob::removeDuplicates()
{
@@ -699,10 +763,18 @@ DropJob::removeDuplicates()
{
bool contains = false;
foreach( const Tomahawk::query_ptr &tmpItem, list )
{
if ( item->album() == tmpItem->album()
&& item->artist() == tmpItem->artist()
&& item->track() == tmpItem->track() )
{
if ( item->playable() && !tmpItem->playable() )
list.replace( list.indexOf( tmpItem ), item );
contains = true;
break;
}
}
if ( !contains )
list.append( item );
}
@@ -733,28 +805,31 @@ DropJob::removeRemoteSources()
void
DropJob::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
{
if ( requestData.caller == "changeme" )
if ( requestData.caller == s_dropJobInfoId )
{
Tomahawk::InfoSystem::InfoStringHash artistInfo;
const Tomahawk::InfoSystem::InfoStringHash info = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
artistInfo = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
const QString artist = info["artist"];
const QString album = info["album"];
QString artist = artistInfo["artist"];
qDebug() << "Got requestData response for artist" << artist << output;
qDebug() << "Got requestData response for artist" << artist << "and album:" << album << output;
QList< query_ptr > results;
int i = 0;
foreach ( const QVariant& title, output.toMap().value( "tracks" ).toList() )
{
qDebug() << "got title" << title;
results << Query::get( artist, title.toString(), QString(), uuid() );
if ( ++i == 10 ) // Only getting top ten for now. Would make sense to make it configurable
break;
}
if ( results.isEmpty() )
{
const QString which = album.isEmpty() ? "artist" : "album";
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "No tracks found for given %1" ).arg( which ), 5 ) );
}
onTracksAdded( results );
}
}
@@ -766,8 +841,10 @@ DropJob::getArtist( const QString &artist )
artist_ptr artistPtr = Artist::get( artist );
if ( artistPtr->playlistInterface()->tracks().isEmpty() )
{
m_artistsToKeep.insert( artistPtr );
connect( artistPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
SLOT( tracksFromDB( QList<Tomahawk::query_ptr> ) ) );
m_queryCount++;
return QList< query_ptr >();
}
@@ -787,9 +864,14 @@ DropJob::getAlbum(const QString &artist, const QString &album)
if ( albumPtr->playlistInterface()->tracks().isEmpty() )
{
// For albums that don't exist until this moment, we are the main shared pointer holding on.
// fetching the tracks is asynchronous, so the resulting signal is queued. when we go out of scope we delete
// the artist_ptr which means we never get the signal delivered. so we hold on to the album pointer till we're done
m_albumsToKeep.insert( albumPtr );
m_dropJob = new DropJobNotifier( QPixmap( RESPATH "images/album-icon.png" ), Album );
connect( albumPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
SLOT( tracksFromDB( QList<Tomahawk::query_ptr> ) ) );
JobStatusView::instance()->model()->addJob( m_dropJob );
m_queryCount++;
@@ -811,7 +893,7 @@ DropJob::getTopTen( const QString &artist )
artistInfo["artist"] = artist;
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = "changeme";
requestData.caller = s_dropJobInfoId;
requestData.customData = QVariantMap();
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
@@ -821,3 +903,28 @@ DropJob::getTopTen( const QString &artist )
m_queryCount++;
}
void
DropJob::getAlbumFromInfoystem( const QString& artist, const QString& album )
{
connect( Tomahawk::InfoSystem::InfoSystem::instance(),
SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
Tomahawk::InfoSystem::InfoStringHash artistInfo;
artistInfo["artist"] = artist;
artistInfo["album"] = album;
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = s_dropJobInfoId;
requestData.customData = QVariantMap();
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
requestData.type = Tomahawk::InfoSystem::InfoAlbumSongs;
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
m_queryCount++;
}

View File

@@ -23,6 +23,8 @@
#include "query.h"
#include "infosystem/infosystem.h"
#include "utils/xspfloader.h"
#include <QObject>
#include <QStringList>
#include <QMimeData>
@@ -120,9 +122,9 @@ private slots:
void onTracksAdded( const QList<Tomahawk::query_ptr>& );
void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
void tracksFromDB( const QList< Tomahawk::query_ptr >& );
private:
/// handle parsing mime data
void handleAllUrls( const QString& urls );
void handleTrackUrls( const QString& urls );
QList< Tomahawk::query_ptr > tracksFromQueryList( const QMimeData* d );
@@ -135,6 +137,7 @@ private:
QList< Tomahawk::query_ptr > getAlbum( const QString& artist, const QString& album );
void getTopTen( const QString& artist );
void getAlbumFromInfoystem( const QString& artist, const QString& album );
void removeDuplicates();
void removeRemoteSources();
@@ -151,6 +154,8 @@ private:
Tomahawk::DropJobNotifier* m_dropJob;
QList< Tomahawk::query_ptr > m_resultList;
QSet< Tomahawk::album_ptr > m_albumsToKeep;
QSet< Tomahawk::artist_ptr > m_artistsToKeep;
static bool s_canParseSpotifyPlaylists;
};

View File

@@ -85,7 +85,7 @@ InfoBar::InfoBar( QWidget* parent )
m_autoUpdate->setText( tr( "Automatically update" ) );
m_autoUpdate->setLayoutDirection( Qt::RightToLeft );
m_autoUpdate->setPalette( whitePal );
connect( m_autoUpdate, SIGNAL( stateChanged( int ) ), this, SIGNAL( autoUpdateChanged( int ) ) );
connect( m_autoUpdate, SIGNAL( toggled( bool ) ), this, SIGNAL( autoUpdateChanged( bool ) ) );
ui->horizontalLayout->addWidget( m_autoUpdate );

View File

@@ -60,7 +60,7 @@ public slots:
void setAutoUpdateAvailable( bool b );
signals:
void filterTextChanged( const QString& filter );
void autoUpdateChanged( int state );
void autoUpdateChanged( bool checked );
protected:
void changeEvent( QEvent* e );

View File

@@ -45,9 +45,9 @@ ChartsPlugin::ChartsPlugin()
, m_chartsFetchJobs( 0 )
{
/// Add resources here
m_chartResources << "billboard" << "itunes" << "rdio" << "wearehunted" << "ex.fm" << "soundcloudwall.com";
m_chartResources << "billboard" << "itunes" << "rdio" << "wearehunted" << "ex.fm" << "soundcloudwall";
/// If you add resource, update version aswell
m_chartVersion = "1.0";
m_chartVersion = "2.1";
m_supportedGetTypes << InfoChart << InfoChartCapabilities;
}
@@ -332,7 +332,9 @@ ChartsPlugin::chartTypes()
if( source == "itunes" ){
chartName = "iTunes";
}
if( source == "soundcloudwall" ){
chartName = "SoundCloudWall";
}
if( source == "wearehunted" ){
chartName = "WeAreHunted";
}

View File

@@ -451,8 +451,15 @@ LastFmPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSy
QString artistName = criteria["artist"];
QString albumName = criteria["album"];
QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b";
QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) );
QUrl imgurl( "http://ws.audioscrobbler.com/2.0/" );
imgurl.addQueryItem( "method", "album.imageredirect" );
imgurl.addEncodedQueryItem( "artist", QUrl::toPercentEncoding( artistName, "", "+" ) );
imgurl.addEncodedQueryItem( "album", QUrl::toPercentEncoding( albumName, "", "+" ) );
imgurl.addQueryItem( "autocorrect", QString::number( 1 ) );
imgurl.addQueryItem( "size", "largesquare" );
imgurl.addQueryItem( "api_key", "7a90f6672a04b809ee309af169f34b8b" );
QNetworkRequest req( imgurl );
QNetworkReply* reply = TomahawkUtils::nam()->get( req );
reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
@@ -464,8 +471,14 @@ LastFmPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSy
{
QString artistName = criteria["artist"];
QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=artist.imageredirect&artist=%1&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b";
QNetworkRequest req( imgurl.arg( artistName ) );
QUrl imgurl( "http://ws.audioscrobbler.com/2.0/" );
imgurl.addQueryItem( "method", "artist.imageredirect" );
imgurl.addEncodedQueryItem( "artist", QUrl::toPercentEncoding( artistName, "", "+" ) );
imgurl.addQueryItem( "autocorrect", QString::number( 1 ) );
imgurl.addQueryItem( "size", "largesquare" );
imgurl.addQueryItem( "api_key", "7a90f6672a04b809ee309af169f34b8b" );
QNetworkRequest req( imgurl );
QNetworkReply* reply = TomahawkUtils::nam()->get( req );
reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
@@ -572,6 +585,8 @@ LastFmPlugin::topTracksReturned()
QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
QStringList topTracks = lastfm::Artist::getTopTracks( reply );
topTracks.removeDuplicates();
QVariantMap returnedData;
returnedData["tracks"] = topTracks;

View File

@@ -43,7 +43,6 @@ SpotifyPlugin::SpotifyPlugin()
: InfoPlugin()
, m_chartsFetchJobs( 0 )
{
m_supportedGetTypes << InfoChart << InfoChartCapabilities;
}
@@ -71,7 +70,6 @@ SpotifyPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
switch ( requestData.type )
{
case InfoChart:
@@ -87,6 +85,7 @@ SpotifyPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
case InfoChartCapabilities:
fetchChartCapabilities( requestData );
break;
default:
dataError( requestData );
}
@@ -110,7 +109,6 @@ SpotifyPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Hash did not contain required params!";
dataError( requestData );
return;
}
/// Set the criterias for current chart
criteria["chart_id"] = hash["chart_id"];
@@ -118,6 +116,8 @@ SpotifyPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
emit getCachedInfo( criteria, 86400000 /* Expire chart cache in 1 day */, requestData );
}
void
SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData )
{
@@ -132,12 +132,12 @@ SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData req
emit getCachedInfo( criteria, 604800000, requestData );
}
void
SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
{
switch ( requestData.type )
{
case InfoChart:
{
/// Fetch the chart, we need source and id
@@ -149,8 +149,6 @@ SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, To
reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) );
return;
}
case InfoChartCapabilities:
{
@@ -212,9 +210,8 @@ SpotifyPlugin::chartTypes()
}
QVariantMap charts;
foreach(QVariant geos, chartObj.value("Charts").toList().takeLast().toMap().value("geo").toList() )
foreach( QVariant geos, chartObj.value( "Charts" ).toList().takeLast().toMap().value( "geo" ).toList() )
{
const QString geo = geos.toMap().value( "name" ).toString();
const QString geoId = geos.toMap().value( "id" ).toString();
QString country;
@@ -225,7 +222,6 @@ SpotifyPlugin::chartTypes()
country = geo;
else
{
QLocale l( QString( "en_%1" ).arg( geo ) );
country = Tomahawk::CountryUtils::fullCountryFromCode( geo );
@@ -240,7 +236,7 @@ SpotifyPlugin::chartTypes()
}
QList< InfoStringHash > chart_types;
foreach(QVariant types, chartObj.value("Charts").toList().takeFirst().toMap().value("types").toList() )
foreach( QVariant types, chartObj.value( "Charts" ).toList().takeFirst().toMap().value( "types" ).toList() )
{
QString type = types.toMap().value( "id" ).toString();
QString label = types.toMap().value( "name" ).toString();
@@ -251,18 +247,15 @@ SpotifyPlugin::chartTypes()
c[ "type" ] = type;
chart_types.append( c );
}
charts.insert( country.toUtf8(), QVariant::fromValue<QList< InfoStringHash > >( chart_types ) );
}
QVariantMap defaultMap;
defaultMap[ "spotify" ] = QStringList() << "United States" << "Top Albums";
m_allChartsMap[ "defaults" ] = defaultMap;
m_allChartsMap.insert( "Spotify", QVariant::fromValue<QVariantMap>( charts ) );
}
else
{
@@ -281,13 +274,12 @@ SpotifyPlugin::chartTypes()
}
m_cachedRequests.clear();
}
}
void
SpotifyPlugin::chartReturned()
{
/// Chart request returned something! Woho
QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
QString url = reply->url().toString();
@@ -318,14 +310,13 @@ SpotifyPlugin::chartReturned()
else
setChartType( None );
foreach(QVariant result, res.value("toplist").toMap().value("result").toList() )
foreach( QVariant result, res.value( "toplist" ).toMap().value( "result" ).toList() )
{
QString title, artist;
QVariantMap chartMap = result.toMap();
if ( !chartMap.isEmpty() )
{
title = chartMap.value( "title" ).toString();
artist = chartMap.value( "artist" ).toString();
@@ -341,7 +332,6 @@ SpotifyPlugin::chartReturned()
if( chartType() == Album )
{
InfoStringHash pair;
pair["artist"] = artist;
pair["album"] = title;
@@ -351,10 +341,8 @@ SpotifyPlugin::chartReturned()
if( chartType() == Artist )
{
top_artists << chartMap.value( "name" ).toString();
qDebug() << "SpotifyChart type is artist";
}
}
}
@@ -393,5 +381,4 @@ SpotifyPlugin::chartReturned()
}
else
qDebug() << "Network error in fetching chart:" << reply->url().toString();
}

View File

@@ -42,8 +42,6 @@ MprisPlugin::MprisPlugin()
: InfoPlugin()
, m_coverTempFile( 0 )
{
qDebug() << Q_FUNC_INFO;
// init
m_playbackStatus = "Stopped";
@@ -54,79 +52,84 @@ MprisPlugin::MprisPlugin()
new MprisPluginRootAdaptor( this );
new MprisPluginPlayerAdaptor( this );
QDBusConnection dbus = QDBusConnection::sessionBus();
dbus.registerObject("/org/mpris/MediaPlayer2", this);
dbus.registerService("org.mpris.MediaPlayer2.tomahawk");
dbus.registerObject( "/org/mpris/MediaPlayer2", this );
dbus.registerService( "org.mpris.MediaPlayer2.tomahawk" );
// Listen to volume changes
connect( AudioEngine::instance(), SIGNAL( volumeChanged( int ) ),
SLOT( onVolumeChanged( int ) ) );
SLOT( onVolumeChanged( int ) ) );
// When the playlist changes, signals for several properties are sent
connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ),
SLOT( onPlaylistChanged( Tomahawk::playlistinterface_ptr ) ) );
SLOT( onPlaylistChanged( Tomahawk::playlistinterface_ptr ) ) );
// When a track is added or removed, CanGoNext updated signal is sent
Tomahawk::playlistinterface_ptr playlist = AudioEngine::instance()->playlist();
if( !playlist.isNull() )
if ( !playlist.isNull() )
{
connect( playlist.data(), SIGNAL( trackCountChanged( unsigned int ) ),
SLOT( onTrackCountChanged( unsigned int ) ) );
SLOT( onTrackCountChanged( unsigned int ) ) );
}
// Connect to AudioEngine's seeked signal
connect( AudioEngine::instance(), SIGNAL( seeked( qint64 ) ),
SLOT( onSeeked( qint64 ) ) );
SLOT( onSeeked( qint64 ) ) );
// Connect to the InfoSystem (we need to get album covers via getInfo)
connect( Tomahawk::InfoSystem::InfoSystem::instance(),
SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) );
SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
connect( Tomahawk::InfoSystem::InfoSystem::instance(),
SIGNAL( finished( QString ) ),
SLOT( infoSystemFinished( QString ) ) );
}
MprisPlugin::~MprisPlugin()
{
qDebug() << Q_FUNC_INFO;
delete m_coverTempFile;
}
// org.mpris.MediaPlayer2
bool
MprisPlugin::canQuit() const
{
qDebug() << Q_FUNC_INFO;
return true;
}
bool
MprisPlugin::canRaise() const
{
qDebug() << Q_FUNC_INFO;
return false;
}
bool
MprisPlugin::hasTrackList() const
{
qDebug() << Q_FUNC_INFO;
return false;
}
QString
MprisPlugin::identity() const
{
return QString("Tomahawk");
return QString( "Tomahawk" );
}
QString
MprisPlugin::desktopEntry() const
{
return QString("tomahawk");
return QString( "tomahawk" );
}
QStringList
MprisPlugin::supportedUriSchemes() const
{
@@ -135,23 +138,27 @@ MprisPlugin::supportedUriSchemes() const
return uriSchemes;
}
QStringList
MprisPlugin::supportedMimeTypes() const
{
return QStringList();
}
void
MprisPlugin::Raise()
{
}
void
MprisPlugin::Quit()
{
QApplication::quit();
}
// org.mpris.MediaPlayer2.Player
bool
@@ -160,24 +167,28 @@ MprisPlugin::canControl() const
return true;
}
bool
MprisPlugin::canGoNext() const
{
return AudioEngine::instance()->canGoNext();
}
bool
MprisPlugin::canGoPrevious() const
{
return AudioEngine::instance()->canGoPrevious();
}
bool
MprisPlugin::canPause() const
{
return AudioEngine::instance()->currentTrack();
}
bool
MprisPlugin::canPlay() const
{
@@ -186,6 +197,7 @@ MprisPlugin::canPlay() const
return AudioEngine::instance()->currentTrack() || ( !p.isNull() && p->trackCount() );
}
bool
MprisPlugin::canSeek() const
{
@@ -196,6 +208,7 @@ MprisPlugin::canSeek() const
}
QString
MprisPlugin::loopStatus() const
{
@@ -215,39 +228,42 @@ MprisPlugin::loopStatus() const
return "None";
break;
default:
return QString("None");
return "None";
break;
}
return QString("None");
return QString( "None" );
}
void
MprisPlugin::setLoopStatus( const QString &value )
MprisPlugin::setLoopStatus( const QString& value )
{
Tomahawk::playlistinterface_ptr p = AudioEngine::instance()->playlist();
if ( p.isNull() )
return;
if( value == "Track")
if ( value == "Track" )
p->setRepeatMode( PlaylistInterface::RepeatOne );
else if( value == "Playlist" )
else if ( value == "Playlist" )
p->setRepeatMode( PlaylistInterface::RepeatAll );
else if( value == "None" )
else if ( value == "None" )
p->setRepeatMode( PlaylistInterface::NoRepeat );
}
double
MprisPlugin::maximumRate() const
{
return 1.0;
}
QVariantMap
MprisPlugin::metadata() const
{
QVariantMap metadataMap;
Tomahawk::result_ptr track = AudioEngine::instance()->currentTrack();
if( track )
if ( track )
{
metadataMap.insert( "mpris:trackid", QString( "/track/" ) + track->id().replace( "-", "" ) );
metadataMap.insert( "mpris:length", track->duration() );
@@ -256,9 +272,11 @@ MprisPlugin::metadata() const
metadataMap.insert( "xesam:title", track->track() );
// Only return art if tempfile exists, and if its name contains the same "artist_album_tomahawk_cover.png"
if( m_coverTempFile && m_coverTempFile->exists() &&
m_coverTempFile->fileName().contains( track->artist()->name() + "_" + track->album()->name() + "_tomahawk_cover.png" ) )
if ( m_coverTempFile && m_coverTempFile->exists() &&
m_coverTempFile->fileName().contains( track->artist()->name() + "_" + track->album()->name() + "_tomahawk_cover.png" ) )
{
metadataMap.insert( "mpris:artUrl", QString( QUrl::fromLocalFile( QFileInfo( *m_coverTempFile ).absoluteFilePath() ).toEncoded() ) );
}
else
{
// Need to fetch the album cover
@@ -280,18 +298,21 @@ MprisPlugin::metadata() const
return metadataMap;
}
double
MprisPlugin::minimumRate() const
{
return 1.0;
}
QString
MprisPlugin::playbackStatus() const
{
return m_playbackStatus;
}
qlonglong
MprisPlugin::position() const
{
@@ -299,18 +320,21 @@ MprisPlugin::position() const
return (qlonglong) ( AudioEngine::instance()->currentTime() * 1000 );
}
double
MprisPlugin::rate() const
{
return 1.0;
}
void
MprisPlugin::setRate( double value )
{
Q_UNUSED( value );
}
bool
MprisPlugin::shuffle() const
{
@@ -320,6 +344,7 @@ MprisPlugin::shuffle() const
return p->shuffled();
}
void
MprisPlugin::setShuffle( bool value )
{
@@ -329,70 +354,76 @@ MprisPlugin::setShuffle( bool value )
return p->setShuffled( value );
}
double
MprisPlugin::volume() const
{
return AudioEngine::instance()->volume();
}
void
MprisPlugin::setVolume( double value )
{
AudioEngine::instance()->setVolume( value );
}
void
MprisPlugin::Next()
{
AudioEngine::instance()->next();
}
void
MprisPlugin::OpenUri( const QString &Uri )
MprisPlugin::OpenUri( const QString& Uri )
{
if( Uri.contains( "tomahawk://" ) )
if ( Uri.contains( "tomahawk://" ) )
GlobalActionManager::instance()->parseTomahawkLink( Uri );
else if( Uri.contains( "spotify:" ) )
else if ( Uri.contains( "spotify:" ) )
GlobalActionManager::instance()->openSpotifyLink( Uri );
}
void
MprisPlugin::Pause()
{
AudioEngine::instance()->pause();
}
void
MprisPlugin::Play()
{
AudioEngine::instance()->play();
}
void
MprisPlugin::PlayPause()
{
AudioEngine::instance()->playPause();
}
void
MprisPlugin::Previous()
{
AudioEngine::instance()->previous();
}
void
MprisPlugin::Seek( qlonglong Offset )
{
qDebug() << Q_FUNC_INFO;
if( !canSeek() )
if ( !canSeek() )
return;
qlonglong seekTime = position() + Offset;
qDebug() << "seekTime: " << seekTime;
if( seekTime < 0 )
if ( seekTime < 0 )
AudioEngine::instance()->seek( 0 );
else if( seekTime > AudioEngine::instance()->currentTrackTotalTime()*1000 )
else if ( seekTime > AudioEngine::instance()->currentTrackTotalTime()*1000 )
Next();
// seekTime is in microseconds, but we work internally in milliseconds
else
@@ -400,50 +431,46 @@ MprisPlugin::Seek( qlonglong Offset )
}
void
MprisPlugin::SetPosition( const QDBusObjectPath &TrackId, qlonglong Position )
MprisPlugin::SetPosition( const QDBusObjectPath& TrackId, qlonglong Position )
{
qDebug() << Q_FUNC_INFO;
if( !canSeek() )
if ( !canSeek() )
return;
qDebug() << "path: " << TrackId.path();
qDebug() << "position: " << Position;
if( TrackId.path() != QString("/track/") + AudioEngine::instance()->currentTrack()->id().replace( "-", "" ) )
if ( TrackId.path() != QString( "/track/" ) + AudioEngine::instance()->currentTrack()->id().replace( "-", "" ) )
return;
if( ( Position < 0) || ( Position > AudioEngine::instance()->currentTrackTotalTime()*1000 ) )
if ( ( Position < 0) || ( Position > AudioEngine::instance()->currentTrackTotalTime()*1000 ) )
return;
qDebug() << "seeking to: " << Position/1000 << "ms";
AudioEngine::instance()->seek( (qint64) (Position / 1000 ) );
}
void
MprisPlugin::Stop()
{
AudioEngine::instance()->stop();
}
// InfoPlugin Methods
void
MprisPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
{
Q_UNUSED( requestData );
qDebug() << Q_FUNC_INFO;
return;
}
void
MprisPlugin::pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input )
{
Q_UNUSED( caller );
qDebug() << Q_FUNC_INFO;
bool isPlayingInfo = false;
switch ( type )
@@ -469,11 +496,11 @@ MprisPlugin::pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVar
break;
}
if( isPlayingInfo )
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "PlaybackStatus");
if ( isPlayingInfo )
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "PlaybackStatus" );
}
void
MprisPlugin::stateChanged( AudioState newState, AudioState oldState )
{
@@ -481,12 +508,11 @@ MprisPlugin::stateChanged( AudioState newState, AudioState oldState )
Q_UNUSED( oldState );
}
/** Audio state slots */
void
MprisPlugin::audioStarted( const QVariant &input )
MprisPlugin::audioStarted( const QVariant& input )
{
qDebug() << Q_FUNC_INFO;
if ( !input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
return;
@@ -495,64 +521,55 @@ MprisPlugin::audioStarted( const QVariant &input )
return;
m_playbackStatus = "Playing";
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata");
//hash["artist"];
//hash["title"];
//QString nowPlaying = "";
//qDebug() << "nowPlaying: " << nowPlaying;
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
}
void
MprisPlugin::audioFinished( const QVariant &input )
MprisPlugin::audioFinished( const QVariant& input )
{
Q_UNUSED( input );
//qDebug() << Q_FUNC_INFO;
}
void
MprisPlugin::audioStopped()
{
qDebug() << Q_FUNC_INFO;
m_playbackStatus = "Stopped";
}
void
MprisPlugin::audioPaused()
{
qDebug() << Q_FUNC_INFO;
m_playbackStatus = "Paused";
}
void
MprisPlugin::audioResumed( const QVariant &input )
MprisPlugin::audioResumed( const QVariant& input )
{
qDebug() << Q_FUNC_INFO;
audioStarted( input );
}
void
MprisPlugin::onVolumeChanged( int volume )
{
Q_UNUSED( volume );
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Volume");
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Volume" );
}
void
MprisPlugin::onPlaylistChanged( Tomahawk::playlistinterface_ptr playlist )
{
qDebug() << Q_FUNC_INFO;
disconnect( this, SLOT( onTrackCountChanged( unsigned int ) ) );
qDebug() << "disconnected";
if( !playlist.isNull() )
qDebug() << "playlist not null";
if( !playlist.isNull() )
if ( !playlist.isNull() )
connect( playlist.data(), SIGNAL( trackCountChanged( unsigned int ) ),
SLOT( onTrackCountChanged( unsigned int ) ) );
qDebug() << "connected new playlist";
// Notify relevant changes
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "LoopStatus" );
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Shuffle" );
@@ -560,6 +577,7 @@ MprisPlugin::onPlaylistChanged( Tomahawk::playlistinterface_ptr playlist )
onTrackCountChanged( 0 );
}
void
MprisPlugin::onTrackCountChanged( unsigned int tracks )
{
@@ -569,12 +587,14 @@ MprisPlugin::onTrackCountChanged( unsigned int tracks )
}
void
MprisPlugin::onSeeked( qint64 ms )
{
void
MprisPlugin::onSeeked( qint64 ms )
{
qlonglong us = (qlonglong) ( ms*1000 );
emit Seeked( us );
}
}
void
MprisPlugin::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
@@ -603,7 +623,7 @@ MprisPlugin::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData,
image.loadFromData( ba );
// Pull out request data for album+artist
if( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
{
qDebug() << "Cannot convert metadata input to album cover retrieval";
return;
@@ -612,41 +632,33 @@ MprisPlugin::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData,
Tomahawk::InfoSystem::InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
// delete the old tempfile and make new one, to avoid caching of filename by mpris clients
if( m_coverTempFile )
if ( m_coverTempFile )
{
delete m_coverTempFile;
m_coverTempFile = new QTemporaryFile( QDir::toNativeSeparators(
QDir::tempPath() + "/" + hash["artist"] + "_" + hash["album"] + "_tomahawk_cover.png" ) );
if( !m_coverTempFile->open() )
m_coverTempFile = 0;
}
if ( image.isNull() )
return;
m_coverTempFile = new QTemporaryFile( QDir::toNativeSeparators( QDir::tempPath() + "/" + hash["artist"] + "_" + hash["album"] + "_tomahawk_cover.png" ) );
if ( !m_coverTempFile->open() )
{
qDebug() << "WARNING: could not write temporary file for cover art!";
}
// Finally, save the image to the new temp file
//if( image.save( QFileInfo( *m_coverTempFile ).absoluteFilePath(), "PNG" ) )
if( image.save( m_coverTempFile, "PNG") )
if ( image.save( m_coverTempFile, "PNG" ) )
{
qDebug() << Q_FUNC_INFO << "Image saving successful, notifying";
qDebug() << "Saving to: " << QFileInfo( *m_coverTempFile ).absoluteFilePath();
qDebug() << "Saving cover image to:" << QFileInfo( *m_coverTempFile ).absoluteFilePath();
m_coverTempFile->close();
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
}
else
{
qDebug() << Q_FUNC_INFO << " failed to save image!";
tDebug() << Q_FUNC_INFO << "failed to save cover image!";
m_coverTempFile->close();
}
/*
if( m_coverTempFile->open() )
{
QTextStream out( m_coverTempFile );
out << ba;
m_coverTempFile->close();
notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
}
*/
}
}
@@ -657,14 +669,14 @@ MprisPlugin::infoSystemFinished( QString target )
Q_UNUSED( target );
}
void
MprisPlugin::notifyPropertyChanged( const QString& interface,
const QString& propertyName )
MprisPlugin::notifyPropertyChanged( const QString& interface, const QString& propertyName )
{
QDBusMessage signal = QDBusMessage::createSignal(
"/org/mpris/MediaPlayer2",
"org.freedesktop.DBus.Properties",
"PropertiesChanged");
"PropertiesChanged" );
signal << interface;
QVariantMap changedProps;
changedProps.insert(propertyName, property(propertyName.toAscii()));

View File

@@ -87,7 +87,7 @@ public:
Q_PROPERTY( QString LoopStatus READ loopStatus WRITE setLoopStatus )
QString loopStatus() const;
void setLoopStatus( const QString &value );
void setLoopStatus( const QString& value );
Q_PROPERTY( double MaximumRate READ maximumRate )
double maximumRate() const;
@@ -129,16 +129,15 @@ public slots:
// org.mpris.MediaPlayer2.Player
void Next();
void OpenUri( const QString &Uri );
void OpenUri( const QString& Uri );
void Pause();
void Play();
void PlayPause();
void Previous();
void Seek( qlonglong Offset );
void SetPosition( const QDBusObjectPath &TrackId, qlonglong Position );
void SetPosition( const QDBusObjectPath& TrackId, qlonglong Position );
void Stop();
protected slots:
void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input );
@@ -160,16 +159,16 @@ private:
// Get Info
// Push Info
void audioStarted( const QVariant &input );
void audioFinished( const QVariant &input );
void audioStarted( const QVariant& input );
void audioFinished( const QVariant& input );
void audioStopped();
void audioPaused();
void audioResumed( const QVariant &input );
void audioResumed( const QVariant& input );
// DBus
void notifyPropertyChanged( const QString& interface, const QString& propertyName );
QString m_playbackStatus;
QTemporaryFile *m_coverTempFile;
QTemporaryFile* m_coverTempFile;
};
};

View File

@@ -0,0 +1,56 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ErrorStatusMessage.h"
#include "utils/tomahawkutils.h"
#include <QTimer>
QPixmap* ErrorStatusMessage::s_pixmap = 0;
ErrorStatusMessage::ErrorStatusMessage( const QString& message, int timeoutSecs )
: JobStatusItem()
, m_message( message )
{
m_timer = new QTimer( this );
m_timer->setInterval( timeoutSecs * 1000 );
m_timer->setSingleShot( true );
connect( m_timer, SIGNAL( timeout() ), this, SIGNAL( finished() ) );
if ( !s_pixmap )
s_pixmap = new QPixmap( RESPATH "images/process-stop.png" );
m_timer->start();
}
QPixmap
ErrorStatusMessage::icon() const
{
Q_ASSERT( s_pixmap );
return *s_pixmap;
}
QString
ErrorStatusMessage::mainText() const
{
return m_message;
}

View File

@@ -0,0 +1,48 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ERRORSTATUSMESSAGE_H
#define ERRORSTATUSMESSAGE_H
#include "JobStatusItem.h"
#include "dllmacro.h"
class QTimer;
class QPixmap;
class DLLEXPORT ErrorStatusMessage : public JobStatusItem
{
Q_OBJECT
public:
explicit ErrorStatusMessage( const QString& errorMessage, int defaultTimeoutSecs = 8 );
QString type() const { return "errormessage"; }
QString rightColumnText() const { return QString(); }
QPixmap icon() const;
QString mainText() const;
bool allowMultiLine() const { return true; }
private:
QString m_message;
QTimer* m_timer;
static QPixmap* s_pixmap;
};
#endif // ERRORSTATUSMESSAGE_H

View File

@@ -0,0 +1,47 @@
/* === This file is part of Tomahawk Player - <http://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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "IndexingJobItem.h"
#include "utils/tomahawkutils.h"
static QPixmap* s_indexIcon = 0;
QString
IndexingJobItem::mainText() const
{
return tr( "Indexing database" );
}
QPixmap
IndexingJobItem::icon() const
{
if ( s_indexIcon == 0 )
s_indexIcon = new QPixmap( RESPATH "images/view-refresh.png" );
return *s_indexIcon;
}
void IndexingJobItem::done()
{
emit finished();
}

View File

@@ -0,0 +1,39 @@
/* === This file is part of Tomahawk Player - <http://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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INDEXINGJOBITEM_H
#define INDEXINGJOBITEM_H
#include <jobview/JobStatusItem.h>
class IndexingJobItem : public JobStatusItem
{
Q_OBJECT
public:
explicit IndexingJobItem() {}
void done();
virtual QString rightColumnText() const { return QString(); }
virtual QString mainText() const;
virtual QPixmap icon() const;
virtual QString type() const { return "indexerjob"; }
};
#endif // INDEXINGJOBITEM_H

View File

@@ -23,14 +23,16 @@
#include <QPainter>
#include <QApplication>
#include <QListView>
#define ROW_HEIGHT 20
#define ICON_PADDING 1
#define PADDING 2
JobStatusDelegate::JobStatusDelegate( QObject* parent )
: QStyledItemDelegate ( parent )
, m_parentView( qobject_cast< QListView* >( parent ) )
{
Q_ASSERT( m_parentView );
}
JobStatusDelegate::~JobStatusDelegate()
@@ -45,6 +47,7 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
QStyleOptionViewItemV4 opt = option;
initStyleOption( &opt, index );
QFontMetrics fm( opt.font );
const bool allowMultiLine = index.data( JobStatusModel::AllowMultiLineRole ).toBool();
opt.state &= ~QStyle::State_MouseOver;
QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget );
@@ -52,7 +55,9 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
// painter->drawLine( opt.rect.topLeft(), opt.rect.topRight() );
painter->setRenderHint( QPainter::Antialiasing );
const QRect iconRect( ICON_PADDING, ICON_PADDING + opt.rect.y(), ROW_HEIGHT - 2*ICON_PADDING, ROW_HEIGHT - 2*ICON_PADDING );
QRect iconRect( ICON_PADDING, ICON_PADDING + opt.rect.y(), ROW_HEIGHT - 2*ICON_PADDING, ROW_HEIGHT - 2*ICON_PADDING );
if ( allowMultiLine )
iconRect.moveTop( opt.rect.top() + opt.rect.height() / 2 - iconRect.height() / 2);
QPixmap p = index.data( Qt::DecorationRole ).value< QPixmap >();
p = p.scaledToHeight( iconRect.height(), Qt::SmoothTransformation );
painter->drawPixmap( iconRect, p );
@@ -71,15 +76,34 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
const int mainW = rightEdge - 3*PADDING - iconRect.right();
QString mainText = index.data( Qt::DisplayRole ).toString();
mainText = fm.elidedText( mainText, Qt::ElideRight, mainW );
painter->drawText( QRect( iconRect.right() + 2*PADDING, PADDING + opt.rect.y(), mainW, opt.rect.height() - 2*PADDING ), Qt::AlignLeft | Qt::AlignVCenter, mainText );
QTextOption to( Qt::AlignLeft | Qt::AlignVCenter );
if ( !allowMultiLine )
mainText = fm.elidedText( mainText, Qt::ElideRight, mainW );
else
to.setWrapMode( QTextOption::WrapAtWordBoundaryOrAnywhere );
painter->drawText( QRect( iconRect.right() + 2*PADDING, PADDING + opt.rect.y(), mainW, opt.rect.height() - 2*PADDING ), mainText, to );
}
QSize
JobStatusDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
// return QStyledItemDelegate::sizeHint( option, index );
const int w = QStyledItemDelegate::sizeHint ( option, index ).width();
return QSize( w, ROW_HEIGHT );
const bool allowMultiLine = index.data( JobStatusModel::AllowMultiLineRole ).toBool();
if ( !allowMultiLine )
return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), ROW_HEIGHT );
else if ( m_cachedMultiLineHeights.contains( index ) )
return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), m_cachedMultiLineHeights[ index ] );
// Don't elide, but stretch across as many rows as required
QStyleOptionViewItemV4 opt = option;
initStyleOption( &opt, index );
const QString text = index.data( Qt::DisplayRole ).toString();
const int leftEdge = ICON_PADDING + ROW_HEIGHT + 2*PADDING;
const QRect rect = opt.fontMetrics.boundingRect( leftEdge, opt.rect.top(), m_parentView->width() - leftEdge, 200, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, text );
m_cachedMultiLineHeights.insert( index, rect.height() + 4*PADDING );
return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), rect.height() + 4*PADDING );
}

View File

@@ -22,6 +22,7 @@
#include <QStyledItemDelegate>
class QPainter;
class QListView;
class JobStatusDelegate : public QStyledItemDelegate
{
@@ -33,6 +34,10 @@ public:
virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const;
private:
mutable QHash< QPersistentModelIndex, int > m_cachedMultiLineHeights;
QListView* m_parentView;
};
#endif // JOBSTATUSDELEGATE_H

View File

@@ -53,6 +53,7 @@ public:
* and a count will be shown instead.
*/
virtual bool collapseItem() const { return false; }
virtual bool allowMultiLine() const { return false; }
signals:
/// Ask for an update

View File

@@ -100,6 +100,8 @@ JobStatusModel::data( const QModelIndex& index, int role ) const
else
return item->rightColumnText();
}
case AllowMultiLineRole:
return item->allowMultiLine();
}
return QVariant();

View File

@@ -31,7 +31,8 @@ public:
enum JobRoles {
// DecorationRole is icon
// DisplayRole is main col
RightColumnRole = Qt::UserRole + 1
RightColumnRole = Qt::UserRole + 1,
AllowMultiLineRole = Qt::UserRole + 2
};
explicit JobStatusModel( QObject* parent = 0 );

View File

@@ -40,6 +40,7 @@ JobStatusView* JobStatusView::s_instance = 0;
JobStatusView::JobStatusView( AnimatedSplitter* parent )
: AnimatedWidget( parent )
, m_parent( parent )
, m_cachedHeight( -1 )
{
s_instance = this;
@@ -56,9 +57,7 @@ JobStatusView::JobStatusView( AnimatedSplitter* parent )
m_view->setFrameShape( QFrame::NoFrame );
m_view->setAttribute( Qt::WA_MacShowFocusRect, 0 );
// new QTreeWidgetItem( m_tree );
m_view->setUniformItemSizes( true );
m_view->setUniformItemSizes( false );
#ifndef Q_WS_WIN
QFont f = font();
@@ -86,12 +85,14 @@ JobStatusView::setModel( JobStatusModel* m )
connect( m_view->model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( checkCount() ) );
connect( m_view->model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( checkCount() ) );
connect( m_view->model(), SIGNAL( modelReset() ), this, SLOT( checkCount() ) );
}
void
JobStatusView::checkCount()
{
m_cachedHeight = -1;
if ( m_view->model()->rowCount() == 0 && !isHidden() )
emit hideWidget();
else
@@ -102,15 +103,21 @@ JobStatusView::checkCount()
QSize
JobStatusView::sizeHint() const
{
if ( m_cachedHeight >= 0 )
return QSize( 0, m_cachedHeight );
unsigned int y = 0;
// y += m_tree->header()->height();
y += m_view->contentsMargins().top() + m_view->contentsMargins().bottom();
if ( m_view->model()->rowCount() )
{
unsigned int rowheight = m_view->sizeHintForRow( 0 );
y += rowheight * m_view->model()->rowCount() + 2;
for ( int i = 0; i < m_view->model()->rowCount(); i++ )
{
y += m_view->sizeHintForRow( i );
}
y += 2; // some padding
}
m_cachedHeight = y;
return QSize( 0, y );
}

View File

@@ -56,6 +56,7 @@ private:
QListView* m_view;
JobStatusModel* m_model;
AnimatedSplitter* m_parent;
mutable int m_cachedHeight;
static JobStatusView* s_instance;
};

View File

@@ -43,7 +43,7 @@ PortFwdThread::~PortFwdThread()
{
qDebug() << Q_FUNC_INFO << "waiting for event loop to finish...";
quit();
wait( 1000 );
wait( 6000 );
delete m_portfwd;
}

View File

@@ -61,6 +61,7 @@ Pipeline::Pipeline( QObject* parent )
Pipeline::~Pipeline()
{
tDebug() << Q_FUNC_INFO;
m_running = false;
// stop script resolvers
@@ -412,10 +413,11 @@ Pipeline::shunt( const query_ptr& q )
r->resolve( q );
emit resolving( q );
m_qidsTimeout.insert( q->id(), true );
if ( r->timeout() > 0 )
{
m_qidsTimeout.insert( q->id(), true );
new FuncTimeout( r->timeout(), boost::bind( &Pipeline::timeoutShunt, this, q ), this );
}
}
else
{

View File

@@ -48,6 +48,8 @@ public:
explicit Pipeline( QObject* parent = 0 );
virtual ~Pipeline();
bool isRunning() const { return m_running; }
unsigned int pendingQueryCount() const { return m_queries_pending.count(); }
unsigned int activeQueryCount() const { return m_qidsState.count(); }

View File

@@ -185,7 +185,7 @@ Playlist::create( const source_ptr& author,
entries << p;
}
playlist_ptr playlist( new Playlist( author, guid, title, info, creator, shared, entries ) );
playlist_ptr playlist( new Playlist( author, guid, title, info, creator, shared, entries ), &QObject::deleteLater );
// save to DB in the background
// Watch for the created() signal if you need to be sure it's written.

View File

@@ -182,7 +182,7 @@ public:
QList<plentry_ptr> entriesFromQueries( const QList<Tomahawk::query_ptr>& queries, bool clearFirst = false );
void setUpdater( PlaylistUpdaterInterface* interface ) { m_updater = interface; }
void setUpdater( PlaylistUpdaterInterface* pluinterface ) { m_updater = pluinterface; }
PlaylistUpdaterInterface* updater() const { return m_updater; }
Tomahawk::playlistinterface_ptr playlistInterface();

View File

@@ -55,27 +55,34 @@ XspfUpdater::~XspfUpdater()
void
XspfUpdater::updateNow()
{
if ( m_url.isEmpty() )
{
qWarning() << "XspfUpdater not updating because we have an empty url...";
return;
}
XSPFLoader* l = new XSPFLoader( false, false );
l->setAutoResolveTracks( false );
l->setErrorTitle( playlist()->title() );
l->load( m_url );
connect( l, SIGNAL( ok ( Tomahawk::playlist_ptr ) ), this, SLOT( playlistLoaded() ) );
connect( l, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( playlistLoaded( QList<Tomahawk::query_ptr> ) ) );
}
void
XspfUpdater::playlistLoaded()
XspfUpdater::playlistLoaded( const QList<Tomahawk::query_ptr>& newEntries )
{
XSPFLoader* loader = qobject_cast<XSPFLoader*>( sender() );
Q_ASSERT( loader );
QList< query_ptr > tracks;
foreach ( const plentry_ptr ple, playlist()->entries() )
tracks << ple->query();
QList< query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( tracks, loader->entries() );
bool changed = false;
QList< query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( tracks, newEntries, changed );
if ( !changed )
return;
QList<Tomahawk::plentry_ptr> el = playlist()->entriesFromQueries( mergedTracks, true );
playlist()->createNewRevision( uuid(), playlist()->currentrevision(), el );
}
void

View File

@@ -37,6 +37,7 @@ public:
virtual ~XspfUpdater();
virtual QString type() const { return "xspf"; }
public slots:
void updateNow();
@@ -46,7 +47,7 @@ protected:
virtual void removeFromSettings(const QString& group) const;
private slots:
void playlistLoaded();
void playlistLoaded( const QList<Tomahawk::query_ptr> & );
private:
QString m_url;

View File

@@ -40,7 +40,6 @@ AlbumItemDelegate::AlbumItemDelegate( QAbstractItemView* parent, AlbumProxyModel
, m_view( parent )
, m_model( proxy )
{
m_defaultCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" );
}
@@ -89,21 +88,19 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
painter->drawLine( shadowRect.bottomLeft() + QPoint( 0, 4 ), shadowRect.bottomRight() + QPoint( 0, 4 ) );
}
QRect r = option.rect.adjusted( 6, 5, -6, -41 );
QPixmap cover;
if ( !item->album().isNull() )
{
cover.loadFromData( item->album()->cover() );
cover = item->album()->cover( r.size() );
if ( cover.isNull() )
cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::CoverInCase, r.size() );
}
else if ( !item->artist().isNull() )
{
cover.loadFromData( item->artist()->cover() );
cover = item->artist()->cover( r.size() );
}
if ( cover.isNull() )
cover = m_defaultCover;
QRect r = option.rect.adjusted( 6, 5, -6, -41 );
if ( option.state & QStyle::State_Selected )
{
#if defined(Q_WS_MAC) || defined(Q_WS_WIN)
@@ -123,17 +120,7 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
#endif
}
QPixmap scover;
if ( m_cache.contains( cover.cacheKey() ) )
{
scover = m_cache.value( cover.cacheKey() );
}
else
{
scover = cover.scaled( r.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_cache.insert( cover.cacheKey(), scover );
}
painter->drawPixmap( r, scover );
painter->drawPixmap( r, cover );
painter->setPen( opt.palette.color( QPalette::Text ) );
QTextOption to;

View File

@@ -49,12 +49,10 @@ private:
QAbstractItemView* m_view;
AlbumProxyModel* m_model;
mutable QHash< qint64, QPixmap > m_cache;
mutable QHash< QPersistentModelIndex, QRect > m_artistNameRects;
QPersistentModelIndex m_hoveringOver;
QPixmap m_shadowPixmap;
QPixmap m_defaultCover;
};
#endif // ALBUMITEMDELEGATE_H

View File

@@ -178,6 +178,7 @@ AlbumModel::mimeData( const QModelIndexList &indexes ) const
QByteArray queryData;
QDataStream queryStream( &queryData, QIODevice::WriteOnly );
bool isAlbumData = true;
foreach ( const QModelIndex& i, indexes )
{
if ( i.column() > 0 )
@@ -185,16 +186,25 @@ AlbumModel::mimeData( const QModelIndexList &indexes ) const
QModelIndex idx = index( i.row(), 0, i.parent() );
AlbumItem* item = itemFromIndex( idx );
if ( item )
if ( item && !item->album().isNull() )
{
const album_ptr& album = item->album();
queryStream << album->artist()->name();
queryStream << album->name();
isAlbumData = true;
}
else if ( item && !item->artist().isNull() )
{
const artist_ptr& artist = item->artist();
queryStream << artist->name();
isAlbumData = false;
}
}
QMimeData* mimeData = new QMimeData();
mimeData->setData( "application/tomahawk.metadata.album", queryData );
QMimeData* mimeData = new QMimeData;
mimeData->setData( isAlbumData ? "application/tomahawk.metadata.album" : "application/tomahawk.metadata.artist", queryData );
return mimeData;
}
@@ -304,7 +314,18 @@ AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums )
if ( m_overwriteOnAdd )
clear();
if ( !albums.count() )
QList<Tomahawk::album_ptr> trimmedAlbums;
foreach ( const album_ptr& album, albums )
{
if ( !album.isNull() && album->name().length() )
{
if ( findItem( album ) || trimmedAlbums.contains( album ) )
continue;
trimmedAlbums << album;
}
}
if ( !trimmedAlbums.count() )
{
emit itemCountChanged( rowCount( QModelIndex() ) );
return;
@@ -313,12 +334,12 @@ AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums )
int c = rowCount( QModelIndex() );
QPair< int, int > crows;
crows.first = c;
crows.second = c + albums.count() - 1;
crows.second = c + trimmedAlbums.count() - 1;
emit beginInsertRows( QModelIndex(), crows.first, crows.second );
AlbumItem* albumitem;
foreach( const album_ptr& album, albums )
foreach( const album_ptr& album, trimmedAlbums )
{
albumitem = new AlbumItem( album, m_rootItem );
albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem );
@@ -339,7 +360,18 @@ AlbumModel::addArtists( const QList<Tomahawk::artist_ptr>& artists )
if ( m_overwriteOnAdd )
clear();
if ( !artists.count() )
QList<Tomahawk::artist_ptr> trimmedArtists;
foreach ( const artist_ptr& artist, artists )
{
if ( !artist.isNull() && artist->name().length() )
{
if ( findItem( artist ) || trimmedArtists.contains( artist ) )
continue;
trimmedArtists << artist;
}
}
if ( !trimmedArtists.count() )
{
emit itemCountChanged( rowCount( QModelIndex() ) );
return;
@@ -348,12 +380,12 @@ AlbumModel::addArtists( const QList<Tomahawk::artist_ptr>& artists )
int c = rowCount( QModelIndex() );
QPair< int, int > crows;
crows.first = c;
crows.second = c + artists.count() - 1;
crows.second = c + trimmedArtists.count() - 1;
emit beginInsertRows( QModelIndex(), crows.first, crows.second );
AlbumItem* albumitem;
foreach( const artist_ptr& artist, artists )
foreach ( const artist_ptr& artist, trimmedArtists )
{
albumitem = new AlbumItem( artist, m_rootItem );
albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem );
@@ -396,3 +428,35 @@ AlbumModel::onDataChanged()
AlbumItem* p = (AlbumItem*)sender();
emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount( QModelIndex() ) - 1 ) );
}
AlbumItem*
AlbumModel::findItem( const artist_ptr& artist ) const
{
for ( int i = 0; i < rowCount( QModelIndex() ); i++ )
{
AlbumItem* item = itemFromIndex( index( i, 0, QModelIndex() ) );
if ( !item->artist().isNull() && item->artist() == artist )
{
return item;
}
}
return 0;
}
AlbumItem*
AlbumModel::findItem( const album_ptr& album ) const
{
for ( int i = 0; i < rowCount( QModelIndex() ); i++ )
{
AlbumItem* item = itemFromIndex( index( i, 0, QModelIndex() ) );
if ( !item->album().isNull() && item->album() == album )
{
return item;
}
}
return 0;
}

View File

@@ -71,6 +71,9 @@ public:
virtual void setTitle( const QString& title ) { m_title = title; }
virtual void setDescription( const QString& description ) { m_description = description; }
AlbumItem* findItem( const Tomahawk::artist_ptr& artist ) const;
AlbumItem* findItem( const Tomahawk::album_ptr& album ) const;
AlbumItem* itemFromIndex( const QModelIndex& index ) const
{
if ( index.isValid() )

View File

@@ -25,6 +25,7 @@
#include <qmath.h>
#include "audio/audioengine.h"
#include "context/ContextWidget.h"
#include "tomahawksettings.h"
#include "artist.h"
#include "albumitem.h"
@@ -113,6 +114,20 @@ AlbumView::setAlbumModel( AlbumModel* model )
}
void
AlbumView::currentChanged( const QModelIndex& current, const QModelIndex& previous )
{
QListView::currentChanged( current, previous );
AlbumItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( current ) );
if ( item )
{
if ( !item->album().isNull() )
ViewManager::instance()->context()->setAlbum( item->album() );
}
}
void
AlbumView::onItemActivated( const QModelIndex& index )
{

View File

@@ -71,6 +71,9 @@ protected:
void paintEvent( QPaintEvent* event );
void resizeEvent( QResizeEvent* event );
protected slots:
virtual void currentChanged( const QModelIndex& current, const QModelIndex& previous );
private slots:
void onItemCountChanged( unsigned int items );

View File

@@ -80,6 +80,11 @@ ArtistView::ArtistView( QWidget* parent )
setFont( f );
#endif
m_timer.setInterval( SCROLL_TIMEOUT );
connect( verticalScrollBar(), SIGNAL( rangeChanged( int, int ) ), SLOT( onViewChanged() ) );
connect( verticalScrollBar(), SIGNAL( valueChanged( int ) ), SLOT( onViewChanged() ) );
connect( &m_timer, SIGNAL( timeout() ), SLOT( onScrollTimeout() ) );
connect( this, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) );
connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) );
connect( m_contextMenu, SIGNAL( triggered( int ) ), SLOT( onMenuTriggered( int ) ) );
@@ -128,7 +133,8 @@ ArtistView::setTreeModel( TreeModel* model )
connect( m_proxyModel, SIGNAL( filteringFinished() ), m_loadingSpinner, SLOT( fadeOut() ) );
connect( m_model, SIGNAL( itemCountChanged( unsigned int ) ), SLOT( onItemCountChanged( unsigned int ) ) );
connect( m_proxyModel, SIGNAL( filterChanged( QString ) ), SLOT( onFilterChanged( QString ) ) );
connect( m_proxyModel, SIGNAL( filteringFinished() ), SLOT( onFilterChangeFinished() ) );
connect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ) );
guid(); // this will set the guid on the header
@@ -145,6 +151,44 @@ ArtistView::setTreeModel( TreeModel* model )
}
void
ArtistView::onViewChanged()
{
if ( m_timer.isActive() )
m_timer.stop();
m_timer.start();
}
void
ArtistView::onScrollTimeout()
{
if ( m_timer.isActive() )
m_timer.stop();
QModelIndex left = indexAt( viewport()->rect().topLeft() );
while ( left.isValid() && left.parent().isValid() )
left = left.parent();
QModelIndex right = indexAt( viewport()->rect().bottomLeft() );
while ( right.isValid() && right.parent().isValid() )
right = right.parent();
int max = m_proxyModel->playlistInterface()->trackCount();
if ( right.isValid() )
max = right.row() + 1;
if ( !max )
return;
for ( int i = left.row(); i < max; i++ )
{
m_model->getCover( m_proxyModel->mapToSource( m_proxyModel->index( i, 0 ) ) );
}
}
void
ArtistView::currentChanged( const QModelIndex& current, const QModelIndex& previous )
{
@@ -236,7 +280,7 @@ ArtistView::onItemCountChanged( unsigned int items )
void
ArtistView::onFilterChanged( const QString& )
ArtistView::onFilterChangeFinished()
{
if ( selectedIndexes().count() )
scrollTo( selectedIndexes().at( 0 ), QAbstractItemView::PositionAtCenter );

View File

@@ -93,8 +93,10 @@ protected slots:
private slots:
void onItemCountChanged( unsigned int items );
void onFilterChanged( const QString& filter );
void onFilterChangeFinished();
void onFilteringStarted();
void onViewChanged();
void onScrollTimeout();
void onCustomContextMenu( const QPoint& pos );
void onMenuTriggered( int action );
@@ -113,6 +115,7 @@ private:
Tomahawk::ContextMenu* m_contextMenu;
bool m_showModes;
QTimer m_timer;
mutable QString m_guid;
};

View File

@@ -42,7 +42,7 @@ CustomPlaylistView::CustomPlaylistView( CustomPlaylistView::PlaylistType type, c
if ( m_type == SourceLovedTracks )
connect( m_source.data(), SIGNAL( socialAttributesChanged( QString ) ), this, SLOT( socialAttributesChanged( QString ) ) );
else if ( m_type == AllLovedTracks )
else if ( m_type == TopLovedTracks )
{
connect( SourceList::instance()->getLocal().data(), SIGNAL( socialAttributesChanged( QString ) ), this, SLOT( socialAttributesChanged( QString ) ) );
foreach ( const source_ptr& s, SourceList::instance()->sources( true ) )
@@ -86,12 +86,12 @@ CustomPlaylistView::generateTracks()
"GROUP BY track.id "
"ORDER BY counter DESC, social_attributes.timestamp DESC " ).arg( m_source->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_source->id() ) );
break;
case AllLovedTracks:
case TopLovedTracks:
sql = QString( "SELECT track.name, artist.name, source, COUNT(*) as counter "
"FROM social_attributes, track, artist "
"WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Love' AND social_attributes.v = 'true'"
"WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Love' AND social_attributes.v = 'true' "
"GROUP BY track.id "
"ORDER BY counter DESC, social_attributes.timestamp DESC " );
"ORDER BY counter DESC, social_attributes.timestamp DESC LIMIT 0, 50" );
break;
}
@@ -104,7 +104,11 @@ CustomPlaylistView::generateTracks()
void
CustomPlaylistView::tracksGenerated( QList< query_ptr > tracks )
{
QList< query_ptr > newTracks = TomahawkUtils::mergePlaylistChanges( m_model->queries(), tracks );
bool changed = false;
QList< query_ptr > newTracks = TomahawkUtils::mergePlaylistChanges( m_model->queries(), tracks, changed);
if ( !changed )
return;
m_model->clear();
m_model->append( newTracks );

View File

@@ -33,7 +33,7 @@ class DLLEXPORT CustomPlaylistView : public PlaylistView
public:
enum PlaylistType {
SourceLovedTracks,
AllLovedTracks
TopLovedTracks
};
explicit CustomPlaylistView( PlaylistType type, const source_ptr& s, QWidget* parent = 0 );

View File

@@ -54,19 +54,22 @@ DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist, bool load
{
Q_UNUSED( loadEntries );
if( !m_playlist.isNull() ) {
if ( !m_playlist.isNull() )
{
disconnect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) );
}
const int oldCount = rowCount( QModelIndex() );
m_playlist = playlist;
m_deduper.clear();
if( m_playlist->mode() == OnDemand )
if ( m_playlist->mode() == OnDemand )
setFilterUnresolvable( true );
connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) );
PlaylistModel::loadPlaylist( m_playlist, m_playlist->mode() == Static );
if( m_playlist->mode() == OnDemand )
if ( m_playlist->mode() == OnDemand && oldCount != rowCount( QModelIndex() ) )
emit trackCountChanged( rowCount( QModelIndex() ) );
}
@@ -74,7 +77,7 @@ DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist, bool load
QString
DynamicModel::description() const
{
if( !m_playlist.isNull() && !m_playlist->generator().isNull() )
if ( !m_playlist.isNull() && !m_playlist->generator().isNull() )
return m_playlist->generator()->sentenceSummary();
else
return QString();
@@ -95,7 +98,8 @@ DynamicModel::startOnDemand()
void
DynamicModel::newTrackGenerated( const Tomahawk::query_ptr& query )
{
if( m_onDemandRunning ) {
if ( m_onDemandRunning )
{
bool isDuplicate = false;
for ( int i = 0; i < m_deduper.size(); i++ )
{
@@ -125,7 +129,7 @@ void
DynamicModel::stopOnDemand( bool stopPlaying )
{
m_onDemandRunning = false;
if( stopPlaying )
if ( stopPlaying )
AudioEngine::instance()->stop();
disconnect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) );
@@ -135,7 +139,7 @@ DynamicModel::stopOnDemand( bool stopPlaying )
void
DynamicModel::changeStation()
{
if( m_onDemandRunning )
if ( m_onDemandRunning )
m_changeOnNext = true;
else // if we're not running, just start
m_playlist->generator()->startOnDemand();
@@ -171,7 +175,7 @@ DynamicModel::trackResolveFinished( bool success )
{
qDebug() << "Got successful resolved track:" << q->track() << q->artist() << m_lastResolvedRow << m_currentAttempts;
if( m_currentAttempts > 0 ) {
if ( m_currentAttempts > 0 ) {
qDebug() << "EMITTING AN ASK FOR COLLAPSE:" << m_lastResolvedRow << m_currentAttempts;
emit collapseFromTo( m_lastResolvedRow, m_currentAttempts );
}
@@ -188,11 +192,14 @@ void
DynamicModel::newTrackLoading()
{
qDebug() << "Got NEW TRACK LOADING signal";
if( m_changeOnNext ) { // reset instead of getting the next one
if ( m_changeOnNext )
{ // reset instead of getting the next one
m_lastResolvedRow = rowCount( QModelIndex() );
m_searchingForNext = true;
m_playlist->generator()->startOnDemand();
} else if( m_onDemandRunning && m_currentAttempts == 0 && !m_searchingForNext ) { // if we're in dynamic mode and we're also currently idle
}
else if ( m_onDemandRunning && m_currentAttempts == 0 && !m_searchingForNext )
{ // if we're in dynamic mode and we're also currently idle
m_lastResolvedRow = rowCount( QModelIndex() );
m_searchingForNext = true;
qDebug() << "IDLE fetching new track!";
@@ -204,13 +211,17 @@ DynamicModel::newTrackLoading()
void
DynamicModel::tracksGenerated( const QList< query_ptr > entries, int limitResolvedTo )
{
if( m_filterUnresolvable && m_playlist->mode() == OnDemand ) { // wait till we get them resolved (for previewing stations)
if ( m_filterUnresolvable && m_playlist->mode() == OnDemand )
{ // wait till we get them resolved (for previewing stations)
m_limitResolvedTo = limitResolvedTo;
filterUnresolved( entries );
} else {
}
else
{
addToPlaylist( entries, m_playlist->mode() == OnDemand ); // if ondemand, we're previewing, so clear old
if( m_playlist->mode() == OnDemand ) {
if ( m_playlist->mode() == OnDemand )
{
m_lastResolvedRow = rowCount( QModelIndex() );
}
}
@@ -224,9 +235,9 @@ DynamicModel::filterUnresolved( const QList< query_ptr >& entries )
{
m_toResolveList = entries;
foreach( const query_ptr& q, entries ) {
foreach ( const query_ptr& q, entries )
connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( filteringTrackResolved( bool ) ) );
}
Pipeline::instance()->resolve( entries, true );
}
@@ -240,7 +251,8 @@ DynamicModel::filteringTrackResolved( bool successful )
// if meantime the user began the station, abort
qDebug() << "Got filtering resolved finished for track, was it successful?:" << q->track() << q->artist() << successful << q->playable();
if( m_onDemandRunning ) {
if ( m_onDemandRunning )
{
m_toResolveList.clear();
m_resolvedList.clear();
@@ -248,8 +260,10 @@ DynamicModel::filteringTrackResolved( bool successful )
}
query_ptr realptr;
foreach( const query_ptr& qptr, m_toResolveList ) {
if( qptr.data() == q ) {
foreach ( const query_ptr& qptr, m_toResolveList )
{
if ( qptr.data() == q )
{
realptr = qptr;
break;
}
@@ -259,25 +273,30 @@ DynamicModel::filteringTrackResolved( bool successful )
m_toResolveList.removeAll( realptr );
if( realptr->playable() ) {
if ( realptr->playable() )
{
m_resolvedList << realptr;
// append and update internal lastResolvedRow
addToPlaylist( QList< query_ptr >() << realptr, false );
if( m_playlist->mode() == OnDemand ) {
if ( m_playlist->mode() == OnDemand )
{
m_lastResolvedRow = rowCount( QModelIndex() );
}
if( m_toResolveList.isEmpty() || m_resolvedList.size() == m_limitResolvedTo ) { // done
if ( m_toResolveList.isEmpty() || m_resolvedList.size() == m_limitResolvedTo )
{ // done
m_toResolveList.clear();
m_resolvedList.clear();
}
} else {
}
else
{
qDebug() << "Got unsuccessful resolve request for this track" << realptr->track() << realptr->artist();
}
if( m_toResolveList.isEmpty() && rowCount( QModelIndex() ) == 0 ) // we failed
if ( m_toResolveList.isEmpty() && rowCount( QModelIndex() ) == 0 ) // we failed
emit trackGenerationFailure( tr( "Could not find a playable track.\n\nPlease change the filters or try again." ) );
}
@@ -285,16 +304,20 @@ DynamicModel::filteringTrackResolved( bool successful )
void
DynamicModel::addToPlaylist( const QList< query_ptr >& entries, bool clearFirst )
{
if( clearFirst )
if ( clearFirst )
clear();
foreach ( const query_ptr& q, entries )
m_deduper.append( QPair< QString, QString >( q->track(), q->artist() ) );
if( m_playlist->author()->isLocal() && m_playlist->mode() == Static ) {
if ( m_playlist->author()->isLocal() && m_playlist->mode() == Static )
{
m_playlist->addEntries( entries, m_playlist->currentrevision() );
} else { // read-only, so add tracks only in the GUI, not to the playlist itself
foreach( const query_ptr& query, entries ) {
}
else
{ // read-only, so add tracks only in the GUI, not to the playlist itself
foreach ( const query_ptr& query, entries )
{
append( query );
}
}
@@ -310,12 +333,15 @@ DynamicModel::remove(const QModelIndex& idx, bool moreToCome)
return;
qDebug() << Q_FUNC_INFO << "DYNAMIC MODEL REMOVIN!" << moreToCome << ( idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) );
if( m_playlist->mode() == OnDemand ) {
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
if ( m_playlist->mode() == OnDemand )
{
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::remove( idx );
} else
}
else
PlaylistModel::remove( idx, moreToCome );
// don't call onPlaylistChanged.

View File

@@ -152,7 +152,7 @@ DynamicPlaylist::create( const Tomahawk::source_ptr& author,
bool autoLoad
)
{
dynplaylist_ptr dynplaylist = Tomahawk::dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, mode, shared, autoLoad ) );
dynplaylist_ptr dynplaylist = Tomahawk::dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, mode, shared, autoLoad ), &QObject::deleteLater );
DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist, autoLoad );
connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) );

View File

@@ -1,6 +1,6 @@
/* === 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

@@ -1,6 +1,6 @@
/* === 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
@@ -32,33 +32,33 @@ namespace Tomahawk
class DynamicModel;
class DynamicView : public PlaylistView
{
Q_OBJECT
public:
explicit DynamicView( QWidget* parent = 0 );
virtual ~DynamicView();
virtual void setDynamicModel( DynamicModel* model );
void setOnDemand( bool onDemand );
void setReadOnly( bool readOnly );
void setDynamicWorking( bool working );
virtual void paintEvent( QPaintEvent* event );
public slots:
void showMessageTimeout( const QString& title, const QString& body );
void showMessage( const QString& message );
// collapse and animate the transition
// there MUST be a row *after* startRow + num. that is, you can't collapse
// entries unless there is at least one entry after the last collapsed row
// optionally you can specify how many rows are past the block of collapsed rows
void collapseEntries( int startRow, int num, int numToKeep = 1 );
private slots:
void onTrackCountChanged( unsigned int );
void checkForOverflow();
@@ -70,7 +70,7 @@ private:
DynamicModel* m_model;
QString m_title;
QString m_body;
bool m_onDemand;
bool m_readOnly;
bool m_checkOnCollapse;
@@ -88,7 +88,7 @@ private:
QTimeLine m_fadeOutAnim;
QTimeLine m_slideAnim;
};
};

View File

@@ -181,6 +181,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide();
m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input );
m_data.first = m_currentType;
} else if( selectedType() == "Artist Description" ) {
m_currentType = Echonest::DynamicPlaylist::Description;
@@ -199,6 +200,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide();
m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input );
m_data.first = m_currentType;
} else if( selectedType() == "User Radio" ) {
m_currentType = Echonest::DynamicPlaylist::SourceCatalog;
@@ -246,6 +248,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide();
m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input );
m_data.first = m_currentType;
} else if( selectedType() == "Variety" ) {
m_currentType = Echonest::DynamicPlaylist::Variety;
@@ -266,6 +269,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide();
m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input );
m_data.first = m_currentType;
} else if( selectedType() == "Adventurousness" ) {
m_currentType = Echonest::DynamicPlaylist::Adventurousness;
@@ -287,6 +291,7 @@ Tomahawk::EchonestControl::updateWidgets()
input->hide();
m_match = QWeakPointer< QWidget >( match );
m_input = QWeakPointer< QWidget >( input );
m_data.first = m_currentType;
} else if( selectedType() == "Tempo" ) {
m_currentType = Echonest::DynamicPlaylist::MinTempo;

View File

@@ -597,7 +597,9 @@ EchonestGenerator::sentenceSummary()
suffix = ", ";
else
suffix = ".";
} else
} else if ( i < required.size() - 2 ) // An item in the list that is before the second to last one, don't use ", and", we only want that for the last item
suffix += ", ";
else
suffix += ", and ";
}
sentence += center + suffix;

View File

@@ -118,32 +118,34 @@ DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
{
// special case: if we have launched multiple setRevision calls, and the number of controls is different, it means that we're getting an intermediate setRevision
// called after the user has already created more revisions. ignore in that case.
if( m_playlist.data() == playlist.data() && m_seqRevLaunched > 0
if ( m_playlist.data() == playlist.data() && m_seqRevLaunched > 0
&& m_controls->controls().size() != playlist->generator()->controls().size() // different number of controls
&& qAbs( m_playlist->generator()->controls().size() - playlist->generator()->controls().size() ) < m_seqRevLaunched ) { // difference in controls has to be less than how many revisions we launched
&& qAbs( m_playlist->generator()->controls().size() - playlist->generator()->controls().size() ) < m_seqRevLaunched )
{ // difference in controls has to be less than how many revisions we launched
return;
}
m_seqRevLaunched = 0;
// if we're being told to load the same dynamic playlist over again, only do it if the controls have a different number
if( !m_playlist.isNull() && ( m_playlist.data() == playlist.data() ) // same playlist pointer
&& m_playlist->generator()->controls().size() == playlist->generator()->controls().size() ) {
if ( !m_playlist.isNull() && ( m_playlist.data() == playlist.data() ) // same playlist pointer
&& m_playlist->generator()->controls().size() == playlist->generator()->controls().size() )
{
// we can skip our work. just let the dynamiccontrollist show the difference
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
m_playlist = playlist;
if( !m_runningOnDemand ) {
if ( !m_runningOnDemand )
m_model->loadPlaylist( m_playlist );
} else if( !m_controlsChanged ) { // if the controls changed, we already dealt with that and don't want to change station yet
else if ( !m_controlsChanged ) // if the controls changed, we already dealt with that and don't want to change station yet
m_model->changeStation();
}
m_controlsChanged = false;
return;
}
if( !m_playlist.isNull() ) {
if ( !m_playlist.isNull() )
{
disconnect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
disconnect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ), this, SLOT(onRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ) );
disconnect( m_playlist->generator().data(), SIGNAL( error( QString, QString ) ), this, SLOT( generatorError( QString, QString ) ) );
@@ -160,23 +162,22 @@ DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
m_setup->setPlaylist( m_playlist );
if( !m_playlist->author()->isLocal() ) { // hide controls, as we show the description in the summary
if ( !m_playlist->author()->isLocal() ) // hide controls, as we show the description in the summary
m_layout->removeWidget( m_controls );
} else if( m_layout->indexOf( m_controls ) == -1 ) {
else if ( m_layout->indexOf( m_controls ) == -1 )
m_layout->insertWidget( 0, m_controls );
}
if( m_playlist->mode() == OnDemand && !m_playlist->generator()->controls().isEmpty() )
showPreview();
if( !m_playlist.isNull() )
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
connect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
connect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) );
connect( m_playlist->generator().data(), SIGNAL( error( QString, QString ) ), this, SLOT( generatorError( QString, QString ) ) );
connect( m_playlist.data(), SIGNAL( deleted( Tomahawk::dynplaylist_ptr ) ), this, SLOT( onDeleted() ) );
connect( m_playlist.data(), SIGNAL( changed() ), this, SLOT( onChanged() ) );
if ( m_playlist->mode() == OnDemand && !m_playlist->generator()->controls().isEmpty() )
showPreview();
if ( !m_playlist.isNull() )
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
}
@@ -226,12 +227,15 @@ DynamicWidget::resizeEvent(QResizeEvent* )
void
DynamicWidget::layoutFloatingWidgets()
{
if( !m_runningOnDemand ) {
if ( !m_runningOnDemand )
{
int x = ( width() / 2 ) - ( m_setup->size().width() / 2 );
int y = height() - m_setup->size().height() - 40; // padding
m_setup->move( x, y );
} else if( m_runningOnDemand && m_steering ) {
}
else if( m_runningOnDemand && m_steering )
{
int x = ( width() / 2 ) - ( m_steering->size().width() / 2 );
int y = height() - m_steering->size().height() - 40; // padding
@@ -243,13 +247,15 @@ DynamicWidget::layoutFloatingWidgets()
void
DynamicWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl )
{
if( pl == m_view->proxyModel()->playlistInterface() ) { // same playlist
if ( pl == m_view->proxyModel()->playlistInterface() ) // same playlist
m_activePlaylist = true;
} else {
else
{
m_activePlaylist = false;
// user started playing something somewhere else, so give it a rest
if( m_runningOnDemand ) {
if ( m_runningOnDemand )
{
stopStation( false );
}
}
@@ -259,9 +265,8 @@ DynamicWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl )
void
DynamicWidget::showEvent(QShowEvent* )
{
if( !m_playlist.isNull() && !m_runningOnDemand ) {
if ( !m_playlist.isNull() && !m_runningOnDemand )
m_setup->fadeIn();
}
}
@@ -289,8 +294,9 @@ DynamicWidget::stationFailed( const QString& msg )
void
DynamicWidget::trackStarted()
{
if( m_activePlaylist && !m_playlist.isNull() &&
m_playlist->mode() == OnDemand && !m_runningOnDemand ) {
if ( m_activePlaylist && !m_playlist.isNull() &&
m_playlist->mode() == OnDemand && !m_runningOnDemand )
{
startStation();
}
@@ -300,7 +306,7 @@ DynamicWidget::trackStarted()
void
DynamicWidget::tracksAdded()
{
if( m_playlist->mode() == OnDemand && m_runningOnDemand && m_setup->isVisible() )
if ( m_playlist->mode() == OnDemand && m_runningOnDemand && m_setup->isVisible() )
m_setup->fadeOut();
}
@@ -325,7 +331,8 @@ DynamicWidget::startStation()
m_setup->fadeOut();
// show the steering controls
if( m_playlist->generator()->onDemandSteerable() ) {
if ( m_playlist->generator()->onDemandSteerable() )
{
// position it horizontally centered, above the botton.
m_steering = m_playlist->generator()->steeringWidget();
Q_ASSERT( m_steering );
@@ -361,7 +368,7 @@ DynamicWidget::tracksGenerated( const QList< query_ptr >& queries )
{
m_resolveOnNextLoad = true;
}
else if( m_playlist->mode() == OnDemand )
else if ( m_playlist->mode() == OnDemand )
{
limit = 5;
}
@@ -380,7 +387,7 @@ DynamicWidget::controlsChanged( bool added )
// when playing a station just ignore it till we're ready and get a controlChanged()
m_controlsChanged = true;
if( !m_playlist->author()->isLocal() )
if ( !m_playlist->author()->isLocal() )
return;
m_playlist->createNewRevision();
m_seqRevLaunched++;
@@ -396,7 +403,7 @@ void
DynamicWidget::controlChanged( const Tomahawk::dyncontrol_ptr& control )
{
Q_UNUSED( control );
if( !m_playlist->author()->isLocal() )
if ( !m_playlist->author()->isLocal() )
return;
m_playlist->createNewRevision();
m_seqRevLaunched++;
@@ -443,8 +450,7 @@ DynamicWidget::steeringChanged()
void
DynamicWidget::showPreview()
{
if ( m_playlist->mode() == OnDemand &&
!m_runningOnDemand )
if ( m_playlist->mode() == OnDemand && !m_runningOnDemand )
{
// if this is a not running station, preview matching tracks
m_model->clear();
@@ -456,12 +462,16 @@ DynamicWidget::showPreview()
void
DynamicWidget::generatorError( const QString& title, const QString& content )
{
if( m_runningOnDemand ) {
stopStation( false );
}
m_view->setDynamicWorking( false );
m_loading->fadeOut();
m_view->showMessageTimeout( title, content );
if ( m_runningOnDemand )
{
stopStation( false );
m_view->showMessage( tr( "Station ran out of tracks!\n\nTry tweaking the filters for a new set of songs to play." ) );
}
else
m_view->showMessageTimeout( title, content );
}
@@ -528,7 +538,7 @@ DynamicWidget::onDeleted()
void
DynamicWidget::onChanged()
{
if( !m_playlist.isNull() &&
ViewManager::instance()->currentPage() == this )
emit nameChanged( m_playlist->title() );
if ( !m_playlist.isNull() &&
ViewManager::instance()->currentPage() == this )
emit nameChanged( m_playlist->title() );
}

View File

@@ -104,10 +104,13 @@ PlaylistItemDelegate::prepareStyleOption( QStyleOptionViewItemV4* option, const
if ( item->isPlaying() )
{
option->palette.setColor( QPalette::Highlight, option->palette.color( QPalette::Mid ) );
option->state |= QStyle::State_Selected;
option->backgroundBrush = option->palette.color( QPalette::Mid );
option->palette.setColor( QPalette::Text, option->palette.color( QPalette::Text ) );
}
if ( option->state & QStyle::State_Selected )
if ( option->state & QStyle::State_Selected && !item->isPlaying() )
{
option->palette.setColor( QPalette::Text, option->palette.color( QPalette::HighlightedText ) );
}

View File

@@ -69,7 +69,7 @@ TrackProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParen
return false;
const Tomahawk::query_ptr& q = pi->query();
if( q.isNull() ) // uh oh? filter out invalid queries i guess
if ( q.isNull() ) // uh oh? filter out invalid queries i guess
return false;
Tomahawk::result_ptr r;
@@ -190,7 +190,7 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
album1 = r->album()->name();
track1 = r->track();
albumpos1 = r->albumpos();
discnumber1 = r->discnumber();
discnumber1 = qMax( 1, (int)r->discnumber() );
bitrate1 = r->bitrate();
mtime1 = r->modificationTime();
id1 = r->trackId();
@@ -203,7 +203,7 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
album2 = r->album()->name();
track2 = r->track();
albumpos2 = r->albumpos();
discnumber2 = r->discnumber();
discnumber2 = qMax( 1, (int)r->discnumber() );
bitrate2 = r->bitrate();
mtime2 = r->modificationTime();
id2 = r->trackId();
@@ -223,7 +223,7 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
{
if ( album1 == album2 )
{
if( discnumber1 == discnumber2 )
if ( discnumber1 == discnumber2 )
{
if ( albumpos1 == albumpos2 )
return id1 < id2;
@@ -243,7 +243,7 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
{
if ( album1 == album2 )
{
if( discnumber1 == discnumber2 )
if ( discnumber1 == discnumber2 )
{
if ( albumpos1 == albumpos2 )
return id1 < id2;
@@ -277,6 +277,18 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
return size1 < size2;
}
else if ( left.column() == TrackModel::AlbumPos ) // sort by album pos
{
if ( discnumber1 != discnumber2 )
{
return discnumber1 < discnumber2;
}
else
{
if ( albumpos1 != albumpos2 )
return albumpos1 < albumpos2;
}
}
const QString& lefts = sourceModel()->data( left ).toString();
const QString& rights = sourceModel()->data( right ).toString();

View File

@@ -32,6 +32,7 @@
#include "dynamic/widgets/LoadingSpinner.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#include "utils/closure.h"
#include "dropjob.h"
#include "artist.h"
#include "album.h"
@@ -152,6 +153,41 @@ TrackView::setTrackModel( TrackModel* model )
}
void
TrackView::startPlayingFromStart()
{
if ( m_proxyModel->rowCount() == 0 )
return;
const QModelIndex index = m_proxyModel->index( 0, 0 );
startAutoPlay( index );
}
void
TrackView::autoPlayResolveFinished( const query_ptr& query, int row )
{
Q_ASSERT( !query.isNull() );
Q_ASSERT( row >= 0 );
if ( query.isNull() || row < 0 || query != m_autoPlaying )
return;
const QModelIndex index = m_proxyModel->index( row, 0 );
if ( query->playable() )
{
onItemActivated( index );
return;
}
// Try the next one..
const QModelIndex sib = index.sibling( index.row() + 1, index.column() );
if ( sib.isValid() )
startAutoPlay( sib );
}
void
TrackView::currentChanged( const QModelIndex& current, const QModelIndex& previous )
{
@@ -174,15 +210,48 @@ TrackView::onItemActivated( const QModelIndex& index )
if ( !index.isValid() )
return;
tryToPlayItem( index );
emit itemActivated( index );
}
void
TrackView::startAutoPlay( const QModelIndex& index )
{
if ( tryToPlayItem( index ) )
return;
// item isn't playable but still resolving
TrackModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) );
if ( item && !item->query().isNull() && !item->query()->resolvingFinished() )
{
m_autoPlaying = item->query(); // So we can kill it if user starts autoplaying this playlist again
NewClosure( item->query().data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( autoPlayResolveFinished( Tomahawk::query_ptr, int ) ),
item->query(), index.row() );
return;
}
// not playable at all, try next
const QModelIndex sib = index.sibling( index.row() + 1, index.column() );
if ( sib.isValid() )
startAutoPlay( sib );
}
bool
TrackView::tryToPlayItem( const QModelIndex& index )
{
TrackModelItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) );
if ( item && !item->query().isNull() && item->query()->numResults() )
{
tDebug() << "Result activated:" << item->query()->toString() << item->query()->results().first()->url();
m_proxyModel->setCurrentIndex( index );
AudioEngine::instance()->playItem( m_proxyModel->playlistInterface(), item->query()->results().first() );
return true;
}
emit itemActivated( index );
return false;
}

View File

@@ -64,6 +64,9 @@ public:
bool updatesContextView() const { return m_updateContextView; }
void setUpdatesContextView( bool b ) { m_updateContextView = b; }
// Starts playing from the beginning if resolved, or waits until a track is playable
void startPlayingFromStart();
public slots:
virtual void onItemActivated( const QModelIndex& index );
@@ -98,7 +101,11 @@ private slots:
void onCustomContextMenu( const QPoint& pos );
void autoPlayResolveFinished( const Tomahawk::query_ptr& query, int row );
private:
void startAutoPlay( const QModelIndex& index );
bool tryToPlayItem( const QModelIndex& index );
void updateHoverIndex( const QPoint& pos );
QString m_guid;
@@ -117,6 +124,9 @@ private:
QModelIndex m_hoveredIndex;
QModelIndex m_contextMenuIndex;
Tomahawk::query_ptr m_autoPlaying;
Tomahawk::ContextMenu* m_contextMenu;
};

View File

@@ -155,32 +155,18 @@ TreeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
QPixmap cover;
if ( !item->album().isNull() )
{
cover.loadFromData( item->album()->cover() );
cover = item->album()->cover( r.size(), false );
if ( cover.isNull() )
cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::ScaledCover, r.size() );
}
else if ( !item->artist().isNull() )
{
cover.loadFromData( item->artist()->cover() );
cover = item->artist()->cover( r.size(), false );
if ( cover.isNull() )
cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::ScaledCover, r.size() );
}
QPixmap scover;
if ( cover.isNull() )
{
if ( !item->artist().isNull() )
cover = m_defaultArtistImage;
else
cover = m_defaultAlbumCover;
}
if ( m_cache.contains( cover.cacheKey() ) )
{
scover = m_cache.value( cover.cacheKey() );
}
else
{
scover = cover.scaled( r.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
m_cache.insert( cover.cacheKey(), scover );
}
painter->drawPixmap( r, scover );
painter->drawPixmap( r, cover );
QTextOption to;
to.setAlignment( Qt::AlignVCenter );

View File

@@ -43,8 +43,6 @@ private:
ArtistView* m_view;
TreeProxyModel* m_model;
mutable QHash< qint64, QPixmap > m_cache;
QPixmap m_nowPlayingIcon;
QPixmap m_defaultAlbumCover;
QPixmap m_defaultArtistImage;

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 2012, 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
@@ -20,6 +21,7 @@
#include <QMimeData>
#include "pipeline.h"
#include "source.h"
#include "sourcelist.h"
#include "audio/audioengine.h"
@@ -87,6 +89,18 @@ TreeModel::collection() const
}
void
TreeModel::getCover( const QModelIndex& index )
{
TreeModelItem* item = itemFromIndex( index );
if ( !item->artist().isNull() && !item->artist()->infoLoaded() )
item->artist()->cover( QSize( 0, 0 ) );
else if ( !item->album().isNull() && !item->album()->infoLoaded() )
item->album()->cover( QSize( 0, 0 ) );
}
void
TreeModel::setCurrentItem( const QModelIndex& index )
{
@@ -609,7 +623,7 @@ TreeModel::addAlbums( const artist_ptr& artist, const QModelIndex& parent, bool
requestData.caller = m_infoId;
requestData.customData["row"] = parent.row();
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
requestData.customData["refetch"] = QVariant( autoRefetch );
requestData.customData["refetch"] = autoRefetch;
requestData.type = Tomahawk::InfoSystem::InfoArtistReleases;
Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
}
@@ -647,8 +661,8 @@ TreeModel::addTracks( const album_ptr& album, const QModelIndex& parent, bool au
m_receivedInfoData.removeAll( artistInfo );
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = m_infoId;
requestData.customData["rows"] = QVariant( rows );
requestData.customData["refetch"] = QVariant( autoRefetch );
requestData.customData["rows"] = rows;
requestData.customData["refetch"] = autoRefetch;
requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
requestData.type = Tomahawk::InfoSystem::InfoAlbumSongs;
requestData.timeoutMillis = 0;
@@ -783,6 +797,8 @@ TreeModel::onAlbumsAdded( const QList<Tomahawk::album_ptr>& albums, const QModel
albumitem = new TreeModelItem( album, parentItem );
albumitem->index = createIndex( parentItem->children.count() - 1, 0, albumitem );
connect( albumitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) );
getCover( albumitem->index );
}
emit endInsertRows();
@@ -871,7 +887,7 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV
QModelIndex idx = index( requestData.customData[ "row" ].toInt(), 0, QModelIndex() );
if ( requestData.customData[ "refetch" ].toInt() > 0 && !al.count() )
if ( requestData.customData[ "refetch" ].toBool() && !al.count() )
{
setMode( DatabaseMode );
@@ -915,16 +931,23 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV
foreach ( const QString& trackName, tracks )
{
query_ptr query = Query::get( inputInfo[ "artist" ], trackName, inputInfo[ "album" ], uuid() );
query_ptr query = Query::get( inputInfo[ "artist" ], trackName, inputInfo[ "album" ] );
query->setAlbumPos( trackNo++ );
ql << query;
}
Pipeline::instance()->resolve( ql );
onTracksAdded( ql, idx );
}
else if ( m_receivedInfoData.count() == 2 /* FIXME */ )
{
if ( requestData.customData[ "refetch" ].toInt() > 0 )
// If the second load got no data, but the first load did, don't do anything
QList< QVariant > rows = requestData.customData[ "rows" ].toList();
QModelIndex idx = index( rows.first().toUInt(), 0, index( rows.at( 1 ).toUInt(), 0, QModelIndex() ) );
if ( rowCount( idx ) )
return;
if ( requestData.customData[ "refetch" ].toBool() )
{
setMode( DatabaseMode );

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