{ "id": "165773", "key": "TIMOB-24372", "fields": { "issuetype": { "id": "2", "description": "A new feature of the product, which has yet to be developed.", "name": "New Feature", "subtask": false }, "project": { "id": "10153", "key": "TIMOB", "name": "Titanium SDK/CLI", "projectCategory": { "id": "10100", "description": "Titanium and related SDKs used in application development", "name": "Client" } }, "fixVersions": [ { "id": "17608", "name": "Release 6.1.0", "archived": false, "released": true, "releaseDate": "2017-05-26" } ], "resolution": { "id": "1", "description": "A fix for this issue is checked into the tree and tested.", "name": "Fixed" }, "resolutiondate": "2017-04-06T13:49:28.000+0000", "created": "2017-02-04T03:27:16.000+0000", "priority": { "name": "Medium", "id": "3" }, "labels": [], "versions": [], "issuelinks": [ { "id": "54575", "type": { "id": "10002", "name": "Duplicate", "inward": "is duplicated by", "outward": "duplicates" }, "inwardIssue": { "id": "166598", "key": "TIMOB-24486", "fields": { "summary": " iOS: How to stringify a mediaItem properly.", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "priority": { "name": "None", "id": "6" }, "issuetype": { "id": "7", "description": "gh.issue.story.desc", "name": "Story", "subtask": false } } } } ], "assignee": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "updated": "2017-04-12T12:10:47.000+0000", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "components": [ { "id": "10206", "name": "iOS", "description": "iOS Platform" } ], "description": "h5. Issue Description \r\n\r\nThe feature consists on use the Ti.Media.openMusicLibrary to pick a sound file or an object with the file and the path. ", "attachment": [], "flagged": false, "summary": "iOS: Expose all missing MediaItem properties, allow Apple Music playback in Ti.Media.AudioPlayer", "creator": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "subtasks": [], "reporter": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "environment": null, "closedSprints": [ { "id": 865, "state": "closed", "name": "2017 Sprint 07 SDK", "startDate": "2017-03-26T22:01:37.741Z", "endDate": "2017-04-09T22:01:00.000Z", "completeDate": "2017-04-10T15:40:13.586Z", "originBoardId": 114 } ], "comment": { "comments": [ { "id": "406336", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "This is not how the iOS music picker works. We *can* expose the {{assetURL}} property to the Titanium API, but that looks for example like this:\r\n{code}\r\nipod-library://item/item.mp3?id=332491690319073515\r\n{code}\r\nSimply, because Apple does not allow the developer to grab music files from the music library to send them somewhere else (piracy, privacy-settings, ...). So yes, we can expose it, but they rather want to use the meta-data to loookup the song on Spotify / Apple Music and link that to the app, like Shazam does for example. Or, if they want to play the music, they should use the [Ti.Media.MusicPlayer|http://docs.appcelerator.com/platform/latest/#!/api/Titanium.Media.MusicPlayer] to playback songs in a self-defined queue.\r\n\r\nPlease advice what they want to do. \r\n\r\n*EDIT*: Side-note, this are all properties we could expose from the native API (we already have lots of them but iOS 8+ added tons more):\r\n{code:objc}\r\n// Properties marked filterable can also be used to build MPMediaPropertyPredicates (see MPMediaQuery.h).\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyPersistentID NS_AVAILABLE(10_12_2, 4_2); // filterable\r\n@property (nonatomic, readonly) MPMediaEntityPersistentID persistentID NS_AVAILABLE_IOS(5_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyMediaType; // filterable\r\n@property (nonatomic, readonly) MPMediaType mediaType NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyTitle; // filterable\r\n@property (nonatomic, readonly, nullable) NSString *title NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyAlbumTitle; // filterable\r\n@property (nonatomic, readonly, nullable) NSString *albumTitle NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyAlbumPersistentID NS_AVAILABLE(10_12_2, 4_2); // filterable\r\n@property (nonatomic, readonly) MPMediaEntityPersistentID albumPersistentID NS_AVAILABLE_IOS(8_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyArtist; // filterable\r\n@property (nonatomic, readonly, nullable) NSString *artist NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyArtistPersistentID NS_AVAILABLE(10_12_2, 4_2); // filterable\r\n@property (nonatomic, readonly) MPMediaEntityPersistentID artistPersistentID NS_AVAILABLE_IOS(8_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyAlbumArtist; // filterable\r\n@property (nonatomic, readonly, nullable) NSString *albumArtist NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyAlbumArtistPersistentID NS_AVAILABLE(10_12_2, 4_2); // filterable\r\n@property (nonatomic, readonly) MPMediaEntityPersistentID albumArtistPersistentID NS_AVAILABLE_IOS(8_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyGenre; // filterable\r\n@property (nonatomic, readonly, nullable) NSString *genre NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyGenrePersistentID NS_AVAILABLE(10_12_2, 4_2); // filterable\r\n@property (nonatomic, readonly) MPMediaEntityPersistentID genrePersistentID NS_AVAILABLE_IOS(8_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyComposer; // filterable\r\n@property (nonatomic, readonly, nullable) NSString *composer NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyComposerPersistentID NS_AVAILABLE(10_12_2, 4_2); // filterable\r\n@property (nonatomic, readonly) MPMediaEntityPersistentID composerPersistentID NS_AVAILABLE_IOS(8_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyPlaybackDuration;\r\n@property (nonatomic, readonly) NSTimeInterval playbackDuration NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyAlbumTrackNumber;\r\n@property (nonatomic, readonly) NSUInteger albumTrackNumber NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyAlbumTrackCount;\r\n@property (nonatomic, readonly) NSUInteger albumTrackCount NS_AVAILABLE_IOS(8_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyDiscNumber;\r\n@property (nonatomic, readonly) NSUInteger discNumber NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyDiscCount;\r\n@property (nonatomic, readonly) NSUInteger discCount NS_AVAILABLE_IOS(8_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyArtwork;\r\n@property (nonatomic, readonly, nullable) MPMediaItemArtwork *artwork NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString *const MPMediaItemPropertyIsExplicit NS_AVAILABLE(10_12_2, 7_0);\r\n@property (nonatomic, readonly, getter = isExplicitItem) BOOL explicitItem NS_AVAILABLE_IOS(10_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyLyrics;\r\n@property (nonatomic, readonly, nullable) NSString *lyrics NS_AVAILABLE_IOS(8_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyIsCompilation; // filterable\r\n@property (nonatomic, readonly, getter = isCompilation) BOOL compilation NS_AVAILABLE_IOS(8_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyReleaseDate NS_AVAILABLE(10_12_2, 4_0);\r\n@property (nonatomic, readonly, nullable) NSDate *releaseDate NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyBeatsPerMinute NS_AVAILABLE(10_12_2, 4_0);\r\n@property (nonatomic, readonly) NSUInteger beatsPerMinute NS_AVAILABLE_IOS(8_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyComments NS_AVAILABLE(10_12_2, 4_0);\r\n@property (nonatomic, readonly, nullable) NSString *comments NS_AVAILABLE_IOS(8_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyAssetURL NS_AVAILABLE(10_12_2, 4_0);\r\n@property (nonatomic, readonly, nullable) NSURL *assetURL NS_AVAILABLE_IOS(8_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyIsCloudItem NS_AVAILABLE(10_12_2, 6_0); // filterable\r\n@property (nonatomic, readonly, getter = isCloudItem) BOOL cloudItem NS_AVAILABLE_IOS(8_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyHasProtectedAsset NS_AVAILABLE(10_12_2, 9_2); // filterable\r\n@property (nonatomic, readonly, getter = hasProtectedAsset) BOOL protectedAsset NS_AVAILABLE_IOS(9_2);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyPodcastTitle; // filterable\r\n@property (nonatomic, readonly, nullable) NSString *podcastTitle NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyPodcastPersistentID NS_AVAILABLE(10_12_2, 4_2); // filterable\r\n@property (nonatomic, readonly) MPMediaEntityPersistentID podcastPersistentID NS_AVAILABLE_IOS(8_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyPlayCount; // filterable\r\n@property (nonatomic, readonly) NSUInteger playCount NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertySkipCount;\r\n@property (nonatomic, readonly) NSUInteger skipCount NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyRating;\r\n@property (nonatomic, readonly) NSUInteger rating NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyLastPlayedDate;\r\n@property (nonatomic, readonly, nullable) NSDate *lastPlayedDate NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyUserGrouping NS_AVAILABLE(10_12_2, 4_0);\r\n@property (nonatomic, readonly, nullable) NSString *userGrouping NS_AVAILABLE_IOS(8_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyBookmarkTime NS_AVAILABLE(10_12_2, 6_0);\r\n@property (nonatomic, readonly) NSTimeInterval bookmarkTime NS_AVAILABLE_IOS(7_0);\r\n\r\nMP_EXTERN NSString * const MPMediaItemPropertyDateAdded NS_AVAILABLE(10_12_2, 10_0);\r\n@property (nonatomic, readonly) NSDate *dateAdded NS_AVAILABLE_IOS(10_0);\r\n\r\n// Matches the id used by MPMusicPlayerController to enqueue store tracks \r\nMP_EXTERN NSString * const MPMediaItemPropertyPlaybackStoreID NS_AVAILABLE_IOS(10_3);\r\n@property (nonatomic, readonly) NSString *playbackStoreID NS_AVAILABLE_IOS(10_3);\r\n{code}", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2017-02-04T10:57:01.000+0000", "updated": "2017-02-04T10:59:10.000+0000" }, { "id": "406337", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "PR (exposing *all* missing properties to be future-safe): https://github.com/appcelerator/titanium_mobile/pull/8817\r\n\r\nNote: Please still read the above comment. If they expect to receive an URL of a file that they can send, they won't succeed. This is - once again - restricted by Apple's privacy settings.\r\n\r\nTest-Case:\r\n{code:js}\r\nvar win = Ti.UI.createWindow({\r\n backgroundColor: '#fff'\r\n});\r\n\r\nvar btn = Ti.UI.createButton({\r\n title: 'Open Music Library'\r\n});\r\n\r\nbtn.addEventListener('click', function() {\r\n Ti.Media.openMusicLibrary({\r\n error: function(e) {\r\n Ti.API.error('Error opening the music library: Check if permissions granted and ensure to run on device!');\r\n },\r\n success: function(e) {\r\n var item = e.items[0];\r\n\r\n // Some of the media item properties. Check the docs for more!\r\n Ti.API.info('Title: ' + item.title);\r\n Ti.API.info('Artist: ' + item.artist);\r\n Ti.API.info('Album: ' + item.albumTitle);\r\n Ti.API.info('Has Protected Asset: ' + item.hasProtectedAsset);\r\n \r\n if (!item.hasProtectedAsset) {\r\n Ti.API.info('Asset URL: ' + item.assetURL);\r\n }\r\n\r\n Ti.API.info('Is Explicit: ' + item.isExplicit);\r\n Ti.API.info('Date Added: ' + item.dateAdded);\r\n }\r\n })\r\n});\r\n\r\nwin.add(btn);\r\nwin.open();\r\n\r\n{code}", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2017-02-04T11:40:23.000+0000", "updated": "2017-02-04T13:38:03.000+0000" }, { "id": "417263", "author": { "name": "htbryant", "key": "htbryant", "displayName": "Harry Bryant", "active": true, "timeZone": "Europe/London" }, "body": "Verified as fixed, tested with demo code provided in JIRA ticket, including additional exposed media item properties. iOS 10.2 & 9.3.\r\n\r\nTested On:\r\n\r\niPhone 7 10.2.1 Device\r\niPhone 5S 9.3.5 Device\r\nMac OS Sierra (10.12.2)\r\nTi SDK: 6.1.0.v20170410104407\r\nAppc Studio: 4.8.1.201701192045\r\nAppc NPM: 4.2.9-1\r\nApp CLI: 6.1.0\r\nXcode 8.2.1\r\nNode v4.6.0\r\n\r\n*Closing ticket.*", "updateAuthor": { "name": "htbryant", "key": "htbryant", "displayName": "Harry Bryant", "active": true, "timeZone": "Europe/London" }, "created": "2017-04-12T12:10:21.000+0000", "updated": "2017-04-12T12:10:39.000+0000" } ], "maxResults": 6, "total": 6, "startAt": 0 } } }