expose Tmdb collection id in editor.

This commit is contained in:
Luke Pulverenti 2013-12-07 20:42:15 -05:00
parent 07e5b74f13
commit 1f162e39b1
19 changed files with 271 additions and 193 deletions

View File

@ -147,9 +147,7 @@
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Folder Include="Filters\" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>

View File

@ -16,6 +16,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates;
using ServiceStack;
using SimpleInjector;
using System;
using System.Collections.Generic;
@ -181,6 +182,9 @@ namespace MediaBrowser.Common.Implementations
/// <returns>Task.</returns>
public virtual async Task Init()
{
// https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Web.config#L4
Licensing.RegisterLicense("1001-e1JlZjoxMDAxLE5hbWU6VGVzdCBCdXNpbmVzcyxUeXBlOkJ1c2luZXNzLEhhc2g6UHVNTVRPclhvT2ZIbjQ5MG5LZE1mUTd5RUMzQnBucTFEbTE3TDczVEF4QUNMT1FhNXJMOWkzVjFGL2ZkVTE3Q2pDNENqTkQyUktRWmhvUVBhYTBiekJGUUZ3ZE5aZHFDYm9hL3lydGlwUHI5K1JsaTBYbzNsUC85cjVJNHE5QVhldDN6QkE4aTlvdldrdTgyTk1relY2eis2dFFqTThYN2lmc0JveHgycFdjPSxFeHBpcnk6MjAxMy0wMS0wMX0=");
JsonSerializer = CreateJsonSerializer();
IsFirstRun = !ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted;

View File

@ -273,7 +273,7 @@ namespace MediaBrowser.Controller.Entities
return base.GetDeletePaths();
}
public IEnumerable<MediaStream> GetMediaStreams()
public virtual IEnumerable<MediaStream> GetMediaStreams()
{
return ItemRepository.GetMediaStreams(new MediaStreamQuery
{
@ -281,7 +281,7 @@ namespace MediaBrowser.Controller.Entities
});
}
public MediaStream GetDefaultVideoStream()
public virtual MediaStream GetDefaultVideoStream()
{
if (!DefaultVideoStreamIndex.HasValue)
{

View File

@ -23,7 +23,7 @@ namespace MediaBrowser.Providers
public class ImageFromMediaLocationProvider : BaseMetadataProvider
{
protected readonly IFileSystem FileSystem;
public ImageFromMediaLocationProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem)
: base(logManager, configurationManager)
{
@ -54,6 +54,7 @@ namespace MediaBrowser.Providers
return item.IsInMixedFolder && item.Parent != null && !(item is Episode);
}
return false;
}
@ -153,6 +154,13 @@ namespace MediaBrowser.Providers
.FirstOrDefault(i => i != null);
}
protected virtual FileSystemInfo GetImage(List<FileSystemInfo> files, string filenameWithoutExtension)
{
return BaseItem.SupportedImageExtensions
.Select(i => files.FirstOrDefault(f => string.Equals(f.Extension, i, StringComparison.OrdinalIgnoreCase) && string.Equals(filenameWithoutExtension, Path.GetFileNameWithoutExtension(f.Name), StringComparison.OrdinalIgnoreCase)))
.FirstOrDefault(i => i != null);
}
protected virtual string GetFullImagePath(BaseItem item, ItemResolveArgs args, string filenameWithoutExtension, string extension)
{
var path = item.MetaLocation;
@ -258,24 +266,13 @@ namespace MediaBrowser.Providers
var isFileSystemItem = item.LocationType == LocationType.FileSystem;
// Support plex/xbmc convention
if (image == null && item is Season && item.IndexNumber.HasValue && isFileSystemItem)
if (image == null)
{
var seasonMarker = item.IndexNumber.Value == 0
? "-specials"
: item.IndexNumber.Value.ToString("00", _usCulture);
// Get this one directly from the file system since we have to go up a level
var filename = "season" + seasonMarker + "-poster";
var path = Path.GetDirectoryName(item.Path);
path = Path.Combine(path, filename);
image = new FileInfo(path);
if (!image.Exists)
// Supprt xbmc conventions
var season = item as Season;
if (season != null && item.IndexNumber.HasValue && isFileSystemItem)
{
image = null;
image = GetSeasonImageFromSeriesFolder(season, "-poster");
}
}
@ -315,26 +312,13 @@ namespace MediaBrowser.Providers
if (image == null)
{
var isFileSystemItem = item.LocationType == LocationType.FileSystem;
// Supprt xbmc conventions
if (item is Season && item.IndexNumber.HasValue && item.LocationType == LocationType.FileSystem)
var season = item as Season;
if (season != null && item.IndexNumber.HasValue && isFileSystemItem)
{
var seasonMarker = item.IndexNumber.Value == 0
? "-specials"
: item.IndexNumber.Value.ToString("00", _usCulture);
// Get this one directly from the file system since we have to go up a level
var filename = "season" + seasonMarker + "-banner";
var path = Path.GetDirectoryName(item.Path);
path = Path.Combine(path, filename);
image = new FileInfo(path);
if (!image.Exists)
{
image = null;
}
image = GetSeasonImageFromSeriesFolder(season, "-banner");
}
}
@ -356,26 +340,13 @@ namespace MediaBrowser.Providers
if (image == null)
{
var isFileSystemItem = item.LocationType == LocationType.FileSystem;
// Supprt xbmc conventions
if (item is Season && item.IndexNumber.HasValue && item.LocationType == LocationType.FileSystem)
var season = item as Season;
if (season != null && item.IndexNumber.HasValue && isFileSystemItem)
{
var seasonMarker = item.IndexNumber.Value == 0
? "-specials"
: item.IndexNumber.Value.ToString("00", _usCulture);
// Get this one directly from the file system since we have to go up a level
var filename = "season" + seasonMarker + "-landscape";
var path = Path.GetDirectoryName(item.Path);
path = Path.Combine(path, filename);
image = new FileInfo(path);
if (!image.Exists)
{
image = null;
}
image = GetSeasonImageFromSeriesFolder(season, "-landscape");
}
}
@ -420,22 +391,12 @@ namespace MediaBrowser.Providers
PopulateBackdrops(item, args, backdropFiles, "background", "background-");
PopulateBackdrops(item, args, backdropFiles, "art", "art-");
if (item is Season && item.IndexNumber.HasValue && isFileSystemItem)
var season = item as Season;
if (season != null && item.IndexNumber.HasValue && isFileSystemItem)
{
var seasonMarker = item.IndexNumber.Value == 0
? "-specials"
: item.IndexNumber.Value.ToString("00", _usCulture);
var image = GetSeasonImageFromSeriesFolder(season, "-fanart");
// Get this one directly from the file system since we have to go up a level
var filename = "season" + seasonMarker + "-fanart";
var path = Path.GetDirectoryName(item.Path);
path = Path.Combine(path, filename);
var image = new FileInfo(path);
if (image.Exists)
if (image != null)
{
backdropFiles.Add(image.FullName);
}
@ -452,6 +413,51 @@ namespace MediaBrowser.Providers
}
}
private FileSystemInfo GetSeasonImageFromSeriesFolder(Season season, string imageSuffix)
{
var series = season.Series;
var seriesFolderArgs = series.ResolveArgs;
var seasonNumber = season.IndexNumber;
string filename = null;
FileSystemInfo image;
if (seasonNumber.HasValue)
{
var seasonMarker = seasonNumber.Value == 0
? "-specials"
: seasonNumber.Value.ToString("00", _usCulture);
// Get this one directly from the file system since we have to go up a level
filename = "season" + seasonMarker + imageSuffix;
image = GetImage(series, seriesFolderArgs, filename);
if (image != null && image.Exists)
{
return image;
}
}
var previousFilename = filename;
// Try using the season name
filename = season.Name.ToLower().Replace(" ", string.Empty) + imageSuffix;
if (!string.Equals(previousFilename, filename))
{
image = GetImage(series, seriesFolderArgs, filename);
if (image != null && image.Exists)
{
return image;
}
}
return null;
}
/// <summary>
/// Populates the backdrops from extra fanart.
/// </summary>

