Merge pull request #2101 from MediaBrowser/beta

Beta
This commit is contained in:
Luke 2016-08-25 14:46:19 -04:00 committed by GitHub
commit ba9577f380
78 changed files with 975 additions and 835 deletions

View File

@ -33,7 +33,9 @@ namespace Emby.Drawing.GDI
graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingMode = CompositingMode.SourceCopy;
// SourceCopy causes the image to be blank in OSX
//graphics.CompositingMode = CompositingMode.SourceCopy;
for (var row = 0; row < rows; row++) for (var row = 0; row < rows; row++)
{ {
@ -44,19 +46,9 @@ namespace Emby.Drawing.GDI
if (files.Count > index) if (files.Count > index)
{ {
using (var fileStream = fileSystem.GetFileStream(files[index], FileMode.Open, FileAccess.Read, FileShare.Read, true)) using (var imgtemp = Image.FromFile(files[index]))
{ {
using (var memoryStream = new MemoryStream()) graphics.DrawImage(imgtemp, x, y, cellWidth, cellHeight);
{
fileStream.CopyTo(memoryStream);
memoryStream.Position = 0;
using (var imgtemp = Image.FromStream(memoryStream, true, false))
{
graphics.DrawImage(imgtemp, x, y, cellWidth, cellHeight);
}
}
} }
} }
@ -90,7 +82,9 @@ namespace Emby.Drawing.GDI
graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingMode = CompositingMode.SourceCopy;
// SourceCopy causes the image to be blank in OSX
//graphics.CompositingMode = CompositingMode.SourceCopy;
for (var row = 0; row < rows; row++) for (var row = 0; row < rows; row++)
{ {
@ -99,21 +93,10 @@ namespace Emby.Drawing.GDI
var x = col * singleSize; var x = col * singleSize;
var y = row * singleSize; var y = row * singleSize;
using (var fileStream = fileSystem.GetFileStream(files[index], FileMode.Open, FileAccess.Read, FileShare.Read, true)) using (var imgtemp = Image.FromFile(files[index]))
{ {
using (var memoryStream = new MemoryStream()) graphics.DrawImage(imgtemp, x, y, singleSize, singleSize);
{
fileStream.CopyTo(memoryStream);
memoryStream.Position = 0;
using (var imgtemp = Image.FromStream(memoryStream, true, false))
{
graphics.DrawImage(imgtemp, x, y, singleSize, singleSize);
}
}
} }
index++; index++;
} }
} }
@ -121,16 +104,5 @@ namespace Emby.Drawing.GDI
} }
} }
} }
private static Stream GetStream(Image image)
{
var ms = new MemoryStream();
image.Save(ms, ImageFormat.Png);
ms.Position = 0;
return ms;
}
} }
} }

View File

@ -119,9 +119,11 @@ namespace Emby.Drawing.GDI
thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality; thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality;
thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic; thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic;
thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality; thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality;
thumbnailGraph.CompositingMode = !hasPostProcessing ?
CompositingMode.SourceCopy : // SourceCopy causes the image to be blank in OSX
CompositingMode.SourceOver; //thumbnailGraph.CompositingMode = !hasPostProcessing ?
// CompositingMode.SourceCopy :
// CompositingMode.SourceOver;
SetBackgroundColor(thumbnailGraph, options); SetBackgroundColor(thumbnailGraph, options);

View File

@ -154,9 +154,12 @@ namespace MediaBrowser.Api.Library
public void Post(PerformOrganization request) public void Post(PerformOrganization request)
{ {
// Don't await this
var task = _iFileOrganizationService.PerformOrganization(request.Id); var task = _iFileOrganizationService.PerformOrganization(request.Id);
Task.WaitAll(task); // Async processing (close dialog early instead of waiting until the file has been copied)
// Wait 2s for exceptions that may occur to have them forwarded to the client for immediate error display
task.Wait(2000);
} }
public void Post(OrganizeEpisode request) public void Post(OrganizeEpisode request)
@ -168,6 +171,7 @@ namespace MediaBrowser.Api.Library
dicNewProviderIds = request.NewSeriesProviderIds; dicNewProviderIds = request.NewSeriesProviderIds;
} }
// Don't await this
var task = _iFileOrganizationService.PerformEpisodeOrganization(new EpisodeFileOrganizationRequest var task = _iFileOrganizationService.PerformEpisodeOrganization(new EpisodeFileOrganizationRequest
{ {
EndingEpisodeNumber = request.EndingEpisodeNumber, EndingEpisodeNumber = request.EndingEpisodeNumber,
@ -182,11 +186,9 @@ namespace MediaBrowser.Api.Library
TargetFolder = request.TargetFolder TargetFolder = request.TargetFolder
}); });
// For async processing (close dialog early instead of waiting until the file has been copied) // Async processing (close dialog early instead of waiting until the file has been copied)
//var tasks = new Task[] { task }; // Wait 2s for exceptions that may occur to have them forwarded to the client for immediate error display
//Task.WaitAll(tasks, 8000); task.Wait(2000);
Task.WaitAll(task);
} }
public object Get(GetSmartMatchInfos request) public object Get(GetSmartMatchInfos request)

View File

@ -192,7 +192,8 @@ namespace MediaBrowser.Api.Movies
SortOrder = SortOrder.Descending, SortOrder = SortOrder.Descending,
Limit = 7, Limit = 7,
ParentId = parentIdGuid, ParentId = parentIdGuid,
Recursive = true Recursive = true,
IsPlayed = true
}; };
var recentlyPlayedMovies = _libraryManager.GetItemList(query).ToList(); var recentlyPlayedMovies = _libraryManager.GetItemList(query).ToList();

View File

@ -298,7 +298,8 @@ namespace MediaBrowser.Api.Playback
// Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this. // Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this.
if (state.VideoType == VideoType.VideoFile) if (state.VideoType == VideoType.VideoFile)
{ {
var hwType = ApiEntryPoint.Instance.GetEncodingOptions().HardwareAccelerationType; var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
var hwType = encodingOptions.HardwareAccelerationType;
if (string.Equals(hwType, "qsv", StringComparison.OrdinalIgnoreCase) || if (string.Equals(hwType, "qsv", StringComparison.OrdinalIgnoreCase) ||
string.Equals(hwType, "h264_qsv", StringComparison.OrdinalIgnoreCase)) string.Equals(hwType, "h264_qsv", StringComparison.OrdinalIgnoreCase))
@ -314,6 +315,10 @@ namespace MediaBrowser.Api.Playback
{ {
return GetAvailableEncoder("h264_omx", defaultEncoder); return GetAvailableEncoder("h264_omx", defaultEncoder);
} }
if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(encodingOptions.VaapiDevice))
{
return GetAvailableEncoder("h264_vaapi", defaultEncoder);
}
} }
return defaultEncoder; return defaultEncoder;
@ -427,7 +432,8 @@ namespace MediaBrowser.Api.Playback
if (!string.IsNullOrEmpty(state.VideoRequest.Profile)) if (!string.IsNullOrEmpty(state.VideoRequest.Profile))
{ {
if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase)) if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{ {
// not supported by h264_omx // not supported by h264_omx
param += " -profile:v " + state.VideoRequest.Profile; param += " -profile:v " + state.VideoRequest.Profile;
@ -482,7 +488,8 @@ namespace MediaBrowser.Api.Playback
if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) && if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) !string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{ {
param = "-pix_fmt yuv420p " + param; param = "-pix_fmt yuv420p " + param;
} }
@ -548,59 +555,97 @@ namespace MediaBrowser.Api.Playback
var filters = new List<string>(); var filters = new List<string>();
if (state.DeInterlace) if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
filters.Add("format=nv12|vaapi");
filters.Add("hwupload");
}
else if (state.DeInterlace && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{ {
filters.Add("yadif=0:-1:0"); filters.Add("yadif=0:-1:0");
} }
// If fixed dimensions were supplied if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
if (request.Width.HasValue && request.Height.HasValue)
{ {
var widthParam = request.Width.Value.ToString(UsCulture); // Work around vaapi's reduced scaling features
var heightParam = request.Height.Value.ToString(UsCulture); var scaler = "scale_vaapi";
filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam)); // Given the input dimensions (inputWidth, inputHeight), determine the output dimensions
// (outputWidth, outputHeight). The user may request precise output dimensions or maximum
// output dimensions. Output dimensions are guaranteed to be even.
decimal inputWidth = Convert.ToDecimal(state.VideoStream.Width);
decimal inputHeight = Convert.ToDecimal(state.VideoStream.Height);
decimal outputWidth = request.Width.HasValue ? Convert.ToDecimal(request.Width.Value) : inputWidth;
decimal outputHeight = request.Height.HasValue ? Convert.ToDecimal(request.Height.Value) : inputHeight;
decimal maximumWidth = request.MaxWidth.HasValue ? Convert.ToDecimal(request.MaxWidth.Value) : outputWidth;
decimal maximumHeight = request.MaxHeight.HasValue ? Convert.ToDecimal(request.MaxHeight.Value) : outputHeight;
if (outputWidth > maximumWidth || outputHeight > maximumHeight)
{
var scale = Math.Min(maximumWidth / outputWidth, maximumHeight / outputHeight);
outputWidth = Math.Min(maximumWidth, Math.Truncate(outputWidth * scale));
outputHeight = Math.Min(maximumHeight, Math.Truncate(outputHeight * scale));
}
outputWidth = 2 * Math.Truncate(outputWidth / 2);
outputHeight = 2 * Math.Truncate(outputHeight / 2);
if (outputWidth != inputWidth || outputHeight != inputHeight)
{
filters.Add(string.Format("{0}=w={1}:h={2}", scaler, outputWidth.ToString(UsCulture), outputHeight.ToString(UsCulture)));
}
} }
else
// If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
{ {
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture); // If fixed dimensions were supplied
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture); if (request.Width.HasValue && request.Height.HasValue)
{
var widthParam = request.Width.Value.ToString(UsCulture);
var heightParam = request.Height.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam)); filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam));
} }
// If a fixed width was requested // If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
else if (request.Width.HasValue) else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
{ {
var widthParam = request.Width.Value.ToString(UsCulture); var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam)); filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
} }
// If a fixed height was requested // If a fixed width was requested
else if (request.Height.HasValue) else if (request.Width.HasValue)
{ {
var heightParam = request.Height.Value.ToString(UsCulture); var widthParam = request.Width.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam)); filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam));
} }
// If a max width was requested // If a fixed height was requested
else if (request.MaxWidth.HasValue) else if (request.Height.HasValue)
{ {
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture); var heightParam = request.Height.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam)); filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
} }
// If a max height was requested // If a max width was requested
else if (request.MaxHeight.HasValue) else if (request.MaxWidth.HasValue)
{ {
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture); var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam)); filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
}
// If a max height was requested
else if (request.MaxHeight.HasValue)
{
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(max(iw/dar\\,ih)\\,{0})", maxHeightParam));
}
} }
var output = string.Empty; var output = string.Empty;
@ -935,6 +980,17 @@ namespace MediaBrowser.Api.Playback
} }
} }
if (state.VideoRequest != null)
{
var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
if (GetVideoEncoder(state).IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1)
{
arg = "-hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device " + encodingOptions.VaapiDevice + " " + arg;
}
}
arg += string.Format(" -ss {0}", MediaEncoder.GetTimeParameter(TimeSpan.FromSeconds(1).Ticks));
return arg.Trim(); return arg.Trim();
} }
@ -1589,13 +1645,6 @@ namespace MediaBrowser.Api.Playback
} }
} }
else if (i == 25) else if (i == 25)
{
if (videoRequest != null)
{
videoRequest.ForceLiveStream = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
}
}
else if (i == 26)
{ {
if (!string.IsNullOrWhiteSpace(val) && videoRequest != null) if (!string.IsNullOrWhiteSpace(val) && videoRequest != null)
{ {
@ -1606,18 +1655,18 @@ namespace MediaBrowser.Api.Playback
} }
} }
} }
else if (i == 27) else if (i == 26)
{ {
request.TranscodingMaxAudioChannels = int.Parse(val, UsCulture); request.TranscodingMaxAudioChannels = int.Parse(val, UsCulture);
} }
else if (i == 28) else if (i == 27)
{ {
if (videoRequest != null) if (videoRequest != null)
{ {
videoRequest.EnableSubtitlesInManifest = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); videoRequest.EnableSubtitlesInManifest = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
} }
} }
else if (i == 29) else if (i == 28)
{ {
request.Tag = val; request.Tag = val;
} }
@ -2218,7 +2267,6 @@ namespace MediaBrowser.Api.Playback
if (state.VideoRequest != null) if (state.VideoRequest != null)
{ {
state.VideoRequest.CopyTimestamps = transcodingProfile.CopyTimestamps; state.VideoRequest.CopyTimestamps = transcodingProfile.CopyTimestamps;
state.VideoRequest.ForceLiveStream = transcodingProfile.ForceLiveStream;
state.VideoRequest.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest; state.VideoRequest.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest;
} }
} }
@ -2244,7 +2292,7 @@ namespace MediaBrowser.Api.Playback
return Task.FromResult(true); return Task.FromResult(true);
} }
if (!string.Equals(MediaEncoder.EncoderLocationType, "Default", StringComparison.OrdinalIgnoreCase)) if (!MediaEncoder.IsDefaultEncoderPath)
{ {
return Task.FromResult(true); return Task.FromResult(true);
} }

View File

@ -281,11 +281,6 @@ namespace MediaBrowser.Api.Playback.Hls
{ {
var isLiveStream = (state.RunTimeTicks ?? 0) == 0; var isLiveStream = (state.RunTimeTicks ?? 0) == 0;
if (state.VideoRequest.ForceLiveStream)
{
return true;
}
return isLiveStream; return isLiveStream;
} }
} }

View File

