Merge lp:~ahayzen/music-app/add-url-dispatcher-album into lp:music-app/trusty

Proposed by Andrew Hayzen
Status: Merged
Approved by: Victor Thompson
Approved revision: 433
Merged at revision: 414
Proposed branch: lp:~ahayzen/music-app/add-url-dispatcher-album
Merge into: lp:music-app/trusty
Diff against target: 268 lines (+105/-64)
4 files modified
CMakeLists.txt (+1/-1)
com.ubuntu.music_music.url-dispatcher (+8/-0)
meta-database.js (+18/-4)
music-app.qml (+78/-59)
To merge this branch: bzr merge lp:~ahayzen/music-app/add-url-dispatcher-album
Reviewer Review Type Date Requested Status
Victor Thompson Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Andrew Hayzen Approve
Review via email: mp+214414@code.launchpad.net

Commit message

* Add album:///artist/album and music:///path/to/file url dispatcher support
* Simplification of file:// handling

Description of the change

* Add album:///artist/album and music:///path/to/file url dispatcher support
* Simplification of file:// handling

Installation:
- Desktop
cd /tmp
bzr branch lp:~andrew-hayzen/music-app/add-url-dispatcher-album music-app
cd music-app
click-buddy --dir . --provision
rm -rf /tmp/music-app

- Mobile
cp -r /usr/share/click/preinstalled/com.ubuntu.music/current/lib /opt/click.ubuntu.com/com.ubuntu.music/current/
pkill unity8

Testing:
On device use the commands below to, for example, start playing the album 'Day & Age' or the track 'Flesh and Bone'.
The protocols file:// and album:// should be tested when the app is in both an open and closed state.
sudo -u phablet -i
url-dispatcher "album:///The Killers/Day & Age"
url-dispatcher "file:///home/phablet/Music/Battle Born/01 Flesh and Bone.m4a"

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michal Hruby (mhr3) wrote :

9 + set(EXEC "qmlscene -qt5 ${MAIN_QML} --file=%u -I ./plugins")

Would be probably better to rename the "file" param to "url".

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Good idea when I'm back at my pc I'll update it, thanks :-)

Revision history for this message
Victor Thompson (vthompson) wrote :

On Trusty r278 url-dispatcher is not in $PATH and when executing the following directly:

/usr/lib/arm-linux-gnueabihf/url-dispatcher/url-dispatcher

I get the following error:

** (process:4424): ERROR **: Unable to connect to D-Bus: Cannot autolaunch D-Bus without X11 $DISPLAY

Is there something else I need to do to utilize this program?

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Victor, are you logged in as the phablet user rather than root. If root run the following command to switch then try again.
sudo -u phablet -i

Revision history for this message
Victor Thompson (vthompson) wrote :

I am:

root@ubuntu-phablet:~# sudo -u phablet -i
phablet@ubuntu-phablet:~$ url-dispatcher "file:///home/phablet/Music/Islands/Return to the Sea/09 If.mp3"
-bash: url-dispatcher: command not found
phablet@ubuntu-phablet:~$ /usr/lib/arm-linux-gnueabihf/url-dispatcher/url-dispatcher "file:///home/phablet/Music/Islands/Return to the Sea/09 If.mp3"

** (process:3060): WARNING **: Unable to get name 'com.canonical.URLDispatcher'
phablet@ubuntu-phablet:~$

I just upgraded to r279 and the error message is different than it was for my attempt with r278.

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Hmmm strange IIRC I was on 227, I'm currently traveling home for easter I'll have a look when I get back.

Revision history for this message
Michal Hruby (mhr3) wrote :

> On Trusty r278 url-dispatcher is not in $PATH and when executing the following
> directly:
>
> /usr/lib/arm-linux-gnueabihf/url-dispatcher/url-dispatcher

That's the url-dispatcher daemon, not the binary to launch urls, that one is in pkg url-dispatcher-tools.

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Victor Thompson (vthompson) wrote :

Michal, thank you.

Andrew, would it make sense to enforce the album uri scheme to be "album://Artist/Album" rather than "album:///Artist/Album". The 3rd "/" in most URI handlers seem to indicate the root directory--which doesn't exist in the URI scheme we are introducing.