View File

@ -74,7 +74,8 @@ namespace MediaBrowser.Server.Implementations.Drawing
try
{
sizeDictionary = jsonSerializer.DeserializeFromFile<Dictionary<Guid, ImageSize>>(ImageSizeFile);
sizeDictionary = jsonSerializer.DeserializeFromFile<Dictionary<Guid, ImageSize>>(ImageSizeFile) ??
new Dictionary<Guid, ImageSize>();
}
catch (FileNotFoundException)
{

View File

@ -5,7 +5,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Logging;
using ServiceStack;
using ServiceStack.Configuration;
using ServiceStack.Host;
using ServiceStack.Host.Handlers;
using ServiceStack.Host.HttpListener;
@ -58,9 +57,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
public HttpListenerHost(IApplicationHost applicationHost, ILogManager logManager, string serviceName, string handlerPath, string defaultRedirectPath, params Assembly[] assembliesWithServices)
: base(serviceName, assembliesWithServices)
{
// https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Web.config#L4
Licensing.RegisterLicense("1001-e1JlZjoxMDAxLE5hbWU6VGVzdCBCdXNpbmVzcyxUeXBlOkJ1c2luZXNzLEhhc2g6UHVNTVRPclhvT2ZIbjQ5MG5LZE1mUTd5RUMzQnBucTFEbTE3TDczVEF4QUNMT1FhNXJMOWkzVjFGL2ZkVTE3Q2pDNENqTkQyUktRWmhvUVBhYTBiekJGUUZ3ZE5aZHFDYm9hL3lydGlwUHI5K1JsaTBYbzNsUC85cjVJNHE5QVhldDN6QkE4aTlvdldrdTgyTk1relY2eis2dFFqTThYN2lmc0JveHgycFdjPSxFeHBpcnk6MjAxMy0wMS0wMX0=");
DefaultRedirectPath = defaultRedirectPath;
ServerName = serviceName;
HandlerPath = handlerPath;

View File

@ -37,18 +37,23 @@
<Reference Include="Alchemy">
<HintPath>..\packages\Alchemy.2.2.1\lib\net40\Alchemy.dll</HintPath>
</Reference>
<Reference Include="ServiceStack, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="ServiceStack, Version=4.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\ServiceStack\ServiceStack.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Client">
<Reference Include="ServiceStack.Client, Version=4.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\ServiceStack\ServiceStack.Client.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Interfaces, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="ServiceStack.Common, Version=4.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\ServiceStack\ServiceStack.Common.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Interfaces, Version=4.0.0.0, Culture=neutral, PublicKeyToken=e06fbc6124f57c43, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="ServiceStack.Text, Version=4.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\ServiceStack\ServiceStack.Text.dll</HintPath>
</Reference>
@ -143,6 +148,7 @@
<Compile Include="Persistence\SqliteMediaStreamsRepository.cs" />
<Compile Include="Persistence\SqliteNotificationsRepository.cs" />
<Compile Include="Persistence\SqliteProviderInfoRepository.cs" />
<Compile Include="Persistence\SqliteShrinkMemoryTimer.cs" />
<Compile Include="Persistence\TypeMapper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Providers\ImageSaver.cs" />
@ -237,9 +243,6 @@
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Localization\Ratings\ca.txt" />
<Content Include="swagger-ui\index.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />

View File

@ -44,7 +44,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create index if not exists idx_chapters on chapters(ItemId, ChapterIndex)",
//pragmas
"pragma temp_store = memory"
"pragma temp_store = memory",
"pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);

View File

@ -86,8 +86,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create table if not exists userdisplaypreferences (id GUID, userId GUID, client text, data BLOB)",
"create unique index if not exists userdisplaypreferencesindex on userdisplaypreferences (id, userId, client)",
//pragmas
"pragma temp_store = memory"
"pragma temp_store = memory",
"pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);

View File

@ -124,18 +124,24 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create index if not exists idx_ChildrenIds on ChildrenIds(ParentId,ItemId)",
//pragmas
"pragma temp_store = memory"
"pragma temp_store = memory",
"pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);
PrepareStatements();
_mediaStreamsRepository.Initialize();
_providerInfoRepository.Initialize();
_chapterRepository.Initialize();
_shrinkMemoryTimer = new SqliteShrinkMemoryTimer(_connection, _writeLock, _logger);
}
private SqliteShrinkMemoryTimer _shrinkMemoryTimer;
/// <summary>
/// The _write lock
/// </summary>
@ -402,6 +408,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
lock (_disposeLock)
{
if (_shrinkMemoryTimer != null)
{
_shrinkMemoryTimer.Dispose();
_shrinkMemoryTimer = null;
}
if (_connection != null)
{
if (_connection.IsOpen())
@ -412,30 +424,30 @@ namespace MediaBrowser.Server.Implementations.Persistence
_connection.Dispose();
_connection = null;
}
if (_chapterRepository != null)
{
_chapterRepository.Dispose();
_chapterRepository = null;
}
if (_mediaStreamsRepository != null)
{
_mediaStreamsRepository.Dispose();
_mediaStreamsRepository = null;
}
if (_providerInfoRepository != null)
{
_providerInfoRepository.Dispose();
_providerInfoRepository = null;
}
}
}
catch (Exception ex)
{
_logger.ErrorException("Error disposing database", ex);
}
if (_chapterRepository != null)
{
_chapterRepository.Dispose();
_chapterRepository = null;
}
if (_mediaStreamsRepository != null)
{
_mediaStreamsRepository.Dispose();
_mediaStreamsRepository = null;
}
if (_providerInfoRepository != null)
{
_providerInfoRepository.Dispose();
_providerInfoRepository = null;
}
}
}