@ -193,8 +193,6 @@ namespace MediaBrowser.Api.Playback
[ApiMember(Name = "CopyTimestamps", Description = "Whether or not to copy timestamps when transcoding with an offset. Defaults to false.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "CopyTimestamps", Description = "Whether or not to copy timestamps when transcoding with an offset. Defaults to false.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool CopyTimestamps { get; set; } public bool CopyTimestamps { get; set; }
public bool ForceLiveStream { get; set; }
public bool EnableSubtitlesInManifest { get; set; } public bool EnableSubtitlesInManifest { get; set; }
public VideoStreamRequest() public VideoStreamRequest()

View File

@ -275,8 +275,6 @@ namespace MediaBrowser.Api.Reports
case ItemFilter.IsPlayed: case ItemFilter.IsPlayed:
query.IsPlayed = true; query.IsPlayed = true;
break; break;
case ItemFilter.IsRecentlyAdded:
break;
case ItemFilter.IsResumable: case ItemFilter.IsResumable:
query.IsResumable = true; query.IsResumable = true;
break; break;

View File

@ -24,6 +24,19 @@ namespace MediaBrowser.Api.Sync
} }
break; break;
} }
if (item.IsAudio)
{
options.Add(SyncJobOption.Quality);
options.Add(SyncJobOption.Profile);
break;
}
if (item.IsMusicGenre || item.IsArtist|| item.IsType("musicalbum"))
{
options.Add(SyncJobOption.Quality);
options.Add(SyncJobOption.Profile);
options.Add(SyncJobOption.ItemLimit);
break;
}
if (item.IsFolderItem && !item.IsMusicGenre && !item.IsArtist && !item.IsType("musicalbum") && !item.IsGameGenre) if (item.IsFolderItem && !item.IsMusicGenre && !item.IsArtist && !item.IsType("musicalbum") && !item.IsGameGenre)
{ {
options.Add(SyncJobOption.Quality); options.Add(SyncJobOption.Quality);

View File

@ -291,7 +291,8 @@ namespace MediaBrowser.Api.Sync
{ {
Fields = new List<ItemFields> Fields = new List<ItemFields>
{ {
ItemFields.SyncInfo ItemFields.SyncInfo,
ItemFields.BasicSyncInfo
} }
}; };

View File

@ -164,8 +164,6 @@ namespace MediaBrowser.Api.UserLibrary
case ItemFilter.IsPlayed: case ItemFilter.IsPlayed:
query.IsPlayed = true; query.IsPlayed = true;
break; break;
case ItemFilter.IsRecentlyAdded:
break;
case ItemFilter.IsResumable: case ItemFilter.IsResumable:
query.IsResumable = true; query.IsResumable = true;
break; break;

View File

@ -149,24 +149,6 @@ namespace MediaBrowser.Api.UserLibrary
item = user == null ? _libraryManager.RootFolder : user.RootFolder; item = user == null ? _libraryManager.RootFolder : user.RootFolder;
} }
if (!string.IsNullOrEmpty(request.Ids))
{
var query = GetItemsQuery(request, user);
var specificItems = _libraryManager.GetItemList(query).ToArray();
if (query.SortBy.Length == 0)
{
var ids = query.ItemIds.ToList();
// Try to preserve order
specificItems = specificItems.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToArray();
}
return new QueryResult<BaseItem>
{
Items = specificItems.ToArray(),
TotalRecordCount = specificItems.Length
};
}
// Default list type = children // Default list type = children
var folder = item as Folder; var folder = item as Folder;
@ -289,8 +271,6 @@ namespace MediaBrowser.Api.UserLibrary
case ItemFilter.IsPlayed: case ItemFilter.IsPlayed:
query.IsPlayed = true; query.IsPlayed = true;
break; break;
case ItemFilter.IsRecentlyAdded:
break;
case ItemFilter.IsResumable: case ItemFilter.IsResumable:
query.IsResumable = true; query.IsResumable = true;
break; break;

View File

@ -33,7 +33,6 @@ namespace MediaBrowser.Common.Implementations.Updates
EnableKeepAlive = false, EnableKeepAlive = false,
CancellationToken = cancellationToken, CancellationToken = cancellationToken,
UserAgent = "Emby/3.0" UserAgent = "Emby/3.0"
}; };
if (_cacheLength.Ticks > 0) if (_cacheLength.Ticks > 0)

View File

@ -281,6 +281,20 @@ namespace MediaBrowser.Controller.Entities
} }
} }
public Task UpdateIsOffline(bool newValue)
{
var item = this;
if (item.IsOffline != newValue)
{
item.IsOffline = newValue;
// this is creating too many repeated db updates
//return item.UpdateToRepository(ItemUpdateType.None, CancellationToken.None);
}
return Task.FromResult(true);
}
/// <summary> /// <summary>
/// Gets or sets the type of the location. /// Gets or sets the type of the location.
/// </summary> /// </summary>
@ -290,10 +304,10 @@ namespace MediaBrowser.Controller.Entities
{ {
get get
{ {
if (IsOffline) //if (IsOffline)
{ //{
return LocationType.Offline; // return LocationType.Offline;
} //}
if (string.IsNullOrWhiteSpace(Path)) if (string.IsNullOrWhiteSpace(Path))
{ {

View File

@ -106,6 +106,7 @@ namespace MediaBrowser.Controller.Entities
{ {
LibraryOptions[path] = options; LibraryOptions[path] = options;
options.SchemaVersion = 1;
XmlSerializer.SerializeToFile(options, GetLibraryOptionsPath(path)); XmlSerializer.SerializeToFile(options, GetLibraryOptionsPath(path));
} }
} }

View File

@ -375,7 +375,7 @@ namespace MediaBrowser.Controller.Entities
if (currentChildren.TryGetValue(child.Id, out currentChild) && IsValidFromResolver(currentChild, child)) if (currentChildren.TryGetValue(child.Id, out currentChild) && IsValidFromResolver(currentChild, child))
{ {
await UpdateIsOffline(currentChild, false).ConfigureAwait(false); await currentChild.UpdateIsOffline(false).ConfigureAwait(false);
validChildren.Add(currentChild); validChildren.Add(currentChild);
continue; continue;
@ -404,7 +404,7 @@ namespace MediaBrowser.Controller.Entities
else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path)) else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path))
{ {
await UpdateIsOffline(item, true).ConfigureAwait(false); await item.UpdateIsOffline(true).ConfigureAwait(false);
} }
else else
{ {
@ -461,17 +461,6 @@ namespace MediaBrowser.Controller.Entities
progress.Report(100); progress.Report(100);
} }
private Task UpdateIsOffline(BaseItem item, bool newValue)
{
if (item.IsOffline != newValue)
{
item.IsOffline = newValue;
return item.UpdateToRepository(ItemUpdateType.None, CancellationToken.None);
}
return Task.FromResult(true);
}
private async Task RefreshMetadataRecursive(MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken) private async Task RefreshMetadataRecursive(MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
{ {
var children = ActualChildren.ToList(); var children = ActualChildren.ToList();
@ -902,16 +891,16 @@ namespace MediaBrowser.Controller.Entities
{ {
if (query.ItemIds.Length > 0) if (query.ItemIds.Length > 0)
{ {
var specificItems = query.ItemIds.Select(LibraryManager.GetItemById).Where(i => i != null).ToList(); var result = LibraryManager.GetItemsResult(query);
if (query.SortBy.Length == 0) if (query.SortBy.Length == 0)
{ {
var ids = query.ItemIds.ToList(); var ids = query.ItemIds.ToList();
// Try to preserve order // Try to preserve order
specificItems = specificItems.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToList(); result.Items = result.Items.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToArray();
} }
return Task.FromResult(PostFilterAndSort(specificItems, query, true, true)); return Task.FromResult(result);
} }
return GetItemsInternal(query); return GetItemsInternal(query);

View File

@ -135,7 +135,11 @@ namespace MediaBrowser.Controller.Entities.TV
[IgnoreDataMember] [IgnoreDataMember]
public Series Series public Series Series
{ {
get { return FindParent<Series>(); } get
{
var seriesId = SeriesId ?? FindSeriesId();
return seriesId.HasValue ? (LibraryManager.GetItemById(seriesId.Value) as Series) : null;
}
} }
[IgnoreDataMember] [IgnoreDataMember]
@ -143,24 +147,8 @@ namespace MediaBrowser.Controller.Entities.TV
{ {
get get
{ {
var season = FindParent<Season>(); var seasonId = SeasonId ?? FindSeasonId();
return seasonId.HasValue ? (LibraryManager.GetItemById(seasonId.Value) as Season) : null;
// Episodes directly in series folder
if (season == null)
{
var series = Series;
if (series != null && ParentIndexNumber.HasValue)
{
var findNumber = ParentIndexNumber.Value;
season = series.Children
.OfType<Season>()
.FirstOrDefault(i => i.IndexNumber.HasValue && i.IndexNumber.Value == findNumber);
}
}
return season;
} }
} }
@ -193,7 +181,23 @@ namespace MediaBrowser.Controller.Entities.TV
public Guid? FindSeasonId() public Guid? FindSeasonId()
{ {
var season = Season; var season = FindParent<Season>();
// Episodes directly in series folder
if (season == null)
{
var series = Series;
if (series != null && ParentIndexNumber.HasValue)
{
var findNumber = ParentIndexNumber.Value;
season = series.Children
.OfType<Season>()
.FirstOrDefault(i => i.IndexNumber.HasValue && i.IndexNumber.Value == findNumber);
}
}
return season == null ? (Guid?)null : season.Id; return season == null ? (Guid?)null : season.Id;
} }
@ -263,7 +267,7 @@ namespace MediaBrowser.Controller.Entities.TV
public Guid? FindSeriesId() public Guid? FindSeriesId()
{ {
var series = Series; var series = FindParent<Series>();
return series == null ? (Guid?)null : series.Id; return series == null ? (Guid?)null : series.Id;
} }

View File

@ -99,7 +99,11 @@ namespace MediaBrowser.Controller.Entities.TV
[IgnoreDataMember] [IgnoreDataMember]
public Series Series public Series Series
{ {
get { return FindParent<Series>(); } get
{
var seriesId = SeriesId ?? FindSeriesId();
return seriesId.HasValue ? (LibraryManager.GetItemById(seriesId.Value) as Series) : null;
}
} }
[IgnoreDataMember] [IgnoreDataMember]
@ -241,7 +245,7 @@ namespace MediaBrowser.Controller.Entities.TV
public Guid? FindSeriesId() public Guid? FindSeriesId()
{ {
var series = Series; var series = FindParent<Series>();
return series == null ? (Guid?)null : series.Id; return series == null ? (Guid?)null : series.Id;
} }

View File

@ -1,5 +1,7 @@
using MediaBrowser.Model.FileOrganization; using MediaBrowser.Model.Events;
using MediaBrowser.Model.FileOrganization;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -7,6 +9,11 @@ namespace MediaBrowser.Controller.FileOrganization
{ {
public interface IFileOrganizationService public interface IFileOrganizationService
{ {
event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemAdded;
event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemUpdated;
event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemRemoved;
event EventHandler LogReset;
/// <summary> /// <summary>
/// Processes the new files. /// Processes the new files.
/// </summary> /// </summary>
@ -81,5 +88,20 @@ namespace MediaBrowser.Controller.FileOrganization
/// <param name="ItemName">Item name.</param> /// <param name="ItemName">Item name.</param>
/// <param name="matchString">The match string to delete.</param> /// <param name="matchString">The match string to delete.</param>
void DeleteSmartMatchEntry(string ItemName, string matchString); void DeleteSmartMatchEntry(string ItemName, string matchString);
/// <summary>
/// Attempts to add a an item to the list of currently processed items.
/// </summary>
/// <param name="result">The result item.</param>
/// <param name="fullClientRefresh">Passing true will notify the client to reload all items, otherwise only a single item will be refreshed.</param>
/// <returns>True if the item was added, False if the item is already contained in the list.</returns>
bool AddToInProgressList(FileOrganizationResult result, bool fullClientRefresh);
/// <summary>
/// Removes an item from the list of currently processed items.
/// </summary>
/// <param name="result">The result item.</param>
/// <returns>True if the item was removed, False if the item was not contained in the list.</returns>
bool RemoveFromInprogressList(FileOrganizationResult result);
} }
} }

View File

@ -134,5 +134,6 @@ namespace MediaBrowser.Controller.MediaEncoding
Task UpdateEncoderPath(string path, string pathType); Task UpdateEncoderPath(string path, string pathType);
bool SupportsEncoder(string encoder); bool SupportsEncoder(string encoder);
bool IsDefaultEncoderPath { get; }
} }
} }

View File

@ -36,8 +36,13 @@ namespace MediaBrowser.Controller.MediaEncoding
return new[] {videoPath}; return new[] {videoPath};
} }
public static List<string> GetPlayableStreamFiles(IFileSystem fileSystem, string rootPath, IEnumerable<string> filenames) private static List<string> GetPlayableStreamFiles(IFileSystem fileSystem, string rootPath, List<string> filenames)
{ {
if (filenames.Count == 0)
{
return new List<string>();
}
var allFiles = fileSystem var allFiles = fileSystem
.GetFilePaths(rootPath, true) .GetFilePaths(rootPath, true)
.ToList(); .ToList();

View File

@ -724,6 +724,15 @@ namespace MediaBrowser.Controller.Providers
} }
break; break;
} }
case "TvMazeId":
{
var id = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(id))
{
item.SetProviderId(MetadataProviders.TvMaze, id);
}
break;
}
case "AudioDbArtistId": case "AudioDbArtistId":
{ {
var id = reader.ReadElementContentAsString(); var id = reader.ReadElementContentAsString();

View File

@ -172,15 +172,21 @@ namespace MediaBrowser.Controller.Session
Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken); Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Sends the message to user sessions. /// Sends the message to admin sessions.
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="userId">The user identifier.</param>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task SendMessageToUserSessions<T>(string userId, string name, T data, CancellationToken cancellationToken); Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken);
/// <summary>
/// Sends the message to user sessions.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>Task.</returns>
Task SendMessageToUserSessions<T>(List<string> userIds, string name, T data, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Sends the message to user device sessions. /// Sends the message to user device sessions.

View File

@ -479,17 +479,17 @@ namespace MediaBrowser.Dlna.PlayTo
_successiveStopCount++; _successiveStopCount++;
_connectFailureCount++; _connectFailureCount++;
if (_successiveStopCount >= maxSuccessiveStopReturns) if (_connectFailureCount >= 3)
{
RestartTimerInactive();
}
if (_connectFailureCount >= maxSuccessiveStopReturns)
{ {
if (OnDeviceUnavailable != null) if (OnDeviceUnavailable != null)
{ {
OnDeviceUnavailable(); OnDeviceUnavailable();
} }
} }
if (_successiveStopCount >= maxSuccessiveStopReturns)
{
RestartTimerInactive();
}
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -99,11 +99,11 @@ namespace MediaBrowser.Dlna.PlayTo
public void Init(Device device) public void Init(Device device)
{ {
_device = device; _device = device;
_device.OnDeviceUnavailable = OnDeviceUnavailable;
_device.PlaybackStart += _device_PlaybackStart; _device.PlaybackStart += _device_PlaybackStart;
_device.PlaybackProgress += _device_PlaybackProgress; _device.PlaybackProgress += _device_PlaybackProgress;
_device.PlaybackStopped += _device_PlaybackStopped; _device.PlaybackStopped += _device_PlaybackStopped;
_device.MediaChanged += _device_MediaChanged; _device.MediaChanged += _device_MediaChanged;
_device.OnDeviceUnavailable = OnDeviceUnavailable;
_device.Start(); _device.Start();

View File

@ -33,7 +33,6 @@ namespace MediaBrowser.Dlna.Profiles
MaxStreamingBitrate = 20000000; MaxStreamingBitrate = 20000000;
MaxStaticBitrate = 20000000; MaxStaticBitrate = 20000000;
MusicStreamingTranscodingBitrate = 192000; MusicStreamingTranscodingBitrate = 192000;
MusicSyncBitrate = 192000;
EnableAlbumArtInDidl = false; EnableAlbumArtInDidl = false;

View File

@ -11,9 +11,7 @@ namespace MediaBrowser.Dlna.Profiles
Name = "Kodi"; Name = "Kodi";
MaxStreamingBitrate = 100000000; MaxStreamingBitrate = 100000000;
MaxStaticBitrate = 100000000;
MusicStreamingTranscodingBitrate = 1280000; MusicStreamingTranscodingBitrate = 1280000;
MusicSyncBitrate = 1280000;
TimelineOffsetSeconds = 5; TimelineOffsetSeconds = 5;

View File

@ -553,6 +553,13 @@ namespace MediaBrowser.LocalMetadata.Savers
builder.Append("<TVRageId>" + SecurityElement.Escape(externalId) + "</TVRageId>"); builder.Append("<TVRageId>" + SecurityElement.Escape(externalId) + "</TVRageId>");
} }
externalId = item.GetProviderId(MetadataProviders.TvMaze);
if (!string.IsNullOrEmpty(externalId))
{
builder.Append("<TvMazeId>" + SecurityElement.Escape(externalId) + "</TvMazeId>");
}
var hasTagline = item as IHasTaglines; var hasTagline = item as IHasTaglines;
if (hasTagline != null) if (hasTagline != null)
{ {

View File

@ -680,7 +680,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!string.IsNullOrEmpty(state.Options.Profile)) if (!string.IsNullOrEmpty(state.Options.Profile))
{ {
if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase)) if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{ {
// not supported by h264_omx // not supported by h264_omx
param += " -profile:v " + state.Options.Profile; param += " -profile:v " + state.Options.Profile;
@ -737,7 +738,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) && if (!string.Equals(videoCodec, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) !string.Equals(videoCodec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{ {
param = "-pix_fmt yuv420p " + param; param = "-pix_fmt yuv420p " + param;
} }
@ -887,66 +889,96 @@ namespace MediaBrowser.MediaEncoding.Encoder
var filters = new List<string>(); var filters = new List<string>();
if (state.DeInterlace) if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
filters.Add("format=nv12|vaapi");
filters.Add("hwupload");
}
else if (state.DeInterlace && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{ {
filters.Add("yadif=0:-1:0"); filters.Add("yadif=0:-1:0");
} }
// If fixed dimensions were supplied if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
if (request.Width.HasValue && request.Height.HasValue)
{ {
var widthParam = request.Width.Value.ToString(UsCulture); // Work around vaapi's reduced scaling features
var heightParam = request.Height.Value.ToString(UsCulture); var scaler = "scale_vaapi";
filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam)); // Given the input dimensions (inputWidth, inputHeight), determine the output dimensions
} // (outputWidth, outputHeight). The user may request precise output dimensions or maximum
// output dimensions. Output dimensions are guaranteed to be even.
decimal inputWidth = Convert.ToDecimal(state.VideoStream.Width);
decimal inputHeight = Convert.ToDecimal(state.VideoStream.Height);
decimal outputWidth = request.Width.HasValue ? Convert.ToDecimal(request.Width.Value) : inputWidth;
decimal outputHeight = request.Height.HasValue ? Convert.ToDecimal(request.Height.Value) : inputHeight;
decimal maximumWidth = request.MaxWidth.HasValue ? Convert.ToDecimal(request.MaxWidth.Value) : outputWidth;
decimal maximumHeight = request.MaxHeight.HasValue ? Convert.ToDecimal(request.MaxHeight.Value) : outputHeight;
// If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size if (outputWidth > maximumWidth || outputHeight > maximumHeight)
else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
{
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
}
// If a fixed width was requested
else if (request.Width.HasValue)
{
var widthParam = request.Width.Value.ToString(UsCulture);
filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam));
}
// If a fixed height was requested
else if (request.Height.HasValue)
{
var heightParam = request.Height.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
}
// If a max width was requested
else if (request.MaxWidth.HasValue)
{
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
}
// If a max height was requested
else if (request.MaxHeight.HasValue)
{
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam));
}
if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
{
if (filters.Count > 1)
{ {
//filters[filters.Count - 1] += ":flags=fast_bilinear"; var scale = Math.Min(maximumWidth / outputWidth, maximumHeight / outputHeight);
outputWidth = Math.Min(maximumWidth, Math.Truncate(outputWidth * scale));
outputHeight = Math.Min(maximumHeight, Math.Truncate(outputHeight * scale));
}
outputWidth = 2 * Math.Truncate(outputWidth / 2);
outputHeight = 2 * Math.Truncate(outputHeight / 2);
if (outputWidth != inputWidth || outputHeight != inputHeight)
{
filters.Add(string.Format("{0}=w={1}:h={2}", scaler, outputWidth.ToString(UsCulture), outputHeight.ToString(UsCulture)));
}
}
else
{
// If fixed dimensions were supplied
if (request.Width.HasValue && request.Height.HasValue)
{
var widthParam = request.Width.Value.ToString(UsCulture);
var heightParam = request.Height.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc({0}/2)*2:trunc({1}/2)*2", widthParam, heightParam));
}
// If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
else if (request.MaxWidth.HasValue && request.MaxHeight.HasValue)
{
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/2)*2:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", maxWidthParam, maxHeightParam));
}
// If a fixed width was requested
else if (request.Width.HasValue)
{
var widthParam = request.Width.Value.ToString(UsCulture);
filters.Add(string.Format("scale={0}:trunc(ow/a/2)*2", widthParam));
}
// If a fixed height was requested
else if (request.Height.HasValue)
{
var heightParam = request.Height.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
}
// If a max width was requested
else if (request.MaxWidth.HasValue)
{
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(min(max(iw\\,ih*dar)\\,{0})/2)*2:trunc(ow/dar/2)*2", maxWidthParam));
}
// If a max height was requested
else if (request.MaxHeight.HasValue)
{
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(max(iw/dar\\,ih)\\,{0})", maxHeightParam));
} }
} }