I'll try to review the rest shortly.

review: Needs Information
Revision history for this message
Victor Thompson (vthompson) wrote :

One thing to think about is that albums and artists can have the slash "/" character in them [1]. Perhaps we should pick another delimiter? I think we should talk with the Music scope folks to see if we can come up with a better solution.

[1] http://www.last.fm/search?q=/&type=album and http://www.last.fm/search?q=/&type=artist

Revision history for this message
Michal Hruby (mhr3) wrote :

> Andrew, would it make sense to enforce the album uri scheme to be
> "album://Artist/Album" rather than "album:///Artist/Album". The 3rd "/" in
> most URI handlers seem to indicate the root directory--which doesn't exist in
> the URI scheme we are introducing.

album://Artist/Album would make the Artist host part of the uri, and the host part has encoding limitations, that's why we're forcing everything to be path. Also, if there's a '/' in the name, it'd be encoded with '%2F', as in http uris.

Revision history for this message
Victor Thompson (vthompson) wrote :

Thanks again. Andrew, in that case most of this looks great. I think we should decode the album:// URI in the split array after we split the string, however.

review: Needs Fixing
428. By Andrew Hayzen

* Add decodeURIComponent to split album:// parameters

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Victor Thompson (vthompson) wrote :

This modification breaks the fix for lp:1292921. It appears as though the decided file parameter won't no longer matches the encoded (?) file in the music library?

review: Needs Fixing
Revision history for this message
Victor Thompson (vthompson) wrote :

Sorry, decoded file parameter

429. By Andrew Hayzen

* Add music:// protocol
* Modify URL handling

430. By Andrew Hayzen

* Fix for encoding

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

After investigations I found that the url stored in the database is neither encoded nor decoded, it is somewhat in between.

If you have a track with the file path ending
~!@#$%^&*(){}[]:,;?+\'"\\ .mp3
It is stored in the database as
~!@%23$%25%5E&*()%7B%7D%5B%5D:,%3B%3F+%5C'%22%5C%5C .mp3
It appears from the url-dispatcher as
~!@%23$%25%5E&*()%7B%7D[]:,;%3F+%5C'%22%5C%5C%20.mp3

Therefore ; [ ] need to be switched to %3B %5B %5D and a %20 needs to be switched to a space.

Note that a / and = cannot be used in the filename as a / is used to declare a folder and a = breaks the command line arguments.

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Ah international characters now cause issues such as é. Anyone got any better ideas to make the dispatcher url the same as the database url?

review: Needs Fixing
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
431. By Andrew Hayzen

* decode file before it goes in the database

432. By Andrew Hayzen

* Revert to using linear search for processFile()

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

I've reverting to using a linear search like before. I believe everything is fixed now, please retest :)

review: Approve
433. By Andrew Hayzen

* Remove tracing

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Victor Thompson (vthompson) wrote :

