MusicKit API - Playlist Update, Delete, Song Remove - apple-musickit

I've been looking at the MusicKit functionality for playlists:
https://developer.apple.com/documentation/applemusicapi/create_a_new_library_playlist
I'm wondering, can anyone confirm if they have been able to:
remove songs from an existing playlist
delete a playlist
update the title of a playlist
For example, I have tried updating the title of a playlist in c# using the following but the endpoint does exist/accept this. Note the appended playlist ID to the POST URL p.ABC123
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + [MYDEVTOKEN]);
client.DefaultRequestHeaders.Add("Music-User-Token", [MYMUSICUSERTOKEN]);
string _postUri = "https://api.music.apple.com/v1/me/library/playlists/p.ABC123";
var jsonObject = JObject.FromObject(new
{
attributes = new
{
name = "Playlist - Edited Title",
description = "This is a playlist edit"
}
});
var _content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");
var response = await client.PostAsync(_postUri, content: _content);
string outputContent = await response.Content.ReadAsStringAsync();
}

It seems as though Apple isn't allowing this functionality.
https://forums.developer.apple.com/thread/107807
They could be doing this as a security precaution. However, Apple doesn't have a great relationship with the developer community, and is most likely doing it to limit people from building applications on top of theirs. (even though they are an extremely expensive API to work with off the bat...)
I wouldn't anticipate getting this functionality any time soon :(

Related

Preview image attachments in ChatMessage

We are using the ms graph api to post messages to a teams channel from a internal desktop application. The main purpose is to attach images to the message. We upload the image files into the one-drive folder of the channel as shown below.
var uploadProps = new DriveItemUploadableProperties
{
ODataType = null,
AdditionalData = new Dictionary<string, object>
{
{ "#microsoft.graph.conflictBehavior", "replace" }
}
};
var session = await graphClient.Drives[driveId]
.Items[parentId].ItemWithPath(fileName).CreateUploadSession(uploadProps).Request().PostAsync(token);
int maxSliceSize = 320 * 1024;
var fileUploadTask =
new LargeFileUploadTask<DriveItem>(session, fileStream, maxSliceSize);
// Create a callback that is invoked after each slice is uploaded
IProgress<long> progress = new Progress<long>(reportAsync);
// Upload the file
var uploadResult = await fileUploadTask.UploadAsync(progress);
if (uploadResult.UploadSucceeded)
{
return uploadResult.ItemResponse;
}
We then send a message to the channel and attach the images uploaded previously as reference attachments.
var chatMsg = new ChatMessage();
chatMsg.Body = new ItemBody();
chatMsg.Body.ContentType = BodyType.Html;
chatMsg.Body.Content = msg + " " + string.Join(" ", attachments.Select(d => $"<attachment id=\"{parseEtag(d.ETag)}\"></attachment>"));
chatMsg.Attachments = attachments.Select(d => new ChatMessageAttachment()
{
Id = parseEtag(d.ETag),
ContentType = "reference",
ContentUrl = d.WebUrl,
Name = d.Name
});
return await this.graphClient.Teams[teamId].Channels[channelId].Messages
.Request()
.AddAsync(chatMsg, token);
The problem is that the message only shows the names of the attachments with no preview as seen in the message at the bottom. We want to have a preview as seen (top message) when attaching a file within the teams application.
We've tried to set the thumbnailurl property of the attachment to the thumbnail url fetched from the ms-graph api with no success.
We've uploaded a file using the teams application (with preview) and then created an identical message with the same file (same driveitem id) in our application (show's no preview). Then we fetched both messages using the graph api and could not discern any differences between the two besides the message id's ofc.
We've scoured these forums, the ms documentations and even suggestion pages and found nothing.
We have been able to show previews separately in the body of the message referencing the thumbnail urls and in messagecards but ideally we want the preview directly in the attachments.
EDIT
The thumbnail urls seem to expire after 24 hours and are therefor not a great solution.
We managed to solve exactly this problem using the Simple Upload Api, with the added ?$expand=thumbnails query parameter. I haven't tried but the query param ought to work for the endpoint you're using as well.
Pick a size from the ThumbnailSet in the upload response and add it to the body of your message as an image tag. See below:
// channel, file, extractIdFromEtag, message omitted for brevity.
// PUT /groups/{group-id}/drive/items/{parent-id}:/{filename}:/content
const uploadUrl = `https://graph.microsoft.com/beta/groups/${channel.teamId}/drive/items/root:/${channel.displayName}/${file.name}:/content?$expand=thumbnails`;
const res = await this.http.put(uploadUrl, file).toPromise(); // FYI Using Angular http service
const attachment = {
id: extractIdFromEtag(res.eTag),
contentType: 'reference',
contentUrl: res.webUrl,
name: res.name,
thumbnailUrl: res.webUrl
};
const postBody = {
subject: null,
body: {
contentType: 'html',
content: message
},
};
// This is what makes the image show in the message as if posted from teams
postBody.body.content += `<br><br><img src="${res.thumbnails[0].large.url}" alt="${res.name}"/>`;
const messageUrl = `https://graph.microsoft.com/beta/teams/${channel.teamId}/channels/${channel.id}/messages`;
const result = await this.http.post(messageUrl, postBody).toPromise();
// Done
You can also keep adding the attachment as you already do, if you want the original image attached as a file, as well as showing the image preview in the message.