View File

@ -586,6 +586,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
{ {
return GetAvailableEncoder(mediaEncoder, "h264_omx", defaultEncoder); return GetAvailableEncoder(mediaEncoder, "h264_omx", defaultEncoder);
} }
if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(options.VaapiDevice))
{
return GetAvailableEncoder(mediaEncoder, "h264_vaapi", defaultEncoder);
}
} }
return defaultEncoder; return defaultEncoder;

View File

@ -123,20 +123,20 @@ namespace MediaBrowser.MediaEncoding.Encoder
return "System"; return "System";
} }
if (IsDefaultPath(FFMpegPath))
{
return "Default";
}
return "Custom"; return "Custom";
} }
} }
private bool IsDefaultPath(string path) public bool IsDefaultEncoderPath
{ {
var parentPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg", "20160410"); get
{
var path = FFMpegPath;
return FileSystem.ContainsSubPath(parentPath, path); var parentPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg", "20160410");
return FileSystem.ContainsSubPath(parentPath, path);
}
} }
private bool IsSystemInstalledPath(string path) private bool IsSystemInstalledPath(string path)

View File

@ -10,6 +10,7 @@ namespace MediaBrowser.Model.Configuration
public int ThrottleDelaySeconds { get; set; } public int ThrottleDelaySeconds { get; set; }
public string HardwareAccelerationType { get; set; } public string HardwareAccelerationType { get; set; }
public string EncoderAppPath { get; set; } public string EncoderAppPath { get; set; }
public string VaapiDevice { get; set; }
public EncodingOptions() public EncodingOptions()
{ {
@ -17,6 +18,7 @@ namespace MediaBrowser.Model.Configuration
EnableThrottling = true; EnableThrottling = true;
ThrottleDelaySeconds = 180; ThrottleDelaySeconds = 180;
EncodingThreadCount = -1; EncodingThreadCount = -1;
VaapiDevice = "/dev/dri/card0";
} }
} }
} }

View File

@ -4,10 +4,13 @@
{ {
public bool EnableArchiveMediaFiles { get; set; } public bool EnableArchiveMediaFiles { get; set; }
public bool EnablePhotos { get; set; } public bool EnablePhotos { get; set; }
public bool EnableRealtimeMonitor { get; set; }
public int SchemaVersion { get; set; }
public LibraryOptions() public LibraryOptions()
{ {
EnablePhotos = true; EnablePhotos = true;
EnableRealtimeMonitor = true;
} }
} }
} }

View File

@ -74,6 +74,8 @@ namespace MediaBrowser.Model.Configuration
/// <value>The metadata path.</value> /// <value>The metadata path.</value>
public string MetadataPath { get; set; } public string MetadataPath { get; set; }
public string LastVersion { get; set; }
/// <summary> /// <summary>
/// Gets or sets the display name of the season zero. /// Gets or sets the display name of the season zero.
/// </summary> /// </summary>

View File

@ -27,8 +27,6 @@ namespace MediaBrowser.Model.Configuration
public bool DisplayMissingEpisodes { get; set; } public bool DisplayMissingEpisodes { get; set; }
public bool DisplayUnairedEpisodes { get; set; } public bool DisplayUnairedEpisodes { get; set; }
public bool GroupMoviesIntoBoxSets { get; set; }
public string[] ExcludeFoldersFromGrouping { get; set; } public string[] ExcludeFoldersFromGrouping { get; set; }
public string[] GroupedFolders { get; set; } public string[] GroupedFolders { get; set; }
@ -48,7 +46,6 @@ namespace MediaBrowser.Model.Configuration
public bool RememberAudioSelections { get; set; } public bool RememberAudioSelections { get; set; }
public bool RememberSubtitleSelections { get; set; } public bool RememberSubtitleSelections { get; set; }
public bool EnableNextEpisodeAutoPlay { get; set; } public bool EnableNextEpisodeAutoPlay { get; set; }
public bool DisplayFoldersView { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="UserConfiguration" /> class. /// Initializes a new instance of the <see cref="UserConfiguration" /> class.

View File

@ -59,7 +59,7 @@ namespace MediaBrowser.Model.Dlna
/// Gets the maximum bitrate. /// Gets the maximum bitrate.
/// </summary> /// </summary>
/// <returns>System.Nullable&lt;System.Int32&gt;.</returns> /// <returns>System.Nullable&lt;System.Int32&gt;.</returns>
public int? GetMaxBitrate() public int? GetMaxBitrate(bool isAudio)
{ {
if (MaxBitrate.HasValue) if (MaxBitrate.HasValue)
{ {
@ -70,6 +70,10 @@ namespace MediaBrowser.Model.Dlna
{ {
if (Context == EncodingContext.Static) if (Context == EncodingContext.Static)
{ {
if (isAudio && Profile.MaxStaticMusicBitrate.HasValue)
{
return Profile.MaxStaticMusicBitrate;
}
return Profile.MaxStaticBitrate; return Profile.MaxStaticBitrate;
} }

View File

@ -55,7 +55,7 @@ namespace MediaBrowser.Model.Dlna
public int? MaxStaticBitrate { get; set; } public int? MaxStaticBitrate { get; set; }
public int? MusicStreamingTranscodingBitrate { get; set; } public int? MusicStreamingTranscodingBitrate { get; set; }
public int? MusicSyncBitrate { get; set; } public int? MaxStaticMusicBitrate { get; set; }
/// <summary> /// <summary>
/// Controls the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace. /// Controls the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace.
@ -115,7 +115,6 @@ namespace MediaBrowser.Model.Dlna
MaxStreamingBitrate = 8000000; MaxStreamingBitrate = 8000000;
MaxStaticBitrate = 8000000; MaxStaticBitrate = 8000000;
MusicStreamingTranscodingBitrate = 128000; MusicStreamingTranscodingBitrate = 128000;
MusicSyncBitrate = 128000;
} }
public List<string> GetSupportedMediaTypes() public List<string> GetSupportedMediaTypes()

View File

@ -55,7 +55,7 @@ namespace MediaBrowser.Model.Dlna
stream.DeviceProfileId = options.Profile.Id; stream.DeviceProfileId = options.Profile.Id;
} }
return GetOptimalStream(streams, options.GetMaxBitrate()); return GetOptimalStream(streams, options.GetMaxBitrate(true));
} }
public StreamInfo BuildVideoItem(VideoOptions options) public StreamInfo BuildVideoItem(VideoOptions options)
@ -88,7 +88,7 @@ namespace MediaBrowser.Model.Dlna
stream.DeviceProfileId = options.Profile.Id; stream.DeviceProfileId = options.Profile.Id;
} }
return GetOptimalStream(streams, options.GetMaxBitrate()); return GetOptimalStream(streams, options.GetMaxBitrate(false));
} }
private StreamInfo GetOptimalStream(List<StreamInfo> streams, int? maxBitrate) private StreamInfo GetOptimalStream(List<StreamInfo> streams, int? maxBitrate)
@ -275,24 +275,32 @@ namespace MediaBrowser.Model.Dlna
playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue); playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue);
} }
int configuredBitrate = options.AudioTranscodingBitrate ?? int transcodingBitrate = options.AudioTranscodingBitrate ??
(options.Context == EncodingContext.Static ? options.Profile.MusicSyncBitrate : options.Profile.MusicStreamingTranscodingBitrate) ?? options.Profile.MusicStreamingTranscodingBitrate ??
128000; 128000;
playlistItem.AudioBitrate = Math.Min(configuredBitrate, playlistItem.AudioBitrate ?? configuredBitrate); int? configuredBitrate = options.GetMaxBitrate(true);
if (configuredBitrate.HasValue)
{
transcodingBitrate = Math.Min(configuredBitrate.Value, transcodingBitrate);
}
playlistItem.AudioBitrate = Math.Min(transcodingBitrate, playlistItem.AudioBitrate ?? transcodingBitrate);
} }
return playlistItem; return playlistItem;
} }
private int? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options) private int? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options, bool isAudio)
{ {
if (item.Protocol == MediaProtocol.File) if (item.Protocol == MediaProtocol.File)
{ {
return options.Profile.MaxStaticBitrate; return options.Profile.MaxStaticBitrate;
} }
return options.GetMaxBitrate(); return options.GetMaxBitrate(isAudio);
} }
private List<PlayMethod> GetAudioDirectPlayMethods(MediaSourceInfo item, MediaStream audioStream, AudioOptions options) private List<PlayMethod> GetAudioDirectPlayMethods(MediaSourceInfo item, MediaStream audioStream, AudioOptions options)
@ -312,7 +320,7 @@ namespace MediaBrowser.Model.Dlna
if (directPlayProfile != null) if (directPlayProfile != null)
{ {
// While options takes the network and other factors into account. Only applies to direct stream // While options takes the network and other factors into account. Only applies to direct stream
if (item.SupportsDirectStream && IsAudioEligibleForDirectPlay(item, options.GetMaxBitrate()) && options.EnableDirectStream) if (item.SupportsDirectStream && IsAudioEligibleForDirectPlay(item, options.GetMaxBitrate(true)) && options.EnableDirectStream)
{ {
playMethods.Add(PlayMethod.DirectStream); playMethods.Add(PlayMethod.DirectStream);
} }
@ -320,7 +328,7 @@ namespace MediaBrowser.Model.Dlna
// The profile describes what the device supports // The profile describes what the device supports
// If device requirements are satisfied then allow both direct stream and direct play // If device requirements are satisfied then allow both direct stream and direct play
if (item.SupportsDirectPlay && if (item.SupportsDirectPlay &&
IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options)) && options.EnableDirectPlay) IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true)) && options.EnableDirectPlay)
{ {
playMethods.Add(PlayMethod.DirectPlay); playMethods.Add(PlayMethod.DirectPlay);
} }
@ -403,8 +411,8 @@ namespace MediaBrowser.Model.Dlna
MediaStream videoStream = item.VideoStream; MediaStream videoStream = item.VideoStream;
// TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough // TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough
bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options), subtitleStream, options, PlayMethod.DirectPlay)); bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true), subtitleStream, options, PlayMethod.DirectPlay));
bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || IsEligibleForDirectPlay(item, options.GetMaxBitrate(), subtitleStream, options, PlayMethod.DirectStream)); bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || IsEligibleForDirectPlay(item, options.GetMaxBitrate(false), subtitleStream, options, PlayMethod.DirectStream));
_logger.Info("Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}", _logger.Info("Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}",
options.Profile.Name ?? "Unknown Profile", options.Profile.Name ?? "Unknown Profile",
@ -469,7 +477,6 @@ namespace MediaBrowser.Model.Dlna
playlistItem.VideoCodec = transcodingProfile.VideoCodec; playlistItem.VideoCodec = transcodingProfile.VideoCodec;
playlistItem.CopyTimestamps = transcodingProfile.CopyTimestamps; playlistItem.CopyTimestamps = transcodingProfile.CopyTimestamps;
playlistItem.ForceLiveStream = transcodingProfile.ForceLiveStream;
playlistItem.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest; playlistItem.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest;
if (!string.IsNullOrEmpty(transcodingProfile.MaxAudioChannels)) if (!string.IsNullOrEmpty(transcodingProfile.MaxAudioChannels))
@ -570,10 +577,10 @@ namespace MediaBrowser.Model.Dlna
playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue); playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue);
} }
int audioBitrate = GetAudioBitrate(playlistItem.SubProtocol, options.GetMaxBitrate(), playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec, audioStream); int audioBitrate = GetAudioBitrate(playlistItem.SubProtocol, options.GetMaxBitrate(false), playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec, audioStream);
playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate); playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate);
int? maxBitrateSetting = options.GetMaxBitrate(); int? maxBitrateSetting = options.GetMaxBitrate(false);
// Honor max rate // Honor max rate
if (maxBitrateSetting.HasValue) if (maxBitrateSetting.HasValue)
{ {
@ -595,19 +602,16 @@ namespace MediaBrowser.Model.Dlna
private int GetAudioBitrate(string subProtocol, int? maxTotalBitrate, int? targetAudioChannels, string targetAudioCodec, MediaStream audioStream) private int GetAudioBitrate(string subProtocol, int? maxTotalBitrate, int? targetAudioChannels, string targetAudioCodec, MediaStream audioStream)
{ {
var defaultBitrate = 128000; var defaultBitrate = audioStream.BitRate ?? 192000;
if (StringHelper.EqualsIgnoreCase(targetAudioCodec, "ac3")) // Reduce the bitrate if we're downmixing
if (targetAudioChannels.HasValue && audioStream != null && audioStream.Channels.HasValue && targetAudioChannels.Value < audioStream.Channels.Value)
{ {
defaultBitrate = 192000; defaultBitrate = StringHelper.EqualsIgnoreCase(targetAudioCodec, "ac3") ? 192000 : 128000;
}
if (!string.IsNullOrEmpty(targetAudioCodec) && audioStream != null && StringHelper.EqualsIgnoreCase(audioStream.Codec, targetAudioCodec))
{
defaultBitrate = audioStream.BitRate ?? defaultBitrate;
} }
if (targetAudioChannels.HasValue) if (targetAudioChannels.HasValue)
{ {
if (targetAudioChannels.Value >= 5 && (maxTotalBitrate ?? 0) >= 1500000) if (targetAudioChannels.Value >= 5 && (maxTotalBitrate ?? 0) >= 1200000)
{ {
if (StringHelper.EqualsIgnoreCase(targetAudioCodec, "ac3")) if (StringHelper.EqualsIgnoreCase(targetAudioCodec, "ac3"))
{ {

View File

@ -36,7 +36,6 @@ namespace MediaBrowser.Model.Dlna
public string VideoProfile { get; set; } public string VideoProfile { get; set; }
public bool CopyTimestamps { get; set; } public bool CopyTimestamps { get; set; }
public bool ForceLiveStream { get; set; }
public bool EnableSubtitlesInManifest { get; set; } public bool EnableSubtitlesInManifest { get; set; }
public string[] AudioCodecs { get; set; } public string[] AudioCodecs { get; set; }
@ -216,7 +215,7 @@ namespace MediaBrowser.Model.Dlna
list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxWidth.Value) : string.Empty)); list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxWidth.Value) : string.Empty));
list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty)); list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty));
if (StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls") && !item.ForceLiveStream) if (StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls"))
{ {
list.Add(new NameValuePair("StartTimeTicks", string.Empty)); list.Add(new NameValuePair("StartTimeTicks", string.Empty));
} }
@ -246,7 +245,6 @@ namespace MediaBrowser.Model.Dlna
} }
list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString().ToLower())); list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString().ToLower()));
list.Add(new NameValuePair("ForceLiveStream", item.ForceLiveStream.ToString().ToLower()));
list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty)); list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty));
list.Add(new NameValuePair("TranscodingMaxAudioChannels", item.TranscodingMaxAudioChannels.HasValue ? StringHelper.ToStringCultureInvariant(item.TranscodingMaxAudioChannels.Value) : string.Empty)); list.Add(new NameValuePair("TranscodingMaxAudioChannels", item.TranscodingMaxAudioChannels.HasValue ? StringHelper.ToStringCultureInvariant(item.TranscodingMaxAudioChannels.Value) : string.Empty));