Awesome, thanks. We'll look at a way not to do the 'needle in a haystack' linear search in the future. Nice work!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-03-10 10:17:36 +0000
3+++ CMakeLists.txt 2014-04-09 20:30:41 +0000
4@@ -25,7 +25,7 @@
5 set(CMAKE_INSTALL_BINDIR /)
6 set(DATA_DIR /)
7 set(ICON ${ICON_FILE})
8- set(EXEC "qmlscene -qt5 ${MAIN_QML} --file=%f -I ./plugins")
9+ set(EXEC "qmlscene -qt5 ${MAIN_QML} --url=%u -I ./plugins")
10 set(DESKTOP_DIR ${DATA_DIR})
11 set(URLS_DIR ${DATA_DIR})
12 else(CLICK_MODE)
13
14=== modified file 'com.ubuntu.music_music.url-dispatcher'
15--- com.ubuntu.music_music.url-dispatcher 2014-01-31 20:43:59 +0000
16+++ com.ubuntu.music_music.url-dispatcher 2014-04-09 20:30:41 +0000
17@@ -0,0 +1,8 @@
18+[
19+ {
20+ "protocol": "album"
21+ },
22+ {
23+ "protocol": "music"
24+ }
25+]
26
27=== modified file 'meta-database.js'
28--- meta-database.js 2014-03-06 01:57:57 +0000
29+++ meta-database.js 2014-04-09 20:30:41 +0000
30@@ -123,17 +123,16 @@
31 }
32
33 // This function is used to retrieve meta data from the database
34-function getMetadata(file,type) {
35+function getMetadata(file) {
36 var db = getDatabase();
37 var res="";
38
39 try {
40 db.transaction(function(tx) {
41- //var rs = tx.executeSql('SELECT type=?;',[type],' FROM metadata WHERE file=?;', [file]); // tries to get the title of track
42- var rs = tx.executeSql('SELECT ? FROM metadata WHERE file=?;', [type,file]); // tries to get the title of track
43+ var rs = tx.executeSql('SELECT * FROM metadata WHERE file=?;', [file]); // tries to get the title of track
44
45 if (rs.rows.length > 0) {
46- res = rs.rows.item(0).value;
47+ res = rs.rows.item(0);
48 } else {
49 res = "Unknown";
50 }
51@@ -335,6 +334,21 @@
52 return res;
53 }
54
55+function getArtistAlbumTracks(artist, album) {
56+ var res = [];
57+ var db = getDatabase();
58+ //console.log("Album: " + album);
59+ db.transaction( function(tx) {
60+ var rs = tx.executeSql("SELECT * FROM metadata WHERE artist=? AND album=? ORDER BY artist COLLATE NOCASE ASC, album COLLATE NOCASE ASC, CAST(number AS int) ASC", [artist, album]);
61+ for(var i = 0; i < rs.rows.length; i++) {
62+ var dbItem = rs.rows.item(i);
63+ //console.log("Artist:"+ dbItem.artist + ", Album:"+dbItem.album + ", Title:"+dbItem.title + ", File:"+dbItem.file + ", Art:"+dbItem.cover + ", Genre:"+dbItem.genre);
64+ res.push({artist:dbItem.artist, album:dbItem.album, title:dbItem.title, file:dbItem.file, cover:dbItem.cover, length:dbItem.length, year:dbItem.year, genre:dbItem.genre});
65+ }
66+ });
67+ return res;
68+}
69+
70 function getGenres() {
71 var res = [];
72 var db = getDatabase();
73
74=== modified file 'music-app.qml'
75--- music-app.qml 2014-03-28 18:59:23 +0000
76+++ music-app.qml 2014-04-09 20:30:41 +0000
77@@ -117,7 +117,7 @@
78 //defaultArgument.valueNames: ["URI"] // should be used when bug is resolved
79 // grab a file
80 Argument {
81- name: "file"
82+ name: "url"
83 help: "URI for track to run at start."
84 required: false
85 valueNames: ["track"]
86@@ -198,42 +198,85 @@
87 // (already in the database), not e.g. http:// URIs or files in directories
88 // not picked up by Grilo
89 Connections {
90+ id: uriHandler
91 target: UriHandler
92+
93+ function processAlbum(uri) {
94+ var split = uri.split("/");
95+
96+ if (split.length < 2) {
97+ console.debug("Unknown artist-album " + uri + ", skipping")
98+ return;
99+ }
100+
101+ // Get tracks
102+ var tracks = Library.getArtistAlbumTracks(decodeURIComponent(split[0]), decodeURIComponent(split[1]));
103+
104+ if (tracks.length === 0) {
105+ console.debug("Unknown artist-album " + uri + ", skipping")
106+ return;
107+ }
108+
109+ // Enqueue
110+ for (var track in tracks) {
111+ trackQueue.append(tracks[track]);
112+ }
113+
114+ // Play first track
115+ trackClicked(trackQueue, 0, true);
116+ }
117+
118+ function processFile(uri, play) {
119+ uri = decodeURIComponent(uri);
120+
121+ // search for path in library
122+ var library = Library.getAll();
123+ var track = false;
124+
125+ for (var item in library) {
126+ if (decodeURIComponent(library[item].file) === uri) {
127+ track = library[item];
128+ break;
129+ }
130+ }
131+
132+ if (!track) {
133+ console.debug("Unknown file " + uri + ", skipping")
134+ return;
135+ }
136+
137+ // enqueue
138+ trackQueue.append(track);
139+
140+ // play first URI
141+ if (play) {
142+ trackClicked(trackQueue, 0, true)
143+ }
144+ }
145+
146+ function process(uri, play) {
147+ if (uri.indexOf("album:///") === 0) {
148+ uriHandler.processAlbum(uri.substring(9));
149+ }
150+ else if (uri.indexOf("file://") === 0) {
151+ uriHandler.processFile(uri.substring(7), play);
152+ }
153+ else if (uri.indexOf("music://") === 0) {
154+ uriHandler.processFile(uri.substring(8), play);
155+ }
156+
157+ else {
158+ console.debug("Unsupported URI " + uri + ", skipping")
159+ }
160+ }
161+
162 onOpened: {
163 // clear play queue
164 trackQueue.model.clear()
165 for (var i=0; i < uris.length; i++) {
166 console.debug("URI=" + uris[i])
167- // skip non-file:// URIs
168- if (uris[i].substring(0, 7) !== "file://") {
169- console.debug("Unsupported URI " + uris[i] + ", skipping")
170- continue
171- }
172-
173- // search pathname in library
174- var file = decodeURIComponent(uris[i])
175- var index = -1;
176-
177- for (var j=0; j < griloModel.count; j++)
178- {
179- if (decodeURIComponent(griloModel.get(j).url.toString()) == file)
180- {
181- index = j;
182- }
183- }
184-
185- if (index <= -1) {
186- console.debug("Unknown file " + file + ", skipping")
187- continue
188- }
189-
190- // enqueue
191- trackQueue.append(griloModel.get(index));
192-
193- // play first URI
194- if (i == 0) {
195- trackClicked(trackQueue, 0, true)
196- }
197+
198+ uriHandler.process(uris[i], i === 0);
199 }
200 }
201 }
202@@ -280,19 +323,6 @@
203 customdebug("Arguments on startup: Debug: "+args.values.debug)
204
205 customdebug("Arguments on startup: Debug: "+args.values.debug+ " and file: ")
206- if (args.values.file) {
207- argFile = args.values.file
208- if (argFile.indexOf("file://") != -1) {
209- //customdebug("arg contained file://")
210- // strip that!
211- argFile = argFile.substring(7)
212- }
213- else {
214- // do nothing
215- customdebug("arg did not contain file://")
216- }
217- customdebug(argFile)
218- }
219
220 Settings.initialize()
221 console.debug("INITIALIZED in tracks")
222@@ -333,7 +363,6 @@
223 property string lastfmusername
224 property string lastfmpassword
225 property string timestamp // used to scrobble
226- property string argFile // used for argumented track
227 property var chosenElement: null
228 property LibraryListModel currentModel: null // Current model being used
229 property var currentQuery: null
230@@ -638,8 +667,6 @@
231 }
232
233 onFinished: {
234- var read_arg = false;
235-
236 // FIXME: remove when grilo is fixed
237 var files = [];
238 var duplicates = 0;
239@@ -673,18 +700,6 @@
240 genre: media.genre || i18n.tr("Unknown Genre")
241 };
242
243- if (read_arg === false && decodeURIComponent(argFile) === decodeURIComponent(file))
244- {
245- trackQueue.model.clear();
246- trackQueue.model.append(record)
247- trackClicked(trackQueue, 0, true);
248-
249- // grilo model sometimes has duplicates
250- // causing the track to be paused the second time
251- // this ignores the second time
252- read_arg = true;
253- }
254-
255 //console.log("Artist:"+ media.artist + ", Album:"+media.album + ", Title:"+media.title + ", File:"+file + ", Cover:"+media.thumbnail + ", Number:"+media.trackNumber + ", Genre:"+media.genre);
256 Library.setMetadata(record)
257 }
258@@ -694,6 +709,10 @@
259 console.debug("Grilo duplicates:", duplicates); // FIXME: remove when grilo is fixed
260 griloModel.loaded = true
261 tabs.ensurePopulated(tabs.selectedTab);
262+
263+ if (args.values.url) {
264+ uriHandler.process(args.values.url, true);
265+ }
266 }
267 }
268 }

Subscribers

People subscribed via source and target branches

to status/vote changes: