sync updates

pull/702/head
Luke Pulverenti 10 years ago
parent 5c84cadb5e
commit 0e02e0559d

@ -5,7 +5,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session;
using System;
@ -89,7 +88,7 @@ namespace MediaBrowser.Api
/// </summary>
private void DeleteEncodedMediaCache()
{
var path = Path.Combine(_config.ApplicationPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower());
var path = _config.ApplicationPaths.TranscodingTempPath;
foreach (var file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)
.ToList())

@ -119,7 +119,7 @@ namespace MediaBrowser.Api.Playback
/// <returns>System.String.</returns>
private string GetOutputFilePath(StreamState state)
{
var folder = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower());
var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath;
var outputFileExtension = GetOutputFileExtension(state);

@ -1,10 +1,6 @@
using MediaBrowser.Controller;
using MediaBrowser.Model.Dlna;
using ServiceStack;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace MediaBrowser.Api.Playback.Hls
{
@ -66,7 +62,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
var file = request.PlaylistId + Path.GetExtension(Request.PathInfo);
file = Path.Combine(_appPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower(), file);
file = Path.Combine(_appPaths.TranscodingTempPath, file);
return ResultFactory.GetStaticFileResult(Request, file, FileShare.ReadWrite);
}
@ -85,7 +81,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
var file = request.SegmentId + Path.GetExtension(Request.PathInfo);
file = Path.Combine(_appPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower(), file);
file = Path.Combine(_appPaths.TranscodingTempPath, file);
return ResultFactory.GetStaticFileResult(Request, file, FileShare.ReadWrite);
}

@ -5,7 +5,6 @@ using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.IO;
using ServiceStack;
using System;
@ -71,7 +70,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
var file = request.SegmentId + Path.GetExtension(Request.PathInfo);
file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower(), file);
file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, file);
return ResultFactory.GetStaticFileResult(Request, file);
}

@ -2,7 +2,6 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Sync;
using MediaBrowser.Model.Users;
@ -85,6 +84,16 @@ namespace MediaBrowser.Api.Sync
public string Id { get; set; }
}
[Route("/Sync/JobItems/{Id}/AdditionalFiles", "GET", Summary = "Gets a sync job item file")]
public class GetSyncJobItemAdditionalFile
{
[ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
[ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string Name { get; set; }
}
[Route("/Sync/OfflineActions", "POST", Summary = "Reports an action that occurred while offline.")]
public class ReportOfflineActions : List<UserAction>, IReturnVoid
{
@ -243,5 +252,24 @@ namespace MediaBrowser.Api.Sync
Task.WaitAll(task);
}
public object Get(GetSyncJobItemAdditionalFile request)
{
var jobItem = _syncManager.GetJobItem(request.Id);
if (jobItem.Status != SyncJobItemStatus.Transferring)
{
throw new ArgumentException("The job item is not yet ready for transfer.");
}
var file = jobItem.AdditionalFiles.FirstOrDefault(i => string.Equals(i.Name, request.Name, StringComparison.OrdinalIgnoreCase));
if (file == null)
{
throw new ArgumentException("Sync job additional file not found.");
}
return ToStaticFileResult(file.Path);
}
}
}

@ -1,7 +1,7 @@
using System.Collections.Generic;
using MediaBrowser.Common;
using MediaBrowser.Common;
using MediaBrowser.Model.System;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller
{

@ -5,6 +5,7 @@ namespace MediaBrowser.Controller.MediaEncoding
public class EncodingJobOptions
{
public string OutputContainer { get; set; }
public string OutputDirectory { get; set; }
public long? StartTimeTicks { get; set; }
public int? Width { get; set; }

@ -280,13 +280,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
private string GetOutputFilePath(EncodingJob state)
{
var folder = ConfigurationManager.ApplicationPaths.TranscodingTempPath;
var folder = string.IsNullOrWhiteSpace(state.Options.OutputDirectory) ?
ConfigurationManager.ApplicationPaths.TranscodingTempPath :
state.Options.OutputDirectory;
var outputFileExtension = GetOutputFileExtension(state);
var context = state.Options.Context;
var filename = state.Id + (outputFileExtension ?? string.Empty).ToLower();
return Path.Combine(folder, context.ToString().ToLower(), filename);
return Path.Combine(folder, filename);
}
protected virtual string GetOutputFileExtension(EncodingJob state)

@ -1094,6 +1094,9 @@
<Compile Include="..\MediaBrowser.Model\Sync\SyncOptions.cs">
<Link>Sync\SyncOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncParameter.cs">
<Link>Sync\SyncParameter.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncQuality.cs">
<Link>Sync\SyncQuality.cs</Link>
</Compile>

@ -1053,6 +1053,9 @@
<Compile Include="..\MediaBrowser.Model\Sync\SyncOptions.cs">
<Link>Sync\SyncOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncParameter.cs">
<Link>Sync\SyncParameter.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Sync\SyncQuality.cs">
<Link>Sync\SyncQuality.cs</Link>
</Compile>

@ -1413,6 +1413,15 @@ namespace MediaBrowser.Model.ApiClient
/// <returns>Task&lt;Stream&gt;.</returns>
Task<Stream> GetSyncJobItemFile(string id, CancellationToken cancellationToken);
/// <summary>
/// Gets the synchronize job item additional file.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="name">The name.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;Stream&gt;.</returns>
Task<Stream> GetSyncJobItemAdditionalFile(string id, string name, CancellationToken cancellationToken);
/// <summary>
/// Opens the web socket.
/// </summary>

@ -212,7 +212,8 @@ namespace MediaBrowser.Model.Dlna
IsForced = stream.IsForced,
Language = stream.Language,
Name = stream.Language ?? "Unknown",
Format = SubtitleFormat
Format = SubtitleFormat,
Index = stream.Index
});
}