View File

@ -35,9 +35,6 @@ namespace MediaBrowser.Model.Dlna
[XmlAttribute("context")] [XmlAttribute("context")]
public EncodingContext Context { get; set; } public EncodingContext Context { get; set; }
[XmlAttribute("forceLiveStream")]
public bool ForceLiveStream { get; set; }
[XmlAttribute("enableSubtitlesInManifest")] [XmlAttribute("enableSubtitlesInManifest")]
public bool EnableSubtitlesInManifest { get; set; } public bool EnableSubtitlesInManifest { get; set; }

View File

@ -39,6 +39,7 @@ namespace MediaBrowser.Model.Entities
TvRage = 15, TvRage = 15,
AudioDbArtist = 16, AudioDbArtist = 16,
AudioDbAlbum = 17, AudioDbAlbum = 17,
MusicBrainzTrack = 18 MusicBrainzTrack = 18,
TvMaze = 19
} }
} }

View File

@ -95,6 +95,12 @@ namespace MediaBrowser.Model.FileOrganization
/// <value>The size of the file.</value> /// <value>The size of the file.</value>
public long FileSize { get; set; } public long FileSize { get; set; }
/// <summary>
/// Indicates if the item is currently being processed.
/// </summary>
/// <remarks>Runtime property not persisted to the store.</remarks>
public bool IsInProgress { get; set; }
public FileOrganizationResult() public FileOrganizationResult()
{ {
DuplicatePaths = new List<string>(); DuplicatePaths = new List<string>();

View File

@ -241,7 +241,7 @@ namespace MediaBrowser.Model.Net
} }
if (StringHelper.EqualsIgnoreCase(ext, ".opus")) if (StringHelper.EqualsIgnoreCase(ext, ".opus"))
{ {
return "audio/opus"; return "audio/ogg";
} }
// Playlists // Playlists

View File

@ -27,10 +27,6 @@ namespace MediaBrowser.Model.Querying
/// </summary> /// </summary>
IsFavorite = 5, IsFavorite = 5,
/// <summary> /// <summary>
/// The is recently added
/// </summary>
IsRecentlyAdded = 6,
/// <summary>
/// The item is resumable /// The item is resumable
/// </summary> /// </summary>
IsResumable = 7, IsResumable = 7,

View File

