Пример #1
0
        public Stream Download(string clientDescription, WebMediaType type, int?provider, string itemId, long?position)
        {
            // validate source first
            MediaSource source = new MediaSource(type, provider, itemId);

            if (!source.Exists)
            {
                throw new FileNotFoundException();
            }

            // create context
            DownloadContext context = new DownloadContext()
            {
                ClientDescription = clientDescription,
                Source            = source,
                StartTime         = DateTime.Now,
                Stream            = new ReadTrackingStreamWrapper(source.Retrieve()),
                MediaInfo         = MediaInfoHelper.LoadMediaInfoOrSurrogate(source) // for playerposition view
            };

            // seek to start position if wanted/needed
            if (position != null && position > 0)
            {
                if (context.Stream.CanSeek)
                {
                    context.Stream.Seek(position.Value, SeekOrigin.Begin);
                }
                else
                {
                    Log.Warn("Download: Cannot seek on stream, failed to set start position to {0}", position);
                }
            }

            // see comment in Streaming.cs:151
            string realIp = WCFUtil.GetHeaderValue("forwardedFor", "X-Forwarded-For");

            context.ClientIP = realIp == null?WCFUtil.GetClientIPAddress() : String.Format("{0} (via {1})", realIp, WCFUtil.GetClientIPAddress());

            // set headers for downloading
            WCFUtil.AddHeader("Content-Disposition", "attachment; filename=\"" + source.GetFileInfo().Name + "\"");
            if (source.MediaType != WebMediaType.TV)
            {
                WCFUtil.SetContentLength(source.GetFileInfo().Size);
            }

            // FIXME: there has to be a better way to do this
            string mime = MIME.GetFromFilename(source.GetFileInfo().Name);

            if (mime != null)
            {
                WCFUtil.SetContentType(mime);
            }

            // finally, save the context and return
            runningDownloads.Add(context);
            return(context.Stream);
        }
Пример #2
0
        public Stream Download(string clientDescription, WebMediaType type, int? provider, string itemId, long? position)
        {
            // validate source first
            MediaSource source = new MediaSource(type, provider, itemId);
            if (!source.Exists)
            {
                throw new FileNotFoundException();
            }

            // create context
            DownloadContext context = new DownloadContext()
            {
                ClientDescription = clientDescription,
                Source = source,
                StartTime = DateTime.Now,
                Stream = new ReadTrackingStreamWrapper(source.Retrieve()),
                MediaInfo = MediaInfoHelper.LoadMediaInfoOrSurrogate(source) // for playerposition view
            };

            // seek to start position if wanted/needed
            if (position != null && position > 0)
            {
                if (context.Stream.CanSeek)
                {
                    context.Stream.Seek(position.Value, SeekOrigin.Begin);
                }
                else
                {
                    Log.Warn("Download: Cannot seek on stream, failed to set start position to {0}", position);
                }
            }

            // see comment in Streaming.cs:151
            string realIp = WCFUtil.GetHeaderValue("forwardedFor", "X-Forwarded-For");
            context.ClientIP = realIp == null ? WCFUtil.GetClientIPAddress() : String.Format("{0} (via {1})", realIp, WCFUtil.GetClientIPAddress());

            // set headers for downloading
            WCFUtil.AddHeader("Content-Disposition", "attachment; filename=\"" + source.GetFileInfo().Name + "\"");
            if (source.MediaType != WebMediaType.TV)
                WCFUtil.SetContentLength(source.GetFileInfo().Size);

            // FIXME: there has to be a better way to do this
            string mime = MIME.GetFromFilename(source.GetFileInfo().Name);
            if (mime != null)
            {
                WCFUtil.SetContentType(mime);
            }

            // finally, save the context and return
            runningDownloads.Add(context);
            return context.Stream;
        }
Пример #3
0
        public static WebMediaInfo GetMediaInfo(MediaSource source)
        {
            // Timeshiftings are a special case, as they can't be cached and need an additional path resolving step
            if (source.MediaType == WebMediaType.TV)
            {
                if (tvCache.ContainsKey(source.Id) && DateTime.Now - tvCache[source.Id].Item1 > TimeSpan.FromSeconds(60))
                    return tvCache[source.Id].Item2;

                TsBuffer tsBuffer = new TsBuffer(source.Id);
                Log.Debug("Using path {0} from TS buffer {1} as source for {2}", tsBuffer.GetCurrentFilePath(), source.Id, source.GetDebugName());
                WebMediaInfo info = LoadMediaInfo(tsBuffer.GetCurrentFilePath());
                tvCache[source.Id] = new Tuple<DateTime, WebMediaInfo>(DateTime.Now, info);
                return info;
            }

            using (var context = source.CreateNetworkContext())
            {
                // verify the file actually exists and is accessible over the local file system
                if (!source.Exists)
                {
                    Log.Warn("Trying to load MediaInfo for {0}, which does not exist or is inaccessible", source.GetDebugName());
                    return null;
                }
                else if (!source.SupportsDirectAccess)
                {
                    Log.Warn("Loading MediaInfo for non-direct access source {0} isn't supported yet", source.GetDebugName());
                    return null;
                }

                // if we got the file in the cache, return it if we have it and the file hasn't been changed
                var fileInfo = source.GetFileInfo();
                if (source.MediaType != WebMediaType.TV && persistentCache.HasForSource(source))
                {
                    var cachedItem = persistentCache.GetForSource(source);
                    if (cachedItem.Size == fileInfo.Size && cachedItem.CachedDate >= fileInfo.LastModifiedTime)
                        return cachedItem.Info;
                }

                var info = LoadMediaInfo(context.RewritePath(source.GetPath()));
                if (info != null)
                    persistentCache.Save(source, new CachedInfoWrapper(info, fileInfo));

                return info;
            }
        }
