UWP: MediaPlaybackItem get ViewModel - c#

My MediaPlaybackList.ShuffledItems has 10 items in it. But when I was trying to convert the items back to a list of ViewModel (in my case it is Music) using the uri, I got null.
Why is that? Is it because I load the file from my local drive?
This is how I get the uri:
public static async Task<List<Music>> GetRealPlayList()
{
if (PlayList.ShuffleEnabled)
{
if (ShuffledPlayList.Count == 0)
{
foreach (var music in PlayList.ShuffledItems)
{
ShuffledPlayList.Add(await Music.GetMusic(music.Source.Uri.AbsolutePath));
}
}
return ShuffledPlayList;
}
else
return CurrentPlayList;
}
This is how I set the items:
public static async Task SetPlayList(IEnumerable<Music> playlist)
{
if (Helper.SamePlayList(playlist, CurrentPlayList)) return;
PlayList.Items.Clear();
CurrentPlayList.Clear();
foreach (var music in playlist)
{
var item = new MediaPlaybackItem(MediaSource.CreateFromStorageFile(await Helper.CurrentFolder.GetFileAsync(music.GetShortPath())));
PlayList.Items.Add(item);
CurrentPlayList.Add(music);
}
}
What ways else can I convert the MediaPlackBackItem back to the ViewModel? The GetDisplayProperties().MusicProperties doesn't have the some properties that I want and the properties in it are also empty.

When you create MediaSource,you can set CustomProperties to save the file path in it.And when you loop through the PlayList.ShuffledItems,get file path from the CustomProperties.
Set the items:
MediaSource source = MediaSource.CreateFromStorageFile(await Helper.CurrentFolder.GetFileAsync(music.GetShortPath()));
source.CustomProperties.Add("Path", file.Path);
var item = new MediaPlaybackItem(source);
Get Music class:
foreach (var music in PlayList.ShuffledItems)
{​
MediaSource source = music.Source;​
String path = sour.CustomProperties["Path"].ToString();​
ShuffledPlayList.Add(await Music.GetMusic(path));​
}

Related

View all file from a list