@ -167,10 +167,13 @@ namespace MediaBrowser.Providers.MediaInfo
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
{ {
var file = directoryService.GetFile(item.Path); if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem)
if (file != null && file.LastWriteTimeUtc != item.DateModified)
{ {
return true; var file = directoryService.GetFile(item.Path);
if (file != null && file.LastWriteTimeUtc != item.DateModified)
{
return true;
}
} }
return false; return false;

View File

@ -171,7 +171,7 @@ namespace MediaBrowser.Providers.MediaInfo
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
{ {
if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path)) if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem)
{ {
var file = directoryService.GetFile(item.Path); var file = directoryService.GetFile(item.Path);
if (file != null && file.LastWriteTimeUtc != item.DateModified) if (file != null && file.LastWriteTimeUtc != item.DateModified)

View File

@ -194,7 +194,7 @@ namespace MediaBrowser.Providers.MediaInfo
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
{ {
if (item.EnableRefreshOnDateModifiedChange) if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem)
{ {
var file = directoryService.GetFile(item.Path); var file = directoryService.GetFile(item.Path);
if (file != null && file.LastWriteTimeUtc != item.DateModified) if (file != null && file.LastWriteTimeUtc != item.DateModified)

View File

@ -154,10 +154,13 @@ namespace MediaBrowser.Providers.Photos
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
{ {
var file = directoryService.GetFile(item.Path); if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem)
if (file != null && file.LastWriteTimeUtc != item.DateModified)
{ {
return true; var file = directoryService.GetFile(item.Path);
if (file != null && file.LastWriteTimeUtc != item.DateModified)
{
return true;
}
} }
return false; return false;

View File

@ -54,18 +54,15 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
private async void SendMessage(string name, TimerEventInfo info) private async void SendMessage(string name, TimerEventInfo info)
{ {
var users = _userManager.Users.Where(i => i.Policy.EnableLiveTvAccess).ToList(); var users = _userManager.Users.Where(i => i.Policy.EnableLiveTvAccess).Select(i => i.Id.ToString("N")).ToList();
foreach (var user in users) try
{ {
try await _sessionManager.SendMessageToUserSessions<TimerEventInfo>(users, name, info, CancellationToken.None);
{ }
await _sessionManager.SendMessageToUserSessions<TimerEventInfo>(user.Id.ToString("N"), name, info, CancellationToken.None); catch (Exception ex)
} {
catch (Exception ex) _logger.ErrorException("Error sending message", ex);
{
_logger.ErrorException("Error sending message", ex);
}
} }
} }

View File

@ -11,6 +11,7 @@ using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
using MediaBrowser.Model.Sync; using MediaBrowser.Model.Sync;
using System; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
namespace MediaBrowser.Server.Implementations.EntryPoints namespace MediaBrowser.Server.Implementations.EntryPoints
@ -164,7 +165,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
private async void SendMessageToUserSession<T>(User user, string name, T data) private async void SendMessageToUserSession<T>(User user, string name, T data)
{ {
await _sessionManager.SendMessageToUserSessions(user.Id.ToString("N"), name, data, CancellationToken.None); await _sessionManager.SendMessageToUserSessions(new List<string> { user.Id.ToString("N") }, name, data, CancellationToken.None);
} }
/// <summary> /// <summary>

View File

@ -272,6 +272,18 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
var originalExtractedSeriesString = result.ExtractedName; var originalExtractedSeriesString = result.ExtractedName;
bool isNew = string.IsNullOrWhiteSpace(result.Id);
if (isNew)
{
await _organizationService.SaveResult(result, cancellationToken);
}
if (!_organizationService.AddToInProgressList(result, isNew))
{
throw new Exception("File is currently processed otherwise. Please try again later.");
}
try try
{ {
// Proceed to sort the file // Proceed to sort the file
@ -363,6 +375,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
_logger.Warn(ex.Message); _logger.Warn(ex.Message);
return; return;
} }
finally
{
_organizationService.RemoveFromInprogressList(result);
}
if (rememberCorrection) if (rememberCorrection)
{ {

View File

@ -0,0 +1,68 @@
using MediaBrowser.Controller.FileOrganization;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.FileOrganization;
using MediaBrowser.Controller.Session;
using System.Threading;
namespace MediaBrowser.Server.Implementations.FileOrganization
{
/// <summary>
/// Class SessionInfoWebSocketListener
/// </summary>
class FileOrganizationNotifier : IServerEntryPoint
{
private readonly IFileOrganizationService _organizationService;
private readonly ISessionManager _sessionManager;
public FileOrganizationNotifier(ILogger logger, IFileOrganizationService organizationService, ISessionManager sessionManager)
{
_organizationService = organizationService;
_sessionManager = sessionManager;
}
public void Run()
{
_organizationService.ItemAdded += _organizationService_ItemAdded;
_organizationService.ItemRemoved += _organizationService_ItemRemoved;
_organizationService.ItemUpdated += _organizationService_ItemUpdated;
_organizationService.LogReset += _organizationService_LogReset;
}
private void _organizationService_LogReset(object sender, EventArgs e)
{
_sessionManager.SendMessageToAdminSessions("AutoOrganizeUpdate", (FileOrganizationResult)null, CancellationToken.None);
}
private void _organizationService_ItemUpdated(object sender, GenericEventArgs<FileOrganizationResult> e)
{
_sessionManager.SendMessageToAdminSessions("AutoOrganizeUpdate", e.Argument, CancellationToken.None);
}
private void _organizationService_ItemRemoved(object sender, GenericEventArgs<FileOrganizationResult> e)
{
_sessionManager.SendMessageToAdminSessions("AutoOrganizeUpdate", (FileOrganizationResult)null, CancellationToken.None);
}
private void _organizationService_ItemAdded(object sender, GenericEventArgs<FileOrganizationResult> e)
{
_sessionManager.SendMessageToAdminSessions("AutoOrganizeUpdate", (FileOrganizationResult)null, CancellationToken.None);
}
public void Dispose()
{
_organizationService.ItemAdded -= _organizationService_ItemAdded;
_organizationService.ItemRemoved -= _organizationService_ItemRemoved;
_organizationService.ItemUpdated -= _organizationService_ItemUpdated;
_organizationService.LogReset -= _organizationService_LogReset;
}
}
}

View File

@ -3,16 +3,21 @@ using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.FileOrganization; using MediaBrowser.Controller.FileOrganization;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.FileOrganization; using MediaBrowser.Model.FileOrganization;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using System; using System;
using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommonIO; using CommonIO;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Events;
using MediaBrowser.Common.Events;
namespace MediaBrowser.Server.Implementations.FileOrganization namespace MediaBrowser.Server.Implementations.FileOrganization
{ {
@ -26,6 +31,12 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IProviderManager _providerManager; private readonly IProviderManager _providerManager;
private readonly ConcurrentDictionary<string, bool> _inProgressItemIds = new ConcurrentDictionary<string, bool>();
public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemAdded;
public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemUpdated;
public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemRemoved;
public event EventHandler LogReset;
public FileOrganizationService(ITaskManager taskManager, IFileOrganizationRepository repo, ILogger logger, ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, IServerConfigurationManager config, IFileSystem fileSystem, IProviderManager providerManager) public FileOrganizationService(ITaskManager taskManager, IFileOrganizationRepository repo, ILogger logger, ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, IServerConfigurationManager config, IFileSystem fileSystem, IProviderManager providerManager)
{ {
@ -58,12 +69,26 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
public QueryResult<FileOrganizationResult> GetResults(FileOrganizationResultQuery query) public QueryResult<FileOrganizationResult> GetResults(FileOrganizationResultQuery query)
{ {
return _repo.GetResults(query); var results = _repo.GetResults(query);
foreach (var result in results.Items)
{
result.IsInProgress = _inProgressItemIds.ContainsKey(result.Id);
}
return results;
} }
public FileOrganizationResult GetResult(string id) public FileOrganizationResult GetResult(string id)
{ {
return _repo.GetResult(id); var result = _repo.GetResult(id);
if (result != null)
{
result.IsInProgress = _inProgressItemIds.ContainsKey(result.Id);
}
return result;
} }
public FileOrganizationResult GetResultBySourcePath(string path) public FileOrganizationResult GetResultBySourcePath(string path)
@ -78,11 +103,17 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
return GetResult(id); return GetResult(id);
} }
public Task DeleteOriginalFile(string resultId) public async Task DeleteOriginalFile(string resultId)
{ {
var result = _repo.GetResult(resultId); var result = _repo.GetResult(resultId);
_logger.Info("Requested to delete {0}", result.OriginalPath); _logger.Info("Requested to delete {0}", result.OriginalPath);
if (!AddToInProgressList(result, false))
{
throw new Exception("Path is currently processed otherwise. Please try again later.");
}
try try
{ {
_fileSystem.DeleteFile(result.OriginalPath); _fileSystem.DeleteFile(result.OriginalPath);
@ -91,8 +122,14 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{ {
_logger.ErrorException("Error deleting {0}", ex, result.OriginalPath); _logger.ErrorException("Error deleting {0}", ex, result.OriginalPath);
} }
finally
{
RemoveFromInprogressList(result);
}
return _repo.Delete(resultId); await _repo.Delete(resultId);
EventHelper.FireEventIfNotNull(ItemRemoved, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
} }
private AutoOrganizeOptions GetAutoOrganizeOptions() private AutoOrganizeOptions GetAutoOrganizeOptions()
@ -121,9 +158,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
} }
} }
public Task ClearLog() public async Task ClearLog()
{ {
return _repo.DeleteAll(); await _repo.DeleteAll();
EventHelper.FireEventIfNotNull(LogReset, this, EventArgs.Empty, _logger);
} }
public async Task PerformEpisodeOrganization(EpisodeFileOrganizationRequest request) public async Task PerformEpisodeOrganization(EpisodeFileOrganizationRequest request)
@ -189,5 +227,55 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
_config.SaveAutoOrganizeOptions(options); _config.SaveAutoOrganizeOptions(options);
} }
} }
/// <summary>
/// Attempts to add a an item to the list of currently processed items.
/// </summary>
/// <param name="result">The result item.</param>
/// <param name="isNewItem">Passing true will notify the client to reload all items, otherwise only a single item will be refreshed.</param>
/// <returns>True if the item was added, False if the item is already contained in the list.</returns>
public bool AddToInProgressList(FileOrganizationResult result, bool isNewItem)
{
if (string.IsNullOrWhiteSpace(result.Id))
{
result.Id = result.OriginalPath.GetMD5().ToString("N");
}
if (!_inProgressItemIds.TryAdd(result.Id, false))
{
return false;
}
result.IsInProgress = true;
if (isNewItem)
{
EventHelper.FireEventIfNotNull(ItemAdded, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
}
else
{
EventHelper.FireEventIfNotNull(ItemUpdated, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
}
return true;
}
/// <summary>
/// Removes an item from the list of currently processed items.
/// </summary>
/// <param name="result">The result item.</param>
/// <returns>True if the item was removed, False if the item was not contained in the list.</returns>
public bool RemoveFromInprogressList(FileOrganizationResult result)
{
bool itemValue;
var retval = _inProgressItemIds.TryRemove(result.Id, out itemValue);
result.IsInProgress = false;
EventHelper.FireEventIfNotNull(ItemUpdated, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
return retval;
}
} }
} }

View File

@ -428,8 +428,24 @@ namespace MediaBrowser.Server.Implementations.HttpServer
if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase) || if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase) || string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase) ||
localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1 || localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1)
localPath.IndexOf("dashboard/", StringComparison.OrdinalIgnoreCase) != -1) {
httpRes.StatusCode = 200;
httpRes.ContentType = "text/html";
var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase)
.Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase);
if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
{
httpRes.Write("<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" + newUrl + "\">" + newUrl + "</a></body></html>");
httpRes.Close();
return;
}
}
if (localPath.IndexOf("dashboard/", StringComparison.OrdinalIgnoreCase) != -1 &&
localPath.IndexOf("web/dashboard", StringComparison.OrdinalIgnoreCase) == -1)
{ {
httpRes.StatusCode = 200; httpRes.StatusCode = 200;
httpRes.ContentType = "text/html"; httpRes.ContentType = "text/html";

View File

@ -172,27 +172,29 @@ namespace MediaBrowser.Server.Implementations.IO
} }
} }
public void Start() private bool IsLibraryMonitorEnabaled(BaseItem item)
{ {
if (EnableLibraryMonitor) var options = LibraryManager.GetLibraryOptions(item);
if (options != null && options.SchemaVersion >= 1)
{ {
StartInternal(); return options.EnableRealtimeMonitor;
} }
return EnableLibraryMonitor;
} }
/// <summary> public void Start()
/// Starts this instance.
/// </summary>
private void StartInternal()
{ {
LibraryManager.ItemAdded += LibraryManager_ItemAdded; LibraryManager.ItemAdded += LibraryManager_ItemAdded;
LibraryManager.ItemRemoved += LibraryManager_ItemRemoved; LibraryManager.ItemRemoved += LibraryManager_ItemRemoved;
var pathsToWatch = new List<string> { LibraryManager.RootFolder.Path }; var pathsToWatch = new List<string> { };
var paths = LibraryManager var paths = LibraryManager
.RootFolder .RootFolder
.Children .Children
.Where(IsLibraryMonitorEnabaled)
.OfType<Folder>() .OfType<Folder>()
.SelectMany(f => f.PhysicalLocations) .SelectMany(f => f.PhysicalLocations)
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
@ -213,6 +215,14 @@ namespace MediaBrowser.Server.Implementations.IO
} }
} }
private void StartWatching(BaseItem item)
{
if (IsLibraryMonitorEnabaled(item))
{
StartWatchingPath(item.Path);
}
}
/// <summary> /// <summary>
/// Handles the ItemRemoved event of the LibraryManager control. /// Handles the ItemRemoved event of the LibraryManager control.
/// </summary> /// </summary>
@ -235,7 +245,7 @@ namespace MediaBrowser.Server.Implementations.IO
{ {
if (e.Item.GetParent() is AggregateFolder) if (e.Item.GetParent() is AggregateFolder)
{ {
StartWatchingPath(e.Item.Path); StartWatching(e.Item);
} }
} }
@ -382,14 +392,6 @@ namespace MediaBrowser.Server.Implementations.IO
Logger.ErrorException("Error in Directory watcher for: " + dw.Path, ex); Logger.ErrorException("Error in Directory watcher for: " + dw.Path, ex);
DisposeWatcher(dw); DisposeWatcher(dw);
if (ConfigurationManager.Configuration.EnableLibraryMonitor == AutoOnOff.Auto)
{
Logger.Info("Disabling realtime monitor to prevent future instability");
ConfigurationManager.Configuration.EnableLibraryMonitor = AutoOnOff.Disabled;
Stop();
}
} }
/// <summary> /// <summary>
@ -420,8 +422,8 @@ namespace MediaBrowser.Server.Implementations.IO
var filename = Path.GetFileName(path); var filename = Path.GetFileName(path);
var monitorPath = !string.IsNullOrEmpty(filename) && var monitorPath = !string.IsNullOrEmpty(filename) &&
!_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) && !_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) &&
!_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase); !_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase);
// Ignore certain files // Ignore certain files

View File