Пример #4
0
        public static Stream ExtractImage(MediaSource source, long position, int?maxWidth, int?maxHeight, string borders, string format)
        {
            if (!source.Exists)
            {
                Log.Warn("ExtractImage: Source {0} (resolved to path {1}) doesn't exists", source.GetDebugName(), source.GetPath());
                WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotFound);
                return(Stream.Null);
            }

            if (!source.SupportsDirectAccess)
            {
                Log.Warn("ExtractImage: Extracting images from remote sources isn't supported yet", source.MediaType, source.Id);
                WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotImplemented);
                return(Stream.Null);
            }

            // get temporary filename
            string filename = String.Format("extract_{0}_{1}.jpg", source.GetUniqueIdentifier(), position);
            string tempFile = cache.GetPath(filename);

            // maybe it exists in cache, return that then
            if (cache.Contains(filename))
            {
                // if source is newer, cached image needs to be recreated
                if (source.GetFileInfo().LastModifiedTime > cache.GetLastModifiedTime(tempFile))
                {
                    cache.Invalidate(filename);
                }
                else
                {
                    return(StreamPostprocessedImage(new ImageMediaSource(tempFile), maxWidth, maxHeight, borders, format));
                }
            }

            // We need to impersonate to access the network drive and check if the thumbnail already exists. However, because
            // impersonation is lost when starting a process, we can't start ffmpeg from in here. We need to check whether the
            // file is accessible from outside the impersonation context again, and start the ffmpeg process as a different user
            // if that is the case.
            string fullPath;

            using (var context = source.CreateNetworkContext())
            {
                fullPath = context.RewritePath(source.GetPath());

                // stream a pre-existing thumbnail, if possible
                if (source.MediaType == WebMediaType.Recording && Path.GetExtension(fullPath).ToLower() == ".ts")
                {
                    var thumbnailFileInfo = new FileInfo(Path.ChangeExtension(fullPath, ".jpg"));
                    if (thumbnailFileInfo.Exists && thumbnailFileInfo.Length > 0)
                    {
                        return(StreamPostprocessedImage(new ImageMediaSource(thumbnailFileInfo.FullName), maxWidth, maxHeight, borders, format));
                    }
                }
            }

            // finally, extract the image with ffmpeg if everything else has failed
            bool extractResult = ExecuteFFMpegExtraction(source, fullPath, position, tempFile);

            if (!extractResult)
            {
                WCFUtil.SetResponseCode(System.Net.HttpStatusCode.InternalServerError);
                return(Stream.Null);
            }

            return(StreamPostprocessedImage(new ImageMediaSource(tempFile), maxWidth, maxHeight, borders, format));
        }
Пример #5
0
        public static Stream ExtractImage(MediaSource source, long position, int? maxWidth, int? maxHeight, string borders, string format)
        {
            if (!source.Exists)
            {
                Log.Warn("ExtractImage: Source {0} (resolved to path {1}) doesn't exists", source.GetDebugName(), source.GetPath());
                WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotFound);
                return Stream.Null;
            }

            if (!source.SupportsDirectAccess)
            {
                Log.Warn("ExtractImage: Extracting images from remote sources isn't supported yet", source.MediaType, source.Id);
                WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotImplemented);
                return Stream.Null;
            }

            // get temporary filename
            string filename = String.Format("extract_{0}_{1}.jpg", source.GetUniqueIdentifier(), position);
            string tempFile = cache.GetPath(filename);

            // maybe it exists in cache, return that then
            if (cache.Contains(filename))
            {
                // if source is newer, cached image needs to be recreated
                if (source.GetFileInfo().LastModifiedTime > cache.GetLastModifiedTime(tempFile))
                {
                    cache.Invalidate(filename);
                }
                else
                {
                    return StreamPostprocessedImage(new ImageMediaSource(tempFile), maxWidth, maxHeight, borders, format);
                }
            }

            // We need to impersonate to access the network drive and check if the thumbnail already exists. However, because
            // impersonation is lost when starting a process, we can't start ffmpeg from in here. We need to check whether the
            // file is accessible from outside the impersonation context again, and start the ffmpeg process as a different user
            // if that is the case.
            string fullPath;
            using (var context = source.CreateNetworkContext())
            {
                fullPath = context.RewritePath(source.GetPath());

                // stream a pre-existing thumbnail, if possible
                if (source.MediaType == WebMediaType.Recording && Path.GetExtension(fullPath).ToLower() == ".ts")
                {
                    var thumbnailFileInfo = new FileInfo(Path.ChangeExtension(fullPath, ".jpg"));
                    if (thumbnailFileInfo.Exists && thumbnailFileInfo.Length > 0)
                        return StreamPostprocessedImage(new ImageMediaSource(thumbnailFileInfo.FullName), maxWidth, maxHeight, borders, format);
                }
            }

            // finally, extract the image with ffmpeg if everything else has failed
            bool extractResult = ExecuteFFMpegExtraction(source, fullPath, position, tempFile);
            if (!extractResult)
            {
                WCFUtil.SetResponseCode(System.Net.HttpStatusCode.InternalServerError);
                return Stream.Null;
            }

            return StreamPostprocessedImage(new ImageMediaSource(tempFile), maxWidth, maxHeight, borders, format);
        }