@ -7,5 +7,6 @@ namespace MediaBrowser.Model.Dlna
public string Name { get; set; }
public bool IsForced { get; set; }
public string Format { get; set; }
public int Index { get; set; }
}
}

@ -385,6 +385,7 @@
<Compile Include="Sync\SyncJobRequest.cs" />
<Compile Include="Sync\SyncJobStatus.cs" />
<Compile Include="Sync\SyncOptions.cs" />
<Compile Include="Sync\SyncParameter.cs" />
<Compile Include="Sync\SyncQuality.cs" />
<Compile Include="Sync\SyncTarget.cs" />
<Compile Include="System\LogFile.cs" />

@ -23,6 +23,11 @@ namespace MediaBrowser.Model.Sync
/// Gets or sets the type of the image.
/// </summary>
/// <value>The type of the image.</value>
public ImageType ImageType { get; set; }
public ImageType? ImageType { get; set; }
/// <summary>
/// Gets or sets the index.
/// </summary>
/// <value>The index.</value>
public int Index { get; set; }
}
}

@ -13,12 +13,12 @@ namespace MediaBrowser.Model.Sync
/// Gets or sets the options.
/// </summary>
/// <value>The options.</value>
public List<SyncOptions> Options { get; set; }
public List<SyncJobOption> Options { get; set; }
public SyncDialogOptions()
{
Targets = new List<SyncTarget>();
Options = new List<SyncOptions>();
Options = new List<SyncJobOption>();
}
}
}

