MVC audio controls playing song from bytes - asp.net-mvc

I have my songs stored in database as bytes[]. How do I use these in the <audio> tag.
So something like this. Do I need to convert the bytes to something else first? I am not sure.
foreach (var item in Model)
{
<audio controls>
<source src=#item.SongBytes type="audio/mp3"/>
</audio>
}

One way would be to add a new action in your controller that returns the data:
public ActionResult Audio(int someId)
{
byte[] songBytes;
// Add code to get data
return new FileStreamResult(songBytes, "audio/mp3");
}
Then put the URL to that into the src attribute:
foreach (var item in Model)
{
<audio controls>
<source src="/Controller/Audio/#item.someId" type="audio/mp3"/>
</audio>
}

Google chrome/Ipad require support for content-range requests, so to add to given answer here, do something like this:
public FileStreamResult StreamUploadedSongs(int id)
{
byte[] song = db.UploadedSongs.Where(x => x.Id == id).FirstOrDefault().SongBytes;
long fSize = song.Length;
long startbyte = 0;
long endbyte = fSize - 1;
int statusCode = 200;
if ((Request.Headers["Range"] != null))
{
//Get the actual byte range from the range header string, and set the starting byte.
string[] range = Request.Headers["Range"].Split(new char[] { '=', '-' });
startbyte = Convert.ToInt64(range[1]);
if (range.Length > 2 && range[2] != "") endbyte = Convert.ToInt64(range[2]);
//If the start byte is not equal to zero, that means the user is requesting partial content.
if (startbyte != 0 || endbyte != fSize - 1 || range.Length > 2 && range[2] == "")
{ statusCode = 206; }//Set the status code of the response to 206 (Partial Content) and add a content range header.
}
long desSize = endbyte - startbyte + 1;
//Headers
Response.StatusCode = statusCode;
Response.ContentType = "audio/mp3";
Response.AddHeader("Content-Accept", Response.ContentType);
Response.AddHeader("Content-Length", desSize.ToString());
Response.AddHeader("Content-Range", string.Format("bytes {0}-{1}/{2}", startbyte, endbyte, fSize));
//Data
var stream = new MemoryStream(song, (int)startbyte, (int)desSize);
return new FileStreamResult(stream, Response.ContentType);
}

It works for me, my issue is when I tried to play the .wav file via wavesurfer.js in chrome by calling controller method which return the ActionResult then file is playing but I was not able to seek, forward and backward. When I do seek, player returns to start position. Although this functionality is working fine in Firfox.

Related

Html5 Audio - Refresh Page not work to reset audio

Hi I need help from you - ASP.NET MVC.
In my web application, I have a html5 audio.
It works once, but when you refresh the page, this video is in the browser's memory.
When I refresh the page, it should hit new controller action, just like the first time.
Please look at my code below:
My View
<audio controls style="width: 400px;">
<source src="#Url.Action("StreamUploadedSong")" type="audio/mp3" />
Your browser does not support the audio element.
</audio>
My Controller
public FileStreamResult StreamUploadedSong() // <--------- Do not enter here the second time.
{
byte[] teste = null;
string query1 = "SELECT * FROM Video WHERE Id= '2'";
using (SqlConnection connection1 = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
using (SqlCommand command1 = new SqlCommand(query1, connection1))
{
connection1.Open();
var reader = command1.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
teste = (byte[])reader["Voice"];
}
connection1.Close();
}
long fSize = teste.Length;
long startbyte = 0;
long endbyte = fSize - 1;
int statusCode = 200;
if ((Request.Headers["Range"] != null))
{
//Get the actual byte range from the range header string, and set the starting byte.
string[] range = Request.Headers["Range"].Split(new char[] { '=', '-' });
startbyte = Convert.ToInt64(range[1]);
if (range.Length > 2 && range[2] != "") endbyte = Convert.ToInt64(range[2]);
//If the start byte is not equal to zero, that means the user is requesting partial content.
if (startbyte != 0 || endbyte != fSize - 1 || range.Length > 2 && range[2] == "")
{ statusCode = 206; }//Set the status code of the response to 206 (Partial Content) and add a content range header.
}
long desSize = endbyte - startbyte + 1;
//Headers
Response.StatusCode = statusCode;
Response.ContentType = "audio/mp3";
Response.AddHeader("Content-Accept", Response.ContentType);
Response.AddHeader("Content-Length", desSize.ToString());
Response.AddHeader("Content-Range", string.Format("bytes {0}-{1}/{2}", startbyte, endbyte, fSize));
//Data
var stream = new MemoryStream(teste, (int)startbyte, (int)desSize);
return new FileStreamResult(stream, Response.ContentType);
}
Any idea how to refresh page to enter controller action StreamUploadedSong again?
I found solution !
Using Guid.NewGuid to Initializes a new instance of the Guid structure.
Bruce commented on this forum The Asp.net MVC:
"The browser caches the audio. If you want it to fetch every time, the URL should be unique. Just add a guid to the URL."
Source: ASP.NET Forums
Replace:
<source src="#Url.Action("StreamUploadedSong")" type="audio/mp3" />
to
<source src="#Url.Action("StreamUploadedSong", "Account", new {id = Guid.NewGuid()})" type="audio/mp3" />

