Ejemplo n.º 1
0
        /// <summary>
        /// Process returns a file stream containing album art
        /// </summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // Check for the MusicBrainz ID
            string musicBrainzId = null;

            uri.Parameters.TryGetValue("musicBrainzId", out musicBrainzId);
            if (musicBrainzId == null)
            {
                processor.WriteErrorHeader();
                return;
            }

            // Find the music brainz id
            string   path = ArtPathForMusicBrainzId(musicBrainzId);
            FileInfo info = new FileInfo(path);

            if (info.Exists)
            {
                DateTime?  lastModified = info.LastWriteTimeUtc;
                FileStream stream       = new FileStream(path, FileMode.Open);
                processor.WriteFile(stream, 0, stream.Length, HttpHeader.MimeTypeForExtension(".jpg"), null, true, lastModified);

                // Close the file so we don't get sharing violations on future accesses
                stream.Close();
            }
            else
            {
                // If all else fails, error
                processor.WriteErrorHeader();
            }
        }
        /// <summary>
        /// Process returns a file stream containing album art
        /// </summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // Check for the MusicBrainz ID
            string musicBrainzId = null;
            uri.Parameters.TryGetValue("musicBrainzId", out musicBrainzId);
            if (musicBrainzId == null)
            {
                processor.WriteErrorHeader();
                return;
            }

            // Find the music brainz id
            string path = ArtPathForMusicBrainzId(musicBrainzId);
            FileInfo info = new FileInfo(path);
            if (info.Exists)
            {
                DateTime? lastModified = info.LastWriteTimeUtc;
                FileStream stream = new FileStream(path, FileMode.Open);
                processor.WriteFile(stream, 0, stream.Length, HttpHeader.MimeTypeForExtension(".jpg"), null, true, lastModified);

                // Close the file so we don't get sharing violations on future accesses
                stream.Close();
            }
            else
            {
                // If all else fails, error
                processor.WriteErrorHeader();
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Process returns a copy of the media database, and can be used to return SQL deltas to update
        /// the local copy of the media database
        /// </summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // If ID set, return all queries >= this id
            if (uri.Id != null)
            {
                processor.WriteJson(new DatabaseResponse(null, Injection.Kernel.Get <IDatabase>().QueryLogsSinceId((int)uri.Id)));
                return;
            }

            // No id parameter, so send down the whole backup database
            long   databaseLastQueryId = -1;
            string databaseFileName    = DatabaseBackup.Backup(out databaseLastQueryId);

            // Verify database filename present
            if ((object)databaseFileName == null)
            {
                processor.WriteErrorHeader();
                return;
            }

            try
            {
                // Read in entire database file
                Stream stream      = new FileStream(ServerUtility.RootPath() + databaseFileName, FileMode.Open, FileAccess.Read);
                long   length      = stream.Length;
                int    startOffset = 0;

                // Handle the Range header to start from later in the file if connection interrupted
                if (processor.HttpHeaders.ContainsKey("Range"))
                {
                    string range = (string)processor.HttpHeaders["Range"];
                    string start = range.Split(new char[] { '-', '=' })[1];
                    logger.IfInfo("Connection retried.  Resuming from " + start);
                    startOffset = Convert.ToInt32(start);
                }

                // We send the last query id as a custom header
                IDictionary <string, string> customHeader = new Dictionary <string, string>();
                customHeader["WaveBox-LastQueryId"] = databaseLastQueryId.ToString();

                // Send the database file
                processor.WriteFile(stream, startOffset, length, "application/octet-stream", customHeader, true, new FileInfo(ServerUtility.RootPath() + databaseFileName).LastWriteTimeUtc);
                stream.Close();
            }
            catch
            {
                // Send JSON on error
                processor.WriteJson(new DatabaseResponse("Could not open backup database " + databaseFileName, null));
            }

            return;
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Process returns a page from the WaveBox web interface
        /// </summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // Store root path, return index by default
            string path = webRoot;

            // Check for and apply theme path
            if (Injection.Kernel.Get<IServerSettings>().Theme == null)
            {
                logger.Error("No theme set in WaveBox configuration, cannot serve Web UI");

                // File not found
                processor.WriteErrorHeader();
                return;
            }

            // Append theme path to web root
            path += Injection.Kernel.Get<IServerSettings>().Theme;

            // Validate theme path
            if (!Directory.Exists(path))
            {
                logger.Error("Invalid theme '" + Injection.Kernel.Get<IServerSettings>().Theme + "' set in WaveBox configuration, cannot serve Web UI");

                // File not found
                processor.WriteErrorHeader();
                return;
            }

            if (uri.UriParts.Count == 0)
            {
                // No path, so return the home page
                path += Path.DirectorySeparatorChar + "index.html";

                // Ensure theme contains an index
                if (!File.Exists(path))
                {
                    logger.Error("Theme '" + Injection.Kernel.Get<IServerSettings>().Theme + "' missing required file index.html");

                    // File not found
                    processor.WriteErrorHeader();
                    return;
                }
            }
            else
            {
                // Iterate UriParts to send web pages
                for (int i = 0; i < uri.UriParts.Count; i++)
                {
                    string pathPart = uri.UriParts[i];
                    if (pathPart.Length > 0 && pathPart[0] == '.')
                    {
                        // Do not return hidden files/folders
                        processor.WriteErrorHeader();
                        return;
                    }
                    else
                    {
                        path += Path.DirectorySeparatorChar + uri.UriParts[i];
                    }
                }
            }

            // Make sure the file exists
            if (!File.Exists(path))
            {
                logger.IfInfo("File does not exist: " + path);

                // File not found
                processor.WriteErrorHeader();
                return;
            }

            // Serve up files inside html directory
            FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read);
            int startOffset = 0;

            // Handle the Range header to start from later in the file
            if (processor.HttpHeaders.ContainsKey("Range"))
            {
                string range = (string)processor.HttpHeaders["Range"];
                string start = range.Split(new char[]{'-', '='})[1];
                logger.IfInfo("Connection retried.  Resuming from " + start);
                startOffset = Convert.ToInt32(start);
            }

            long length = file.Length - startOffset;

            processor.WriteFile(file, startOffset, length, HttpHeader.MimeTypeForExtension(Path.GetExtension(path)), null, true, new FileInfo(path).LastWriteTimeUtc);
            file.Close();
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Process returns a page from the WaveBox web interface
        /// </summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // Store root path, return index by default
            string path = webRoot;

            // Check for and apply theme path
            if (Injection.Kernel.Get <IServerSettings>().Theme == null)
            {
                logger.Error("No theme set in WaveBox configuration, cannot serve Web UI");

                // File not found
                processor.WriteErrorHeader();
                return;
            }

            // Append theme path to web root
            path += Injection.Kernel.Get <IServerSettings>().Theme;

            // Validate theme path
            if (!Directory.Exists(path))
            {
                logger.Error("Invalid theme '" + Injection.Kernel.Get <IServerSettings>().Theme + "' set in WaveBox configuration, cannot serve Web UI");

                // File not found
                processor.WriteErrorHeader();
                return;
            }

            if (uri.UriParts.Count == 0)
            {
                // No path, so return the home page
                path += Path.DirectorySeparatorChar + "index.html";

                // Ensure theme contains an index
                if (!File.Exists(path))
                {
                    logger.Error("Theme '" + Injection.Kernel.Get <IServerSettings>().Theme + "' missing required file index.html");

                    // File not found
                    processor.WriteErrorHeader();
                    return;
                }
            }
            else
            {
                // Iterate UriParts to send web pages
                for (int i = 0; i < uri.UriParts.Count; i++)
                {
                    string pathPart = uri.UriParts[i];
                    if (pathPart.Length > 0 && pathPart[0] == '.')
                    {
                        // Do not return hidden files/folders
                        processor.WriteErrorHeader();
                        return;
                    }
                    else
                    {
                        path += Path.DirectorySeparatorChar + uri.UriParts[i];
                    }
                }
            }

            // Make sure the file exists
            if (!File.Exists(path))
            {
                logger.IfInfo("File does not exist: " + path);

                // File not found
                processor.WriteErrorHeader();
                return;
            }

            // Serve up files inside html directory
            FileStream file        = new FileStream(path, FileMode.Open, FileAccess.Read);
            int        startOffset = 0;

            // Handle the Range header to start from later in the file
            if (processor.HttpHeaders.ContainsKey("Range"))
            {
                string range = (string)processor.HttpHeaders["Range"];
                string start = range.Split(new char[] { '-', '=' })[1];
                logger.IfInfo("Connection retried.  Resuming from " + start);
                startOffset = Convert.ToInt32(start);
            }

            long length = file.Length - startOffset;

            processor.WriteFile(file, startOffset, length, HttpHeader.MimeTypeForExtension(Path.GetExtension(path)), null, true, new FileInfo(path).LastWriteTimeUtc);
            file.Close();
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Process produces a direct file stream of the requested media file
        /// </summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // Verify ID received
            if (uri.Id == null)
            {
                processor.WriteJson(new StreamResponse("Missing required parameter 'id'"));
                return;
            }

            logger.IfInfo("Starting file streaming sequence");

            // Try to get seconds
            float seconds = 0f;

            if (uri.Parameters.ContainsKey("seconds"))
            {
                float.TryParse(uri.Parameters["seconds"], out seconds);
            }

            try {
                // Get the media item associated with this id
                ItemType   itemType = Injection.Kernel.Get <IItemRepository>().ItemTypeForItemId((int)uri.Id);
                IMediaItem item     = null;
                if (itemType == ItemType.Song)
                {
                    item = Injection.Kernel.Get <ISongRepository>().SongForId((int)uri.Id);
                    logger.IfInfo("Preparing audio stream: " + item.FileName);
                }
                else if (itemType == ItemType.Video)
                {
                    item = Injection.Kernel.Get <IVideoRepository>().VideoForId((int)uri.Id);
                    logger.IfInfo("Preparing video stream: " + item.FileName);
                }

                // Return an error if none exists
                if ((item == null) || (!File.Exists(item.FilePath())))
                {
                    processor.WriteJson(new StreamResponse("No media item exists with ID: " + (int)uri.Id));
                    return;
                }

                // Prepare file stream
                Stream stream      = item.File();
                long   length      = stream.Length;
                int    startOffset = 0;
                long?  limitToSize = null;

                if (seconds > 0)
                {
                    // Guess the file position based on the seconds requested
                    // this is just rough now, but will be improved to take into account the header size
                    float percent = seconds / (float)item.Duration;
                    if (percent < 1f)
                    {
                        startOffset = (int)(item.FileSize * percent);
                        logger.IfInfo("seconds: " + seconds + "  Resuming from " + startOffset);
                    }
                }
                else if (processor.HttpHeaders.ContainsKey("Range"))
                {
                    // Handle the Range header to start from later in the file
                    string range = (string)processor.HttpHeaders["Range"];
                    var    split = range.Split(new char[] { '-', '=' });
                    string start = split[1];
                    string end   = split.Length > 2 ? split[2] : null;

                    logger.IfInfo("Range header: " + range + "  Resuming from " + start);
                    startOffset = Convert.ToInt32(start);
                    if (!ReferenceEquals(end, null) && end != String.Empty)
                    {
                        limitToSize = (Convert.ToInt64(end) + 1) - startOffset;
                    }
                }

                // Send the file
                processor.WriteFile(stream, startOffset, length, item.FileType.MimeType(), null, true, new FileInfo(item.FilePath()).LastWriteTimeUtc, limitToSize);
                stream.Close();

                logger.IfInfo("Successfully streamed file!");
            } catch (Exception e) {
                logger.Error(e);
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Process returns a copy of the media database, and can be used to return SQL deltas to update
        /// the local copy of the media database
        /// </summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // If ID set, return all queries >= this id
            if (uri.Id != null)
            {
                processor.WriteJson(new DatabaseResponse(null, Injection.Kernel.Get<IDatabase>().QueryLogsSinceId((int)uri.Id)));
                return;
            }

            // No id parameter, so send down the whole backup database
            long databaseLastQueryId = -1;
            string databaseFileName = DatabaseBackup.Backup(out databaseLastQueryId);

            // Verify database filename present
            if ((object)databaseFileName == null)
            {
                processor.WriteErrorHeader();
                return;
            }

            try
            {
                // Read in entire database file
                Stream stream = new FileStream(ServerUtility.RootPath() + databaseFileName, FileMode.Open, FileAccess.Read);
                long length = stream.Length;
                int startOffset = 0;

                // Handle the Range header to start from later in the file if connection interrupted
                if (processor.HttpHeaders.ContainsKey("Range"))
                {
                    string range = (string)processor.HttpHeaders["Range"];
                    string start = range.Split(new char[]{'-', '='})[1];
                    logger.IfInfo("Connection retried.  Resuming from " + start);
                    startOffset = Convert.ToInt32(start);
                }

                // We send the last query id as a custom header
                IDictionary<string, string> customHeader = new Dictionary<string, string>();
                customHeader["WaveBox-LastQueryId"] = databaseLastQueryId.ToString();

                // Send the database file
                processor.WriteFile(stream, startOffset, length, "application/octet-stream", customHeader, true, new FileInfo(ServerUtility.RootPath() + databaseFileName).LastWriteTimeUtc);
                stream.Close();
            }
            catch
            {
                // Send JSON on error
                processor.WriteJson(new DatabaseResponse("Could not open backup database " + databaseFileName, null));
            }

            return;
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Process handles the initialization of the file transcoding sequence
        /// <summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // Get TranscodeService instance
            TranscodeService transcodeService = (TranscodeService)ServiceManager.GetInstance("transcode");

            // Ensure transcode service is ready
            if ((object)transcodeService == null)
            {
                processor.WriteJson(new TranscodeResponse("TranscodeService is not running!"));
                return;
            }

            // Create transcoder
            ITranscoder transcoder = null;

            // Get seconds offset
            float seconds = 0f;
            if (uri.Parameters.ContainsKey("seconds"))
            {
                float.TryParse(uri.Parameters["seconds"], out seconds);
            }

            // Verify ID received
            if (uri.Id == null)
            {
                processor.WriteJson(new TranscodeResponse("Missing required parameter 'id'"));
                return;
            }

            try
            {
                // Set up default transcoding parameters
                ItemType itemType = Injection.Kernel.Get<IItemRepository>().ItemTypeForItemId((int)uri.Id);
                IMediaItem item = null;
                TranscodeType transType = TranscodeType.MP3;
                bool isDirect = false;
                Stream stream = null;
                int startOffset = 0;
                long? limitToSize = null;
                long length = 0;
                bool estimateContentLength = false;

                // Optionally estimate content length
                if (uri.Parameters.ContainsKey("estimateContentLength"))
                {
                    estimateContentLength = uri.Parameters["estimateContentLength"].IsTrue();
                }

                // Get the media item associated with this id
                if (itemType == ItemType.Song)
                {
                    item = Injection.Kernel.Get<ISongRepository>().SongForId((int)uri.Id);
                    logger.IfInfo("Preparing audio transcode: " + item.FileName);

                    // Default to MP3 transcoding
                    transType = TranscodeType.MP3;
                }
                else if (itemType == ItemType.Video)
                {
                    item = Injection.Kernel.Get<IVideoRepository>().VideoForId((int)uri.Id);
                    logger.IfInfo("Preparing video transcode: " + item.FileName);

                    // Default to h.264 transcoding
                    transType = TranscodeType.X264;
                }

                // Return an error if no item exists
                if ((item == null) || (!File.Exists(item.FilePath())))
                {
                    processor.WriteJson(new TranscodeResponse("No media item exists with ID: " + (int)uri.Id));
                    return;
                }

                // Optionally add isDirect parameter
                if (uri.Parameters.ContainsKey("isDirect"))
                {
                    isDirect = uri.Parameters["isDirect"].IsTrue();
                }

                if (seconds > 0)
                {
                    // Guess the file position based on the seconds requested
                    // this is wrong now, but will be improved to take into account the header size and transcode quality
                    // or even better, we should be able to just pass the offset seconds to ffmpeg
                    float percent = seconds / (float)item.Duration;
                    if (percent < 1f)
                    {
                        startOffset = (int)(item.FileSize * percent);
                        logger.IfInfo("seconds: " + seconds + "  Resuming from " + startOffset);
                    }
                }
                else if (processor.HttpHeaders.ContainsKey("Range"))
                {
                    // Handle the Range header to start from later in the file
                    string range = (string)processor.HttpHeaders["Range"];
                    var split = range.Split(new char[]{'-', '='});
                    string start = split[1];
                    string end = split.Length > 2 ? split[2] : null;
                    logger.IfInfo("Range header: " + range + "  Resuming from " + start + " end: " + end);

                    if (isDirect)
                    {
                        // TODO: Actually implement this lol
                        // This is a direct transfer with no file buffer, so treat a Range request as if it
                        // were start offset, unless an offsetSeconds was specified
                        if (uri.Parameters.ContainsKey("offsetSeconds"))
                        {

                        }
                        else
                        {

                        }
                    }
                    else
                    {
                        // This is a file request so use the range header to specify where in the file to return
                        startOffset = Convert.ToInt32(start);
                        if (!ReferenceEquals(end, null) && end.Length > 0)
                        {
                            limitToSize = (Convert.ToInt64(end) + 1) - startOffset;
                        }
                    }
                }

                // Get the transcoding type if specified
                if (uri.Parameters.ContainsKey("transType"))
                {
                    // Parse transcoding type
                    TranscodeType transTypeTemp;
                    if (Enum.TryParse<TranscodeType>(uri.Parameters["transType"], true, out transTypeTemp))
                    {
                        // Verify this is a valid transcode type for this item type
                        if (transTypeTemp.IsValidForItemType(item.ItemType))
                        {
                            // It is, so use it
                            transType = transTypeTemp;
                        }
                    }
                }

                // Get the quality, default to medium
                uint quality = (uint)TranscodeQuality.Medium;
                if (uri.Parameters.ContainsKey("transQuality"))
                {
                    string qualityString = uri.Parameters["transQuality"];
                    TranscodeQuality qualityEnum;
                    uint qualityValue;
                    // First try and parse a word enum value
                    if (Enum.TryParse<TranscodeQuality>(qualityString, true, out qualityEnum))
                    {
                        quality = (uint)qualityEnum;
                    }
                    // Otherwise look for a number to use as bitrate
                    else if (UInt32.TryParse(qualityString, out qualityValue))
                    {
                        quality = qualityValue;
                    }
                }

                // Create the transcoder
                if (item.ItemType == ItemType.Song)
                {
                    // Begin transcoding song
                    transcoder = transcodeService.TranscodeSong(item, transType, (uint)quality, isDirect, 0, (uint)item.Duration);
                }
                else
                {
                    // Video transcoding is just a bit more complicated.
                    // Check to see if the width, height, and maintainAspect options were used
                    uint? width = null;
                    if (uri.Parameters.ContainsKey("width"))
                    {
                        uint widthTemp;
                        width = UInt32.TryParse(uri.Parameters["width"], out widthTemp) ? (uint?)widthTemp : null;
                    }

                    uint? height = 0;
                    if (uri.Parameters.ContainsKey("height"))
                    {
                        uint heightTemp;
                        height = UInt32.TryParse(uri.Parameters["height"], out heightTemp) ? (uint?)heightTemp : null;
                    }

                    bool maintainAspect = true;
                    if (uri.Parameters.ContainsKey("maintainAspect"))
                    {
                        if (!Boolean.TryParse(uri.Parameters["maintainAspect"], out maintainAspect))
                        {
                            maintainAspect = true;
                        }
                    }

                    // Check for offset seconds and length seconds parameters
                    uint offsetSeconds = 0;
                    if (uri.Parameters.ContainsKey("offsetSeconds"))
                    {
                        UInt32.TryParse(uri.Parameters["offsetSeconds"], out offsetSeconds);
                    }

                    uint lengthSeconds = 0;
                    if (uri.Parameters.ContainsKey("lengthSeconds"))
                    {
                        UInt32.TryParse(uri.Parameters["lengthSeconds"], out lengthSeconds);
                    }

                    // Either stream the rest of the file, or the duration specified
                    lengthSeconds = lengthSeconds == 0 ? (uint)item.Duration - offsetSeconds : lengthSeconds;

                    // Begin video transcoding
                    transcoder = transcodeService.TranscodeVideo(item, transType, quality, isDirect, width, height, maintainAspect, offsetSeconds, lengthSeconds);
                }

                // If a transcoder was generated...
                if ((object)transcoder != null)
                {
                    length = (long)transcoder.EstimatedOutputSize;

                    // Wait up 5 seconds for file or basestream to appear
                    for (int i = 0; i < 20; i++)
                    {
                        if (transcoder.IsDirect)
                        {
                            logger.IfInfo("Checking if base stream exists");
                            if ((object)transcoder.TranscodeProcess != null && (object)transcoder.TranscodeProcess.StandardOutput.BaseStream != null)
                            {
                                // The base stream exists, so the transcoding process has started
                                logger.IfInfo("Base stream exists, starting transfer");
                                stream = transcoder.TranscodeProcess.StandardOutput.BaseStream;
                                break;
                            }
                        }
                        else
                        {
                            logger.IfInfo("Checking if file exists (" + transcoder.OutputPath + ")");
                            if (File.Exists(transcoder.OutputPath))
                            {
                                // The file exists, so the transcoding process has started
                                stream = new FileStream(transcoder.OutputPath, FileMode.Open, FileAccess.Read);
                                break;
                            }
                        }
                        Thread.Sleep(250);
                    }
                }

                // Send the file if either there is no transcoder and the original file exists OR
                // it's a direct transcoder and the base stream exists OR
                // it's a file transcoder and the transcoded file exists
                if ((object)transcoder == null && File.Exists(item.FilePath()) ||
                    (transcoder.IsDirect && (object)stream != null) ||
                    (!transcoder.IsDirect && File.Exists(transcoder.OutputPath)))
                {
                    logger.IfInfo("Sending direct stream");
                    string mimeType = (object)transcoder == null ? item.FileType.MimeType() : transcoder.MimeType;
                    processor.Transcoder = transcoder;

                    if (uri.Parameters.ContainsKey("offsetSeconds"))
                    {
                        logger.IfInfo("Writing file at offsetSeconds " + uri.Parameters["offsetSeconds"]);
                    }

                    DateTime lastModified = transcoder.IsDirect ? DateTime.UtcNow : new FileInfo(transcoder.OutputPath).LastWriteTimeUtc;

                    // Direct write file
                    processor.WriteFile(stream, startOffset, length, mimeType, null, estimateContentLength, lastModified, limitToSize);
                    stream.Close();
                    logger.IfInfo("Successfully sent direct stream");

                    if (uri.Parameters.ContainsKey("offsetSeconds"))
                    {
                        logger.IfInfo("DONE writing file at offsetSeconds " + uri.Parameters["offsetSeconds"]);
                    }
                }
                else
                {
                    processor.WriteErrorHeader();
                }

                // Spin off a thread to consume the transcoder in 30 seconds.
                Thread consume = new Thread(() => transcodeService.ConsumedTranscode(transcoder));
                consume.Start();
            }
            catch (Exception e)
            {
                logger.Error(e);
            }
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Process returns a file stream containing album art
        /// </summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // Check for the itemId
            if (uri.Id == null)
            {
                processor.WriteErrorHeader();
                return;
            }

            // Check for blur (value between 0 and 100)
            double blurSigma = 0;

            if (uri.Parameters.ContainsKey("blur"))
            {
                int blur = 0;
                Int32.TryParse(uri.Parameters["blur"], out blur);
                if (blur < 0)
                {
                    blur = 0;
                }
                else if (blur > 100)
                {
                    blur = 100;
                }

                blurSigma = (double)blur / 10.0;
            }

            // Grab art stream
            Art    art    = Injection.Kernel.Get <IArtRepository>().ArtForId((int)uri.Id);
            Stream stream = CreateStream(art);

            // If the stream could not be produced, return error
            if ((object)stream == null)
            {
                processor.WriteErrorHeader();
                return;
            }

            // If art size requested...
            if (uri.Parameters.ContainsKey("size"))
            {
                int size = Int32.MaxValue;
                Int32.TryParse(uri.Parameters["size"], out size);

                // Parse size if valid
                if (size != Int32.MaxValue)
                {
                    bool imageMagickFailed = false;
                    if (ServerUtility.DetectOS() != ServerUtility.OS.Windows)
                    {
                        // First try ImageMagick
                        try {
                            Byte[] data = ResizeImageMagick(stream, size, blurSigma);
                            stream = new MemoryStream(data, false);
                        } catch {
                            imageMagickFailed = true;
                        }
                    }

                    // If ImageMagick dll isn't loaded, or this is Windows,
                    if (imageMagickFailed || ServerUtility.DetectOS() == ServerUtility.OS.Windows)
                    {
                        // Resize image, put it in memory stream
                        Image resized = ResizeImageGDI(new Bitmap(stream), new Size(size, size));
                        stream = new MemoryStream();
                        resized.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
                    }
                }
            }

            DateTime?lastModified = null;

            if (!ReferenceEquals(art.LastModified, null))
            {
                lastModified = ((long)art.LastModified).ToDateTime();
            }
            processor.WriteFile(stream, 0, stream.Length, HttpHeader.MimeTypeForExtension(".jpg"), null, true, lastModified);

            // Close the file so we don't get sharing violations on future accesses
            stream.Close();
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Process produces a direct file stream of the requested media file
        /// </summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // Verify ID received
            if (uri.Id == null)
            {
                processor.WriteJson(new StreamResponse("Missing required parameter 'id'"));
                return;
            }

            logger.IfInfo("Starting file streaming sequence");

            // Try to get seconds
            float seconds = 0f;
            if (uri.Parameters.ContainsKey("seconds"))
            {
                float.TryParse(uri.Parameters["seconds"], out seconds);
            }

            try
            {
                // Get the media item associated with this id
                ItemType itemType = Injection.Kernel.Get<IItemRepository>().ItemTypeForItemId((int)uri.Id);
                IMediaItem item = null;
                if (itemType == ItemType.Song)
                {
                    item = Injection.Kernel.Get<ISongRepository>().SongForId((int)uri.Id);
                    logger.IfInfo("Preparing audio stream: " + item.FileName);
                }
                else if (itemType == ItemType.Video)
                {
                    item = Injection.Kernel.Get<IVideoRepository>().VideoForId((int)uri.Id);
                    logger.IfInfo("Preparing video stream: " + item.FileName);
                }

                // Return an error if none exists
                if ((item == null) || (!File.Exists(item.FilePath())))
                {
                    processor.WriteJson(new StreamResponse("No media item exists with ID: " + (int)uri.Id));
                    return;
                }

                // Prepare file stream
                Stream stream = item.File();
                long length = stream.Length;
                int startOffset = 0;
                long? limitToSize = null;

                if (seconds > 0)
                {
                    // Guess the file position based on the seconds requested
                    // this is just rough now, but will be improved to take into account the header size
                    float percent = seconds / (float)item.Duration;
                    if (percent < 1f)
                    {
                        startOffset = (int)(item.FileSize * percent);
                        logger.IfInfo("seconds: " + seconds + "  Resuming from " + startOffset);
                    }
                }
                else if (processor.HttpHeaders.ContainsKey("Range"))
                {
                    // Handle the Range header to start from later in the file
                    string range = (string)processor.HttpHeaders["Range"];
                    var split = range.Split(new char[]{'-', '='});
                    string start = split[1];
                    string end = split.Length > 2 ? split[2] : null;

                    logger.IfInfo("Range header: " + range + "  Resuming from " + start);
                    startOffset = Convert.ToInt32(start);
                    if (!ReferenceEquals(end, null) && end != String.Empty)
                    {
                        limitToSize = (Convert.ToInt64(end) + 1) - startOffset;
                    }
                }

                // Send the file
                processor.WriteFile(stream, startOffset, length, item.FileType.MimeType(), null, true, new FileInfo(item.FilePath()).LastWriteTimeUtc, limitToSize);
                stream.Close();

                logger.IfInfo("Successfully streamed file!");
            }
            catch (Exception e)
            {
                logger.Error(e);
            }
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Process handles the initialization of the file transcoding sequence
        /// <summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // Get TranscodeService instance
            TranscodeService transcodeService = (TranscodeService)ServiceManager.GetInstance("transcode");

            // Ensure transcode service is ready
            if ((object)transcodeService == null)
            {
                processor.WriteJson(new TranscodeResponse("TranscodeService is not running!"));
                return;
            }

            // Create transcoder
            ITranscoder transcoder = null;

            // Get seconds offset
            float seconds = 0f;

            if (uri.Parameters.ContainsKey("seconds"))
            {
                float.TryParse(uri.Parameters["seconds"], out seconds);
            }

            // Verify ID received
            if (uri.Id == null)
            {
                processor.WriteJson(new TranscodeResponse("Missing required parameter 'id'"));
                return;
            }

            try
            {
                // Set up default transcoding parameters
                ItemType      itemType              = Injection.Kernel.Get <IItemRepository>().ItemTypeForItemId((int)uri.Id);
                IMediaItem    item                  = null;
                TranscodeType transType             = TranscodeType.MP3;
                bool          isDirect              = false;
                Stream        stream                = null;
                int           startOffset           = 0;
                long?         limitToSize           = null;
                long          length                = 0;
                bool          estimateContentLength = false;

                // Optionally estimate content length
                if (uri.Parameters.ContainsKey("estimateContentLength"))
                {
                    estimateContentLength = uri.Parameters["estimateContentLength"].IsTrue();
                }

                // Get the media item associated with this id
                if (itemType == ItemType.Song)
                {
                    item = Injection.Kernel.Get <ISongRepository>().SongForId((int)uri.Id);
                    logger.IfInfo("Preparing audio transcode: " + item.FileName);

                    // Default to MP3 transcoding
                    transType = TranscodeType.MP3;
                }
                else if (itemType == ItemType.Video)
                {
                    item = Injection.Kernel.Get <IVideoRepository>().VideoForId((int)uri.Id);
                    logger.IfInfo("Preparing video transcode: " + item.FileName);

                    // Default to h.264 transcoding
                    transType = TranscodeType.X264;
                }

                // Return an error if no item exists
                if ((item == null) || (!File.Exists(item.FilePath())))
                {
                    processor.WriteJson(new TranscodeResponse("No media item exists with ID: " + (int)uri.Id));
                    return;
                }

                // Optionally add isDirect parameter
                if (uri.Parameters.ContainsKey("isDirect"))
                {
                    isDirect = uri.Parameters["isDirect"].IsTrue();
                }

                if (seconds > 0)
                {
                    // Guess the file position based on the seconds requested
                    // this is wrong now, but will be improved to take into account the header size and transcode quality
                    // or even better, we should be able to just pass the offset seconds to ffmpeg
                    float percent = seconds / (float)item.Duration;
                    if (percent < 1f)
                    {
                        startOffset = (int)(item.FileSize * percent);
                        logger.IfInfo("seconds: " + seconds + "  Resuming from " + startOffset);
                    }
                }
                else if (processor.HttpHeaders.ContainsKey("Range"))
                {
                    // Handle the Range header to start from later in the file
                    string range = (string)processor.HttpHeaders["Range"];
                    var    split = range.Split(new char[] { '-', '=' });
                    string start = split[1];
                    string end   = split.Length > 2 ? split[2] : null;
                    logger.IfInfo("Range header: " + range + "  Resuming from " + start + " end: " + end);

                    if (isDirect)
                    {
                        // TODO: Actually implement this lol
                        // This is a direct transfer with no file buffer, so treat a Range request as if it
                        // were start offset, unless an offsetSeconds was specified
                        if (uri.Parameters.ContainsKey("offsetSeconds"))
                        {
                        }
                        else
                        {
                        }
                    }
                    else
                    {
                        // This is a file request so use the range header to specify where in the file to return
                        startOffset = Convert.ToInt32(start);
                        if (!ReferenceEquals(end, null) && end.Length > 0)
                        {
                            limitToSize = (Convert.ToInt64(end) + 1) - startOffset;
                        }
                    }
                }

                // Get the transcoding type if specified
                if (uri.Parameters.ContainsKey("transType"))
                {
                    // Parse transcoding type
                    TranscodeType transTypeTemp;
                    if (Enum.TryParse <TranscodeType>(uri.Parameters["transType"], true, out transTypeTemp))
                    {
                        // Verify this is a valid transcode type for this item type
                        if (transTypeTemp.IsValidForItemType(item.ItemType))
                        {
                            // It is, so use it
                            transType = transTypeTemp;
                        }
                    }
                }

                // Get the quality, default to medium
                uint quality = (uint)TranscodeQuality.Medium;
                if (uri.Parameters.ContainsKey("transQuality"))
                {
                    string           qualityString = uri.Parameters["transQuality"];
                    TranscodeQuality qualityEnum;
                    uint             qualityValue;
                    // First try and parse a word enum value
                    if (Enum.TryParse <TranscodeQuality>(qualityString, true, out qualityEnum))
                    {
                        quality = (uint)qualityEnum;
                    }
                    // Otherwise look for a number to use as bitrate
                    else if (UInt32.TryParse(qualityString, out qualityValue))
                    {
                        quality = qualityValue;
                    }
                }

                // Create the transcoder
                if (item.ItemType == ItemType.Song)
                {
                    // Begin transcoding song
                    transcoder = transcodeService.TranscodeSong(item, transType, (uint)quality, isDirect, 0, (uint)item.Duration);
                }
                else
                {
                    // Video transcoding is just a bit more complicated.
                    // Check to see if the width, height, and maintainAspect options were used
                    uint?width = null;
                    if (uri.Parameters.ContainsKey("width"))
                    {
                        uint widthTemp;
                        width = UInt32.TryParse(uri.Parameters["width"], out widthTemp) ? (uint?)widthTemp : null;
                    }

                    uint?height = 0;
                    if (uri.Parameters.ContainsKey("height"))
                    {
                        uint heightTemp;
                        height = UInt32.TryParse(uri.Parameters["height"], out heightTemp) ? (uint?)heightTemp : null;
                    }

                    bool maintainAspect = true;
                    if (uri.Parameters.ContainsKey("maintainAspect"))
                    {
                        if (!Boolean.TryParse(uri.Parameters["maintainAspect"], out maintainAspect))
                        {
                            maintainAspect = true;
                        }
                    }

                    // Check for offset seconds and length seconds parameters
                    uint offsetSeconds = 0;
                    if (uri.Parameters.ContainsKey("offsetSeconds"))
                    {
                        UInt32.TryParse(uri.Parameters["offsetSeconds"], out offsetSeconds);
                    }

                    uint lengthSeconds = 0;
                    if (uri.Parameters.ContainsKey("lengthSeconds"))
                    {
                        UInt32.TryParse(uri.Parameters["lengthSeconds"], out lengthSeconds);
                    }

                    // Either stream the rest of the file, or the duration specified
                    lengthSeconds = lengthSeconds == 0 ? (uint)item.Duration - offsetSeconds : lengthSeconds;

                    // Begin video transcoding
                    transcoder = transcodeService.TranscodeVideo(item, transType, quality, isDirect, width, height, maintainAspect, offsetSeconds, lengthSeconds);
                }

                // If a transcoder was generated...
                if ((object)transcoder != null)
                {
                    length = (long)transcoder.EstimatedOutputSize;

                    // Wait up 5 seconds for file or basestream to appear
                    for (int i = 0; i < 20; i++)
                    {
                        if (transcoder.IsDirect)
                        {
                            logger.IfInfo("Checking if base stream exists");
                            if ((object)transcoder.TranscodeProcess != null && (object)transcoder.TranscodeProcess.StandardOutput.BaseStream != null)
                            {
                                // The base stream exists, so the transcoding process has started
                                logger.IfInfo("Base stream exists, starting transfer");
                                stream = transcoder.TranscodeProcess.StandardOutput.BaseStream;
                                break;
                            }
                        }
                        else
                        {
                            logger.IfInfo("Checking if file exists (" + transcoder.OutputPath + ")");
                            if (File.Exists(transcoder.OutputPath))
                            {
                                // The file exists, so the transcoding process has started
                                stream = new FileStream(transcoder.OutputPath, FileMode.Open, FileAccess.Read);
                                break;
                            }
                        }
                        Thread.Sleep(250);
                    }
                }

                // Send the file if either there is no transcoder and the original file exists OR
                // it's a direct transcoder and the base stream exists OR
                // it's a file transcoder and the transcoded file exists
                if ((object)transcoder == null && File.Exists(item.FilePath()) ||
                    (transcoder.IsDirect && (object)stream != null) ||
                    (!transcoder.IsDirect && File.Exists(transcoder.OutputPath)))
                {
                    logger.IfInfo("Sending direct stream");
                    string mimeType = (object)transcoder == null?item.FileType.MimeType() : transcoder.MimeType;

                    processor.Transcoder = transcoder;

                    if (uri.Parameters.ContainsKey("offsetSeconds"))
                    {
                        logger.IfInfo("Writing file at offsetSeconds " + uri.Parameters["offsetSeconds"]);
                    }

                    DateTime lastModified = transcoder.IsDirect ? DateTime.UtcNow : new FileInfo(transcoder.OutputPath).LastWriteTimeUtc;

                    // Direct write file
                    processor.WriteFile(stream, startOffset, length, mimeType, null, estimateContentLength, lastModified, limitToSize);
                    stream.Close();
                    logger.IfInfo("Successfully sent direct stream");

                    if (uri.Parameters.ContainsKey("offsetSeconds"))
                    {
                        logger.IfInfo("DONE writing file at offsetSeconds " + uri.Parameters["offsetSeconds"]);
                    }
                }
                else
                {
                    processor.WriteErrorHeader();
                }

                // Spin off a thread to consume the transcoder in 30 seconds.
                Thread consume = new Thread(() => transcodeService.ConsumedTranscode(transcoder));
                consume.Start();
            }
            catch (Exception e)
            {
                logger.Error(e);
            }
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Process returns a file stream containing album art
        /// </summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // Check for the itemId
            if (uri.Id == null)
            {
                processor.WriteErrorHeader();
                return;
            }

            // Check for blur (value between 0 and 100)
            double blurSigma = 0;
            if (uri.Parameters.ContainsKey("blur"))
            {
                int blur = 0;
                Int32.TryParse(uri.Parameters["blur"], out blur);
                if (blur < 0)
                {
                    blur = 0;
                }
                else if (blur > 100)
                {
                    blur = 100;
                }

                blurSigma = (double)blur / 10.0;
            }

            // Grab art stream
            Art art = Injection.Kernel.Get<IArtRepository>().ArtForId((int)uri.Id);
            Stream stream = CreateStream(art);

            // If the stream could not be produced, return error
            if ((object)stream == null)
            {
                processor.WriteErrorHeader();
                return;
            }

            // If art size requested...
            if (uri.Parameters.ContainsKey("size"))
            {
                int size = Int32.MaxValue;
                Int32.TryParse(uri.Parameters["size"], out size);

                // Parse size if valid
                if (size != Int32.MaxValue)
                {
                    bool imageMagickFailed = false;
                    if (ServerUtility.DetectOS() != ServerUtility.OS.Windows)
                    {
                        // First try ImageMagick
                        try
                        {
                            Byte[] data = ResizeImageMagick(stream, size, blurSigma);
                            stream = new MemoryStream(data, false);
                        }
                        catch
                        {
                            imageMagickFailed = true;
                        }
                    }

                    // If ImageMagick dll isn't loaded, or this is Windows,
                    if (imageMagickFailed || ServerUtility.DetectOS() == ServerUtility.OS.Windows)
                    {
                        // Resize image, put it in memory stream
                        Image resized = ResizeImageGDI(new Bitmap(stream), new Size(size, size));
                        stream = new MemoryStream();
                        resized.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
                    }
                }
            }

            DateTime? lastModified = null;
            if (!ReferenceEquals(art.LastModified, null))
            {
                lastModified = ((long)art.LastModified).ToDateTime();
            }
            processor.WriteFile(stream, 0, stream.Length, HttpHeader.MimeTypeForExtension(".jpg"), null, true, lastModified);

            // Close the file so we don't get sharing violations on future accesses
            stream.Close();
        }