View File

@ -43,7 +43,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create index if not exists idx_mediastreams on mediastreams(ItemId, StreamIndex)",
//pragmas
"pragma temp_store = memory"
"pragma temp_store = memory",
"pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);

View File

@ -45,7 +45,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create index if not exists idx_Notifications on Notifications(Id, UserId)",
//pragmas
"pragma temp_store = memory"
"pragma temp_store = memory",
"pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);

View File

@ -42,7 +42,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create index if not exists idx_providerinfos on providerinfos(ItemId, ProviderId)",
//pragmas
"pragma temp_store = memory"
"pragma temp_store = memory",
"pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);

View File

@ -0,0 +1,84 @@
using MediaBrowser.Model.Logging;
using System;
using System.Data;
using System.Threading;
namespace MediaBrowser.Server.Implementations.Persistence
{
class SqliteShrinkMemoryTimer : IDisposable
{
private Timer _shrinkMemoryTimer;
private readonly SemaphoreSlim _writeLock;
private readonly ILogger _logger;
private readonly IDbConnection _connection;
public SqliteShrinkMemoryTimer(IDbConnection connection, SemaphoreSlim writeLock, ILogger logger)
{
_connection = connection;
_writeLock = writeLock;
_logger = logger;
_shrinkMemoryTimer = new Timer(TimerCallback, null, TimeSpan.FromMinutes(30), TimeSpan.FromMinutes(30));
}
private async void TimerCallback(object state)
{
await _writeLock.WaitAsync(CancellationToken.None).ConfigureAwait(false);
IDbTransaction transaction = null;
try
{
transaction = _connection.BeginTransaction();
using (var cmd = _connection.CreateCommand())
{
cmd.Transaction = transaction;
cmd.CommandText = "pragma shrink_memory";
cmd.ExecuteNonQuery();
}
transaction.Commit();
}
catch (OperationCanceledException)
{
if (transaction != null)
{
transaction.Rollback();
}
throw;
}
catch (Exception e)
{
_logger.ErrorException("Failed to save items:", e);
if (transaction != null)
{
transaction.Rollback();
}
throw;
}
finally
{
if (transaction != null)
{
transaction.Dispose();
}
_writeLock.Release();
}
}
public void Dispose()
{
if (_shrinkMemoryTimer != null)
{
_shrinkMemoryTimer.Dispose();
_shrinkMemoryTimer = null;
}
}
}
}