@ -203,12 +203,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
/// <returns>Video.</returns> /// <returns>Video.</returns>
protected override Video Resolve(ItemResolveArgs args) protected override Video Resolve(ItemResolveArgs args)
{ {
if (args.Path != null && args.Path.IndexOf("disney", StringComparison.OrdinalIgnoreCase) != -1)
{
var b = true;
var a = b;
}
var collectionType = args.GetCollectionType(); var collectionType = args.GetCollectionType();
if (IsInvalid(args.Parent, collectionType)) if (IsInvalid(args.Parent, collectionType))

View File

@ -149,6 +149,7 @@
<Compile Include="EntryPoints\UsageReporter.cs" /> <Compile Include="EntryPoints\UsageReporter.cs" />
<Compile Include="FileOrganization\EpisodeFileOrganizer.cs" /> <Compile Include="FileOrganization\EpisodeFileOrganizer.cs" />
<Compile Include="FileOrganization\Extensions.cs" /> <Compile Include="FileOrganization\Extensions.cs" />
<Compile Include="FileOrganization\FileOrganizationNotifier.cs" />
<Compile Include="FileOrganization\FileOrganizationService.cs" /> <Compile Include="FileOrganization\FileOrganizationService.cs" />
<Compile Include="FileOrganization\NameUtils.cs" /> <Compile Include="FileOrganization\NameUtils.cs" />
<Compile Include="FileOrganization\TvFolderOrganizer.cs" /> <Compile Include="FileOrganization\TvFolderOrganizer.cs" />
@ -271,6 +272,7 @@
<Compile Include="Sorting\StartDateComparer.cs" /> <Compile Include="Sorting\StartDateComparer.cs" />
<Compile Include="Sync\SyncHelper.cs" /> <Compile Include="Sync\SyncHelper.cs" />
<Compile Include="Sync\SyncJobOptions.cs" /> <Compile Include="Sync\SyncJobOptions.cs" />
<Compile Include="Sync\SyncNotificationEntryPoint.cs" />
<Compile Include="UserViews\CollectionFolderImageProvider.cs" /> <Compile Include="UserViews\CollectionFolderImageProvider.cs" />
<Compile Include="UserViews\DynamicImageProvider.cs" /> <Compile Include="UserViews\DynamicImageProvider.cs" />
<Compile Include="News\NewsEntryPoint.cs" /> <Compile Include="News\NewsEntryPoint.cs" />

View File

@ -123,7 +123,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
{ {
if (extractImages) if (extractImages)
{ {
if (video.VideoType == VideoType.HdDvd || video.VideoType == VideoType.Iso || video.VideoType == VideoType.BluRay) if (video.VideoType == VideoType.HdDvd || video.VideoType == VideoType.Iso || video.VideoType == VideoType.BluRay || video.VideoType == VideoType.Dvd)
{ {
continue; continue;
} }

View File

@ -313,8 +313,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (Folder.IsPathOffline(path)) if (Folder.IsPathOffline(path))
{ {
libraryItem.IsOffline = true; await libraryItem.UpdateIsOffline(true).ConfigureAwait(false);
await libraryItem.UpdateToRepository(ItemUpdateType.None, cancellationToken).ConfigureAwait(false);
continue; continue;
} }

View File

@ -211,7 +211,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
_connection.AddColumn(Logger, "TypedBaseItems", "ProductionYear", "INT"); _connection.AddColumn(Logger, "TypedBaseItems", "ProductionYear", "INT");
_connection.AddColumn(Logger, "TypedBaseItems", "ParentId", "GUID"); _connection.AddColumn(Logger, "TypedBaseItems", "ParentId", "GUID");
_connection.AddColumn(Logger, "TypedBaseItems", "Genres", "Text"); _connection.AddColumn(Logger, "TypedBaseItems", "Genres", "Text");
_connection.AddColumn(Logger, "TypedBaseItems", "ParentalRatingValue", "INT");
_connection.AddColumn(Logger, "TypedBaseItems", "SchemaVersion", "INT"); _connection.AddColumn(Logger, "TypedBaseItems", "SchemaVersion", "INT");
_connection.AddColumn(Logger, "TypedBaseItems", "SortName", "Text"); _connection.AddColumn(Logger, "TypedBaseItems", "SortName", "Text");
_connection.AddColumn(Logger, "TypedBaseItems", "RunTimeTicks", "BIGINT"); _connection.AddColumn(Logger, "TypedBaseItems", "RunTimeTicks", "BIGINT");
@ -488,7 +487,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
"ProductionYear", "ProductionYear",
"ParentId", "ParentId",
"Genres", "Genres",
"ParentalRatingValue",
"InheritedParentalRatingValue", "InheritedParentalRatingValue",
"SchemaVersion", "SchemaVersion",
"SortName", "SortName",
@ -795,7 +793,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
} }
_saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Genres.ToArray()); _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Genres.ToArray());
_saveItemCommand.GetParameter(index++).Value = item.GetParentalRatingValue() ?? 0;
_saveItemCommand.GetParameter(index++).Value = item.GetInheritedParentalRatingValue() ?? 0; _saveItemCommand.GetParameter(index++).Value = item.GetInheritedParentalRatingValue() ?? 0;
_saveItemCommand.GetParameter(index++).Value = LatestSchemaVersion; _saveItemCommand.GetParameter(index++).Value = LatestSchemaVersion;
@ -4286,6 +4283,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
var index = 0; var index = 0;
foreach (var image in images) foreach (var image in images)
{ {
if (string.IsNullOrWhiteSpace(image.Path))
{
// Invalid
continue;
}
_saveImagesCommand.GetParameter(0).Value = itemId; _saveImagesCommand.GetParameter(0).Value = itemId;
_saveImagesCommand.GetParameter(1).Value = image.Type; _saveImagesCommand.GetParameter(1).Value = image.Type;
_saveImagesCommand.GetParameter(2).Value = image.Path; _saveImagesCommand.GetParameter(2).Value = image.Path;

View File

@ -1869,10 +1869,17 @@ namespace MediaBrowser.Server.Implementations.Session
return GetSessionByAuthenticationToken(info, deviceId, remoteEndpoint, null); return GetSessionByAuthenticationToken(info, deviceId, remoteEndpoint, null);
} }
public Task SendMessageToUserSessions<T>(string userId, string name, T data, public Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken)
{
var adminUserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id.ToString("N")).ToList();
return SendMessageToUserSessions(adminUserIds, name, data, cancellationToken);
}
public Task SendMessageToUserSessions<T>(List<string> userIds, string name, T data,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null && i.ContainsUser(userId)).ToList(); var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null && userIds.Any(i.ContainsUser)).ToList();
var tasks = sessions.Select(session => Task.Run(async () => var tasks = sessions.Select(session => Task.Run(async () =>
{ {

View File

@ -85,6 +85,11 @@ namespace MediaBrowser.Server.Implementations.Sync
{ {
Name = "Low", Name = "Low",
Id = "low" Id = "low"
},
new SyncQualityOption
{
Name = "Custom",
Id = "custom"
} }
}; };
} }

View File

@ -0,0 +1,48 @@
using System.Threading;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Sync;
namespace MediaBrowser.Server.Implementations.Sync
{
public class SyncNotificationEntryPoint : IServerEntryPoint
{
private readonly ISessionManager _sessionManager;
private readonly ISyncManager _syncManager;
public SyncNotificationEntryPoint(ISyncManager syncManager, ISessionManager sessionManager)
{
_syncManager = syncManager;
_sessionManager = sessionManager;
}
public void Run()
{
_syncManager.SyncJobItemUpdated += _syncManager_SyncJobItemUpdated;
}
private async void _syncManager_SyncJobItemUpdated(object sender, GenericEventArgs<SyncJobItem> e)
{
var item = e.Argument;
if (item.Status == SyncJobItemStatus.ReadyToTransfer)
{
try
{
await _sessionManager.SendMessageToUserDeviceSessions(item.TargetId, "SyncJobItemReady", item, CancellationToken.None).ConfigureAwait(false);
}
catch
{
}
}
}
public void Dispose()
{
_syncManager.SyncJobItemUpdated -= _syncManager_SyncJobItemUpdated;
}
}
}

View File

@ -390,6 +390,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\autoorganizetv.html"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\autoorganizetv.html">
<Link>Resources\dashboard-ui\autoorganizetv.html</Link> <Link>Resources\dashboard-ui\autoorganizetv.html</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\camerauploadsettings.html">
<Link>Resources\dashboard-ui\camerauploadsettings.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\channelitems.html"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\channelitems.html">
<Link>Resources\dashboard-ui\channelitems.html</Link> <Link>Resources\dashboard-ui\channelitems.html</Link>
</BundleResource> </BundleResource>
@ -1128,9 +1131,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\multidownload.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\multidownload.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\multidownload.js</Link> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\multidownload.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\objectassign.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\objectassign.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playmenu.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playmenu.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playmenu.js</Link> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playmenu.js</Link>
</BundleResource> </BundleResource>
@ -1155,6 +1155,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\shortcuts.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\shortcuts.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\shortcuts.js</Link> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\shortcuts.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\thememediaplayer.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\thememediaplayer.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\visibleinviewport.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\visibleinviewport.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\visibleinviewport.js</Link> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\visibleinviewport.js</Link>
</BundleResource> </BundleResource>
@ -1566,6 +1569,15 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playlisteditor\playlisteditor.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playlisteditor\playlisteditor.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playlisteditor\playlisteditor.js</Link> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playlisteditor\playlisteditor.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\polyfills\array.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\polyfills\array.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\polyfills\bind.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\polyfills\bind.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\polyfills\objectassign.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\polyfills\objectassign.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\prompt\nativeprompt.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\prompt\nativeprompt.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\prompt\nativeprompt.js</Link> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\prompt\nativeprompt.js</Link>
</BundleResource> </BundleResource>
@ -1773,6 +1785,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\subtitleeditor\subtitleeditor.template.html"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\subtitleeditor\subtitleeditor.template.html">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\subtitleeditor\subtitleeditor.template.html</Link> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\subtitleeditor\subtitleeditor.template.html</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\sync\sync.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\sync\sync.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\toast\toast.css"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\toast\toast.css">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\toast\toast.css</Link> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\toast\toast.css</Link>
</BundleResource> </BundleResource>
@ -1860,36 +1875,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fetch\fetch.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fetch\fetch.js">
<Link>Resources\dashboard-ui\bower_components\fetch\fetch.js</Link> <Link>Resources\dashboard-ui\bower_components\fetch\fetch.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\.bower.json">
<Link>Resources\dashboard-ui\bower_components\fingerprintjs2\.bower.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\CONTRIBUTING.md">
<Link>Resources\dashboard-ui\bower_components\fingerprintjs2\CONTRIBUTING.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\FAQ.md">
<Link>Resources\dashboard-ui\bower_components\fingerprintjs2\FAQ.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\README.md">
<Link>Resources\dashboard-ui\bower_components\fingerprintjs2\README.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\bower.json">
<Link>Resources\dashboard-ui\bower_components\fingerprintjs2\bower.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\fingerprint2.js">
<Link>Resources\dashboard-ui\bower_components\fingerprintjs2\fingerprint2.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\gulpfile.js">
<Link>Resources\dashboard-ui\bower_components\fingerprintjs2\gulpfile.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\index.html">
<Link>Resources\dashboard-ui\bower_components\fingerprintjs2\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\package.json">
<Link>Resources\dashboard-ui\bower_components\fingerprintjs2\package.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fingerprintjs2\dist\fingerprint2.min.js">
<Link>Resources\dashboard-ui\bower_components\fingerprintjs2\dist\fingerprint2.min.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\font-roboto\.bower.json"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\font-roboto\.bower.json">
<Link>Resources\dashboard-ui\bower_components\font-roboto\.bower.json</Link> <Link>Resources\dashboard-ui\bower_components\font-roboto\.bower.json</Link>
</BundleResource> </BundleResource>
@ -2094,45 +2079,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-keys-behavior\test\index.html"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-keys-behavior\test\index.html">
<Link>Resources\dashboard-ui\bower_components\iron-a11y-keys-behavior\test\index.html</Link> <Link>Resources\dashboard-ui\bower_components\iron-a11y-keys-behavior\test\index.html</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\.bower.json">
<Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\.bower.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\.gitignore">
<Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\.gitignore</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\.travis.yml">
<Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\.travis.yml</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\CONTRIBUTING.md">
<Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\CONTRIBUTING.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\README.md">
<Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\README.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\bower.json">
<Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\bower.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\hero.svg">
<Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\hero.svg</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\index.html">
<Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\iron-autogrow-textarea.html">
<Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\iron-autogrow-textarea.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\.github\ISSUE_TEMPLATE.md">
<Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\.github\ISSUE_TEMPLATE.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\demo\index.html">
<Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\demo\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\test\basic.html">
<Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\test\basic.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\test\index.html">
<Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\test\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\.bower.json"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\.bower.json">
<Link>Resources\dashboard-ui\bower_components\iron-behaviors\.bower.json</Link> <Link>Resources\dashboard-ui\bower_components\iron-behaviors\.bower.json</Link>
</BundleResource> </BundleResource>
@ -2502,51 +2448,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-iconset-svg\test\iron-iconset-svg.html"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-iconset-svg\test\iron-iconset-svg.html">
<Link>Resources\dashboard-ui\bower_components\iron-iconset-svg\test\iron-iconset-svg.html</Link> <Link>Resources\dashboard-ui\bower_components\iron-iconset-svg\test\iron-iconset-svg.html</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\.bower.json">
<Link>Resources\dashboard-ui\bower_components\iron-input\.bower.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\.gitignore">
<Link>Resources\dashboard-ui\bower_components\iron-input\.gitignore</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\.travis.yml">
<Link>Resources\dashboard-ui\bower_components\iron-input\.travis.yml</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\CONTRIBUTING.md">
<Link>Resources\dashboard-ui\bower_components\iron-input\CONTRIBUTING.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\README.md">
<Link>Resources\dashboard-ui\bower_components\iron-input\README.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\bower.json">
<Link>Resources\dashboard-ui\bower_components\iron-input\bower.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\hero.svg">
<Link>Resources\dashboard-ui\bower_components\iron-input\hero.svg</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\index.html">
<Link>Resources\dashboard-ui\bower_components\iron-input\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\iron-input.html">
<Link>Resources\dashboard-ui\bower_components\iron-input\iron-input.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\.github\ISSUE_TEMPLATE.md">
<Link>Resources\dashboard-ui\bower_components\iron-input\.github\ISSUE_TEMPLATE.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\demo\index.html">
<Link>Resources\dashboard-ui\bower_components\iron-input\demo\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\test\disabled-input.html">
<Link>Resources\dashboard-ui\bower_components\iron-input\test\disabled-input.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\test\index.html">
<Link>Resources\dashboard-ui\bower_components\iron-input\test\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\test\iron-input.html">
<Link>Resources\dashboard-ui\bower_components\iron-input\test\iron-input.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\test\letters-only.html">
<Link>Resources\dashboard-ui\bower_components\iron-input\test\letters-only.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\.bower.json"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\.bower.json">
<Link>Resources\dashboard-ui\bower_components\iron-meta\.bower.json</Link> <Link>Resources\dashboard-ui\bower_components\iron-meta\.bower.json</Link>
</BundleResource> </BundleResource>
@ -2574,6 +2475,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\iron-meta.html"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\iron-meta.html">
<Link>Resources\dashboard-ui\bower_components\iron-meta\iron-meta.html</Link> <Link>Resources\dashboard-ui\bower_components\iron-meta\iron-meta.html</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\.github\ISSUE_TEMPLATE.md">
<Link>Resources\dashboard-ui\bower_components\iron-meta\.github\ISSUE_TEMPLATE.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\demo\index.html"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\demo\index.html">
<Link>Resources\dashboard-ui\bower_components\iron-meta\demo\index.html</Link> <Link>Resources\dashboard-ui\bower_components\iron-meta\demo\index.html</Link>
</BundleResource> </BundleResource>
@ -2586,45 +2490,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\test\iron-meta.html"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\test\iron-meta.html">
<Link>Resources\dashboard-ui\bower_components\iron-meta\test\iron-meta.html</Link> <Link>Resources\dashboard-ui\bower_components\iron-meta\test\iron-meta.html</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\.bower.json">
<Link>Resources\dashboard-ui\bower_components\iron-range-behavior\.bower.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\.gitignore">
<Link>Resources\dashboard-ui\bower_components\iron-range-behavior\.gitignore</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\.travis.yml">
<Link>Resources\dashboard-ui\bower_components\iron-range-behavior\.travis.yml</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\CONTRIBUTING.md">
<Link>Resources\dashboard-ui\bower_components\iron-range-behavior\CONTRIBUTING.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\README.md">
<Link>Resources\dashboard-ui\bower_components\iron-range-behavior\README.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\bower.json">
<Link>Resources\dashboard-ui\bower_components\iron-range-behavior\bower.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\index.html">
<Link>Resources\dashboard-ui\bower_components\iron-range-behavior\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\iron-range-behavior.html">
<Link>Resources\dashboard-ui\bower_components\iron-range-behavior\iron-range-behavior.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\.github\ISSUE_TEMPLATE.md">
<Link>Resources\dashboard-ui\bower_components\iron-range-behavior\.github\ISSUE_TEMPLATE.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\demo\index.html">
<Link>Resources\dashboard-ui\bower_components\iron-range-behavior\demo\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\test\basic.html">
<Link>Resources\dashboard-ui\bower_components\iron-range-behavior\test\basic.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\test\index.html">
<Link>Resources\dashboard-ui\bower_components\iron-range-behavior\test\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\test\x-progressbar.html">
<Link>Resources\dashboard-ui\bower_components\iron-range-behavior\test\x-progressbar.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-validatable-behavior\.bower.json"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-validatable-behavior\.bower.json">
<Link>Resources\dashboard-ui\bower_components\iron-validatable-behavior\.bower.json</Link> <Link>Resources\dashboard-ui\bower_components\iron-validatable-behavior\.bower.json</Link>
</BundleResource> </BundleResource>
@ -3252,126 +3117,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-icon-button\test\index.html"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-icon-button\test\index.html">
<Link>Resources\dashboard-ui\bower_components\paper-icon-button\test\index.html</Link> <Link>Resources\dashboard-ui\bower_components\paper-icon-button\test\index.html</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\.bower.json">
<Link>Resources\dashboard-ui\bower_components\paper-input\.bower.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\.gitignore">
<Link>Resources\dashboard-ui\bower_components\paper-input\.gitignore</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\.travis.yml">
<Link>Resources\dashboard-ui\bower_components\paper-input\.travis.yml</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\CONTRIBUTING.md">
<Link>Resources\dashboard-ui\bower_components\paper-input\CONTRIBUTING.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\README.md">
<Link>Resources\dashboard-ui\bower_components\paper-input\README.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\all-imports.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\all-imports.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\bower.json">
<Link>Resources\dashboard-ui\bower_components\paper-input\bower.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\hero.svg">
<Link>Resources\dashboard-ui\bower_components\paper-input\hero.svg</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\index.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input-addon-behavior.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\paper-input-addon-behavior.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input-behavior.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\paper-input-behavior.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input-char-counter.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\paper-input-char-counter.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input-container.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\paper-input-container.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input-error.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\paper-input-error.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\paper-input.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-textarea.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\paper-textarea.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\.github\ISSUE_TEMPLATE.md">
<Link>Resources\dashboard-ui\bower_components\paper-input\.github\ISSUE_TEMPLATE.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\demo\index.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\demo\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\demo\ssn-input.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\demo\ssn-input.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\demo\ssn-validator.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\demo\ssn-validator.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\index.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\test\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\letters-only.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\test\letters-only.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\paper-input-char-counter.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\test\paper-input-char-counter.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\paper-input-container.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\test\paper-input-container.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\paper-input-error.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\test\paper-input-error.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\paper-input.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\test\paper-input.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\paper-textarea.html">
<Link>Resources\dashboard-ui\bower_components\paper-input\test\paper-textarea.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\.bower.json">
<Link>Resources\dashboard-ui\bower_components\paper-progress\.bower.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\.gitignore">
<Link>Resources\dashboard-ui\bower_components\paper-progress\.gitignore</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\.travis.yml">
<Link>Resources\dashboard-ui\bower_components\paper-progress\.travis.yml</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\CONTRIBUTING.md">
<Link>Resources\dashboard-ui\bower_components\paper-progress\CONTRIBUTING.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\README.md">
<Link>Resources\dashboard-ui\bower_components\paper-progress\README.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\bower.json">
<Link>Resources\dashboard-ui\bower_components\paper-progress\bower.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\hero.svg">
<Link>Resources\dashboard-ui\bower_components\paper-progress\hero.svg</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\index.html">
<Link>Resources\dashboard-ui\bower_components\paper-progress\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\paper-progress.html">
<Link>Resources\dashboard-ui\bower_components\paper-progress\paper-progress.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\.github\ISSUE_TEMPLATE.md">
<Link>Resources\dashboard-ui\bower_components\paper-progress\.github\ISSUE_TEMPLATE.md</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\demo\index.html">
<Link>Resources\dashboard-ui\bower_components\paper-progress\demo\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\test\basic.html">
<Link>Resources\dashboard-ui\bower_components\paper-progress\test\basic.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\test\index.html">
<Link>Resources\dashboard-ui\bower_components\paper-progress\test\index.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-ripple\.bower.json"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-ripple\.bower.json">
<Link>Resources\dashboard-ui\bower_components\paper-ripple\.bower.json</Link> <Link>Resources\dashboard-ui\bower_components\paper-ripple\.bower.json</Link>
</BundleResource> </BundleResource>
@ -3729,6 +3474,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\tvproviders\xmltv.template.html"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\tvproviders\xmltv.template.html">
<Link>Resources\dashboard-ui\components\tvproviders\xmltv.template.html</Link> <Link>Resources\dashboard-ui\components\tvproviders\xmltv.template.html</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\autoorganizetable.css">
<Link>Resources\dashboard-ui\css\autoorganizetable.css</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\chromecast.css"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\chromecast.css">
<Link>Resources\dashboard-ui\css\chromecast.css</Link> <Link>Resources\dashboard-ui\css\chromecast.css</Link>
</BundleResource> </BundleResource>
@ -3801,6 +3549,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\rotten.png"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\rotten.png">
<Link>Resources\dashboard-ui\css\images\rotten.png</Link> <Link>Resources\dashboard-ui\css\images\rotten.png</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\throbber.gif">
<Link>Resources\dashboard-ui\css\images\throbber.gif</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\userflyoutdefault.png"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\userflyoutdefault.png">
<Link>Resources\dashboard-ui\css\images\userflyoutdefault.png</Link> <Link>Resources\dashboard-ui\css\images\userflyoutdefault.png</Link>
</BundleResource> </BundleResource>
@ -3987,6 +3738,48 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\userdata\password.png"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\userdata\password.png">
<Link>Resources\dashboard-ui\css\images\userdata\password.png</Link> <Link>Resources\dashboard-ui\css\images\userdata\password.png</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\aboutpage.js">
<Link>Resources\dashboard-ui\dashboard\aboutpage.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\autoorganizelog.js">
<Link>Resources\dashboard-ui\dashboard\autoorganizelog.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\autoorganizesmart.js">
<Link>Resources\dashboard-ui\dashboard\autoorganizesmart.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\autoorganizetv.js">
<Link>Resources\dashboard-ui\dashboard\autoorganizetv.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\cinemamodeconfiguration.js">
<Link>Resources\dashboard-ui\dashboard\cinemamodeconfiguration.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\dashboardgeneral.js">
<Link>Resources\dashboard-ui\dashboard\dashboardgeneral.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\dashboardhosting.js">
<Link>Resources\dashboard-ui\dashboard\dashboardhosting.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\devicesupload.js">
<Link>Resources\dashboard-ui\dashboard\devicesupload.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\librarydisplay.js">
<Link>Resources\dashboard-ui\dashboard\librarydisplay.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\librarysettings.js">
<Link>Resources\dashboard-ui\dashboard\librarysettings.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\livetvtunerprovider-satip.js">
<Link>Resources\dashboard-ui\dashboard\livetvtunerprovider-satip.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\logpage.js">
<Link>Resources\dashboard-ui\dashboard\logpage.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\wizardcomponents.js">
<Link>Resources\dashboard-ui\dashboard\wizardcomponents.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\wizardfinishpage.js">
<Link>Resources\dashboard-ui\dashboard\wizardfinishpage.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\devices\android\android.css"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\devices\android\android.css">
<Link>Resources\dashboard-ui\devices\android\android.css</Link> <Link>Resources\dashboard-ui\devices\android\android.css</Link>
</BundleResource> </BundleResource>
@ -4008,9 +3801,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\legacy\selectmenu.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\legacy\selectmenu.js">
<Link>Resources\dashboard-ui\legacy\selectmenu.js</Link> <Link>Resources\dashboard-ui\legacy\selectmenu.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\aboutpage.js">
<Link>Resources\dashboard-ui\scripts\aboutpage.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\addpluginpage.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\addpluginpage.js">
<Link>Resources\dashboard-ui\scripts\addpluginpage.js</Link> <Link>Resources\dashboard-ui\scripts\addpluginpage.js</Link>
</BundleResource> </BundleResource>
@ -4020,14 +3810,8 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\autobackdrops.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\autobackdrops.js">
<Link>Resources\dashboard-ui\scripts\autobackdrops.js</Link> <Link>Resources\dashboard-ui\scripts\autobackdrops.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\autoorganizelog.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\camerauploadsettings.js">
<Link>Resources\dashboard-ui\scripts\autoorganizelog.js</Link> <Link>Resources\dashboard-ui\scripts\camerauploadsettings.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\autoorganizesmart.js">
<Link>Resources\dashboard-ui\scripts\autoorganizesmart.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\autoorganizetv.js">
<Link>Resources\dashboard-ui\scripts\autoorganizetv.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\channelitems.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\channelitems.js">
<Link>Resources\dashboard-ui\scripts\channelitems.js</Link> <Link>Resources\dashboard-ui\scripts\channelitems.js</Link>
@ -4041,18 +3825,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\chromecast.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\chromecast.js">
<Link>Resources\dashboard-ui\scripts\chromecast.js</Link> <Link>Resources\dashboard-ui\scripts\chromecast.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\cinemamodeconfiguration.js">
<Link>Resources\dashboard-ui\scripts\cinemamodeconfiguration.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\connectlogin.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\connectlogin.js">
<Link>Resources\dashboard-ui\scripts\connectlogin.js</Link> <Link>Resources\dashboard-ui\scripts\connectlogin.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\dashboardgeneral.js">
<Link>Resources\dashboard-ui\scripts\dashboardgeneral.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\dashboardhosting.js">
<Link>Resources\dashboard-ui\scripts\dashboardhosting.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\dashboardpage.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\dashboardpage.js">
<Link>Resources\dashboard-ui\scripts\dashboardpage.js</Link> <Link>Resources\dashboard-ui\scripts\dashboardpage.js</Link>
</BundleResource> </BundleResource>
@ -4062,9 +3837,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\devices.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\devices.js">
<Link>Resources\dashboard-ui\scripts\devices.js</Link> <Link>Resources\dashboard-ui\scripts\devices.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\devicesupload.js">
<Link>Resources\dashboard-ui\scripts\devicesupload.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\dlnaprofile.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\dlnaprofile.js">
<Link>Resources\dashboard-ui\scripts\dlnaprofile.js</Link> <Link>Resources\dashboard-ui\scripts\dlnaprofile.js</Link>
</BundleResource> </BundleResource>
@ -4140,18 +3912,12 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarybrowser.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarybrowser.js">
<Link>Resources\dashboard-ui\scripts\librarybrowser.js</Link> <Link>Resources\dashboard-ui\scripts\librarybrowser.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarydisplay.js">
<Link>Resources\dashboard-ui\scripts\librarydisplay.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarymenu.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarymenu.js">
<Link>Resources\dashboard-ui\scripts\librarymenu.js</Link> <Link>Resources\dashboard-ui\scripts\librarymenu.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarypathmapping.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarypathmapping.js">
<Link>Resources\dashboard-ui\scripts\librarypathmapping.js</Link> <Link>Resources\dashboard-ui\scripts\librarypathmapping.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarysettings.js">
<Link>Resources\dashboard-ui\scripts\librarysettings.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvchannel.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvchannel.js">
<Link>Resources\dashboard-ui\scripts\livetvchannel.js</Link> <Link>Resources\dashboard-ui\scripts\livetvchannel.js</Link>
</BundleResource> </BundleResource>
@ -4197,18 +3963,12 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvtunerprovider-m3u.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvtunerprovider-m3u.js">
<Link>Resources\dashboard-ui\scripts\livetvtunerprovider-m3u.js</Link> <Link>Resources\dashboard-ui\scripts\livetvtunerprovider-m3u.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvtunerprovider-satip.js">
<Link>Resources\dashboard-ui\scripts\livetvtunerprovider-satip.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\localsync.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\localsync.js">
<Link>Resources\dashboard-ui\scripts\localsync.js</Link> <Link>Resources\dashboard-ui\scripts\localsync.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\loginpage.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\loginpage.js">
<Link>Resources\dashboard-ui\scripts\loginpage.js</Link> <Link>Resources\dashboard-ui\scripts\loginpage.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\logpage.js">
<Link>Resources\dashboard-ui\scripts\logpage.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mediacontroller.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mediacontroller.js">
<Link>Resources\dashboard-ui\scripts\mediacontroller.js</Link> <Link>Resources\dashboard-ui\scripts\mediacontroller.js</Link>
</BundleResource> </BundleResource>
@ -4371,9 +4131,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\supporterkeypage.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\supporterkeypage.js">
<Link>Resources\dashboard-ui\scripts\supporterkeypage.js</Link> <Link>Resources\dashboard-ui\scripts\supporterkeypage.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\sync.js">
<Link>Resources\dashboard-ui\scripts\sync.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\syncactivity.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\syncactivity.js">
<Link>Resources\dashboard-ui\scripts\syncactivity.js</Link> <Link>Resources\dashboard-ui\scripts\syncactivity.js</Link>
</BundleResource> </BundleResource>
@ -4386,9 +4143,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\taskbutton.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\taskbutton.js">
<Link>Resources\dashboard-ui\scripts\taskbutton.js</Link> <Link>Resources\dashboard-ui\scripts\taskbutton.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\thememediaplayer.js">
<Link>Resources\dashboard-ui\scripts\thememediaplayer.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\tvgenres.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\tvgenres.js">
<Link>Resources\dashboard-ui\scripts\tvgenres.js</Link> <Link>Resources\dashboard-ui\scripts\tvgenres.js</Link>
</BundleResource> </BundleResource>
@ -4431,15 +4185,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardagreement.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardagreement.js">
<Link>Resources\dashboard-ui\scripts\wizardagreement.js</Link> <Link>Resources\dashboard-ui\scripts\wizardagreement.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardcomponents.js">
<Link>Resources\dashboard-ui\scripts\wizardcomponents.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardcontroller.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardcontroller.js">
<Link>Resources\dashboard-ui\scripts\wizardcontroller.js</Link> <Link>Resources\dashboard-ui\scripts\wizardcontroller.js</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardfinishpage.js">
<Link>Resources\dashboard-ui\scripts\wizardfinishpage.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardlivetvguide.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardlivetvguide.js">
<Link>Resources\dashboard-ui\scripts\wizardlivetvguide.js</Link> <Link>Resources\dashboard-ui\scripts\wizardlivetvguide.js</Link>
</BundleResource> </BundleResource>

View File

@ -132,7 +132,7 @@ namespace MediaBrowser.Server.Mono.Native
{ {
get get
{ {
return Environment.OperatingSystem != Startup.Common.OperatingSystem.Osx; return Environment.OperatingSystem != Startup.Common.OperatingSystem.Osx;
} }
} }
@ -187,7 +187,7 @@ namespace MediaBrowser.Server.Mono.Native
{ {
info.SystemArchitecture = Architecture.X64; info.SystemArchitecture = Architecture.X64;
} }
else else
{ {
info.SystemArchitecture = Architecture.X86; info.SystemArchitecture = Architecture.X86;
} }
@ -273,32 +273,11 @@ namespace MediaBrowser.Server.Mono.Native
break; break;
} }
info.DownloadUrls = GetDownloadUrls(environment); // No version available - user requirement
info.DownloadUrls = new string[] { };
return info; return info;
} }
private static string[] GetDownloadUrls(NativeEnvironment environment)
{
switch (environment.OperatingSystem)
{
case OperatingSystem.Linux:
switch (environment.SystemArchitecture)
{
case Architecture.X64:
return new[]
{
"https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z"
};
}
break;
}
// No version available
return new string[] { };
}
} }
public class NullPowerManagement : IPowerManagement public class NullPowerManagement : IPowerManagement