An exception of type 'System.OutOfMemoryException' occurred in itextsharp.dll but was not handled in user code

I am using iTextSharp to create pdf. I have 100k records, but I am getting following exception:
An exception of type 'System.OutOfMemoryException' occurred in
itextsharp.dll but was not handled in user code At the line:
bodyTable.AddCell(currentProperty.GetValue(lst, null).ToString());
Code is:
var doc = new Document(pageSize);
PdfWriter.GetInstance(doc, stream);
doc.Open();
//Get exportable count
int columns = 0;
Type currentType = list[0].GetType();
//PREPARE HEADER
//foreach visible columns check if current object has proerpty
//else search in inner properties
foreach (var visibleColumn in visibleColumns)
{
if (currentType.GetProperties().FirstOrDefault(p => p.Name == visibleColumn.Key) != null)
{
columns++;
}
else
{
//check child property objects
var childProperties = currentType.GetProperties();
foreach (var prop in childProperties)
{
if (prop.PropertyType.BaseType == typeof(BaseEntity))
{
if (prop.PropertyType.GetProperties().FirstOrDefault(p => p.Name == visibleColumn.Key) != null)
{
columns++;
break;
}
}
}
}
}
//header
var headerTable = new PdfPTable(columns);
headerTable.WidthPercentage = 100f;
foreach (var visibleColumn in visibleColumns)
{
if (currentType.GetProperties().FirstOrDefault(p => p.Name == visibleColumn.Key) != null)
{
//headerTable.AddCell(prop.Name);
headerTable.AddCell(visibleColumn.Value);
}
else
{
//check child property objects
var childProperties = currentType.GetProperties();
foreach (var prop in childProperties)
{
if (prop.PropertyType.BaseType == typeof(BaseEntity))
{
if (prop.PropertyType.GetProperties().FirstOrDefault(p => p.Name == visibleColumn.Key) != null)
{
//headerTable.AddCell(prop.Name);
headerTable.AddCell(visibleColumn.Value);
break;
}
}
}
}
}
doc.Add(headerTable);
var bodyTable = new PdfPTable(columns);
bodyTable.Complete = false;
bodyTable.WidthPercentage = 100f;
//PREPARE DATA
foreach (var lst in list)
{
int col = 1;
foreach (var visibleColumn in visibleColumns)
{
var currentProperty = currentType.GetProperties().FirstOrDefault(p => p.Name == visibleColumn.Key);
if (currentProperty != null)
{
if (currentProperty.GetValue(lst, null) != null)
bodyTable.AddCell(currentProperty.GetValue(lst, null).ToString());
else
bodyTable.AddCell(string.Empty);
col++;
}
else
{
//check child property objects
var childProperties = currentType.GetProperties().Where(p => p.PropertyType.BaseType == typeof(BaseEntity));
foreach (var prop in childProperties)
{
currentProperty = prop.PropertyType.GetProperties().FirstOrDefault(p => p.Name == visibleColumn.Key);
if (currentProperty != null)
{
var currentPropertyObjectValue = prop.GetValue(lst, null);
if (currentPropertyObjectValue != null)
{
bodyTable.AddCell(currentProperty.GetValue(currentPropertyObjectValue, null).ToString());
}
else
{
bodyTable.AddCell(string.Empty);
}
break;
}
}
}
}
}
doc.Add(bodyTable);
doc.Close();
A back of the envelope computation of the memory requirements given the data you provided for memory consumption gives 100000 * 40 * (2*20+4) = 167MBs. Well within your memory limit, but it is just a lower bound. I imagine each Cell object is pretty big. If each cell would have a 512 byte overhead you could be well looking at 2GB taken. I reckon it might be even more, as PDF is a complex beast.
So you might realistically be looking at a situation where you are actually running out of memory. If not your computers, then at least the bit C# has set aside for its own thing.
I would do one thing first - check memory consumption like here. You might even do well to try with 10, 100, 1000, 10000, 100000 rows and see up until what number of rows the program works.
You could perhaps try a different thing altogether. If you're trying to print a nicely formatted table with a lot of data, perhaps you could output an HTML document, which can be done incrementally and which you can do by just writing stuff to a file, rather than using a third party library. You can then "print" that HTML document to PDF. StackOverflow to the rescue again with this problem.