I´m working on a project that uses Caliburn micro in wpf C#.
I´m in the process that I want to rewrite my method ReadMediaFile() so it displays all files in a folder in a list.
My method looks lite this:
private void ReadMediaFile()
{
string result;
_movieviewmodel = new MoviesViewModel();
string[] filePaths = Directory.GetFiles(#"C:/Users/v80770/Desktop/Movies/");
foreach (var file in filePaths)
{
result = Path.GetFileName(file);
_movieviewmodel.MovieName = result;
}
AddItem(_movieviewmodel);
}
When I debug the program all the files show in filePaths but only one shows in my list.
The AddItem is located in a class called TreeViewBase (belongs to caliburn micro I think) and it looks like this:
public void AddItem(T item)
{
_dispatcher.SmartInvoke(() => Items.Add(item));
}
I got the movie files viewing in my list but my MediaUri binding in view is bind against a specific path file but I want it to change dependent on what I choose
I tried to edit the binding to this:
string test = _movieviewmodel.MovieName;
MediaUri = new Uri(test);
But only get a exception "System.UriFormatException: 'Invalid URI: The format of the URI could not be determined.'"
Picture of Uri
New Uri code:
_movieviewmodel.MovieFilePath = #"C:/Users/v80770/Desktop/Movies/";
string test = _movieviewmodel.MovieFilePath;
MediaUri = new Uri(test + _movieviewmodel.MovieName);
But it always shows the same movie and my _movieviewmodel.MovieName does not change name dependent which movie I choose, it always is the same movie.
The creation of a MoviesViewModel item object and AddItem(_movieviewmodel); must be inside foreach, otherwise it would add only the last item:
foreach (var file in filePaths)
{
var movieviewmodel = new MoviesViewModel();
movieviewmodel.MovieName = Path.GetFileName(file);
AddItem(movieviewmodel);
}
or
foreach (var file in filePaths)
{
AddItem(new MoviesViewModel
{
MovieName = Path.GetFileName(file)
});
}

How to Parse Json to obtains values

This is the Json Format from the Imgur API using gallery search (heavily simplified, what matters is still there)
{
"data":[
{
"id":"q33FYFh",
"is_album":true,
"images":[
{
"id":"ObcYQRc",
"link":"https:\/\/i.imgur.com\/ObcYQRc.jpg",
"is_album":false
},
{
"id":"ifB0uac",
"link":"https:\/\/i.imgur.com\/ifB0uac.jpg",
"is_album":false
}
]
},
{
"id":"jYInL3c",
"is_album":true,
"images":[
{
"id":"bq2L5C4",
"link":"https:\/\/i.imgur.com\/bq2L5C4.jpg",
"is_album":false
},
{
"id":"Z0OPngk",
"link":"https:\/\/i.imgur.com\/Z0OPngk.jpg",
"is_album":false
}
]
},
{
"id":"8xxM5TO",
"link":"https:\/\/i.imgur.com\/8xxM5TO.jpg",
"is_album":false
}
],
"success":true,
"status":200
}
I need a way to get all image ID, not album ID, you can tell if an item is an album or a image by looking at the "is_album" tag
So first I tried to at least access the "id" subfield in the "images" field :
using Newtonsoft.Json;
string response = "Change this with the json file above"
dynamic dynJson = JsonConvert.DeserializeObject(response);
foreach (var data in dynJson)
{
string id = data["images"]["id"].ToString();
Debug.WriteLine(id);
}
This gave me this error : (By the way, I need to use Debug.WriteLine because Console doesn't work in PCL code in Xamarin.Forms)
System.InvalidOperationException: Cannot access child value on Newtonsoft.Json.Linq.JProperty.
But even if it worked, it would not get the id's for the images that are not part of an album.
using pseudo code this is what I would want (I think) :
for each (item in myjson)
{
if (item.is_album == "false")
{
Console.write(item.id);
}
else
{
for each (image in element)
{
Console.write(image.id);
}
}
}
You are not accessing your data correctly based on your JSON data.
data is your top level array, so your foreach would look like this:
foreach (var data in dynJson["data"])
Inside your foreach you would access your images like this:
string id = data["images"][0]["id"].ToString();
Where 0 is the index of the images array.
So combining this with another loop to get all the images for that data:
foreach (var data in dynJson["data"])
{
if (data["is_album"] == false)
{
// continue or do something
continue;
}
foreach(var image in data["images"])
{
string id = image["id"].ToString();
Debug.WriteLine(id);
}
}
You are accessing the dynamic object incorrectly. Here is the code you need:
public static List<string> GetImageIds(string jsonData)
{
List<string> imageIds = new List<string> ();
dynamic temp = JsonConvert.DeserializeObject (jsonData);
dynamic dynJson = temp.data;
foreach (dynamic data in dynJson)
{
int j = 0;
if (data.is_album == false)
{
imageIds.Add (data.id.ToString ());
}
else
{
dynamic images = data.images;
foreach (var image in images)
{
imageIds.Add (image.id.ToString ());
}
}
}
return imageIds;
}
Note: This is a simple example of how to traverse dynamic object. You will need to add validations and business logic to it as needed per your need.
Hope this helps!

UWP Parse Windows.Storage.StorageFolder and add in a TreeView

I want to parse a StorageFolder that the user picks and create a TreeView which shows its tree structure. This is when I ask to him to choose a file :
private async void browserPathGitDirButton_ClickAsync(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
FolderPicker picker = new FolderPicker();
picker.FileTypeFilter.Add("*");
folder = await picker.PickSingleFolderAsync();
if(folder != null)
{
parser.CreateTreeView(tree, folder);
tree.UpdateLayout();
}
}
After he has picked, I call this method :
public void CreateTreeView(TreeView tree)
{
System.Diagnostics.Debug.WriteLine("root : " + root.Name);
TreeViewItem racine = new TreeViewItem { Header = root.Name };
racine.Tag = root.Path;
tree.Items.Add(ParseStorageAsync(root));
}
And this method too :
public async Task<TreeViewItem> ParseStorageAsync(Windows.Storage.StorageFolder storage)
{
var dirNode = new TreeViewItem { Header = storage.Name };
//directories
foreach (var dir in await storage.GetFoldersAsync())
{
dirNode.Items.Add(ParseStorageAsync(dir));
}
//files
foreach (var file in await storage.GetFilesAsync())
{
TreeViewItem item = new TreeViewItem
{
Header = Path.GetFileNameWithoutExtension(file.Name),
Tag = file.Path
};
dirNode.Items.Add(item);
}
return dirNode;
}
If I use the DirectoryInfo class instead of StorageFolder, it works but only in a WPF project and I have to do an UWP project.
The problem is that it prints this :
because of the asynchronous method...
Is it possible to parse in an other way a folder in an UWP project ?
EDIT : My TreeView class is a library wrote on the Microsoft GitHub so it works well and this is not the problem.
You are adding a Task to the collection without executing it, you need to await your method
tree.Items.Add(await ParseStorageAsync(root));

How to reload playlist in BackgroundAudioPlayer?

I have a solution, which includes 2 projects: main progect, and PlayListFilePlayBackAgent.
in main project, in MainPage a have a listBox, which shows different content. Some content has audioformat, and when i see title, and performer in listbox's item - i click button "play" in this listbox's item. This button make some operations(take track's urls from some server) and than go to page AudioPage. In AudioPage some method make a playlist from those urls, than its saves in isolatedStorage in xml format(serialized).
Here is PlayList.cs
public class Playlist
{
public Playlist()
{
Tracks = new List<PlaylistTrack>();
}
[XmlElement(ElementName = "Track")]
public List<PlaylistTrack> Tracks { set; get; }
[XmlElement(ElementName = "TrackCount")]
public int TrackCount { set; get; }
public static Playlist Load(string filename)
{
Playlist playlist = null;
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = storage.OpenFile(filename, FileMode.Open))
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Playlist));
playlist = xmlSerializer.Deserialize(stream) as Playlist;
}
}
return playlist;
}
public void Save(string filename)
{
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = storage.CreateFile(filename))
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Playlist));
xmlSerializer.Serialize(stream, this);
}
}
}
}
And a PlayBackAgent than deserializes this xml into playlist, and plays it.
That's all good, but when i want to listen tracks which were not in those item of listbox i have a problem.
I go back to my listbox, than i select some track and push button "play". In button handler, i .Clear(); my collection, take new urls of new tracks, go to AudioPage and make a new playlist. But in emulator i see the same music i listen before.I thought the problem was in that PlayListFilePlayBackAgent do not update a playlist from isolatedStorade. But when i click different buttons "play" - field TrackCount changes(don't mind to its name, it just says what number of track in playlist must play player), i can see it in output when write Debug.WriteLine(Convert.ToString(playlist.TrackCount)); in AudioPlayer.cs. So what I have: an audioplayer, which plays music only from first playlist I gave to him. When I want to listen another playlist - in AudioPage i see old playlist and can listen music only from those old playlist. What I want: an audioplayer which can change playlist everytime when I push button "play", and can play this music.
PS: I use PlayListFilePlayBackAgent because it can play music even if i close this application. If you need more code - just tell me. Thanks.
Update:
Button handler
private void Audio_Button_Click(object sender, RoutedEventArgs e)
{
string uri = null;
TextBox tb = null;
var grid = (Grid)((Button)sender).Parent;
foreach (var child in grid.Children)
{
if (child is TextBox && (string)((TextBox)child).Tag == "URL")
{
tb = (TextBox)child;
}
}
uri = tb.Text;
BackgroundAudioPlayer.Instance.SkipNext();
//MessageBox.Show(uri);
string url = string.Format("https://api.vk.com/method/audio.getById.xml?audios={0}&access_token={1}", uri, App.AccessToken);
var c = new WebClient();
c.OpenReadCompleted += (sender1, e1) =>
{
XDocument xml = XDocument.Load(e1.Result);
MessageBox.Show(xml.ToString());
var inf = from u in xml.Descendants("audio")
select u;
AudioPage.audios.Clear();
foreach (var item in inf)
{
AudioPage.audios.Add(new AudioAttachment((string)item.Element("url"), (string)item.Element("title"), (string)item.Element("artist")));
}
string destination = string.Format("/AudioPage.xaml");
NavigationService.Navigate(new Uri(destination, UriKind.Relative));
AudioPage.count = 0;
};
c.OpenReadAsync(new Uri(url));
}
OnUseraction.cs
protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
{
switch (action)
{
case UserAction.Play:
if (player.Track == null)
{
// Load playlist from isolated storage
if (playlist == null)
playlist = Playlist.Load("playlist.xml");
// Debug.WriteLine(playlist.Tracks[0].Title);
Debug.WriteLine(Convert.ToString(playlist.TrackCount));
currentTrack = playlist.TrackCount;
player.Track = playlist.Tracks[currentTrack].ToAudioTrack();
}
else
{
player.Play();
}
break;
case UserAction.Pause:
player.Pause();
break;
case UserAction.SkipNext:
if (currentTrack < playlist.Tracks.Count - 1)
{
currentTrack += 1;
player.Track = playlist.Tracks[currentTrack].ToAudioTrack();
}
else
{
player.Track = null;
}
break;
case UserAction.SkipPrevious:
if (currentTrack > 0)
{
currentTrack -= 1;
player.Track = playlist.Tracks[currentTrack].ToAudioTrack();
}
else
{
player.Track = null;
}
break;
case UserAction.Seek:
player.Position = (TimeSpan)param;
break;
}
NotifyComplete();
}
If you change your playlist in the UI you will need some way of signalling to the agent that it needs to change what it is playing, otherwise it will continue playing the current track.
When user hits the play button, call BackgroundAudioPlayer.Instance.SkipNext from your app, and when you get the OnUserAction event in your agent code you'll be able to recheck your playlist XML file.
if (playlist == null)
playlist = Playlist.Load("playlist.xml");
Where do you set playlist back to Null?
Man, I feel your pain. I have exactly same situation and used exactly the same approach (with the exception that I used IsolatedStorageSettings.ApplicationSettings collection instead of writing into the file).
It works perfectly for the 1st run.
The behavior is really weird.. I saw data updated on the foreground app, but reading same settings from the player would not give me new data. I also tried writing with a new settings key (in case BAP caches results or something) but I got some unstable results where it would work to 2-5 updates and then just stopped seeing new settings keys in the collection. Drives me crazy...
I also noticed that the Tag property of a audio track seems to be copied properly, so I tried to serialize my playlist and pass as a string value through the tag, but got an errors that some memory was not enough or something.. Looks like they have their own restrictions there.
Please let me know if you get find luck with this issue.
Perhaps one of the most detailed info on playing audio on Windows Phone. Check the project here http://developer.nokia.com/community/wiki/Streaming_MP3_player_in_WP7.
However I must say in my case I had to load the Database and extract the information by looping through the Lists without using JSON. Below is the code;
private void LoadPlayListFromIsolatedStorage(BackgroundAudioPlayer player)
{
// clear previous playlist
_playList.Clear();
// access to isolated storage
IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication();
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("AlbumsData.dat", FileMode.OpenOrCreate, isoStore))
{
if (stream.Length > 0)
{
DataContractSerializer serializer = new DataContractSerializer(typeof(List<Album>));
List<Album> strm = serializer.ReadObject(stream) as List<Album>;
List<Song> _songs = strm[0].Songs;
for (int i = 0; i < _songs.Count; i++)
{
AudioTrack audioTrack = new AudioTrack(
new Uri(_songs[i].Directory, UriKind.Absolute), // URL
_songs[i].Title, // MP3 Music Title
"Artist", // MP3 Music Artist
"Album", // MP3 Music Album name
new Uri(_songs[i].AlbumArt, UriKind.Absolute) // MP3 Music Artwork URL
);
_playList.Add(audioTrack);
}
}
else
{
// no AudioTracks from Isolated Storage
}
// start playing
PlayTrack(player);
}

copy list items from one list to another in sharepoint

In Sharepoint how can you copy a list item from one list to another list
eg copy from "List A" to "List B" (both are at the root of the site)
I want this copying to occur when a new list item is added to "List A"
I tried using the CopyTo() method of an SPListItem inside the ItemAdded event receiver but couldnt figure out the url to copy to.
Here is the code I use. Pass it a SPlistItem and the name of the destination list as seen in Sharepoint(Not the URL). The only restriction is that both list must be in the same site:
private SPListItem CopyItem(SPListItem sourceItem, string destinationListName) {
//Copy sourceItem to destinationList
SPList destinationList = sourceItem.Web.Lists[destinationListName];
SPListItem targetItem = destinationList.Items.Add();
foreach (SPField f in sourceItem.Fields) {
//Copy all except attachments.
if (!f.ReadOnlyField && f.InternalName != "Attachments"
&& null != sourceItem[f.InternalName])
{
targetItem[f.InternalName] = sourceItem[f.InternalName];
}
}
//Copy attachments
foreach (string fileName in sourceItem.Attachments) {
SPFile file = sourceItem.ParentList.ParentWeb.GetFile(sourceItem.Attachments.UrlPrefix + fileName);
byte[] imageData = file.OpenBinary();
targetItem.Attachments.Add(fileName, imageData);
}
return targetItem;
}
Indeed as Lars said, it can be tricky to move items and retain versions and correct userinfo. I have done similar things with that before so if you need some code examples, let me know through a comment and can supply you with some guidance.
The CopyTo method (if you decide to go with that) need an absolute Uri like:
http://host/site/web/list/filename.doc
So, if you are performing this in an event receiver you need to concatinate a string containing the elements needed. Something like (note that this can be done in other ways to):
string dest=
siteCollection.Url + "/" + site.Name + list.Name + item.File.Name;
Copying and moving files, items and folders in SharePoint can be tricky if you want to retain all metadata, timestamps, author info and version history. Take a look a CopyMove for SharePoint - it also has a Web Service API.
There's many tools on the market for copying a list item to another list (avepoint, metavis, etc.) but they are pretty expensive if you're planning to do this on only one list.
If you can do this manually once a week for example, look at the following tool : http://en.share-gate.com/sharepoint-tools/copy-move-sharepoint-list-items-with-metadata-and-version-history
Here is a powershell equivalent of Sylvian's that does allow for cross-site copy. His code could be modified similarly as well...
param([string]$sourceWebUrl, [string]$sourceListName, [string]$destWebUrl, [string]$destListName)
$sourceWeb = get-spweb $sourceWebUrl;
$sourceList = $sourceWeb.Lists[$sourceListName];
$destWeb = get-spweb $destWebUrl;
$destList = $destWeb.Lists[$destListName];
$sourceList.Items |%{
$destItem = $destList.Items.Add();
$sourceItem = $_;
$sourceItem.Fields |%{
$f = $_;
if($f.ReadOnlyField -eq $false -and $f.InternalName -ne "Attachments" -and $sourceItem[$f.InternalName] -ne $null){
$destItem[$f.InternalName] = $sourceItem[$f.InternalName];
}
}
$destItem.Update();
}
To use, copy and past to a file copy-listitems.ps1 and run using Sharpoint powerhsell commandline...
Make sure you call CopyTo(url) method on SPFile, not on SPListItem.
for example:
ItemUpdated(SPItemEventProperties properties)
{
//...
string url = properties.Web.Site.Url + "/" + properties.Web.Name + "Lists/ListName/" + properties.ListItem.File.Name;
//properties.ListItem.File.MoveTo(url);
properties.ListItem.File.CopyTo(url);
//...
}
private void CopyAttachmentsToList(SPListItem srcItem, SPListItem tgtItem)
{
try
{
//get source item attachments from the folder
SPFolder srcAttachmentsFolder =
srcItem.Web.Folders["Lists"].SubFolders[srcItem.ParentList.Title].SubFolders["Attachments"].SubFolders[srcItem.ID.ToString()];
//Add items to the target item
foreach (SPFile file in srcAttachmentsFolder.Files)
{
byte[] binFile = file.OpenBinary();
tgtItem.Update();
tgtItem.Attachments.AddNow(file.Name, binFile);
tgtItem.Update();
}
}
catch
{
//exception message goes here
}
finally
{
srcItem.Web.Dispose();
}
}
Don't forget to add this line, tgtItem.Update();, else you will get an err.
So, the lists have the exact same or similar columns? Either way, you could create a simple workflow that runs automatically when an item is created in "List A". Since the workflow in question is relatively simple, I'd recommend using SharePoint Designer (which is free) to create it, since you can easily match up the columns from the two lists. The walk through below should be able to help you get started.
Create a Workflow - SharePoint Designer
I had the same problem.
After experimenting a bit instead of
targetItem[f.InternalName] = sourceItem[f.InternalName];
I used:
targetItem[childField.Title] = sourceItem[parentField.Title];
How to copy field and save versions:
public static SPListItem CopyItem(SPListItem sourceItem, SPList destinationList)
{
SPListItem targetItem = destinationList.AddItem();
//loop over the soureitem, restore it
for (int i = sourceItem.Versions.Count - 1; i >= 0; i--)
{
//set the values into the archive
foreach (SPField sourceField in sourceItem.Fields)
{
SPListItemVersion version = sourceItem.Versions[i];
if ((!sourceField.ReadOnlyField) && (sourceField.InternalName != "Attachments"))
{
SetFields(targetItem, sourceField, version);
}
}
//update the archive item and
//loop over the the next version
targetItem.Update();
}
foreach (string fileName in sourceItem.Attachments)
{
SPFile file = sourceItem.ParentList.ParentWeb.GetFile(sourceItem.Attachments.UrlPrefix + fileName);
targetItem.Attachments.Add(fileName, file.OpenBinary());
}
targetItem.SystemUpdate();
return targetItem;
}
private static bool SetFields(SPListItem targetItem, SPField sourceField, SPListItemVersion version)
{
try
{
targetItem[sourceField.InternalName] = version.ListItem[sourceField.InternalName];
return true;
}
catch (System.ArgumentException)//field not filled
{
return false;
}
catch (SPException)//field not filled
{
return false;
}
}
Copy List Items from one SharePoint List or library to Another SharePoint list or library using c# server side code
//Itecollection is a collection of data from source list
public void CopyItemsFromOneListToAnotherList(SPListItemCollection itemCollection)
{
using (SPSite site = new SPSite(siteUrl))
{
using (SPWeb web = site.OpenWeb())
{
//Get destination list/library
//destListName - Destination list/library name
SPList destList = web.Lists.TryGetList(destListName);
foreach (SPListItem sourceItem in itemCollection)
{
//Add new Item to list
SPListItem destItem = destList.Items.Add();
foreach (SPField field in sourceItem.Fields)
{
if (!field.ReadOnlyField && !field.Hidden && field.InternalName != "Attachments")
{
if (destItem.Fields.ContainsField(field.InternalName))
{
//Copy item to destination library
destItem[field.InternalName] = sourceItem[field.InternalName];
}
}
}
//Update item in destination library or list
destItem.Update();
Console.WriteLine("Copied " + sourceItem["ID"] + "to destination list/library");
}
}
}
}

Categories

Resources