I am developing an UWP app where you can download videos from YouTube and convert them to a mp3 file. My problem here is that the process of writing the bytes of the downloaded video to the created .mp4 file freezes the application although it is marked as async and all the necessary function calls are marked with the await keyword.
The following function has been assigned to the Click attribute of the button:
private async void DownloadMp3File(object sender, RoutedEventArgs e)
{
string url = youtubeUrl.Text;
if (!string.IsNullOrEmpty(url) && url.Contains("youtube"))
{
string savePath = Environment.GetFolderPath(Environment.SpecialFolder.MyMusic);
StorageFolder storageFolder = KnownFolders.MusicLibrary;
var youTube = YouTube.Default; // starting point for YouTube actions
var video = await youTube.GetVideoAsync(url); // gets a Video object with info about the video
string videoPath = Path.Combine(savePath, video.FullName);
string mp3Path = Path.Combine(savePath, videoPath.Replace(".mp4", ".mp3"));
StorageFile videoFile = await storageFolder.CreateFileAsync(Path.GetFileName(videoPath), CreationCollisionOption.ReplaceExisting);
StorageFile mp3File = await storageFolder.CreateFileAsync(Path.GetFileName(mp3Path), CreationCollisionOption.ReplaceExisting);
await WriteBytesIntoVideoFile(videoFile, video).ConfigureAwait(false);
await ConvertMp4ToMp3(videoFile, mp3File);
await videoFile.DeleteAsync();
}
}
During debugging I managed to find out where the app would freeze. It freezes exactly during the time of writing the bytes of the YouTube video into the mp4 file.
The function WriteBytesIntoVideoFile writes the bytes of the downloaded video into the created video file. before this implementation I used FileIO.WriteBytesAsync but it is the same behaviour:
public async Task WriteBytesIntoVideoFile(StorageFile videoFile, YouTubeVideo downloadedVideo)
{
//await FileIO.WriteBytesAsync(videoFile, downloadedVideo.GetBytes());
var outputStream = await videoFile.OpenAsync(FileAccessMode.ReadWrite);
using (var dataWriter = new DataWriter(outputStream))
{
foreach (var byt in downloadedVideo.GetBytes())
{
dataWriter.WriteByte(byt);
}
await dataWriter.StoreAsync();
await outputStream.FlushAsync();
}
}
Finally the convert method which converts the mp4 to a mp3. It uses the MediaTranscoder to achieve this.
public async Task ConvertMp4ToMp3(IStorageFile videoFile, IStorageFile mp3File)
{
MediaEncodingProfile profile = MediaEncodingProfile.CreateMp3(AudioEncodingQuality.Auto);
MediaTranscoder transcoder = new MediaTranscoder();
PrepareTranscodeResult prepareOp = await transcoder.PrepareFileTranscodeAsync(videoFile, mp3File, profile);
if (prepareOp.CanTranscode)
{
await prepareOp.TranscodeAsync();
//transcodeOp.Progress +=
// new AsyncActionProgressHandler<double>(TranscodeProgress);
//transcodeOp.Completed +=
// new AsyncActionWithProgressCompletedHandler<double>(TranscodeComplete);
}
else
{
switch (prepareOp.FailureReason)
{
case TranscodeFailureReason.CodecNotFound:
System.Diagnostics.Debug.WriteLine("Codec not found.");
break;
case TranscodeFailureReason.InvalidProfile:
System.Diagnostics.Debug.WriteLine("Invalid profile.");
break;
default:
System.Diagnostics.Debug.WriteLine("Unknown failure.");
break;
}
}
}
Could anyone explain to me why the app freezes during the process of writing the bytes of the downloaded video to the mp4 file? Any help is appreciated.
Related
I want to share the screenshot of my app. The screenshot is saved into the phone's picture library then it's shared as StorageFile
The problem is, the image is not attached to the sharing apps. I've verified that the screenshot was saved successfully in the phone's picture library.
Here's my code. What am I missing?
private async void askFacebook()
{
// Render some UI to a RenderTargetBitmap
var renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(this.gridRoot, (int)this.gridRoot.ActualWidth, (int)this.gridRoot.ActualHeight);
// Get the pixel buffer and copy it into a WriteableBitmap
var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
var width = renderTargetBitmap.PixelWidth;
var height = renderTargetBitmap.PixelHeight;
var wbmp = await new WriteableBitmap(1, 1).FromPixelBuffer(pixelBuffer, width, height);
imageToShare = await saveWriteableBitmapAsJpeg(wbmp, string.Format("{0}.jpg", getAppTitle()));
DataTransferManager.ShowShareUI();
}
private string getAppTitle()
{
// Get the assembly with Reflection:
Assembly assembly = typeof(App).GetTypeInfo().Assembly;
// Get the custom attribute informations:
var titleAttribute = assembly.CustomAttributes.Where(ca => ca.AttributeType == typeof(AssemblyTitleAttribute)).FirstOrDefault();
// Now get the string value contained in the constructor:
return titleAttribute.ConstructorArguments[0].Value.ToString();
}
private async Task<StorageFile> saveWriteableBitmapAsJpeg(WriteableBitmap bmp, string fileName)
{
// Create file in Pictures library and write jpeg to it
var outputFile = await KnownFolders.PicturesLibrary.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
using (var writeStream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
{
await encodeWriteableBitmap(bmp, writeStream, BitmapEncoder.JpegEncoderId);
}
return outputFile;
}
private async Task encodeWriteableBitmap(WriteableBitmap bmp, IRandomAccessStream writeStream, Guid encoderId)
{
// Copy buffer to pixels
byte[] pixels;
using (var stream = bmp.PixelBuffer.AsStream())
{
pixels = new byte[(uint)stream.Length];
await stream.ReadAsync(pixels, 0, pixels.Length);
}
// Encode pixels into stream
var encoder = await BitmapEncoder.CreateAsync(encoderId, writeStream);
var logicalDpi = DisplayInformation.GetForCurrentView().LogicalDpi;
encoder.SetPixelData(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Premultiplied,
(uint)bmp.PixelWidth,
(uint)bmp.PixelHeight,
logicalDpi,
logicalDpi,
pixels);
await encoder.FlushAsync();
}
private void ShareImageHandler(DataTransferManager sender, DataRequestedEventArgs e)
{
DataRequest request = e.Request;
request.Data.Properties.Title = "Ask Social Media";
request.Data.Properties.Description = "Do you know the answer to this question?";
// Because we are making async calls in the DataRequested event handler,
// we need to get the deferral first.
DataRequestDeferral deferral = request.GetDeferral();
// Make sure we always call Complete on the deferral.
try
{
request.Data.SetBitmap(RandomAccessStreamReference.CreateFromFile(imageToShare));
}
finally
{
deferral.Complete();
}
}
Apparently for Windows Phone, the image needs to be a StorageItem as the SetBitmap methods only works with Windows 8.x
So for Windows phone, instead of
request.Data.SetBitmap(RandomAccessStreamReference.CreateFromFile(imageToShare));
I created a storage item and use SetStorageItems to share it. It works with native Windows Phone apps such as emails, OneNote but I haven't tested it for sharing on Facebook, Twitter etc.
var imageItems = new List<IStorageItem>();
imageItems.Add(imageToShare);
request.Data.SetStorageItems(imageItems);
I have the image URL.
How can I save it into Isolated Storage in WP 8.1.
Can I trigger both save it and then share it onto Facebook with only one button?
This is my code - which work well following Burak Kaan Köse's:
public async void GetImage()
{
StorageFolder folder = ApplicationData.Current.LocalFolder;
if (folder != null)
{
StorageFile file = await folder.CreateFileAsync("imagefile", CreationCollisionOption.ReplaceExisting);
string url = imgUri[fLFl.SelectedIndex].ToString();
HttpClient client = new HttpClient();
byte[] fileContent = await client.GetByteArrayAsync(url); ; // This is where you set your content as byteArray
Stream fileStream = await file.OpenStreamForWriteAsync();
fileStream.Write(fileContent, 0, fileContent.Length);
fileStream.Flush();
fileStream.Dispose();
}
}
Don't forget to change 'imagefile' path and fileContent variable.
private async void SaveFile()
{
try
{
StorageFolder folder = ApplicationData.Current.LocalFolder;
if(folder != null)
{
StorageFile file = await folder.CreateFileAsync("imagefile", CreationCollisionOption.ReplaceExisting);
byte[] fileContnet = null; // This is where you set your content as byteArray
Stream fileStream = await file.OpenStreamForWriteAsync();
fileStream.Write(fileContent, 0, fileContent.Length);
fileStream.Flush();
fileStream.Close();
}
}
catch (Exception ex)
{
// Some Exception handling code
}
}
I am writing text to a file in Windows Phone 8.1 like this:
private static async Task WriteData(String fileName, String content)
{
Byte[] data = Encoding.Unicode.GetBytes(content);
var folder = ApplicationData.Current.LocalFolder;
var file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
using (var s = await file.OpenStreamForWriteAsync())
{
await s.WriteAsync(data, 0, data.Length);
}
}
But when I read the file, it comes back empty. I have verified that there isn't some other method overwriting it by reading from the file immediately after I write to it. Am I missing something obvious here?
Maybe you can try:
public async Task SaveStreamToFile(Stream streamToSave, string fileName, CancellationToken cancelToken)
{
Byte[] buf= Encoding.Unicode.GetBytes(content);
var folder = ApplicationData.Current.LocalFolder;
StorageFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
using (Stream fileStram = await file.OpenStreamForWriteAsync())
{
int bytesread = 0;
while ((bytesread = await streamToSave.ReadAsync(buf, 0, BUFFER_SIZE)) > 0)
{
await fileStram.WriteAsync(buf, 0, bytesread);
cancelToken.ThrowIfCancellationRequested();
}
}
}
I was having the same problem. I save 2 files, one with debug information, and one with application specific information (a list of routes).The debug information seems to work OK
StorageFolder local = ApplicationData.Current.LocalFolder;
var file = await local.CreateFileAsync "debug.txt",CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(file, sbDebugInformation.ToString());
but the CreateCollisionOption.ReplaceExisting seemed to be causing problems with the other information, and I ended up with this, after many alternate attempts
StorageFolder local = ApplicationData.Current.LocalFolder;
var json = new DataContractJsonSerializer(typeof(RouteList));
var file = await local.CreateFileAsync(filename,CreationCollisionOption.OpenIfExists);
MemoryStream stream = new MemoryStream();
json.WriteObject(stream, routes);
await FileIO.WriteBytesAsync(file, stream.ToArray());
I'm trying to launch pdf reader with the code below but it does not work. Can somebody help me?
private async Task<StorageFile> WriteData(string fileName, byte[] data)
{
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
using (Stream s = await file.OpenStreamForWriteAsync())
{
await s.WriteAsync(data, 0, data.Length);
s.Close();
}
return file;
}
private async Task<bool> OpenPdf(StorageFile file)
{
var uri = new Uri(file.Path, UriKind.RelativeOrAbsolute);
bool result = await Windows.System.Launcher.LaunchUriAsync(uri);
return result;
}
private async void FetchPdf() {
// Fetch pdf bytes to network
//....
StorageFile file = await WriteData("test.pdf", data);
if (file != null) {
bool result = await OpenPdf(file);
if (result)
Debug.WriteLine("Success");
else
Debug.WriteLine("Cannot open pdf file.");
}
}
result is always false and so launcher is not presented.
I used LaunchUriAsync because LaunchFileAsync is not implemented on Windows Phone.
LaunchUriAsync isn't supported on Windows Phone 8 per the documentation. It throws an exception if called
You can use Windows.System.Launcher.LaunchFileAsync to launch a StorageFile.
This code works for example (assming there's a file called "metro.pdf" in the project, with the Build Action set to Content, with Copy to Output Directory set to Copy if Newer).
var installedLocation = Windows.ApplicationModel.Package.Current.InstalledLocation;
var assets = await installedLocation.GetFolderAsync("Assets");
var pdf = await assets.GetFileAsync("metro.pdf");
Windows.System.Launcher.LaunchFileAsync(pdf);
Called the API and saved the byte array to file
public static async void WriteDataToIsolatedStorageFile(string fileName, byte[] data)
{
using (IsolatedStorageFile storageFile = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = storageFile.OpenFile(fileName, FileMode.Create))
{
if ((data != null) && (data.Length > 0))
{
await stream.WriteAsync(data, 0, data.Length);
}
}
}
}
opened the file in pdf reader using
private async void StartExternalPDFApp()
{
StorageFolder localFolder = await FileManager.FindDirectory(FileManager.RelativeStorageDirectoryLocalStorage);
StorageFile storageFile = await localFolder.GetFileAsync(PdfFileName);
await Windows.System.Launcher.LaunchFileAsync(storageFile);
}
localFolder is Windows.Storage.ApplicationData.Current.LocalFolder
just put the anyFile.pdf in Assets folder, and make its build action to Content, and then Just make the function Async ... and then Put "await" before Windows.System.Launcher.LaunchFileAsync(pdf);
it worked fine for me. Nice.
See this.
private async void privacyPolicy_Click(object sender, EventArgs e)
{
var installedLocation = Windows.ApplicationModel.Package.Current.InstalledLocation;
var assets = await installedLocation.GetFolderAsync("Assets");
var pdf = await assets.GetFileAsync("PrivacyPolicy.pdf");
await Windows.System.Launcher.LaunchFileAsync(pdf);
}
In a windows store app, how can I delete text from a file ? For example
If I have
StorageFile file = await roamingfolder.CreateFileAsync(filename,
CreationCollisionOption.OpenIfExists);
await FileIO.AppendTextAsync(file, temp);
How can i remove some text from this file ?
You generally read the text into a string, remove the text, and rewrite the file.
Here I get a file then I put the content to a stringbuilder then do some string operations, finally put the string back to the file using DataWriter
public static async Task UpdateTextContent(string contentItemId)
{
var storageFolder = await ApplicationData.Current.LocalFolder.GetFolderAsync(TARGET_FOLDER);
StorageFile sf = null;
try
{
//get content of the file make sure that it exist
sf = await storageFolder.GetFileAsync(TARGET_FILE);
}
catch (Exception ex)
{
}
if (sf != null)
{
var targettxtfile = await Windows.Storage.FileIO.ReadTextAsync(sf);
var sbProcessedTextToWrite = new StringBuilder(targettxtfile);
if (targettxtfile.IndexOf(contentItemId) >= 0)
{
string startmarker = new StringBuilder("[").Append(contentItemId).Append("#start]").ToString();
string endmarker = new StringBuilder("[").Append(contentItemId).Append("#end]").ToString();
int start = sbProcessedTextToWrite.ToString().IndexOf(startmarker);
int end = sbProcessedTextToWrite.ToString().IndexOf(endmarker);
int slen = end + endmarker.Length - start;
//compute position to remove
sbProcessedTextToWrite.Remove(start, slen);
}
using (IRandomAccessStream fileStream = await sf.OpenAsync(FileAccessMode.ReadWrite))
{
using (IOutputStream outputStream = fileStream.GetOutputStreamAt(0))
{
using (DataWriter dataWriter = new DataWriter(outputStream))
{
dataWriter.WriteString(sbProcessedTextToWrite.ToString());
await dataWriter.StoreAsync();
// For the in-memory stream implementation we are using, the flushAsync call
// is superfluous,but other types of streams may require it.
await dataWriter.FlushAsync();
// In order to prolong the lifetime of the stream, detach it from the
// DataWriter so that it will not be closed when Dispose() is called on
// dataWriter. Were we to fail to detach the stream, the call to
// dataWriter.Dispose() would close the underlying stream, preventing
// its subsequent use by the DataReader below.
dataWriter.DetachStream();
}
//same here flush the outputStream as well
await outputStream.FlushAsync();
}
}
}
}
Some references for this code