Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-24486] iOS: How to stringify a mediaItem properly.

GitHub Issuen/a
TypeStory
PriorityNone
StatusClosed
ResolutionDuplicate
Resolution Date2017-03-18T16:39:07.000+0000
Affected Version/sRelease 6.0.2
Fix Version/sn/a
Componentsn/a
Labelsi0S, media, openMusicLibrary
ReporterMotiur Rahman
AssigneeHans Knöchel
Created2017-03-14T20:51:04.000+0000
Updated2017-04-19T16:50:00.000+0000

Description

description

Experiencing the JSON.stringify on a media item returns "{}" instead of a real stringified output that I can parse.

var win = Ti.UI.createWindow({
		backgroundColor : '#fff'
	});

	var btn = Ti.UI.createButton({
		title : 'Trigger'
	});

	btn.addEventListener('click', function() {
		Titanium.Media.openMusicLibrary({
			allowMultipleSelections : false,
			mediaTypes : Titanium.Media.MUSIC_MEDIA_TYPE_MUSIC | Titanium.Media.MUSIC_MEDIA_TYPE_ANY_AUDIO,
			success : function(event) {
				// called when media returned from the MusicLibrary
				alert("Results: " + JSON.stringify(event.items[0]));
			},
			cancel : function() {
				alert("Aborting ");
			},
			error : function(error) {
				// called when there's an error
				var a = Titanium.UI.createAlertDialog({
					title : 'Music Library'
				});
				if (error.code == Titanium.Media.NO_MUSIC_PLAYER) {
					a.setMessage('Please run this test on device.');
				} else {
					a.setMessage('Unexpected error: ' + error.code);
				}
				a.show();
			}
		});

	});

	win.add(btn);

 win.open();

Thanks