How to convert Office files to PDF using Microsoft Graph

I'm looking for a way to convert Office files to PDF.
I found out that Microsoft Graph could be used.
I'm trying to download converted PDF using Microsoft Graph from OneDrive.
I'd like to convert .docx to .pdf.
However, when I sent the following request, I did not receive a response even if I waited.
GET https://graph.microsoft.com/v1.0/users/{id}/drive/root:/test.docx:/content?format=pdf
Also, the error code is not returned.
If syntax is wrong, an error code will be returned as expected.
It will not return only when it is correct.
In addition, I can download the file if I do not convert.
GET https://graph.microsoft.com/v1.0/users/{id}/drive/root:/test.docx:/content
Is my method wrong or else I need conditions?
If possible, please give me sample code that you can actually do.
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
client.BaseAddress = new Uri(graphUrl);
var result = await client.GetAsync("/v1.0/users/xxxxxxxxxxxxxxxxxxxxxxxxx/drive/root:/test.docx:/content?format=pdf");
:
I would like to elaborate a bit Marc's answer by providing a few examples for HttpClient.
Since by default for HttpClient HttpClientHandler.AllowAutoRedirect property is set to True there is no need to explicitly follow HTTP redirection headers and the content could be downloaded like this:
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
client.BaseAddress = new Uri("https://graph.microsoft.com");
var response = await client.GetAsync($"/v1.0/drives/{driveId}/root:/{filePath}:/content?format=pdf");
//save content into file
using (var file = System.IO.File.Create(fileName))
{
var stream = await response.Content.ReadAsStreamAsync();
await stream.CopyToAsync(file);
}
}
In case if follow HTTP redirection is disabled, to download the converted file, your app must follow the Location header in the response as demonstrated below:
var handler = new HttpClientHandler()
{
AllowAutoRedirect = false
};
using (HttpClient client = new HttpClient(handler))
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
client.BaseAddress = new Uri("https://graph.microsoft.com");
var response = await client.GetAsync($"/v1.0/drives/{driveId}/root:/{filePath}:/content?format=pdf");
if(response.StatusCode == HttpStatusCode.Redirect)
{
response = await client.GetAsync(response.Headers.Location); //get the actual content
}
//save content into file
using (var file = System.IO.File.Create(fileName))
{
var stream = await response.Content.ReadAsStreamAsync();
await stream.CopyToAsync(file);
}
}
The API doesn't return the converted content directly, it returns a link to the converted file. From the documentation:
Returns a 302 Found response redirecting to a pre-authenticated download URL for the converted file.
To download the converted file, your app must follow the Location header in the response.
Pre-authenticated URLs are only valid for a short period of time (a few minutes) and do not require an Authorization header to access.
You need to capture the 302 and make a 2nd call to the URI in the Location header in order to download the converted file.

Google Youtube API Search doesn't retrieve all videos in the channel

I have to retrieve all video of my channel with Youtube API.
All videos are published on Youtube and I can see them correctly.
I tried to make the request directly from this page:
https://developers.google.com/youtube/v3/docs/search/list
and this is the example request:
GET http s://www.googleapis.com/youtube/v3/search?part=snippet&channelId=myChannelID&maxResults=50&key={YOUR_API_KEY}
Request doesn't retrieve all videos, it returns only 7 on the total of 9.
All videos have the same configuration. Missing videos are always the same.
If I use the video API passing the ID of one of those videos excluded from the search response, it returns a correct response and it belong correctly to my channel:
https://developers.google.com/youtube/v3/docs/videos/list#try-it
Someone can help me?
thank you in advance
Francesco
The answer to "How do I obtain a list of all videos in a channel using the YouTube Data API v3?" here may be what you need. Look especially at the video linked to in the answer.
To summarize, to get all the uploads from a channel, you need to get the items from the uploads playlist for the channel using playlistItems.list on that playlist's ID rather than calling search.list on the channel ID.
Try this two-step approach:
Get the ID of your channel's uploads playlist using the channels.list API call: GET https://www.googleapis.com/youtube/v3/channels?part=contentDetails&id={YOUR_CHANNEL_ID}&key={YOUR_API_KEY}
Get the videos from the uploads playlist using the playlistItems.list call: GET https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=3&playlistId={YOUR_PLAYLIST_ID}&key={YOUR_API_KEY}
try this
async static Task<IEnumerable<YouTubeVideo>> GetVideosList(Configurations configurations, string searchText = "", int maxResult = 20)
{
List<YouTubeVideo> videos = new List<YouTubeVideo>();
using (var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
ApiKey = configurations.ApiKey
}))
{
var searchListRequest = youtubeService.Search.List("snippet");
searchListRequest.Q = searchText;
searchListRequest.MaxResults = maxResult;
searchListRequest.ChannelId = configurations.ChannelId;
searchListRequest.Type = "video";
searchListRequest.Order = SearchResource.ListRequest.OrderEnum.Date;// Relevance;
var searchListResponse = await searchListRequest.ExecuteAsync();
foreach (var responseVideo in searchListResponse.Items)
{
videos.Add(new YouTubeVideo()
{
Id = responseVideo.Id.VideoId,
Description = responseVideo.Snippet.Description,
Title = responseVideo.Snippet.Title,
Picture = GetMainImg(responseVideo.Snippet.Thumbnails),
Thumbnail = GetThumbnailImg(responseVideo.Snippet.Thumbnails)
});
}
return videos;
}
}