Пример #6
0
        public WebItemSupportStatus GetItemSupportStatus(WebMediaType type, int? provider, string itemId)
        {
            // check if we actually now about this file
            MediaSource source = new MediaSource(type, provider, itemId);
            string path = source.GetPath();
            if (path == null || path.Length == 0)
            {
                return new WebItemSupportStatus(false, "Cannot resolve item to a path");
            }

            // some checks based upon the file info. apparantly people have broken files in their connections
            var fileinfo = source.GetFileInfo();
            if (!fileinfo.Exists)
            {
                // add a special warning message for files that are on a network drive, as this often causes problems
                Uri uri = new Uri(path);
                if (uri.IsUnc && !NetworkInformation.IsLocalAddress(uri.Host))
                {
                    return new WebItemSupportStatus(false, "File is on an inaccessible network share");
                }

                return new WebItemSupportStatus(false, "File does not exists or is inaccessible");
            }
            if (fileinfo.Size == 0)
            {
                return new WebItemSupportStatus(false, "This file has a size of 0KB");
            }

            // we don't support some things yet
            if (path.EndsWith(".IFO"))
            {
                return new WebItemSupportStatus(false, "Streaming DVD files is not supported");
            }

            // while corrupt files may work, it's probably a better idea to warn early. check for a valid file using mediainfo
            if (MediaInfo.MediaInfoWrapper.GetMediaInfo(source) == null)
            {
                return new WebItemSupportStatus(false, "This file might be corrupt");
            }

            return new WebItemSupportStatus() { Supported = true };
        }
Пример #7
0
        public Stream GetMediaItem(WebStreamMediaType type, int? provider, string itemId)
        {
            if (!_authorizedHosts.Contains(WCFUtil.GetClientIPAddress()) && !NetworkInformation.IsLocalAddress(WCFUtil.GetClientIPAddress()))
            {
                Log.Warn("Host {0} isn't authorized to call GetMediaItem", WCFUtil.GetClientIPAddress());
                WCFUtil.SetResponseCode(HttpStatusCode.Unauthorized);
                return Stream.Null;
            }

            MediaSource source = new MediaSource(type, provider, itemId);
            try
            {
                if (!source.Exists)
                {
                    throw new FileNotFoundException();
                }

                WCFUtil.AddHeader("Content-Disposition", "attachment; filename=\"" + source.GetFileInfo().Name + "\"");
                WCFUtil.SetContentLength(source.GetFileInfo().Size);

                // there has to be a better way to do this
                object mime = RegistryReader.ReadKey(Microsoft.Win32.RegistryHive.ClassesRoot, Path.GetExtension(source.GetFileInfo().Name), "Content Type");
                if (mime != null)
                {
                    WCFUtil.SetContentType(mime.ToString());
                }

                return source.Retrieve();
            }
            catch (Exception ex)
            {
                WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotFound);
                Log.Info(String.Format("GetMediaItem() failed for {0}", source.GetDebugName()), ex);
                return Stream.Null;
            }
        }
Пример #8
0
        public Stream GetMediaItem(WebStreamMediaType type, int? provider, string itemId)
        {
            MediaSource source = new MediaSource(type, provider, itemId);
            try
            {
                if (!source.Exists)
                {
                    throw new FileNotFoundException();
                }

                WCFUtil.AddHeader("Content-Disposition", "attachment; filename=\"" + source.GetFileInfo().Name + "\"");

                // there has to be a better way to do this
                object mime = RegistryReader.ReadKey(Microsoft.Win32.RegistryHive.ClassesRoot, Path.GetExtension(source.GetFileInfo().Name), "Content Type");
                if (mime != null)
                {
                    WCFUtil.AddHeader("Content-Type", mime.ToString());
                }

                return source.Retrieve();
            }
            catch (Exception ex)
            {
                WCFUtil.SetResponseCode(System.Net.HttpStatusCode.NotFound);
                Log.Info(String.Format("GetMediaItem() failed for {0}", source.GetDebugName()), ex);
                return Stream.Null;
            }
        }