View File

@ -385,8 +385,7 @@ namespace MediaBrowser.Server.Startup.Common
new OmdbEpisodeProviderMigration(ServerConfigurationManager), new OmdbEpisodeProviderMigration(ServerConfigurationManager),
new MovieDbEpisodeProviderMigration(ServerConfigurationManager), new MovieDbEpisodeProviderMigration(ServerConfigurationManager),
new DbMigration(ServerConfigurationManager, TaskManager), new DbMigration(ServerConfigurationManager, TaskManager),
new FolderViewSettingMigration(ServerConfigurationManager, UserManager), new UpdateLevelMigration(ServerConfigurationManager, this, HttpClient, JsonSerializer, _releaseAssetFilename),
new CollectionGroupingMigration(ServerConfigurationManager, UserManager),
new CollectionsViewMigration(ServerConfigurationManager, UserManager) new CollectionsViewMigration(ServerConfigurationManager, UserManager)
}; };

View File

@ -70,13 +70,12 @@
<Compile Include="FFMpeg\FFMpegInfo.cs" /> <Compile Include="FFMpeg\FFMpegInfo.cs" />
<Compile Include="INativeApp.cs" /> <Compile Include="INativeApp.cs" />
<Compile Include="MbLinkShortcutHandler.cs" /> <Compile Include="MbLinkShortcutHandler.cs" />
<Compile Include="Migrations\CollectionGroupingMigration.cs" />
<Compile Include="Migrations\CollectionsViewMigration.cs" /> <Compile Include="Migrations\CollectionsViewMigration.cs" />
<Compile Include="Migrations\FolderViewSettingMigration.cs" />
<Compile Include="Migrations\IVersionMigration.cs" /> <Compile Include="Migrations\IVersionMigration.cs" />
<Compile Include="Migrations\DbMigration.cs" /> <Compile Include="Migrations\DbMigration.cs" />
<Compile Include="Migrations\MovieDbEpisodeProviderMigration.cs" /> <Compile Include="Migrations\MovieDbEpisodeProviderMigration.cs" />
<Compile Include="Migrations\OmdbEpisodeProviderMigration.cs" /> <Compile Include="Migrations\OmdbEpisodeProviderMigration.cs" />
<Compile Include="Migrations\UpdateLevelMigration.cs" />
<Compile Include="NativeEnvironment.cs" /> <Compile Include="NativeEnvironment.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="StartupOptions.cs" /> <Compile Include="StartupOptions.cs" />

View File

@ -1,40 +0,0 @@
using System.Linq;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
namespace MediaBrowser.Server.Startup.Common.Migrations
{
public class CollectionGroupingMigration : IVersionMigration
{
private readonly IServerConfigurationManager _config;
private readonly IUserManager _userManager;
public CollectionGroupingMigration(IServerConfigurationManager config, IUserManager userManager)
{
_config = config;
_userManager = userManager;
}
public void Run()
{
var migrationKey = this.GetType().Name;
var migrationKeyList = _config.Configuration.Migrations.ToList();
if (!migrationKeyList.Contains(migrationKey))
{
if (_config.Configuration.IsStartupWizardCompleted)
{
if (_userManager.Users.Any(i => i.Configuration.GroupMoviesIntoBoxSets))
{
_config.Configuration.EnableGroupingIntoCollections = true;
}
}
migrationKeyList.Add(migrationKey);
_config.Configuration.Migrations = migrationKeyList.ToArray();
_config.SaveConfiguration();
}
}
}
}

View File

@ -1,40 +0,0 @@
using System.Linq;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
namespace MediaBrowser.Server.Startup.Common.Migrations
{
public class FolderViewSettingMigration : IVersionMigration
{
private readonly IServerConfigurationManager _config;
private readonly IUserManager _userManager;
public FolderViewSettingMigration(IServerConfigurationManager config, IUserManager userManager)
{
_config = config;
_userManager = userManager;
}
public void Run()
{
var migrationKey = this.GetType().Name;
var migrationKeyList = _config.Configuration.Migrations.ToList();
if (!migrationKeyList.Contains(migrationKey))
{
if (_config.Configuration.IsStartupWizardCompleted)
{
if (_userManager.Users.Any(i => i.Configuration.DisplayFoldersView))
{
_config.Configuration.EnableFolderView = true;
}
}
migrationKeyList.Add(migrationKey);
_config.Configuration.Migrations = migrationKeyList.ToArray();
_config.SaveConfiguration();
}
}
}
}

View File