Downloading Azure Blob files in MVC3

Our ASP.NET MVC 3 application is running on Azure and using Blob as file storage. I have the upload part figured out.
The View is going to have the File Name, which, when clicked will prompt the file download screen to appear.
Can anyone tell me how to go about doing this?
Two options really... the first is to just redirect the user to the blob directly (if the blobs are in a public container). That would look a bit like:
return Redirect(container.GetBlobReference(name).Uri.AbsoluteUri);
If the blob is in a private container, you could either use a Shared Access Signature and do redirection like the previous example, or you could read the blob in your controller action and push it down to the client as a download:
Response.AddHeader("Content-Disposition", "attachment; filename=" + name); // force download
container.GetBlobReference(name).DownloadToStream(Response.OutputStream);
return new EmptyResult();
Here's a resumable version (useful for large files or allowing seek in video or audio playback) of private blob access:
public class AzureBlobStream : ActionResult
{
private string filename, containerName;
public AzureBlobStream(string containerName, string filename)
{
this.containerName = containerName;
this.filename = filename;
this.contentType = contentType;
}
public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
var request = context.HttpContext.Request;
var connectionString = ConfigurationManager.ConnectionStrings["Storage"].ConnectionString;
var account = CloudStorageAccount.Parse(connectionString);
var client = account.CreateCloudBlobClient();
var container = client.GetContainerReference(containerName);
var blob = container.GetBlockBlobReference(filename);
blob.FetchAttributes();
var fileLength = blob.Properties.Length;
var fileExists = fileLength > 0;
var etag = blob.Properties.ETag;
var responseLength = fileLength;
var buffer = new byte[4096];
var startIndex = 0;
//if the "If-Match" exists and is different to etag (or is equal to any "*" with no resource) then return 412 precondition failed
if (request.Headers["If-Match"] == "*" && !fileExists ||
request.Headers["If-Match"] != null && request.Headers["If-Match"] != "*" && request.Headers["If-Match"] != etag)
{
response.StatusCode = (int)HttpStatusCode.PreconditionFailed;
return;
}
if (!fileExists)
{
response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
if (request.Headers["If-None-Match"] == etag)
{
response.StatusCode = (int)HttpStatusCode.NotModified;
return;
}
if (request.Headers["Range"] != null && (request.Headers["If-Range"] == null || request.Headers["IF-Range"] == etag))
{
var match = Regex.Match(request.Headers["Range"], #"bytes=(\d*)-(\d*)");
startIndex = Util.Parse<int>(match.Groups[1].Value);
responseLength = (Util.Parse<int?>(match.Groups[2].Value) + 1 ?? fileLength) - startIndex;
response.StatusCode = (int)HttpStatusCode.PartialContent;
response.Headers["Content-Range"] = "bytes " + startIndex + "-" + (startIndex + responseLength - 1) + "/" + fileLength;
}
response.Headers["Accept-Ranges"] = "bytes";
response.Headers["Content-Length"] = responseLength.ToString();
response.Cache.SetCacheability(HttpCacheability.Public); //required for etag output
response.Cache.SetETag(etag); //required for IE9 resumable downloads
response.ContentType = blob.Properties.ContentType;
blob.DownloadRangeToStream(response.OutputStream, startIndex, responseLength);
}
}
Example:
Response.AddHeader("Content-Disposition", "attachment; filename=" + filename); // force download
return new AzureBlobStream(blobContainerName, filename);
I noticed that writing to the response stream from the action method messes up the HTTP headers. Some expected headers are missing and others are not set correctly.
So instead of writing to the response stream, I get the blob content as a stream and pass it to the Controller.File() method.
CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
Stream blobStream = blob.OpenRead();
return File(blobStream, blob.Properties.ContentType, "FileName.txt");

Youtube video download URL

I wrote a program that gets youtube video URL and downloads it
Up today I did this:
1. get video "token" from "/get_video_info?video_id=ID" like:
http://www.youtube.com/get_video_info?video_id=jN0nWjvzeNc
2. Download Video by requesting it from "/get_video?video_id=ID&t=TOKEN&fmt=FORMAT_ID" like:
http://www.youtube.com/get_video?video_id=jN0nWjvzeNc&t=vjVQa1PpcFMgAK0HB1VRbinpVOwm29eGugPh3fBi6Dg%3D&fmt=18
But this doesn't work anymore!
What is the new download URL?
Thanks
Actually I'm working on the similar project that downloading the video file from youtube. I find that the get_video might be blocked by Youtube. so instead of using get_video., I use the video info retrieved from get_video_info and extract it to get the video file url.
Within the get_video_info, there are url_encoded_fmt_stream_map. After encoding it, you can find url and signature value of every video with different format. So the file url is like [url value]+'&signature='+[sig value].
Additionally I find the following topic that using same method with mine. Hope it can help you.
Can't Download from youtube
If you are interested about how to downloading youtube video file, there is a small program written by me to demonstrate the process. You are free to use it.
https://github.com/johnny0614/YoutubeVideoDownload
Add &asv=2 to the end of the URL.
You can get the stream directly by using only
http://www.youtube.com/get_video_info?video_id=jN0nWjvzeNc
I made a little script to stream youtube videos in PHP. See how the script get the video file.
<?php
#set_time_limit(0);
$id = $_GET['id']; //The youtube video ID
$type = $_GET['type']; //the MIME type of the video
parse_str(file_get_contents('http://www.youtube.com/get_video_info?video_id='.$id),$info);
$streams = explode(',',$info['url_encoded_fmt_stream_map']);
foreach($streams as $stream){
parse_str($stream,$real_stream);
$stype = $real_stream['type'];
if(strpos($real_stream['type'],';') !== false){
$tmp = explode(';',$real_stream['type']);
$stype = $tmp[0];
unset($tmp);
}
if($stype == $type && ($real_stream['quality'] == 'large' || $real_stream['quality'] == 'medium' || $real_stream['quality'] == 'small')){
header('Content-type: '.$stype);
header('Transfer-encoding: chunked');
#readfile($real_stream['url'].'&signature='.$real_stream['sig']); //Change here to do other things such as save the file to the filesystem etc.
ob_flush();
flush();
break;
}
}
?>
See the working demo here. I hope this can help you.
After a lot of failed tries, this github repositories help me:
https://github.com/rg3/youtube-dl
Get the url only like:
youtube-dl 'https://www.youtube.com/watch?v=bo_efYhYU2A' --get-url
download an mp4 and save as a.mp4 like:
youtube-dl 'https://www.youtube.com/watch?v=bo_efYhYU2A' -f mp4 -o a.mp4
Good luck.
Last time I was working on fixing one of the broken Chrome extensions to download YouTube video. I fixed it by altering the script part.
(Javascript)
var links = new String();
var downlink = new String();
var has22 = new Boolean();
has22 = false;
var Marked = false;
var FMT_DATA = fmt_url_map;//This is html text that you have to grab. In case of extension it was readily available through:document.getElementsByTagName('script');
var StrSplitter1 = '%2C', StrSplitter2 = '%26', StrSplitter3 = '%3D';
if (FMT_DATA.indexOf(',') > -1) { //Found ,
StrSplitter1 = ',';
StrSplitter2 = (FMT_DATA.indexOf('&') > -1) ? '&' : '\\u0026';
StrSplitter3 = '=';
}
var videoURL = new Array();
var FMT_DATA_PACKET = new Array();
var FMT_DATA_PACKET = FMT_DATA.split(StrSplitter1);
for (var i = 0; i < FMT_DATA_PACKET.length; i++) {
var FMT_DATA_FRAME = FMT_DATA_PACKET[i].split(StrSplitter2);
var FMT_DATA_DUEO = new Array();
for (var j = 0; j < FMT_DATA_FRAME.length; j++) {
var pair = FMT_DATA_FRAME[j].split(StrSplitter3);
if (pair.length == 2) {
FMT_DATA_DUEO[pair[0]] = pair[1];
}
}
var url = (FMT_DATA_DUEO['url']) ? FMT_DATA_DUEO['url'] : null;
if (url == null) continue;
url = unescape(unescape(url)).replace(/\\\//g, '/').replace(/\\u0026/g, '&');
var itag = (FMT_DATA_DUEO['itag']) ? FMT_DATA_DUEO['itag'] : null;
var itag = (FMT_DATA_DUEO['itag']) ? FMT_DATA_DUEO['itag'] : null;
if (itag == null) continue;
var signature = (FMT_DATA_DUEO['sig']) ? FMT_DATA_DUEO['sig'] : null;
if (signature != null) {
url = url + "&signature=" + signature;
}
if (url.toLowerCase().indexOf('http') == 0) { // validate URL
if (itag == '5') {
links += '<span class="yt-uix-button-menu-item" id="v240p">FLV (240p)</span>';
}
if (itag == '18') {
links += '<span class="yt-uix-button-menu-item" id="v360p">MP4 (360p)</span>';
}
if (itag == '35') {
links += '<span class="yt-uix-button-menu-item" id="v480p">FLV (480p)</span>';
}
if (itag == '22') {
links += '<span class="yt-uix-button-menu-item" id="v720p">MP4 HD (720p)</span>';
}
if (itag == '37') {
links += ' <span class="yt-uix-button-menu-item" id="v1080p">MP4 HD (1080p)</span>';
}
if (itag == '38') {
links += '<span class="yt-uix-button-menu-item" id="v4k">MP4 HD (4K)</span>';
}
FavVideo();
videoURL[itag] = url;
console.log(itag);
}
}
You can get separate video link from videoURL[itag] array.
The extension can be downloaded from here.
I hope this would help someone. This is working solution (as of 06-Apr-2013)

Data missing in ASP post to MVC

I have an ASP page:
<%
sData = "VARIABLE=SOMEDATAHERE"
oHttpRequest.Open "POST", "http://????/AAA/BBB", False
oHttpRequest.SetRequestHeader "Content-Type", "application/x-www-form-urlencoded"
oHttpRequest.Send sData
oPostResponse = oHttpRequest.ResponseText
Response.Write oPostResponse
%>
I am posting this to an MVC site:
[HttpPost]
public ActionResult BBB(string PORTAL)
{
}
In my BBB function I am unable to get to SOMEDATAHERE.
I have tried:
Request.Form["VARIABLE"]
Request.ServerVariables["VARIABLE"]
Ive tried reading the raw stream using the code below but thats blank too..
I am not sure what else I can try?
System.IO.Stream str; String strmContents;
Int32 counter, strLen, strRead;
// Create a Stream object.
str = Request.InputStream;
// Find number of bytes in stream.
strLen = Convert.ToInt32(str.Length);
// Create a byte array.
byte[] strArr = new byte[strLen];
// Read stream into byte array.
strRead = str.Read(strArr, 0, strLen);
// Convert byte array to a text string.
strmContents = "";
for (counter = 0; counter < strLen; counter++)
{
strmContents = strmContents + strArr[counter].ToString();
}

Resources