View File

@ -72,7 +72,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create unique index if not exists userdataindex on userdata (key, userId)",
//pragmas
"pragma temp_store = memory"
"pragma temp_store = memory",
"pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);

View File

@ -77,8 +77,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create table if not exists users (guid GUID primary key, data BLOB)",
"create index if not exists idx_users on users(guid)",
"create table if not exists schema_version (table_name primary key, version)",
//pragmas
"pragma temp_store = memory"
"pragma temp_store = memory",
"pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);

View File

@ -1,80 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Swagger UI</title>
<link href='//fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'/>
<link href='css/hightlight.default.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
<script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
<script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
<script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
<script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
<script src='lib/handlebars-1.0.rc.1.js' type='text/javascript'></script>
<script src='lib/underscore-min.js' type='text/javascript'></script>
<script src='lib/backbone-min.js' type='text/javascript'></script>
<script src='lib/swagger.js' type='text/javascript'></script>
<script src='swagger-ui.js' type='text/javascript'></script>
<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
<script type="text/javascript">
$(function () {
window.swaggerUi = new SwaggerUi({
discoveryUrl: "../resources",
apiKey: "special-key",
dom_id:"swagger-ui-container",
supportHeaderParams: false,
supportedSubmitMethods: ['get', 'post', 'put'],
onComplete: function(swaggerApi, swaggerUi){
if(console) {
console.log("Loaded SwaggerUI")
console.log(swaggerApi);
console.log(swaggerUi);
}
$('pre code').each(function(i, e) {hljs.highlightBlock(e)});
},
onFailure: function(data) {
if(console) {
console.log("Unable to Load SwaggerUI");
console.log(data);
}
},
docExpansion: "none"
});
window.swaggerUi.load();
});
</script>
</head>
<body>
<div id='header'>
<div class="swagger-ui-wrap">
<a id="logo" href="http://swagger.wordnik.com">swagger</a>
<form id='api_selector'>
<div class='input icon-btn'>
<img id="show-pet-store-icon" src="images/pet_store_api.png" title="Show Swagger Petstore Example Apis">
</div>
<div class='input icon-btn'>
<img id="show-wordnik-dev-icon" src="images/wordnik_api.png" title="Show Wordnik Developer Apis">
</div>
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl"
type="text"/></div>
<div class='input'><input placeholder="api_key" id="input_apiKey" name="apiKey" type="text"/></div>
<div class='input'><a id="explore" href="#">Explore</a></div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap">
&nbsp;
</div>
<div id="swagger-ui-container" class="swagger-ui-wrap">
</div>
</body>
</html>