@ -5,13 +5,13 @@ namespace MediaBrowser.Model.Sync
{
public static class SyncHelper
{
public static List<SyncOptions> GetSyncOptions(List<BaseItemDto> items)
public static List<SyncJobOption> GetSyncOptions(List<BaseItemDto> items)
{
List<SyncOptions> options = new List<SyncOptions>();
List<SyncJobOption> options = new List<SyncJobOption>();
if (items.Count > 1)
{
options.Add(SyncOptions.Name);
options.Add(SyncJobOption.Name);
}
foreach (BaseItemDto item in items)
@ -20,23 +20,23 @@ namespace MediaBrowser.Model.Sync
{
if (item.IsVideo)
{
options.Add(SyncOptions.Quality);
options.Add(SyncJobOption.Quality);
if (items.Count > 1)
{
options.Add(SyncOptions.UnwatchedOnly);
options.Add(SyncJobOption.UnwatchedOnly);
}
break;
}
if (item.IsFolder && !item.IsMusicGenre && !item.IsArtist && !item.IsType("musicalbum") && !item.IsGameGenre)
{
options.Add(SyncOptions.Quality);
options.Add(SyncOptions.UnwatchedOnly);
options.Add(SyncJobOption.Quality);
options.Add(SyncJobOption.UnwatchedOnly);
break;
}
if (item.IsGenre)
{
options.Add(SyncOptions.SyncNewContent);
options.Add(SyncOptions.ItemLimit);
options.Add(SyncJobOption.SyncNewContent);
options.Add(SyncJobOption.ItemLimit);
break;
}
}
@ -48,8 +48,8 @@ namespace MediaBrowser.Model.Sync
{
if (item.IsFolder || item.IsGameGenre || item.IsMusicGenre || item.IsGenre || item.IsArtist || item.IsStudio || item.IsPerson)
{
options.Add(SyncOptions.SyncNewContent);
options.Add(SyncOptions.ItemLimit);
options.Add(SyncJobOption.SyncNewContent);
options.Add(SyncJobOption.ItemLimit);
break;
}
}
@ -58,15 +58,15 @@ namespace MediaBrowser.Model.Sync
return options;
}
public static List<SyncOptions> GetSyncOptions(SyncCategory category)
public static List<SyncJobOption> GetSyncOptions(SyncCategory category)
{
List<SyncOptions> options = new List<SyncOptions>();
List<SyncJobOption> options = new List<SyncJobOption>();
options.Add(SyncOptions.Name);
options.Add(SyncOptions.Quality);
options.Add(SyncOptions.UnwatchedOnly);
options.Add(SyncOptions.SyncNewContent);
options.Add(SyncOptions.ItemLimit);
options.Add(SyncJobOption.Name);
options.Add(SyncJobOption.Quality);
options.Add(SyncJobOption.UnwatchedOnly);
options.Add(SyncJobOption.SyncNewContent);
options.Add(SyncJobOption.ItemLimit);
return options;
}

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Model.Sync
{
@ -27,12 +29,18 @@ namespace MediaBrowser.Model.Sync
/// </summary>
/// <value>The name of the item.</value>
public string ItemName { get; set; }
/// <summary>
/// Gets or sets the media source identifier.
/// </summary>
/// <value>The media source identifier.</value>
public string MediaSourceId { get; set; }
/// <summary>
/// Gets or sets the media source.
/// </summary>
/// <value>The media source.</value>
public MediaSourceInfo MediaSource { get; set; }
/// <summary>
/// Gets or sets the target identifier.
@ -74,9 +82,15 @@ namespace MediaBrowser.Model.Sync
/// <value>The primary image tag.</value>
public string PrimaryImageTag { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [requires conversion].
/// Gets or sets the temporary path.
/// </summary>
/// <value><c>true</c> if [requires conversion]; otherwise, <c>false</c>.</value>
public bool RequiresConversion { get; set; }
/// <value>The temporary path.</value>
public string TemporaryPath { get; set; }
public List<ItemFileInfo> AdditionalFiles { get; set; }
public SyncJobItem()
{
AdditionalFiles = new List<ItemFileInfo>();
}
}
}

@ -1,12 +1,8 @@

namespace MediaBrowser.Model.Sync
{
public enum SyncOptions
public class SyncOptions
{
Name = 0,
Quality = 1,
UnwatchedOnly = 2,
SyncNewContent = 3,
ItemLimit = 4
public string TemporaryPath { get; set; }
}
}

@ -0,0 +1,12 @@

namespace MediaBrowser.Model.Sync
{
public enum SyncJobOption
{
Name = 0,
Quality = 1,
UnwatchedOnly = 2,
SyncNewContent = 3,
ItemLimit = 4
}
}

@ -1,4 +1,5 @@
using MediaBrowser.Model.Dto;
using System.Collections.Generic;
namespace MediaBrowser.Model.Sync
{
@ -34,5 +35,15 @@ namespace MediaBrowser.Model.Sync
/// </summary>
/// <value>The user identifier.</value>
public string UserId { get; set; }
/// <summary>
/// Gets or sets the additional files.
/// </summary>
/// <value>The additional files.</value>
public List<ItemFileInfo> AdditionalFiles { get; set; }
public SyncedItem()
{
AdditionalFiles = new List<ItemFileInfo>();
}
}
}

@ -300,6 +300,7 @@
<Compile Include="Sorting\VideoBitRateComparer.cs" />
<Compile Include="Sync\AppSyncProvider.cs" />
<Compile Include="Sync\CloudSyncProvider.cs" />
<Compile Include="Sync\SyncConfig.cs" />
<Compile Include="Sync\SyncJobProcessor.cs" />
<Compile Include="Sync\SyncManager.cs" />
<Compile Include="Sync\SyncRepository.cs" />

@ -307,21 +307,21 @@ namespace MediaBrowser.Server.Implementations.Session
/// <param name="libraryItem">The library item.</param>
private void UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem)
{
var runtimeTicks = libraryItem == null ? null : libraryItem.RunTimeTicks;
if (string.IsNullOrWhiteSpace(info.MediaSourceId))
{
info.MediaSourceId = info.ItemId;
}
if (!string.Equals(info.ItemId, info.MediaSourceId) &&
!string.IsNullOrWhiteSpace(info.MediaSourceId))
if (!string.IsNullOrWhiteSpace(info.ItemId) && info.Item == null && libraryItem != null)
{
runtimeTicks = _libraryManager.GetItemById(new Guid(info.MediaSourceId)).RunTimeTicks;
}
var runtimeTicks = libraryItem.RunTimeTicks;
if (!string.Equals(info.ItemId, info.MediaSourceId) &&
!string.IsNullOrWhiteSpace(info.MediaSourceId))
{
runtimeTicks = _libraryManager.GetItemById(new Guid(info.MediaSourceId)).RunTimeTicks;
}
if (!string.IsNullOrWhiteSpace(info.ItemId) && libraryItem != null)
{
var current = session.NowPlayingItem;
if (current == null || !string.Equals(current.Id, info.ItemId, StringComparison.OrdinalIgnoreCase))
@ -711,7 +711,7 @@ namespace MediaBrowser.Server.Implementations.Session
info.MediaSourceId = info.ItemId;
}
if (!string.IsNullOrWhiteSpace(info.ItemId) && libraryItem != null)
if (!string.IsNullOrWhiteSpace(info.ItemId) && info.Item == null && libraryItem != null)
{
var current = session.NowPlayingItem;

@ -0,0 +1,29 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Sync;
using System.Collections.Generic;
namespace MediaBrowser.Server.Implementations.Sync
{
public class SyncConfigurationFactory : IConfigurationFactory
{
public IEnumerable<ConfigurationStore> GetConfigurations()
{
return new List<ConfigurationStore>
{
new ConfigurationStore
{
ConfigurationType = typeof(SyncOptions),
Key = "sync"
}
};
}
}
public static class SyncExtensions
{
public static SyncOptions GetSyncOptions(this IConfigurationManager config)
{
return config.GetConfiguration<SyncOptions>("sync");
}
}
}

@ -1,4 +1,7 @@
using MediaBrowser.Common.Progress;
using System.Globalization;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
@ -16,6 +19,7 @@ using MediaBrowser.Model.Sync;
using MoreLinq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -32,8 +36,10 @@ namespace MediaBrowser.Server.Implementations.Sync
private readonly ITVSeriesManager _tvSeriesManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly ISubtitleEncoder _subtitleEncoder;
private readonly IConfigurationManager _config;
private readonly IFileSystem _fileSystem;
public SyncJobProcessor(ILibraryManager libraryManager, ISyncRepository syncRepo, ISyncManager syncManager, ILogger logger, IUserManager userManager, ITVSeriesManager tvSeriesManager, IMediaEncoder mediaEncoder, ISubtitleEncoder subtitleEncoder)
public SyncJobProcessor(ILibraryManager libraryManager, ISyncRepository syncRepo, ISyncManager syncManager, ILogger logger, IUserManager userManager, ITVSeriesManager tvSeriesManager, IMediaEncoder mediaEncoder, ISubtitleEncoder subtitleEncoder, IConfigurationManager config, IFileSystem fileSystem)
{
_libraryManager = libraryManager;
_syncRepo = syncRepo;
@ -43,6 +49,8 @@ namespace MediaBrowser.Server.Implementations.Sync
_tvSeriesManager = tvSeriesManager;
_mediaEncoder = mediaEncoder;
_subtitleEncoder = subtitleEncoder;
_config = config;
_fileSystem = fileSystem;
}
public async Task EnsureJobItems(SyncJob job)
@ -329,6 +337,13 @@ namespace MediaBrowser.Server.Implementations.Sync
});
await SyncJobItems(result.Items, true, progress, cancellationToken).ConfigureAwait(false);
CleanDeadSyncFiles();
}
private void CleanDeadSyncFiles()
{
// TODO
}
public async Task SyncJobItems(SyncJobItem[] items, bool enableConversion, IProgress<double> progress, CancellationToken cancellationToken)
@ -416,24 +431,36 @@ namespace MediaBrowser.Server.Implementations.Sync
var streamInfo = new StreamBuilder().BuildVideoItem(options);
var mediaSource = streamInfo.MediaSource;
var externalSubs = streamInfo.GetExternalSubtitles("dummy", false);
var hasExternalSubs = externalSubs.Count > 0;
jobItem.MediaSourceId = streamInfo.MediaSourceId;
var requiresConversion = streamInfo.PlayMethod == PlayMethod.Transcode || hasExternalSubs;
if (streamInfo.PlayMethod == PlayMethod.Transcode)
if (requiresConversion && !enableConversion)
{
if (!enableConversion)
{
return;
}
return;
}
jobItem.MediaSourceId = streamInfo.MediaSourceId;
jobItem.TemporaryPath = GetTemporaryPath(jobItem);
if (requiresConversion)
{
jobItem.Status = SyncJobItemStatus.Converting;
jobItem.RequiresConversion = true;
}
if (streamInfo.PlayMethod == PlayMethod.Transcode)
{
// Save the job item now since conversion could take a while
await _syncRepo.Update(jobItem).ConfigureAwait(false);
try
{
jobItem.OutputPath = await _mediaEncoder.EncodeVideo(new EncodingJobOptions(streamInfo, profile), progress,
cancellationToken);
jobItem.OutputPath = await _mediaEncoder.EncodeVideo(new EncodingJobOptions(streamInfo, profile)
{
OutputDirectory = jobItem.TemporaryPath
}, progress, cancellationToken);
}
catch (OperationCanceledException)
{
@ -445,18 +472,16 @@ namespace MediaBrowser.Server.Implementations.Sync
_logger.ErrorException("Error during sync transcoding", ex);
}
await ConvertSubtitles(jobItem, streamInfo, cancellationToken).ConfigureAwait(false);
if (jobItem.Status == SyncJobItemStatus.Failed || jobItem.Status == SyncJobItemStatus.Queued)
{
await _syncRepo.Update(jobItem).ConfigureAwait(false);
return;
}
jobItem.MediaSource = await GetEncodedMediaSource(jobItem.OutputPath, user, true).ConfigureAwait(false);
}
else
{
jobItem.RequiresConversion = false;
if (mediaSource.Protocol == MediaProtocol.File)
{
jobItem.OutputPath = mediaSource.Path;
@ -469,6 +494,16 @@ namespace MediaBrowser.Server.Implementations.Sync
{
throw new InvalidOperationException(string.Format("Cannot direct stream {0} protocol", mediaSource.Protocol));
}
jobItem.MediaSource = mediaSource;
}
if (hasExternalSubs)
{
// Save the job item now since conversion could take a while
await _syncRepo.Update(jobItem).ConfigureAwait(false);
await ConvertSubtitles(jobItem, externalSubs, streamInfo, cancellationToken).ConfigureAwait(false);
}
jobItem.Progress = 50;
@ -476,18 +511,72 @@ namespace MediaBrowser.Server.Implementations.Sync
await _syncRepo.Update(jobItem).ConfigureAwait(false);
}
private async Task ConvertSubtitles(SyncJobItem jobItem, StreamInfo streamInfo, CancellationToken cancellationToken)
private async Task ConvertSubtitles(SyncJobItem jobItem,
IEnumerable<SubtitleStreamInfo> subtitles,
StreamInfo streamInfo,
CancellationToken cancellationToken)
{
if (streamInfo.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External || !streamInfo.SubtitleStreamIndex.HasValue)
var files = new List<ItemFileInfo>();
var mediaStreams = jobItem.MediaSource.MediaStreams
.Where(i => i.Type != MediaStreamType.Subtitle || !i.IsExternal)
.ToList();
var startingIndex = mediaStreams.Count == 0 ?
0 :
(mediaStreams.Select(i => i.Index).Max() + 1);
foreach (var subtitle in subtitles)
{
// Nothing to do
return;
var fileInfo = await ConvertSubtitles(jobItem.TemporaryPath, streamInfo, subtitle, cancellationToken).ConfigureAwait(false);
// Reset this to a value that will be based on the output media
fileInfo.Index = startingIndex;
files.Add(fileInfo);
mediaStreams.Add(new MediaStream
{
Index = startingIndex,
Codec = subtitle.Format,
IsForced = subtitle.IsForced,
IsExternal = true,
Language = subtitle.Language,
Path = fileInfo.Path
});
startingIndex++;
}
//using (var stream = await _subtitleEncoder.GetSubtitles(streamInfo.ItemId, streamInfo.MediaSourceId, streamInfo.SubtitleStreamIndex.Value, streamInfo.SubtitleFormat, 0, null, cancellationToken).ConfigureAwait(false))
//{
jobItem.AdditionalFiles.AddRange(files);
//}
jobItem.MediaSource.MediaStreams = mediaStreams;
}
private async Task<ItemFileInfo> ConvertSubtitles(string temporaryPath, StreamInfo streamInfo, SubtitleStreamInfo subtitleStreamInfo, CancellationToken cancellationToken)
{
var subtitleStreamIndex = subtitleStreamInfo.Index;
var filename = Guid.NewGuid() + "." + subtitleStreamInfo.Format.ToLower();
var path = Path.Combine(temporaryPath, filename);
Directory.CreateDirectory(Path.GetDirectoryName(path));
using (var stream = await _subtitleEncoder.GetSubtitles(streamInfo.ItemId, streamInfo.MediaSourceId, subtitleStreamIndex, subtitleStreamInfo.Format, 0, null, cancellationToken).ConfigureAwait(false))
{
using (var fs = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
{
await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);
}
}
return new ItemFileInfo
{
Name = Path.GetFileName(path),
Path = path,
Type = ItemFileType.Subtitles,
Index = subtitleStreamIndex
};
}
private async Task Sync(SyncJobItem jobItem, Audio item, User user, DeviceProfile profile, bool enableConversion, IProgress<double> progress, CancellationToken cancellationToken)
@ -505,6 +594,7 @@ namespace MediaBrowser.Server.Implementations.Sync
var mediaSource = streamInfo.MediaSource;
jobItem.MediaSourceId = streamInfo.MediaSourceId;
jobItem.TemporaryPath = GetTemporaryPath(jobItem);
if (streamInfo.PlayMethod == PlayMethod.Transcode)
{
@ -514,12 +604,15 @@ namespace MediaBrowser.Server.Implementations.Sync
}
jobItem.Status = SyncJobItemStatus.Converting;
jobItem.RequiresConversion = true;
await _syncRepo.Update(jobItem).ConfigureAwait(false);
try
{
jobItem.OutputPath = await _mediaEncoder.EncodeAudio(new EncodingJobOptions(streamInfo, profile), progress, cancellationToken);
jobItem.OutputPath = await _mediaEncoder.EncodeAudio(new EncodingJobOptions(streamInfo, profile)
{
OutputDirectory = jobItem.TemporaryPath
}, progress, cancellationToken);
}
catch (OperationCanceledException)
{
@ -536,11 +629,11 @@ namespace MediaBrowser.Server.Implementations.Sync
await _syncRepo.Update(jobItem).ConfigureAwait(false);
return;
}
jobItem.MediaSource = await GetEncodedMediaSource(jobItem.OutputPath, user, false).ConfigureAwait(false);
}
else
{
jobItem.RequiresConversion = false;
if (mediaSource.Protocol == MediaProtocol.File)
{
jobItem.OutputPath = mediaSource.Path;
@ -553,6 +646,8 @@ namespace MediaBrowser.Server.Implementations.Sync
{
throw new InvalidOperationException(string.Format("Cannot direct stream {0} protocol", mediaSource.Protocol));
}
jobItem.MediaSource = mediaSource;
}
jobItem.Progress = 50;
@ -583,5 +678,65 @@ namespace MediaBrowser.Server.Implementations.Sync
// TODO: Download
return mediaSource.Path;
}
private string GetTemporaryPath(SyncJobItem jobItem)
{
var basePath = _config.GetSyncOptions().TemporaryPath;
if (string.IsNullOrWhiteSpace(basePath))
{
basePath = Path.Combine(_config.CommonApplicationPaths.ProgramDataPath, "sync");
}
return Path.Combine(basePath, jobItem.JobId, jobItem.Id);
}
private async Task<MediaSourceInfo> GetEncodedMediaSource(string path, User user, bool isVideo)
{
var item = _libraryManager.ResolvePath(new FileInfo(path));
await item.RefreshMetadata(CancellationToken.None).ConfigureAwait(false);
var hasMediaSources = item as IHasMediaSources;
var mediaSources = hasMediaSources.GetMediaSources(false).ToList();
var preferredAudio = string.IsNullOrEmpty(user.Configuration.AudioLanguagePreference)
? new string[] { }
: new[] { user.Configuration.AudioLanguagePreference };
var preferredSubs = string.IsNullOrEmpty(user.Configuration.SubtitleLanguagePreference)
? new List<string> { }
: new List<string> { user.Configuration.SubtitleLanguagePreference };
foreach (var source in mediaSources)
{
if (isVideo)
{
source.DefaultAudioStreamIndex =
MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.Configuration.PlayDefaultAudioTrack);
var defaultAudioIndex = source.DefaultAudioStreamIndex;
var audioLangage = defaultAudioIndex == null
? null
: source.MediaStreams.Where(i => i.Type == MediaStreamType.Audio && i.Index == defaultAudioIndex).Select(i => i.Language).FirstOrDefault();
source.DefaultAudioStreamIndex =
MediaStreamSelector.GetDefaultSubtitleStreamIndex(source.MediaStreams, preferredSubs, user.Configuration.SubtitleMode, audioLangage);
}
else
{
var audio = source.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
if (audio != null)
{
source.DefaultAudioStreamIndex = audio.Index;
}
}
}
return mediaSources.FirstOrDefault();
}
}
}