Comments

  1. Hans Knöchel 2017-03-14

    Just check the docs on the available methods. Media items cannot automatically be stringified. Also ensure to run on device and include the NSAppleMusicUsageDescription key.
  2. Hans Knöchel 2017-03-14

    Example of stringified objects:
       var win = Ti.UI.createWindow({
           backgroundColor: '#fff'
       });
       
       var btn = Ti.UI.createButton({
           title: 'Trigger'
       });
       
       btn.addEventListener('click', function() {
           Titanium.Media.openMusicLibrary({
               allowMultipleSelections: false,
               mediaTypes: Titanium.Media.MUSIC_MEDIA_TYPE_MUSIC | Titanium.Media.MUSIC_MEDIA_TYPE_ANY_AUDIO,
               success: function(event) {
                   // called when media returned from the MusicLibrary
       
                   var item = event.items[0];
                   var stringifiedItem = JSON.stringify({
                       title: item.title,
                       artist: item.artist,
                       albumTrackNumber: item.albumTrackNumber,
                       // ...
                   });
       
                   Ti.API.info(stringifiedItem)
       
                   alert("Result: " + stringifiedItem);
               },
               cancel: function() {
                   alert("Aborting ");
               },
               error: function(error) {
                   // called when there's an error
                   var a = Titanium.UI.createAlertDialog({
                       title: 'Music Library'
                   });
                   if (error.code == Titanium.Media.NO_MUSIC_PLAYER) {
                       a.setMessage('Please run this test on device.');
                   } else {
                       a.setMessage('Unexpected error: ' + error.code);
                   }
                   a.show();
               }
           });
       
       });
       
       win.add(btn);
       
       win.open();
       
  3. Tamir Ishay Sharbat 2017-03-15

    when I use JSON.parse on the stringifiedItem in the code above. will the output be a media item that I can play using the musicPlayer?
  4. Hans Knöchel 2017-03-15

    As discussed in an earlier ticket, you should provide the Ti.Media.Item to the Ti.Media.AudioPlayer and check the docs for both API's. Like this:
       // "items" is the array of media items
       var musicPlayer = Ti.Media.systemMusicPlayer;
       musicPlayer.setQueue(items[0]); // Get the first song into the queue
       musicPlayer.play();
       
    More docs: - http://docs.appcelerator.com/platform/latest/#!/api/Titanium.Media.MusicPlayer-method-setQueue - http://docs.appcelerator.com/platform/latest/#!/api/Titanium.Media.Item - http://www.clearlyinnovative.com/titanium-appcelerator-quickie-using-system-music-player (a bit outdated, but explains the concept)
  5. Tamir Ishay Sharbat 2017-03-15

    I know how to use them... the thing is I would like to save the music picked by the user. and I want to use JSON.stringify on a media item in order to do this, I think you know why (in case you don't know I am planning on using JSON.parse on the saved string and I am hoping to get a media item from that action, a media item I can play using the music player, like you did above) but the output of JSON.stringify on the media item is just an empty object ("{}") and when I parse that? well guess what, I get an empty object that I can do absolutely nothing with. So if you by any chance find a way to save media items (I mean store them like in Ti.App.Properties) I will be more than glad to hear about it. In case you don't know about any then this is a bug (because we should be able to store this information) and let me know when you solve. do you understand the problem now?
  6. Hans Knöchel 2017-03-16

    I do an did before. But it's still the same thing: The Ti.MediaItem has no properties like other API's like Ti.Blob or Ti.UI.ListItem. The properties are received from the native side generically (see [this method](https://github.com/appcelerator/titanium_mobile/blob/master/iphone/Classes/TiMediaItem.m#L52) for details). This has been done to keep the properties flexible and only return the properties that are actually called. We would also never store them in Ti.App.Properties, because the API is simply not made for that and would make no sense here. And as said before, you would just need to build an object with the properties that you need and stringify that, I don't see any problem with that. The change would be to have manual properties for all item properties, causing the performance, compile time and flexibility to drop a bit, which isn't suitable as well.
  7. Tamir Ishay Sharbat 2017-03-17

    Hans I don't want the user to choose a song every time they go into the app. I want them to choose once and when they quit the app I want the song they chose to be saved so I can use it next time they enter the app
       	var item = this.avalSounds[0].item;
       	var mediaItem = {
       		albumArtist : item.albumArtist,
       		albumTitle : item.albumTitle,
       		albumTrackCount : item.albumTrackCount,
       		albumTrackNumber : item.albumTrackNumber,
       		apiName : item.apiName,
       		artist : item.artist,
       		artwork : item.artwork,
       		bubbleParent : item.bubbleParent,
       		composer : item.composer,
       		discCount : item.discCount,
       		discNumber : item.discNumber,
       		genre : item.genre,
       		isCompilation : item.isCompilation,
       		lyrics : item.lyrics,
       		mediaType : item.mediaType,
       		playCount : item.playCount,
       		playbackDuration : item.playbackDuration,
       		podcastTitle : item.podcastTitle,
       		rating : item.rating,
       		skipCount : item.skipCount,
       		title : item.title,
       	};
       	var x = JSON.stringify(mediaItem);
       	var y = JSON.parse(x);
       	var musicPlayer = Ti.Media.appMusicPlayer;
       	musicPlayer.setQueue([y]);
       	musicPlayer.play();
       
    the code above doesn't play the song the user picked. Which means that I cannot save the media item in the way you suggested and still play it using the music player. (this is the functionality I am looking for, not just saving information but actually playing it). (By the way when I change the second to last row with "musicPlayer.setQueue([item])" everything works fine so the problem is not my code but the fact that you can't play the media item after stringifying like you suggested) I am pretty sure there is a way to save media items like so using native ios programing. if you have any other suggestions please notify me otherwise I suggest you add this ability in the next sdk release
  8. Hans Knöchel 2017-03-18

    You cannot save media items, not in Titanium and not natively (see reference [here](http://stackoverflow.com/a/8624460/5537752)). You would query items based on their properties, like you can do with Ti.Media.queryMusicLibrary, quere you would pass the track information and get matches to that specific predicate. One thing we should improve is to expose the MPMediaItemPropertyPersistentID property as the "persistentID", so you could store that one in your user defaults / Ti.App.Properties and use it as the argument for musicPlayer.setQueue(). The following works after my patch:
       var win = Ti.UI.createWindow({
           backgroundColor: '#fff'
       });
        
       var btn = Ti.UI.createButton({
           title: 'Trigger'
       });
        
       btn.addEventListener('click', function() {
           Titanium.Media.openMusicLibrary({
               allowMultipleSelections: false,
               mediaTypes: Titanium.Media.MUSIC_MEDIA_TYPE_MUSIC | Titanium.Media.MUSIC_MEDIA_TYPE_ANY_AUDIO,
               success: function(event) {
                   // called when media returned from the MusicLibrary
        
                   var item = event.items[0];
                   Ti.App.Properties.setString('mySong', item.persistentID);
       
                   var musicPlayer = Ti.Media.systemMusicPlayer;
                   musicPlayer.setQueue(Ti.App.Properties.getString('mySong', null)); // Get the first song into the queue
                   musicPlayer.play();
               },
               cancel: function() {
                   alert("Aborting ");
               },
               error: function(error) {
                   // called when there's an error
                   var a = Titanium.UI.createAlertDialog({
                       title: 'Music Library'
                   });
                   if (error.code == Titanium.Media.NO_MUSIC_PLAYER) {
                       a.setMessage('Please run this test on device.');
                   } else {
                       a.setMessage('Unexpected error: ' + error.code);
                   }
                   a.show();
               }
           });
        
       });
        
       win.add(btn);
       win.open();
       
    In your case, you could check if the track exists already and start your player afterwards. Is that something you could think of? Then I'd add it as part of [this PR](https://github.com/appcelerator/titanium_mobile/pull/8817) and you can patch your SDK right now. It would also be part of 6.1.0 then. *EDIT*: If just [added](https://github.com/appcelerator/titanium_mobile/pull/8817/commits/deeb9f46bd700f4490a0fc31824b59518130f131) the above discussed changes to the existing PR. If you agree, we'd resolve this ticket and get the PR handled in the other ticket, so we can include it into the core asap.
  9. Tamir Ishay Sharbat 2017-03-18

    Thanks Hans :) . this solves my problem. just a few questions: 1. from what sdk version is the "persistentID" property available and how can I check if a certain ID already exists? 2. from what sdk version will I be able to use musicPlayer.setQueue() with persistentID? please notify me when the master branch will include these functionalities. thanks again :)
  10. Hans Knöchel 2017-03-18

    Cool! So the PR is open and will be in 6.1.0 (final release not sure, yet). As soon the PR is merged, you cam grab a build and use it. Or you can go to Github and change the files manually (remove the red parts and replace it with the green ones). I will resolve this ticket as Duplicte now and ensure that the other gets reviewing in time.
  11. Tamir Ishay Sharbat 2017-03-19

    thanks! who do I need to talk to in order to get notify when the PR is merged?
  12. Hans Knöchel 2017-03-19

    Just ensure you follow TIMOB-24372 and check when the status changes to "Resolved - Fixed". I will also ping you when it's done!
  13. Lee Morris 2017-03-30

    Closing ticket as duplicate, please see TIMOB-24372.
  14. Tamir Ishay Sharbat 2017-04-11

    Was the PR merged yet? Can I use persistentID with the current master version?
  15. Hans Knöchel 2017-04-11

    Hey Tamir, yeah you can (even with 6_1_X)! Use the persistentID on the Ti.Media.Item. See the example given in TIMOB-24372.
  16. Tamir Ishay Sharbat 2017-04-12

    Great!, thanks Hans. Just one more question, how can I check if a given persistentID is indeed present in the user's music library?
  17. Hans Knöchel 2017-04-12

    It should always be present, because it is the "primary-key" used by iOS to identify the asset. See the [Apple docs](https://developer.apple.com/reference/mediaplayer/mpmediaitempropertypersistentid) for more infos.
  18. Tamir Ishay Sharbat 2017-04-12

    but what if the user happens to delete the item with the given persistentID. What will happen then? and how can I know when such thing happened?
  19. Hans Knöchel 2017-04-12

    You mean if you pass an identifier to the player that does not exist? I didn't actually tested that case but I think the player would through an error event.
  20. Tamir Ishay Sharbat 2017-04-12

    and how would I know if the identifier does not exist before I pass it to the player? and how can I listen to that error event you mentioned? (I mean code wise).
  21. Tamir Ishay Sharbat 2017-04-14

    Hans, May I suggest that you would be able to request the whole mediaItem (meaning an actual mediaItem like the ones we get from openMusicLibrary) using the persistentID... I am sure this would be very helpful to many people :) thanks;
  22. Tamir Ishay Sharbat 2017-04-19

    Hans the current builds on the build server are all fucked up!! can you please instruct me on how to merge the PR myself?

JSON Source