Google Youtube API v3 Playlist title new

I am trying to port a application developed using version 2 API of google youtube to version 3.
How can I get title of a playlist using version 3 API? We could get the title of playlist using version 2. However, title I get when I query playlist's snippet is different from what it is shown on the youtube website.
Is there any difference in Version 3?
I am using .NET API library from Google. if this helps.
Can anyone please help?
I tried using Version 3 API from Google and when I am trying to get playlists using
var channelsListRequest = youtubeService.Channels.List("snippet,contentDetails");
after setting channelsListRequest.ForUserName, i call var channelsListResponse = await channelsListRequest.ExecuteAsync();
From the response, I would then get the playlist list sent using:
foreach (var channel in channelsListResponse.Items)
{
var uploadsListId = channel.ContentDetails.RelatedPlaylists.Uploads;
var nextPageToken = "";
while (nextPageToken != null)
{
var playlistRequest = youtubeService.Playlists.List("id,snippet,contentDetails,status,player");
playlistRequest.Id = uploadsListId;
playlistRequest.MaxResults = 50;
playlistRequest.PageToken = nextPageToken;
var playlistListResponse = await playlistRequest.ExecuteAsync();
if (playlistListResponse.Items.Count > 0)
MessageBox.Show(playlistListResponse.Items[0].Snippet.Title);
}
}
The messagebox displays the comment that was added when creating playlist. However, when I view in youtube using a browser, the playlist title is displayed properly.

Spreadsheets API Can not update a read-only feed

I'm trying to add new spreadsheet if it not exists with GData Spreadsheet API for .NET but it gives me following exception:
Can not update a read-only feed
Here's my code:
var service = new SpreadsheetsService("<my-app>");
service.setUserCredentials("<login>", "<password>");
// Instantiate a SpreadsheetQuery object to retrieve spreadsheets.
SpreadsheetQuery query = new SpreadsheetQuery();
var title = "test";
query.Title = title;
// Make a request to the API and get all spreadsheets.
SpreadsheetFeed feed = service.Query(query);
if (!feed.Entries.Any())
{
var worksheet = new WorksheetEntry(20, 20, title);
service.Insert(feed, worksheet);
}
Through Fiddler I see that I'm doing request to:
GET /feeds/spreadsheets/private/full?title=test
and it goes fine, but I don't see any requests for updating data. I suppose that I should change somehow SpreadsheetQuery to make it capable to write data, but I can't find how.
It's me being inattentive because google documentation on Spreadsheet API says:
It is possible to create a new spreadsheet by uploading a spreadsheet
file via the Google Drive API. The Spreadsheets API does not currently
provide a way to delete a spreadsheet, but this is also provided in
the Google Drive API. For testing purposes, you may create a
spreadsheet manually or upload one.
So I basically installed GoogleDrive API with Nuget. And then added following method for adding file:
private static void AddFile(string title)
{
var clientID = "put here a clientID";
var clientSecret = "put here a clientSecret";
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = clientID,
ClientSecret = clientSecret,
},
new[] { DriveService.Scope.Drive },
"here goes your account",
CancellationToken.None).Result;
// Create the service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Drive API Sample",
});
var body = new Google.Apis.Drive.v2.Data.File();
body.Title = title;
//body.Description = "A test document";
body.MimeType = "application/vnd.google-apps.spreadsheet";
service.Files.Insert(body).Execute();
}
When I run code above at the first time - I received an exception that said
Could not load file or assembly
'Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.16.0
I run these in Package Manager Console:
Uninstall-Package Microsoft.Bcl.Async -Force
Install-Package Microsoft.Bcl.Async
and it worked. Hope it would help somebody who will stumble over the same issue.

Resources