View File

@ -0,0 +1,38 @@
using MediaBrowser.Controller.Plugins;
using System;
using System.Threading;
namespace MediaBrowser.ServerApplication.EntryPoints
{
public class ResourceEntryPoint : IServerEntryPoint
{
private Timer _timer;
public void Run()
{
_timer = new Timer(TimerCallback, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(30));
}
private void TimerCallback(object state)
{
try
{
// Bad practice, i know. But we keep a lot in memory, unfortunately.
GC.Collect(2, GCCollectionMode.Forced, true);
GC.Collect(2, GCCollectionMode.Forced, true);
}
catch
{
}
}
public void Dispose()
{
if (_timer != null)
{
_timer.Dispose();
_timer = null;
}
}
}
}

View File

@ -131,8 +131,7 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\MediaBrowser.IsoMounting.3.0.65\lib\net45\pfmclrapi.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Interfaces, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Reference Include="ServiceStack.Interfaces">
<HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
</Reference>
<Reference Include="SimpleInjector, Version=2.3.6.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
@ -161,6 +160,7 @@
<Compile Include="BackgroundService.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="EntryPoints\ResourceEntryPoint.cs" />
<Compile Include="EntryPoints\StartupWizard.cs" />
<Compile Include="FFMpeg\FFMpegDownloadInfo.cs" />
<Compile Include="FFMpeg\FFMpegInfo.cs" />