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); }
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; }
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; } }
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)); }
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); }
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 }; }
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; } }
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; } }