@ -0,0 +1,97 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Implementations.Updates;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates;
namespace MediaBrowser.Server.Startup.Common.Migrations
{
public class UpdateLevelMigration : IVersionMigration
{
private readonly IServerConfigurationManager _config;
private readonly IServerApplicationHost _appHost;
private readonly IHttpClient _httpClient;
private readonly IJsonSerializer _jsonSerializer;
private readonly string _releaseAssetFilename;
public UpdateLevelMigration(IServerConfigurationManager config, IServerApplicationHost appHost, IHttpClient httpClient, IJsonSerializer jsonSerializer, string releaseAssetFilename)
{
_config = config;
_appHost = appHost;
_httpClient = httpClient;
_jsonSerializer = jsonSerializer;
_releaseAssetFilename = releaseAssetFilename;
}
public async void Run()
{
var lastVersion = _config.Configuration.LastVersion;
var currentVersion = _appHost.ApplicationVersion;
if (string.Equals(lastVersion, currentVersion.ToString(), StringComparison.OrdinalIgnoreCase))
{
return;
}
try
{
var updateLevel = _config.Configuration.SystemUpdateLevel;
// Go down a level
if (updateLevel == PackageVersionClass.Release)
{
updateLevel = PackageVersionClass.Beta;
}
else if (updateLevel == PackageVersionClass.Beta)
{
updateLevel = PackageVersionClass.Dev;
}
else if (updateLevel == PackageVersionClass.Dev)
{
// It's already dev, there's nothing to check
return;
}
await CheckVersion(currentVersion, updateLevel, CancellationToken.None).ConfigureAwait(false);
}
catch
{
}
}
private async Task CheckVersion(Version currentVersion, PackageVersionClass updateLevel, CancellationToken cancellationToken)
{
var result = await new GithubUpdater(_httpClient, _jsonSerializer, TimeSpan.FromMinutes(5))
.CheckForUpdateResult("MediaBrowser", "Emby", currentVersion, PackageVersionClass.Beta, _releaseAssetFilename, "MBServer", "Mbserver.zip",
cancellationToken).ConfigureAwait(false);
if (result != null && result.IsUpdateAvailable)
{
_config.Configuration.SystemUpdateLevel = updateLevel;
_config.SaveConfiguration();
return;
}
// Go down a level
if (updateLevel == PackageVersionClass.Release)
{
updateLevel = PackageVersionClass.Beta;
}
else if (updateLevel == PackageVersionClass.Beta)
{
updateLevel = PackageVersionClass.Dev;
}
else
{
return;
}
await CheckVersion(currentVersion, updateLevel, cancellationToken).ConfigureAwait(false);
}
}
}

View File

@ -157,11 +157,21 @@ namespace MediaBrowser.WebDashboard.Api
var creator = GetPackageCreator(); var creator = GetPackageCreator();
var directory = creator.DashboardUIPath; var directory = creator.DashboardUIPath;
var skipExtensions = GetUndeployedExtensions(); var skipExtensions = GetDeployIgnoreExtensions();
var skipNames = GetDeployIgnoreFilenames();
return return
Directory.GetFiles(directory, "*", SearchOption.AllDirectories) Directory.GetFiles(directory, "*", SearchOption.AllDirectories)
.Where(i => !skipExtensions.Contains(Path.GetExtension(i) ?? string.Empty, StringComparer.OrdinalIgnoreCase)) .Where(i => !skipExtensions.Contains(Path.GetExtension(i) ?? string.Empty, StringComparer.OrdinalIgnoreCase))
.Where(i => !skipNames.Any(s =>
{
if (s.Item2)
{
return string.Equals(s.Item1, Path.GetFileName(i), StringComparison.OrdinalIgnoreCase);
}
return (Path.GetFileName(i) ?? string.Empty).IndexOf(s.Item1, StringComparison.OrdinalIgnoreCase) != -1;
}))
.Select(i => i.Replace(directory, string.Empty, StringComparison.OrdinalIgnoreCase).Replace("\\", "/").TrimStart('/') + "?v=" + _appHost.ApplicationVersion.ToString()) .Select(i => i.Replace(directory, string.Empty, StringComparison.OrdinalIgnoreCase).Replace("\\", "/").TrimStart('/') + "?v=" + _appHost.ApplicationVersion.ToString())
.ToList(); .ToList();
} }
@ -300,7 +310,7 @@ namespace MediaBrowser.WebDashboard.Api
return new PackageCreator(_fileSystem, _localization, Logger, _serverConfigurationManager, _jsonSerializer); return new PackageCreator(_fileSystem, _localization, Logger, _serverConfigurationManager, _jsonSerializer);
} }
private List<string> GetUndeployedExtensions() private List<string> GetDeployIgnoreExtensions()
{ {
var list = new List<string>(); var list = new List<string>();
@ -315,6 +325,28 @@ namespace MediaBrowser.WebDashboard.Api
return list; return list;
} }
private List<Tuple<string,bool>> GetDeployIgnoreFilenames()
{
var list = new List<Tuple<string, bool>>();
list.Add(new Tuple<string, bool>("copying", true));
list.Add(new Tuple<string, bool>("license", true));
list.Add(new Tuple<string, bool>("license-mit", true));
list.Add(new Tuple<string, bool>("gitignore", false));
list.Add(new Tuple<string, bool>("npmignore", false));
list.Add(new Tuple<string, bool>("jshintrc", false));
list.Add(new Tuple<string, bool>("gruntfile", false));
list.Add(new Tuple<string, bool>("bowerrc", false));
list.Add(new Tuple<string, bool>("jscsrc", false));
list.Add(new Tuple<string, bool>("hero.svg", false));
list.Add(new Tuple<string, bool>("travis.yml", false));
list.Add(new Tuple<string, bool>("build.js", false));
list.Add(new Tuple<string, bool>("editorconfig", false));
list.Add(new Tuple<string, bool>("gitattributes", false));
return list;
}
public async Task<object> Get(GetDashboardPackage request) public async Task<object> Get(GetDashboardPackage request)
{ {
var path = Path.Combine(_serverConfigurationManager.ApplicationPaths.ProgramDataPath, var path = Path.Combine(_serverConfigurationManager.ApplicationPaths.ProgramDataPath,
@ -344,30 +376,12 @@ namespace MediaBrowser.WebDashboard.Api
// Try to trim the output size a bit // Try to trim the output size a bit
var bowerPath = Path.Combine(path, "bower_components"); var bowerPath = Path.Combine(path, "bower_components");
if (!string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase)) GetDeployIgnoreExtensions().ForEach(i => DeleteFilesByExtension(bowerPath, i));
{
//var versionedBowerPath = Path.Combine(Path.GetDirectoryName(bowerPath), "bower_components" + _appHost.ApplicationVersion);
//Directory.Move(bowerPath, versionedBowerPath);
//bowerPath = versionedBowerPath;
}
GetUndeployedExtensions().ForEach(i => DeleteFilesByExtension(bowerPath, i));
DeleteFilesByExtension(bowerPath, ".json", "strings\\"); DeleteFilesByExtension(bowerPath, ".json", "strings\\");
DeleteFilesByName(bowerPath, "copying", true);
DeleteFilesByName(bowerPath, "license", true); GetDeployIgnoreFilenames().ForEach(i => DeleteFilesByName(bowerPath, i.Item1, i.Item2));
DeleteFilesByName(bowerPath, "license-mit", true);
DeleteFilesByName(bowerPath, "gitignore");
DeleteFilesByName(bowerPath, "npmignore");
DeleteFilesByName(bowerPath, "jshintrc");
DeleteFilesByName(bowerPath, "gruntfile");
DeleteFilesByName(bowerPath, "bowerrc");
DeleteFilesByName(bowerPath, "jscsrc");
DeleteFilesByName(bowerPath, "hero.svg");
DeleteFilesByName(bowerPath, "travis.yml");
DeleteFilesByName(bowerPath, "build.js");
DeleteFilesByName(bowerPath, "editorconfig");
DeleteFilesByName(bowerPath, "gitattributes");
DeleteFoldersByName(bowerPath, "demo"); DeleteFoldersByName(bowerPath, "demo");
DeleteFoldersByName(bowerPath, "test"); DeleteFoldersByName(bowerPath, "test");
DeleteFoldersByName(bowerPath, "guides"); DeleteFoldersByName(bowerPath, "guides");
@ -382,8 +396,6 @@ namespace MediaBrowser.WebDashboard.Api
} }
_fileSystem.DeleteDirectory(Path.Combine(bowerPath, "jquery", "src"), true); _fileSystem.DeleteDirectory(Path.Combine(bowerPath, "jquery", "src"), true);
//_fileSystem.DeleteDirectory(Path.Combine(bowerPath, "fingerprintjs2", "flash"), true);
//_fileSystem.DeleteDirectory(Path.Combine(bowerPath, "fingerprintjs2", "specs"), true);
DeleteCryptoFiles(Path.Combine(bowerPath, "cryptojslib", "components")); DeleteCryptoFiles(Path.Combine(bowerPath, "cryptojslib", "components"));

View File

@ -101,6 +101,9 @@
<Content Include="dashboard-ui\autoorganizesmart.html"> <Content Include="dashboard-ui\autoorganizesmart.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\camerauploadsettings.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\components\appfooter\appfooter.css"> <Content Include="dashboard-ui\components\appfooter\appfooter.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@ -182,6 +185,12 @@
<Content Include="dashboard-ui\components\tvproviders\xmltv.template.html"> <Content Include="dashboard-ui\components\tvproviders\xmltv.template.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\css\images\throbber.gif">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\camerauploadsettings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\userpasswordpage.js"> <Content Include="dashboard-ui\scripts\userpasswordpage.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@ -191,6 +200,9 @@
<Content Include="dashboard-ui\css\dashboard.css"> <Content Include="dashboard-ui\css\dashboard.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\css\autoorganizetable.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\css\images\logo.png"> <Content Include="dashboard-ui\css\images\logo.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@ -320,7 +332,7 @@
<Content Include="dashboard-ui\scripts\homeupcoming.js"> <Content Include="dashboard-ui\scripts\homeupcoming.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\librarydisplay.js"> <Content Include="dashboard-ui\dashboard\librarydisplay.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\livetvguideprovider.js"> <Content Include="dashboard-ui\scripts\livetvguideprovider.js">
@ -332,7 +344,7 @@
<Content Include="dashboard-ui\scripts\livetvtunerprovider-m3u.js"> <Content Include="dashboard-ui\scripts\livetvtunerprovider-m3u.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\livetvtunerprovider-satip.js"> <Content Include="dashboard-ui\dashboard\livetvtunerprovider-satip.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\localsync.js"> <Content Include="dashboard-ui\scripts\localsync.js">
@ -350,7 +362,7 @@
<Content Include="dashboard-ui\scripts\mysyncsettings.js"> <Content Include="dashboard-ui\scripts\mysyncsettings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\autoorganizesmart.js"> <Content Include="dashboard-ui\dashboard\autoorganizesmart.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\searchpage.js"> <Content Include="dashboard-ui\scripts\searchpage.js">
@ -371,7 +383,7 @@
<Content Include="dashboard-ui\scripts\tvlatest.js"> <Content Include="dashboard-ui\scripts\tvlatest.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\wizardcomponents.js"> <Content Include="dashboard-ui\dashboard\wizardcomponents.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\wizardcontroller.js"> <Content Include="dashboard-ui\scripts\wizardcontroller.js">
@ -497,7 +509,7 @@
<Content Include="dashboard-ui\photos.html"> <Content Include="dashboard-ui\photos.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\dashboardhosting.js"> <Content Include="dashboard-ui\dashboard\dashboardhosting.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\forgotpassword.js"> <Content Include="dashboard-ui\scripts\forgotpassword.js">
@ -857,13 +869,13 @@
<Content Include="dashboard-ui\scripts\chromecast.js"> <Content Include="dashboard-ui\scripts\chromecast.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\cinemamodeconfiguration.js"> <Content Include="dashboard-ui\dashboard\cinemamodeconfiguration.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\connectlogin.js"> <Content Include="dashboard-ui\scripts\connectlogin.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\dashboardgeneral.js"> <Content Include="dashboard-ui\dashboard\dashboardgeneral.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\syncactivity.js"> <Content Include="dashboard-ui\scripts\syncactivity.js">
@ -875,7 +887,7 @@
<Content Include="dashboard-ui\scripts\devices.js"> <Content Include="dashboard-ui\scripts\devices.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\devicesupload.js"> <Content Include="dashboard-ui\dashboard\devicesupload.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\dlnaprofile.js"> <Content Include="dashboard-ui\scripts\dlnaprofile.js">
@ -890,10 +902,10 @@
<Content Include="dashboard-ui\scripts\encodingsettings.js"> <Content Include="dashboard-ui\scripts\encodingsettings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\autoorganizetv.js"> <Content Include="dashboard-ui\dashboard\autoorganizetv.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\autoorganizelog.js"> <Content Include="dashboard-ui\dashboard\autoorganizelog.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\externalplayer.js"> <Content Include="dashboard-ui\scripts\externalplayer.js">
@ -989,12 +1001,6 @@
<Content Include="dashboard-ui\scripts\livetvsuggested.js"> <Content Include="dashboard-ui\scripts\livetvsuggested.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\sync.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\thememediaplayer.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\tvupcoming.js"> <Content Include="dashboard-ui\scripts\tvupcoming.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@ -1086,7 +1092,7 @@
<Content Include="dashboard-ui\scripts\edititemmetadata.js"> <Content Include="dashboard-ui\scripts\edititemmetadata.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\librarysettings.js"> <Content Include="dashboard-ui\dashboard\librarysettings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\musicrecommended.js"> <Content Include="dashboard-ui\scripts\musicrecommended.js">
@ -1353,7 +1359,7 @@
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="dashboard-ui\scripts\logpage.js"> <Content Include="dashboard-ui\dashboard\logpage.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
@ -1426,7 +1432,7 @@
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="dashboard-ui\scripts\aboutpage.js"> <Content Include="dashboard-ui\dashboard\aboutpage.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\css\images\supporter\supporterflag.png"> <Content Include="dashboard-ui\css\images\supporter\supporterflag.png">
@ -1438,7 +1444,7 @@
<Content Include="dashboard-ui\itemlist.html"> <Content Include="dashboard-ui\itemlist.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\scripts\wizardfinishpage.js"> <Content Include="dashboard-ui\dashboard\wizardfinishpage.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\css\images\items\detail\video.png"> <Content Include="dashboard-ui\css\images\items\detail\video.png">

View File

@ -827,6 +827,15 @@ namespace MediaBrowser.XbmcMetadata.Parsers
} }
break; break;
} }
case "tvmazeid":
{
var id = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(id))
{
item.SetProviderId(MetadataProviders.TvMaze, id);
}
break;
}
case "audiodbartistid": case "audiodbartistid":
{ {
var id = reader.ReadElementContentAsString(); var id = reader.ReadElementContentAsString();

View File

@ -822,6 +822,12 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("tvrageid", externalId); writer.WriteElementString("tvrageid", externalId);
} }
externalId = item.GetProviderId(MetadataProviders.TvMaze);
if (!string.IsNullOrEmpty(externalId))
{
writer.WriteElementString("tvmazeid", externalId);
}
if (options.SaveImagePathsInNfo) if (options.SaveImagePathsInNfo)
{ {
AddImages(item, writer, libraryManager, config); AddImages(item, writer, libraryManager, config);

View File

@ -118,7 +118,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
"airsbefore_season", "airsbefore_season",
"DVD_episodenumber", "DVD_episodenumber",
"DVD_season", "DVD_season",
"absolute_number" "absolute_number",
"displayseason",
"displayepisode"
}; };
return list; return list;

View File

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

View File

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

View File

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