@ -1,4 +1,5 @@
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels;
@ -13,6 +14,7 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Sync;
using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
@ -41,10 +43,11 @@ namespace MediaBrowser.Server.Implementations.Sync
private readonly Func<IMediaEncoder> _mediaEncoder;
private readonly IFileSystem _fileSystem;
private readonly Func<ISubtitleEncoder> _subtitleEncoder;
private readonly IConfigurationManager _config;
private ISyncProvider[] _providers = { };
public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger, IUserManager userManager, Func<IDtoService> dtoService, IApplicationHost appHost, ITVSeriesManager tvSeriesManager, Func<IMediaEncoder> mediaEncoder, IFileSystem fileSystem, Func<ISubtitleEncoder> subtitleEncoder)
public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger, IUserManager userManager, Func<IDtoService> dtoService, IApplicationHost appHost, ITVSeriesManager tvSeriesManager, Func<IMediaEncoder> mediaEncoder, IFileSystem fileSystem, Func<ISubtitleEncoder> subtitleEncoder, IConfigurationManager config)
{
_libraryManager = libraryManager;
_repo = repo;
@ -57,6 +60,7 @@ namespace MediaBrowser.Server.Implementations.Sync
_mediaEncoder = mediaEncoder;
_fileSystem = fileSystem;
_subtitleEncoder = subtitleEncoder;
_config = config;
}
public void AddParts(IEnumerable<ISyncProvider> providers)
@ -66,7 +70,7 @@ namespace MediaBrowser.Server.Implementations.Sync
public async Task<SyncJobCreationResult> CreateJob(SyncJobRequest request)
{
var processor = new SyncJobProcessor(_libraryManager, _repo, this, _logger, _userManager, _tvSeriesManager, _mediaEncoder(), _subtitleEncoder());
var processor = GetSyncJobProcessor();
var user = _userManager.GetUserById(request.UserId);
@ -129,7 +133,7 @@ namespace MediaBrowser.Server.Implementations.Sync
await _repo.Create(job).ConfigureAwait(false);
await processor.EnsureJobItems(job).ConfigureAwait(false);
// If it already has a converting status then is must have been aborted during conversion
var jobItemsResult = _repo.GetJobItems(new SyncJobItemQuery
{
@ -180,7 +184,7 @@ namespace MediaBrowser.Server.Implementations.Sync
if (item == null)
{
var processor = new SyncJobProcessor(_libraryManager, _repo, this, _logger, _userManager, _tvSeriesManager, _mediaEncoder(), _subtitleEncoder());
var processor = GetSyncJobProcessor();
var user = _userManager.GetUserById(job.UserId);
@ -408,11 +412,14 @@ namespace MediaBrowser.Server.Implementations.Sync
jobItem.Status = SyncJobItemStatus.Synced;
jobItem.Progress = 100;
if (jobItem.RequiresConversion)
if (!string.IsNullOrWhiteSpace(jobItem.TemporaryPath))
{
try
{
_fileSystem.DeleteFile(jobItem.OutputPath);
_fileSystem.DeleteDirectory(jobItem.TemporaryPath, true);
}
catch (DirectoryNotFoundException)
{
}
catch (Exception ex)
{
@ -422,11 +429,16 @@ namespace MediaBrowser.Server.Implementations.Sync
await _repo.Update(jobItem).ConfigureAwait(false);
var processor = new SyncJobProcessor(_libraryManager, _repo, this, _logger, _userManager, _tvSeriesManager, _mediaEncoder(), _subtitleEncoder());
var processor = GetSyncJobProcessor();
await processor.UpdateJobStatus(jobItem.JobId).ConfigureAwait(false);
}
private SyncJobProcessor GetSyncJobProcessor()
{
return new SyncJobProcessor(_libraryManager, _repo, this, _logger, _userManager, _tvSeriesManager, _mediaEncoder(), _subtitleEncoder(), _config, _fileSystem);
}
public SyncJobItem GetJobItem(string id)
{
return _repo.GetJobItem(id);
@ -455,7 +467,15 @@ namespace MediaBrowser.Server.Implementations.Sync
SyncJobId = jobItem.JobId,
SyncJobItemId = jobItem.Id,
ServerId = _appHost.SystemId,
UserId = job.UserId
UserId = job.UserId,
AdditionalFiles = jobItem.AdditionalFiles.Select(i => new ItemFileInfo
{
ImageType = i.ImageType,
Name = i.Name,
Type = i.Type,
Index = i.Index
}).ToList()
};
var dtoOptions = new DtoOptions();
@ -472,14 +492,11 @@ namespace MediaBrowser.Server.Implementations.Sync
syncedItem.Item = _dtoService().GetBaseItemDto(libraryItem, dtoOptions);
// TODO: this should be the media source of the transcoded output
syncedItem.Item.MediaSources = syncedItem.Item.MediaSources
.Where(i => string.Equals(i.Id, jobItem.MediaSourceId))
.ToList();
var mediaSource = syncedItem.Item.MediaSources
.FirstOrDefault(i => string.Equals(i.Id, jobItem.MediaSourceId));
syncedItem.Item.MediaSources = new List<MediaSourceInfo>();
// This will be null for items that are not audio/video
if (mediaSource == null)
{
@ -488,6 +505,7 @@ namespace MediaBrowser.Server.Implementations.Sync
else
{
syncedItem.OriginalFileName = Path.GetFileName(mediaSource.Path);
syncedItem.Item.MediaSources.Add(mediaSource);
}
return syncedItem;

@ -1,7 +1,9 @@
using MediaBrowser.Controller;
using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Sync;
using MediaBrowser.Server.Implementations.Persistence;
using System;
@ -28,15 +30,18 @@ namespace MediaBrowser.Server.Implementations.Sync
private IDbCommand _saveJobCommand;
private IDbCommand _saveJobItemCommand;
public SyncRepository(ILogger logger, IServerApplicationPaths appPaths)
private readonly IJsonSerializer _json;
public SyncRepository(ILogger logger, IServerApplicationPaths appPaths, IJsonSerializer json)
{
_logger = logger;
_appPaths = appPaths;
_json = json;
}
public async Task Initialize()
{
var dbFile = Path.Combine(_appPaths.DataPath, "sync11.db");
var dbFile = Path.Combine(_appPaths.DataPath, "sync12.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false);
@ -45,7 +50,7 @@ namespace MediaBrowser.Server.Implementations.Sync
"create table if not exists SyncJobs (Id GUID PRIMARY KEY, TargetId TEXT NOT NULL, Name TEXT NOT NULL, Quality TEXT NOT NULL, Status TEXT NOT NULL, Progress FLOAT, UserId TEXT NOT NULL, ItemIds TEXT NOT NULL, Category TEXT, ParentId TEXT, UnwatchedOnly BIT, ItemLimit INT, SyncNewContent BIT, DateCreated DateTime, DateLastModified DateTime, ItemCount int)",
"create index if not exists idx_SyncJobs on SyncJobs(Id)",
"create table if not exists SyncJobItems (Id GUID PRIMARY KEY, ItemId TEXT, ItemName TEXT, MediaSourceId TEXT, JobId TEXT, RequiresConversion BIT, OutputPath TEXT, Status TEXT, TargetId TEXT, DateCreated DateTime, Progress FLOAT)",
"create table if not exists SyncJobItems (Id GUID PRIMARY KEY, ItemId TEXT, ItemName TEXT, MediaSourceId TEXT, JobId TEXT, TemporaryPath TEXT, OutputPath TEXT, Status TEXT, TargetId TEXT, DateCreated DateTime, Progress FLOAT, AdditionalFiles TEXT, MediaSource TEXT)",
"create index if not exists idx_SyncJobItems on SyncJobs(Id)",
//pragmas
@ -90,23 +95,25 @@ namespace MediaBrowser.Server.Implementations.Sync
_saveJobCommand.Parameters.Add(_saveJobCommand, "@ItemCount");
_saveJobItemCommand = _connection.CreateCommand();
_saveJobItemCommand.CommandText = "replace into SyncJobItems (Id, ItemId, ItemName, MediaSourceId, JobId, RequiresConversion, OutputPath, Status, TargetId, DateCreated, Progress) values (@Id, @ItemId, @ItemName, @MediaSourceId, @JobId, @RequiresConversion, @OutputPath, @Status, @TargetId, @DateCreated, @Progress)";
_saveJobItemCommand.CommandText = "replace into SyncJobItems (Id, ItemId, ItemName, MediaSourceId, JobId, TemporaryPath, OutputPath, Status, TargetId, DateCreated, Progress, AdditionalFiles, MediaSource) values (@Id, @ItemId, @ItemName, @MediaSourceId, @JobId, @TemporaryPath, @OutputPath, @Status, @TargetId, @DateCreated, @Progress, @AdditionalFiles, @MediaSource)";
_saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@Id");
_saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@ItemId");
_saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@ItemName");
_saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@MediaSourceId");
_saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@JobId");
_saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@RequiresConversion");
_saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@TemporaryPath");
_saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@OutputPath");
_saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@Status");
_saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@TargetId");
_saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@DateCreated");
_saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@Progress");
_saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@AdditionalFiles");
_saveJobItemCommand.Parameters.Add(_saveJobItemCommand, "@MediaSource");
}
private const string BaseJobSelectText = "select Id, TargetId, Name, Quality, Status, Progress, UserId, ItemIds, Category, ParentId, UnwatchedOnly, ItemLimit, SyncNewContent, DateCreated, DateLastModified, ItemCount from SyncJobs";
private const string BaseJobItemSelectText = "select Id, ItemId, ItemName, MediaSourceId, JobId, RequiresConversion, OutputPath, Status, TargetId, DateCreated, Progress from SyncJobItems";
private const string BaseJobItemSelectText = "select Id, ItemId, ItemName, MediaSourceId, JobId, TemporaryPath, OutputPath, Status, TargetId, DateCreated, Progress, AdditionalFiles, MediaSource from SyncJobItems";
public SyncJob GetJob(string id)
{
@ -557,12 +564,14 @@ namespace MediaBrowser.Server.Implementations.Sync
_saveJobItemCommand.GetParameter(index++).Value = jobItem.ItemName;
_saveJobItemCommand.GetParameter(index++).Value = jobItem.MediaSourceId;
_saveJobItemCommand.GetParameter(index++).Value = jobItem.JobId;
_saveJobItemCommand.GetParameter(index++).Value = jobItem.RequiresConversion;
_saveJobItemCommand.GetParameter(index++).Value = jobItem.TemporaryPath;
_saveJobItemCommand.GetParameter(index++).Value = jobItem.OutputPath;
_saveJobItemCommand.GetParameter(index++).Value = jobItem.Status.ToString();
_saveJobItemCommand.GetParameter(index++).Value = jobItem.TargetId;
_saveJobItemCommand.GetParameter(index++).Value = jobItem.DateCreated;
_saveJobItemCommand.GetParameter(index++).Value = jobItem.Progress;
_saveJobItemCommand.GetParameter(index++).Value = _json.SerializeToString(jobItem.AdditionalFiles);
_saveJobItemCommand.GetParameter(index++).Value = jobItem.MediaSource == null ? null : _json.SerializeToString(jobItem.MediaSource);
_saveJobItemCommand.Transaction = transaction;
@ -620,8 +629,11 @@ namespace MediaBrowser.Server.Implementations.Sync
}
info.JobId = reader.GetString(4);
info.RequiresConversion = reader.GetBoolean(5);
if (!reader.IsDBNull(5))
{
info.TemporaryPath = reader.GetString(5);
}
if (!reader.IsDBNull(6))
{
info.OutputPath = reader.GetString(6);
@ -641,6 +653,26 @@ namespace MediaBrowser.Server.Implementations.Sync
info.Progress = reader.GetDouble(10);
}
if (!reader.IsDBNull(11))
{
var json = reader.GetString(11);
if (!string.IsNullOrWhiteSpace(json))
{
info.AdditionalFiles = _json.DeserializeFromString<List<ItemFileInfo>>(json);
}
}
if (!reader.IsDBNull(12))
{
var json = reader.GetString(12);
if (!string.IsNullOrWhiteSpace(json))
{
info.MediaSource = _json.DeserializeFromString<MediaSourceInfo>(json);
}
}
return info;
}

@ -1,4 +1,6 @@
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Sync;
@ -21,8 +23,10 @@ namespace MediaBrowser.Server.Implementations.Sync
private readonly ITVSeriesManager _tvSeriesManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly ISubtitleEncoder _subtitleEncoder;
private readonly IConfigurationManager _config;
private readonly IFileSystem _fileSystem;
public SyncScheduledTask(ILibraryManager libraryManager, ISyncRepository syncRepo, ISyncManager syncManager, ILogger logger, IUserManager userManager, ITVSeriesManager tvSeriesManager, IMediaEncoder mediaEncoder, ISubtitleEncoder subtitleEncoder)
public SyncScheduledTask(ILibraryManager libraryManager, ISyncRepository syncRepo, ISyncManager syncManager, ILogger logger, IUserManager userManager, ITVSeriesManager tvSeriesManager, IMediaEncoder mediaEncoder, ISubtitleEncoder subtitleEncoder, IConfigurationManager config, IFileSystem fileSystem)
{
_libraryManager = libraryManager;
_syncRepo = syncRepo;
@ -32,6 +36,8 @@ namespace MediaBrowser.Server.Implementations.Sync
_tvSeriesManager = tvSeriesManager;
_mediaEncoder = mediaEncoder;
_subtitleEncoder = subtitleEncoder;
_config = config;
_fileSystem = fileSystem;
}
public string Name
@ -54,7 +60,7 @@ namespace MediaBrowser.Server.Implementations.Sync
public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{
return new SyncJobProcessor(_libraryManager, _syncRepo, _syncManager, _logger, _userManager, _tvSeriesManager, _mediaEncoder, _subtitleEncoder).Sync(progress,
return new SyncJobProcessor(_libraryManager, _syncRepo, _syncManager, _logger, _userManager, _tvSeriesManager, _mediaEncoder, _subtitleEncoder, _config, _fileSystem).Sync(progress,
cancellationToken);
}

@ -483,7 +483,7 @@ namespace MediaBrowser.Server.Startup.Common
TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager);
RegisterSingleInstance(TVSeriesManager);
SyncManager = new SyncManager(LibraryManager, SyncRepository, ImageProcessor, LogManager.GetLogger("SyncManager"), UserManager, () => DtoService, this, TVSeriesManager, () => MediaEncoder, FileSystemManager, () => SubtitleEncoder);
SyncManager = new SyncManager(LibraryManager, SyncRepository, ImageProcessor, LogManager.GetLogger("SyncManager"), UserManager, () => DtoService, this, TVSeriesManager, () => MediaEncoder, FileSystemManager, () => SubtitleEncoder, ServerConfigurationManager);
RegisterSingleInstance(SyncManager);
DtoService = new DtoService(Logger, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager, () => ChannelManager, SyncManager, this);
@ -663,7 +663,7 @@ namespace MediaBrowser.Server.Startup.Common
private async Task<ISyncRepository> GetSyncRepository()
{
var repo = new SyncRepository(LogManager.GetLogger("SyncRepository"), ServerConfigurationManager.ApplicationPaths);
var repo = new SyncRepository(LogManager.GetLogger("SyncRepository"), ServerConfigurationManager.ApplicationPaths, JsonSerializer);
await repo.Initialize().ConfigureAwait(false);

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
<version>3.0.541</version>
<version>3.0.543</version>
<title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
<dependency id="MediaBrowser.Common" version="3.0.541" />
<dependency id="MediaBrowser.Common" version="3.0.543" />
<dependency id="NLog" version="3.1.0.0" />
<dependency id="SimpleInjector" version="2.6.1" />
</dependencies>

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
<version>3.0.541</version>
<version>3.0.543</version>
<title>MediaBrowser.Common</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Model.Signed</id>
<version>3.0.541</version>
<version>3.0.543</version>
<title>MediaBrowser.Model - Signed Edition</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
<version>3.0.541</version>
<version>3.0.543</version>
<title>Media Browser.Server.Core</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Media Browser Server.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
<dependency id="MediaBrowser.Common" version="3.0.541" />
<dependency id="MediaBrowser.Common" version="3.0.543" />
</dependencies>
</metadata>
<files>

Loading…
Cancel
Save