diff options
| author | Luke <luke.pulverenti@gmail.com> | 2017-01-26 15:40:15 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-01-26 15:40:15 -0500 |
| commit | 1cd5e208713a057680b915c9136e0d4493611ed3 (patch) | |
| tree | 59e258ecd246dfb253c06cade5f0092af9874774 | |
| parent | 0d0e4ad695f3657d7ca3eef1ed54fe8608a490a1 (diff) | |
| parent | 9ebf9162ab68fbf61a1dd5e7801eb9abe985762c (diff) | |
Merge pull request #2425 from MediaBrowser/beta
Beta
177 files changed, 2154 insertions, 1382 deletions
diff --git a/Emby.Common.Implementations/BaseApplicationHost.cs b/Emby.Common.Implementations/BaseApplicationHost.cs index 9c9e14ec6..87b97863b 100644 --- a/Emby.Common.Implementations/BaseApplicationHost.cs +++ b/Emby.Common.Implementations/BaseApplicationHost.cs @@ -161,12 +161,6 @@ namespace Emby.Common.Implementations /// <value>The name.</value> public abstract string Name { get; } - /// <summary> - /// Gets a value indicating whether this instance is running as service. - /// </summary> - /// <value><c>true</c> if this instance is running as service; otherwise, <c>false</c>.</value> - public abstract bool IsRunningAsService { get; } - protected ICryptoProvider CryptographyProvider = new CryptographyProvider(); protected IEnvironmentInfo EnvironmentInfo { get; private set; } diff --git a/Emby.Common.Implementations/IO/ManagedFileSystem.cs b/Emby.Common.Implementations/IO/ManagedFileSystem.cs index 78070a5d9..3fe20f659 100644 --- a/Emby.Common.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Common.Implementations/IO/ManagedFileSystem.cs @@ -20,10 +20,13 @@ namespace Emby.Common.Implementations.IO private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>(); private bool EnableFileSystemRequestConcat = true; - public ManagedFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars, bool enableFileSystemRequestConcat) + private string _tempPath; + + public ManagedFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars, bool enableFileSystemRequestConcat, string tempPath) { Logger = logger; _supportsAsyncFileStreams = supportsAsyncFileStreams; + _tempPath = tempPath; EnableFileSystemRequestConcat = enableFileSystemRequestConcat; SetInvalidFileNameChars(enableManagedInvalidFileNameChars); } @@ -487,18 +490,37 @@ namespace Emby.Common.Implementations.IO throw new ArgumentNullException("file2"); } - var temp1 = Path.GetTempFileName(); + var temp1 = Path.Combine(_tempPath, Guid.NewGuid().ToString("N")); // Copying over will fail against hidden files SetHidden(file1, false); SetHidden(file2, false); + Directory.CreateDirectory(_tempPath); CopyFile(file1, temp1, true); CopyFile(file2, file1, true); CopyFile(temp1, file2, true); } + public bool AreEqual(string path1, string path2) + { + if (path1 == null && path2 == null) + { + return true; + } + + if (path1 == null || path2 == null) + { + return false; + } + + path1 = path1.TrimEnd(DirectorySeparatorChar); + path2 = path2.TrimEnd(DirectorySeparatorChar); + + return string.Equals(path1, path2, StringComparison.OrdinalIgnoreCase); + } + public bool ContainsSubPath(string parentPath, string path) { if (string.IsNullOrEmpty(parentPath)) @@ -656,21 +678,7 @@ namespace Emby.Common.Implementations.IO private IEnumerable<FileSystemMetadata> ToMetadata(string parentPath, IEnumerable<FileSystemInfo> infos) { - return infos.Select(i => - { - try - { - return GetFileSystemMetadata(i); - } - catch (PathTooLongException) - { - // Can't log using the FullName because it will throw the PathTooLongExceptiona again - //Logger.Warn("Path too long: {0}", i.FullName); - Logger.Warn("File or directory path too long. Parent folder: {0}", parentPath); - return null; - } - - }).Where(i => i != null); + return infos.Select(GetFileSystemMetadata); } public string[] ReadAllLines(string path) diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 5718cbd09..2251a8f58 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -260,7 +260,7 @@ namespace Emby.Dlna.ContentDirectory { totalCount = 1; - if (item.IsFolder || serverItem.StubType.HasValue) + if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue) { var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount).ConfigureAwait(false)); @@ -285,7 +285,7 @@ namespace Emby.Dlna.ContentDirectory var childItem = i.Item; var displayStubType = i.StubType; - if (childItem.IsFolder || displayStubType.HasValue) + if (childItem.IsDisplayedAsFolder || displayStubType.HasValue) { var childCount = (await GetUserItems(childItem, displayStubType, user, sortCriteria, null, 0).ConfigureAwait(false)) .TotalRecordCount; @@ -381,7 +381,7 @@ namespace Emby.Dlna.ContentDirectory foreach (var i in childrenResult.Items) { - if (i.IsFolder) + if (i.IsDisplayedAsFolder) { var childCount = (await GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0).ConfigureAwait(false)) .TotalRecordCount; diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index 3e47362f6..ca4c3b912 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -176,6 +176,18 @@ namespace Emby.Dlna.Didl return new NullLogger(); } + private string GetMimeType(string input) + { + var mime = MimeTypes.GetMimeType(input); + + if (string.Equals(mime, "video/mp2t", StringComparison.OrdinalIgnoreCase)) + { + mime = "video/mpeg"; + } + + return mime; + } + private void AddVideoResource(DlnaOptions options, XmlWriter writer, IHasMediaSources video, string deviceId, Filter filter, StreamInfo streamInfo = null) { if (streamInfo == null) @@ -360,7 +372,7 @@ namespace Emby.Dlna.Didl var filename = url.Substring(0, url.IndexOf('?')); var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType) - ? MimeTypes.GetMimeType(filename) + ? GetMimeType(filename) : mediaProfile.MimeType; writer.WriteAttributeString("protocolInfo", String.Format( @@ -481,7 +493,7 @@ namespace Emby.Dlna.Didl var filename = url.Substring(0, url.IndexOf('?')); var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType) - ? MimeTypes.GetMimeType(filename) + ? GetMimeType(filename) : mediaProfile.MimeType; var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container, @@ -674,7 +686,7 @@ namespace Emby.Dlna.Didl writer.WriteStartElement("upnp", "class", NS_UPNP); - if (item.IsFolder || stubType.HasValue) + if (item.IsDisplayedAsFolder || stubType.HasValue) { string classType = null; @@ -760,7 +772,7 @@ namespace Emby.Dlna.Didl // Seeing some LG models locking up due content with large lists of people // The actual issue might just be due to processing a more metadata than it can handle - var limit = 10; + var limit = 6; foreach (var actor in people) { @@ -1007,7 +1019,7 @@ namespace Emby.Dlna.Didl writer.WriteAttributeString("protocolInfo", String.Format( "http-get:*:{0}:{1}", - MimeTypes.GetMimeType("file." + format), + GetMimeType("file." + format), contentFeatures )); diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 4daaa5046..020038422 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -595,6 +595,7 @@ namespace Emby.Dlna new LinksysDMA2100Profile(), new LgTvProfile(), new Foobar2000Profile(), + new SharpSmartTvProfile(), new MediaMonkeyProfile(), //new Windows81Profile(), //new WindowsMediaCenterProfile(), diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index c83aaecab..a8c05a527 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -92,6 +92,7 @@ <Compile Include="Profiles\PanasonicVieraProfile.cs" /> <Compile Include="Profiles\PopcornHourProfile.cs" /> <Compile Include="Profiles\SamsungSmartTvProfile.cs" /> + <Compile Include="Profiles\SharpSmartTvProfile.cs" /> <Compile Include="Profiles\SonyBlurayPlayer2013.cs" /> <Compile Include="Profiles\SonyBlurayPlayer2014.cs" /> <Compile Include="Profiles\SonyBlurayPlayer2015.cs" /> @@ -148,7 +149,9 @@ <Name>RSSDP</Name> </ProjectReference> </ItemGroup> - <ItemGroup /> + <ItemGroup> + <EmbeddedResource Include="Profiles\Xml\Sharp Smart TV.xml" /> + </ItemGroup> <ItemGroup> <EmbeddedResource Include="Profiles\Xml\BubbleUPnp.xml" /> <EmbeddedResource Include="Profiles\Xml\Default.xml" /> diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs index f6be47989..e22298010 100644 --- a/Emby.Dlna/PlayTo/Device.cs +++ b/Emby.Dlna/PlayTo/Device.cs @@ -112,7 +112,7 @@ namespace Emby.Dlna.PlayTo private int GetInactiveTimerIntervalMs() { - return 20000; + return 30000; } public void Start() @@ -440,6 +440,11 @@ namespace Emby.Dlna.PlayTo { var transportState = await GetTransportInfo().ConfigureAwait(false); + if (_disposed) + { + return; + } + DateLastActivity = DateTime.UtcNow; if (transportState.HasValue) @@ -530,6 +535,11 @@ namespace Emby.Dlna.PlayTo private async Task GetVolume() { + if (_disposed) + { + return; + } + var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume"); if (command == null) return; @@ -563,6 +573,11 @@ namespace Emby.Dlna.PlayTo private async Task GetMute() { + if (_disposed) + { + return; + } + var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMute"); if (command == null) return; @@ -793,6 +808,11 @@ namespace Emby.Dlna.PlayTo private async Task GetAVProtocolAsync() { + if (_disposed) + { + return; + } + var avService = GetAvTransportService(); if (avService == null) return; @@ -807,6 +827,11 @@ namespace Emby.Dlna.PlayTo private async Task GetRenderingProtocolAsync() { + if (_disposed) + { + return; + } + var avService = GetServiceRenderingControl(); if (avService == null) diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index 4ad62216e..3c07e95db 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -149,6 +149,11 @@ namespace Emby.Dlna.PlayTo async void _device_MediaChanged(object sender, MediaChangedEventArgs e) { + if (_disposed) + { + return; + } + try { var streamInfo = await StreamParams.ParseFromUrl(e.OldMediaInfo.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false); @@ -176,6 +181,11 @@ namespace Emby.Dlna.PlayTo async void _device_PlaybackStopped(object sender, PlaybackStoppedEventArgs e) { + if (_disposed) + { + return; + } + try { var streamInfo = await StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager) @@ -239,6 +249,11 @@ namespace Emby.Dlna.PlayTo async void _device_PlaybackStart(object sender, PlaybackStartEventArgs e) { + if (_disposed) + { + return; + } + try { var info = await StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false); @@ -258,6 +273,11 @@ namespace Emby.Dlna.PlayTo async void _device_PlaybackProgress(object sender, PlaybackProgressEventArgs e) { + if (_disposed) + { + return; + } + try { var info = await StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false); diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index a93f7da14..962a982c1 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -43,6 +43,7 @@ namespace Emby.Dlna.PlayTo private readonly List<string> _nonRendererUrls = new List<string>(); private DateTime _lastRendererClear; + private bool _disposed; public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, ITimerFactory timerFactory) { @@ -70,6 +71,11 @@ namespace Emby.Dlna.PlayTo async void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e) { + if (_disposed) + { + return; + } + var info = e.Argument; string usn; @@ -121,6 +127,11 @@ namespace Emby.Dlna.PlayTo } } + if (_disposed) + { + return; + } + _logger.Debug("Logging session activity from location {0}", location); var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, _appHost.ApplicationVersion.ToString(), device.Properties.UUID, device.Properties.Name, uri.OriginalString, null) .ConfigureAwait(false); @@ -129,6 +140,11 @@ namespace Emby.Dlna.PlayTo if (controller == null) { + if (_disposed) + { + return; + } + string serverAddress; if (info.LocalIpAddress == null) { @@ -187,6 +203,11 @@ namespace Emby.Dlna.PlayTo catch (Exception ex) { _logger.ErrorException("Error creating PlayTo device.", ex); + + lock (_nonRendererUrls) + { + _nonRendererUrls.Add(location); + } } } @@ -202,6 +223,7 @@ namespace Emby.Dlna.PlayTo public void Dispose() { + _disposed = true; _deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered; } } diff --git a/Emby.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs index ff30f2e15..c670f268e 100644 --- a/Emby.Dlna/Profiles/DefaultProfile.cs +++ b/Emby.Dlna/Profiles/DefaultProfile.cs @@ -64,7 +64,7 @@ namespace Emby.Dlna.Profiles { new DirectPlayProfile { - Container = "m4v,ts,mkv,avi,mpg,mpeg,mp4", + Container = "m4v,ts,mpegts,mkv,avi,mpg,mpeg,mp4,mov", VideoCodec = "h264", AudioCodec = "aac,mp3,ac3", Type = DlnaProfileType.Video @@ -72,7 +72,7 @@ namespace Emby.Dlna.Profiles new DirectPlayProfile { - Container = "mp3,wma,aac,wav", + Container = "mp3,wma,aac,wav,flac", Type = DlnaProfileType.Audio } }; diff --git a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs index d494a7bfc..89e0697c1 100644 --- a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs +++ b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs @@ -23,7 +23,7 @@ namespace Emby.Dlna.Profiles { Match = HeaderMatchType.Substring, Name = "User-Agent", - Value ="XiP" + Value ="Zip_" } } }; @@ -63,22 +63,7 @@ namespace Emby.Dlna.Profiles new DirectPlayProfile { - Container = "mp3", - AudioCodec = "mp3", - Type = DlnaProfileType.Audio - }, - - new DirectPlayProfile - { - Container = "alac", - AudioCodec = "alac", - Type = DlnaProfileType.Audio - }, - - new DirectPlayProfile - { - Container = "flac", - AudioCodec = "flac", + Container = "mp3,alac,flac", Type = DlnaProfileType.Audio }, diff --git a/Emby.Dlna/Profiles/LgTvProfile.cs b/Emby.Dlna/Profiles/LgTvProfile.cs index f7cf7b9a1..71f684ec4 100644 --- a/Emby.Dlna/Profiles/LgTvProfile.cs +++ b/Emby.Dlna/Profiles/LgTvProfile.cs @@ -67,7 +67,7 @@ namespace Emby.Dlna.Profiles }, new DirectPlayProfile { - Container = "mp4", + Container = "mp4,m4v", VideoCodec = "h264,mpeg4", AudioCodec = "aac,ac3,mp3,dca,dts", Type = DlnaProfileType.Video @@ -203,7 +203,15 @@ namespace Emby.Dlna.Profiles } }; - ResponseProfiles = new ResponseProfile[] { }; + ResponseProfiles = new ResponseProfile[] + { + new ResponseProfile + { + Container = "m4v", + Type = DlnaProfileType.Video, + MimeType = "video/mp4" + } + }; } } } diff --git a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs index 4a4ecdc58..2b31ab55f 100644 --- a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs +++ b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs @@ -26,12 +26,20 @@ namespace Emby.Dlna.Profiles new DirectPlayProfile { - Container = "avi,mp4,mkv,ts", + Container = "avi,mp4,mkv,ts,m4v", Type = DlnaProfileType.Video } }; - ResponseProfiles = new ResponseProfile[] { }; + ResponseProfiles = new ResponseProfile[] + { + new ResponseProfile + { + Container = "m4v", + Type = DlnaProfileType.Video, + MimeType = "video/mp4" + } + }; } } } diff --git a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs b/Emby.Dlna/Profiles/PanasonicVieraProfile.cs index f3d7f5951..63c7e3a8e 100644 --- a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs +++ b/Emby.Dlna/Profiles/PanasonicVieraProfile.cs @@ -80,7 +80,7 @@ namespace Emby.Dlna.Profiles new DirectPlayProfile { - Container = "mp4", + Container = "mp4,m4v", VideoCodec = "h264", AudioCodec = "aac,ac3,mp3,pcm", Type = DlnaProfileType.Video @@ -208,6 +208,12 @@ namespace Emby.Dlna.Profiles Container = "ts", OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO", MimeType = "video/vnd.dlna.mpeg-tts" + }, + new ResponseProfile + { + Container = "m4v", + Type = DlnaProfileType.Video, + MimeType = "video/mp4" } }; } diff --git a/Emby.Dlna/Profiles/PopcornHourProfile.cs b/Emby.Dlna/Profiles/PopcornHourProfile.cs index 0095c80a2..d13b5c6ad 100644 --- a/Emby.Dlna/Profiles/PopcornHourProfile.cs +++ b/Emby.Dlna/Profiles/PopcornHourProfile.cs @@ -38,7 +38,7 @@ namespace Emby.Dlna.Profiles { new DirectPlayProfile { - Container = "mp4,mov", + Container = "mp4,mov,m4v", Type = DlnaProfileType.Video, VideoCodec = "h264,mpeg4", AudioCodec = "aac" @@ -201,7 +201,15 @@ namespace Emby.Dlna.Profiles } }; - ResponseProfiles = new ResponseProfile[] { }; + ResponseProfiles = new ResponseProfile[] + { + new ResponseProfile + { + Container = "m4v", + Type = DlnaProfileType.Video, + MimeType = "video/mp4" + } + }; } } } diff --git a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs b/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs index 5acdde327..c582cb52e 100644 --- a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs +++ b/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs @@ -43,7 +43,7 @@ namespace Emby.Dlna.Profiles AudioCodec = "ac3", VideoCodec = "h264", Type = DlnaProfileType.Video, - EstimateContentLength = true + EstimateContentLength = false }, new TranscodingProfile { @@ -77,7 +77,7 @@ namespace Emby.Dlna.Profiles }, new DirectPlayProfile { - Container = "mp4", + Container = "mp4,m4v", VideoCodec = "h264,mpeg4", AudioCodec = "mp3,aac", Type = DlnaProfileType.Video @@ -335,6 +335,12 @@ namespace Emby.Dlna.Profiles Container = "flac", MimeType = "audio/x-flac", Type = DlnaProfileType.Audio + }, + new ResponseProfile + { + Container = "m4v", + Type = DlnaProfileType.Video, + MimeType = "video/mp4" } }; diff --git a/Emby.Dlna/Profiles/SharpSmartTvProfile.cs b/Emby.Dlna/Profiles/SharpSmartTvProfile.cs new file mode 100644 index 000000000..b49ad0197 --- /dev/null +++ b/Emby.Dlna/Profiles/SharpSmartTvProfile.cs @@ -0,0 +1,120 @@ +using System.Xml.Serialization; +using MediaBrowser.Model.Dlna; + +namespace Emby.Dlna.Profiles +{ + [XmlRoot("Profile")] + public class SharpSmartTvProfile : DefaultProfile + { + public SharpSmartTvProfile() + { + Name = "Sharp Smart TV"; + + RequiresPlainFolders = true; + RequiresPlainVideoItems = true; + + Identification = new DeviceIdentification + { + Manufacturer = "Sharp", + + Headers = new[] + { + new HttpHeaderInfo + { + Name = "User-Agent", + Value = "Sharp", + Match = HeaderMatchType.Substring + } + } + }; + + TranscodingProfiles = new[] + { + new TranscodingProfile + { + Container = "mp3", + AudioCodec = "mp3", + Type = DlnaProfileType.Audio + }, + + new TranscodingProfile + { + Container = "ts", + Type = DlnaProfileType.Video, + AudioCodec = "ac3,aac,mp3,dts,dca", + VideoCodec = "h264", + EnableMpegtsM2TsMode = true + }, + + new TranscodingProfile + { + Container = "jpeg", + Type = DlnaProfileType.Photo + } + }; + + DirectPlayProfiles = new[] + { + new DirectPlayProfile + { + Container = "m4v,mkv,avi,mov,mp4", + VideoCodec = "h264,mpeg4", + AudioCodec = "aac,mp3,ac3,dts,dca", + Type = DlnaProfileType.Video + }, + + new DirectPlayProfile + { + Container = "asf,wmv", + Type = DlnaProfileType.Video + }, + + new DirectPlayProfile + { + Container = "mpg,mpeg", + VideoCodec = "mpeg2video", + AudioCodec = "mp3,aac", + Type = DlnaProfileType.Video + }, + + new DirectPlayProfile + { + Container = "flv", + VideoCodec = "h264", + AudioCodec = "mp3,aac", + Type = DlnaProfileType.Video + }, + + new DirectPlayProfile + { + Container = "mp3,wav", + Type = DlnaProfileType.Audio + } + }; + + SubtitleProfiles = new[] + { + new SubtitleProfile + { + Format = "srt", + Method = SubtitleDeliveryMethod.Embed + }, + new SubtitleProfile + { + Format = "srt", + Method = SubtitleDeliveryMethod.External + } + }; + + ResponseProfiles = new[] + { + new ResponseProfile + { + Container = "m4v", + Type = DlnaProfileType.Video, + MimeType = "video/mp4" + } + }; + } + } +} diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs index d1305d424..c67bd85b2 100644 --- a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs +++ b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs @@ -83,7 +83,7 @@ namespace Emby.Dlna.Profiles }, new DirectPlayProfile { - Container = "avi,mp4", + Container = "avi,mp4,m4v", VideoCodec = "mpeg4,h264", AudioCodec = "ac3,aac,mp3,pcm", Type = DlnaProfileType.Video @@ -250,6 +250,13 @@ namespace Emby.Dlna.Profiles new ResponseProfile { + Container = "m4v", + MimeType = "video/mpeg", + Type = DlnaProfileType.Video + }, + + new ResponseProfile + { Container = "mpeg", MimeType = "video/mpeg", Type = DlnaProfileType.Video diff --git a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs index c21022aa3..427820a33 100644 --- a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs +++ b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs @@ -80,7 +80,7 @@ namespace Emby.Dlna.Profiles }, new DirectPlayProfile { - Container = "mp4", + Container = "mp4,m4v", VideoCodec = "h264,mpeg4", AudioCodec = "ac3,aac,mp3", Type = DlnaProfileType.Video @@ -211,6 +211,12 @@ namespace Emby.Dlna.Profiles MimeType = "video/mpeg", OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL", Type = DlnaProfileType.Video + }, + new ResponseProfile + { + Container = "m4v", + Type = DlnaProfileType.Video, + MimeType = "video/mp4" } }; diff --git a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs index 1bbd40e91..206ca554c 100644 --- a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs +++ b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs @@ -80,7 +80,7 @@ namespace Emby.Dlna.Profiles }, new DirectPlayProfile { - Container = "mp4", + Container = "mp4,m4v", VideoCodec = "h264,mpeg4", AudioCodec = "ac3,aac,mp3,mp2", Type = DlnaProfileType.Video @@ -199,6 +199,12 @@ namespace Emby.Dlna.Profiles MimeType = "video/mpeg", OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL", Type = DlnaProfileType.Video + }, + new ResponseProfile + { + Container = "m4v", + Type = DlnaProfileType.Video, + MimeType = "video/mp4" } }; diff --git a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs index 019bbafcb..c618c9990 100644 --- a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs +++ b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs @@ -79,7 +79,7 @@ namespace Emby.Dlna.Profiles }, new DirectPlayProfile { - Container = "mp4", + Container = "mp4,m4v", VideoCodec = "h264,mpeg4", AudioCodec = "ac3,eac3,aac,mp3,mp2", Type = DlnaProfileType.Video @@ -255,6 +255,12 @@ namespace Emby.Dlna.Profiles MimeType = "video/mpeg", OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL", Type = DlnaProfileType.Video + }, + new ResponseProfile + { + Container = "m4v", + Type = DlnaProfileType.Video, + MimeType = "video/mp4" } }; diff --git a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs index 910786b83..c30bcfc85 100644 --- a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs +++ b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs @@ -79,7 +79,7 @@ namespace Emby.Dlna.Profiles }, new DirectPlayProfile { - Container = "mp4", + Container = "mp4,m4v", VideoCodec = "h264,mpeg4", AudioCodec = "ac3,eac3,aac,mp3,mp2", Type = DlnaProfileType.Video @@ -255,6 +255,12 @@ namespace Emby.Dlna.Profiles MimeType = "video/mpeg", OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL", Type = DlnaProfileType.Video + }, + new ResponseProfile + { + Container = "m4v", + Type = DlnaProfileType.Video, + MimeType = "video/mp4" } }; diff --git a/Emby.Dlna/Profiles/SonyPs4Profile.cs b/Emby.Dlna/Profiles/SonyPs4Profile.cs index 44649911d..832733184 100644 --- a/Emby.Dlna/Profiles/SonyPs4Profile.cs +++ b/Emby.Dlna/Profiles/SonyPs4Profile.cs @@ -63,7 +63,7 @@ namespace Emby.Dlna.Profiles }, new DirectPlayProfile { - Container = "mp4,mkv", + Container = "mp4,mkv,m4v", Type = DlnaProfileType.Video, VideoCodec = "h264,mpeg4", AudioCodec = "aac,ac3" @@ -86,7 +86,9 @@ namespace Emby.Dlna.Profiles { Container = "mp3", AudioCodec = "mp3", - Type = DlnaProfileType.Audio + Type = DlnaProfileType.Audio, + // Transcoded audio won't be playable at all without this + TranscodeSeekInfo = TranscodeSeekInfo.Bytes }, new TranscodingProfile { @@ -253,6 +255,13 @@ namespace Emby.Dlna.Profiles Container = "wav", MimeType = "audio/wav", Type = DlnaProfileType.Audio + }, + + new ResponseProfile + { + Container = "m4v", + Type = DlnaProfileType.Video, + MimeType = "video/mp4" } }; } diff --git a/Emby.Dlna/Profiles/WdtvLiveProfile.cs b/Emby.Dlna/Profiles/WdtvLiveProfile.cs index e524816af..6cef2d965 100644 --- a/Emby.Dlna/Profiles/WdtvLiveProfile.cs +++ b/Emby.Dlna/Profiles/WdtvLiveProfile.cs @@ -87,7 +87,7 @@ namespace Emby.Dlna.Profiles new DirectPlayProfile { - Container = "mp4,mov", + Container = "mp4,mov,m4v", Type = DlnaProfileType.Video, VideoCodec = "h264,mpeg4", AudioCodec = "ac3,aac,mp2,mp3,dca,dts" diff --git a/Emby.Dlna/Profiles/XboxOneProfile.cs b/Emby.Dlna/Profiles/XboxOneProfile.cs index 370534a67..8994082ad 100644 --- a/Emby.Dlna/Profiles/XboxOneProfile.cs +++ b/Emby.Dlna/Profiles/XboxOneProfile.cs @@ -80,7 +80,7 @@ namespace Emby.Dlna.Profiles }, new DirectPlayProfile { - Container = "mp4,mov,mkv", + Container = "mp4,mov,mkv,m4v", VideoCodec = "h264,mpeg4,mpeg2video", AudioCodec = "aac,ac3", Type = DlnaProfileType.Video @@ -349,6 +349,12 @@ namespace Emby.Dlna.Profiles Container = "avi", MimeType = "video/avi", Type = DlnaProfileType.Video + }, + new ResponseProfile + { + Container = "m4v", + Type = DlnaProfileType.Video, + MimeType = "video/mp4" } }; } diff --git a/Emby.Dlna/Profiles/Xml/Default.xml b/Emby.Dlna/Profiles/Xml/Default.xml index 4e29f651b..b5a5d24b6 100644 --- a/Emby.Dlna/Profiles/Xml/Default.xml +++ b/Emby.Dlna/Profiles/Xml/Default.xml @@ -29,8 +29,8 @@ <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests> <XmlRootAttributes /> <DirectPlayProfiles> - <DirectPlayProfile container="m4v,ts,mkv,avi,mpg,mpeg,mp4" audioCodec="aac,mp3,ac3" videoCodec="h264" type="Video" /> - <DirectPlayProfile container="mp3,wma,aac,wav" type="Audio" /> + <DirectPlayProfile container="m4v,ts,mpegts,mkv,avi,mpg,mpeg,mp4,mov" audioCodec="aac,mp3,ac3" videoCodec="h264" type="Video" /> + <DirectPlayProfile container="mp3,wma,aac,wav,flac" type="Audio" /> </DirectPlayProfiles> <TranscodingProfiles> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" /> diff --git a/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml b/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml index eb63352a3..556a3d673 100644 --- a/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml +++ b/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml @@ -5,7 +5,7 @@ <Manufacturer>Echostar Technologies LLC</Manufacturer> <ManufacturerUrl>http://www.echostar.com</ManufacturerUrl> <Headers> - <HttpHeaderInfo name="User-Agent" value="XiP" match="Substring" /> + <HttpHeaderInfo name="User-Agent" value="Zip_" match="Substring" /> </Headers> </Identification> <Manufacturer>Emby</Manufacturer> @@ -37,9 +37,7 @@ <XmlRootAttributes /> <DirectPlayProfiles> <DirectPlayProfile container="mp4,mkv,mpeg,ts" audioCodec="mp3,ac3,aac,he-aac,pcm" videoCodec="h264,mpeg2video" type="Video" /> - <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" /> - <DirectPlayProfile container="alac" audioCodec="alac" type="Audio" /> - <DirectPlayProfile container="flac" audioCodec="flac" type="Audio" /> + <DirectPlayProfile container="mp3,alac,flac" type="Audio" /> <DirectPlayProfile container="jpeg" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> diff --git a/Emby.Dlna/Profiles/Xml/LG Smart TV.xml b/Emby.Dlna/Profiles/Xml/LG Smart TV.xml index dc0e97772..2963e5f38 100644 --- a/Emby.Dlna/Profiles/Xml/LG Smart TV.xml +++ b/Emby.Dlna/Profiles/Xml/LG Smart TV.xml @@ -37,7 +37,7 @@ <DirectPlayProfiles> <DirectPlayProfile container="ts" audioCodec="aac,ac3,mp3,dca,dts" videoCodec="h264" type="Video" /> <DirectPlayProfile container="mkv" audioCodec="aac,ac3,mp3,dca,dts" videoCodec="h264" type="Video" /> - <DirectPlayProfile container="mp4" audioCodec="aac,ac3,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" /> + <DirectPlayProfile container="mp4,m4v" audioCodec="aac,ac3,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" /> <DirectPlayProfile container="mp3" type="Audio" /> <DirectPlayProfile container="jpeg" type="Photo" /> </DirectPlayProfiles> @@ -79,7 +79,11 @@ <ApplyConditions /> </CodecProfile> </CodecProfiles> - <ResponseProfiles /> + <ResponseProfiles> + <ResponseProfile container="m4v" type="Video" mimeType="video/mp4"> + <Conditions /> + </ResponseProfile> + </ResponseProfiles> <SubtitleProfiles> <SubtitleProfile format="srt" method="Embed" /> <SubtitleProfile format="srt" method="External" /> diff --git a/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml b/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml index 862bede9b..c9ea6daff 100644 --- a/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml +++ b/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml @@ -34,7 +34,7 @@ <XmlRootAttributes /> <DirectPlayProfiles> <DirectPlayProfile container="mp3,flac,m4a,wma" type="Audio" /> - <DirectPlayProfile container="avi,mp4,mkv,ts" type="Video" /> + <DirectPlayProfile container="avi,mp4,mkv,ts,m4v" type="Video" /> </DirectPlayProfiles> <TranscodingProfiles> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" /> @@ -43,7 +43,11 @@ </TranscodingProfiles> <ContainerProfiles /> <CodecProfiles /> - <ResponseProfiles /> + <ResponseProfiles> + <ResponseProfile container="m4v" type="Video" mimeType="video/mp4"> + <Conditions /> + </ResponseProfile> + </ResponseProfiles> <SubtitleProfiles> <SubtitleProfile format="srt" method="Embed" /> </SubtitleProfiles> diff --git a/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml b/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml index d7b142d84..b520b2b53 100644 --- a/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml +++ b/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml @@ -41,7 +41,7 @@ <DirectPlayProfile container="mpeg,mpg" audioCodec="ac3,mp3,pcm_dvd" videoCodec="mpeg2video,mpeg4" type="Video" /> <DirectPlayProfile container="mkv" audioCodec="aac,ac3,dca,mp3,mp2,pcm,dts" videoCodec="h264,mpeg2video" type="Video" /> <DirectPlayProfile container="ts" audioCodec="aac,mp3,mp2" videoCodec="h264,mpeg2video" type="Video" /> - <DirectPlayProfile container="mp4" audioCodec="aac,ac3,mp3,pcm" videoCodec="h264" type="Video" /> + <DirectPlayProfile container="mp4,m4v" audioCodec="aac,ac3,mp3,pcm" videoCodec="h264" type="Video" /> <DirectPlayProfile container="mov" audioCodec="aac,pcm" videoCodec="h264" type="Video" /> <DirectPlayProfile container="avi" audioCodec="pcm" videoCodec="mpeg4" type="Video" /> <DirectPlayProfile container="flv" audioCodec="aac" videoCodec="h264" type="Video" /> @@ -76,6 +76,9 @@ <ResponseProfile container="ts" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts"> <Conditions /> </ResponseProfile> + <ResponseProfile container="m4v" type="Video" mimeType="video/mp4"> + <Conditions /> + </ResponseProfile> </ResponseProfiles> <SubtitleProfiles> <SubtitleProfile format="srt" method="Embed" /> diff --git a/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml b/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml index 9bc4c2e31..9fa49e94a 100644 --- a/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml +++ b/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml @@ -29,7 +29,7 @@ <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests> <XmlRootAttributes /> <DirectPlayProfiles> - <DirectPlayProfile container="mp4,mov" audioCodec="aac" videoCodec="h264,mpeg4" type="Video" /> + <DirectPlayProfile container="mp4,mov,m4v" audioCodec="aac" videoCodec="h264,mpeg4" type="Video" /> <DirectPlayProfile container="ts" audioCodec="aac,ac3,eac3,mp3,mp2,pcm" videoCodec="h264" type="Video" /> <DirectPlayProfile container="asf,wmv" audioCodec="wmav2,wmapro" videoCodec="wmv3,vc1" type="Video" /> <DirectPlayProfile container="avi" audioCodec="mp3,ac3,eac3,mp2,pcm" videoCodec="mpeg4,msmpeg4" type="Video" /> @@ -81,7 +81,11 @@ <ApplyConditions /> </CodecProfile> </CodecProfiles> - <ResponseProfiles /> + <ResponseProfiles> + <ResponseProfile container="m4v" type="Video" mimeType="video/mp4"> + <Conditions /> + </ResponseProfile> + </ResponseProfiles> <SubtitleProfiles> <SubtitleProfile format="srt" method="Embed" /> </SubtitleProfiles> diff --git a/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml b/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml index 5b2106da5..a3acea829 100644 --- a/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml +++ b/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml @@ -40,7 +40,7 @@ <DirectPlayProfile container="asf" audioCodec="mp3,ac3,wmav2,wmapro,wmavoice" videoCodec="h264,mpeg4,mjpeg" type="Video" /> <DirectPlayProfile container="avi" audioCodec="mp3,ac3,dca,dts" videoCodec="h264,mpeg4,mjpeg" type="Video" /> <DirectPlayProfile container="mkv" audioCodec="mp3,ac3,dca,aac,dts" videoCodec="h264,mpeg4,mjpeg4" type="Video" /> - <DirectPlayProfile container="mp4" audioCodec="mp3,aac" videoCodec="h264,mpeg4" type="Video" /> + <DirectPlayProfile container="mp4,m4v" audioCodec="mp3,aac" videoCodec="h264,mpeg4" type="Video" /> <DirectPlayProfile container="3gp" audioCodec="aac,he-aac" videoCodec="h264,mpeg4" type="Video" /> <DirectPlayProfile container="mpg,mpeg" audioCodec="ac3,mp2,mp3,aac" videoCodec="mpeg1video,mpeg2video,h264" type="Video" /> <DirectPlayProfile container="vro,vob" audioCodec="ac3,mp2,mp3" videoCodec="mpeg1video,mpeg2video" type="Video" /> @@ -51,7 +51,7 @@ </DirectPlayProfiles> <TranscodingProfiles> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" /> - <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="true" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" /> <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" /> </TranscodingProfiles> <ContainerProfiles> @@ -117,6 +117,9 @@ <ResponseProfile container="flac" type="Audio" mimeType="audio/x-flac"> <Conditions /> </ResponseProfile> + <ResponseProfile container="m4v" type="Video" mimeType="video/mp4"> + <Conditions /> + </ResponseProfile> </ResponseProfiles> <SubtitleProfiles> <SubtitleProfile format="srt" method="Embed" /> diff --git a/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml b/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml new file mode 100644 index 000000000..e367a8c15 --- /dev/null +++ b/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <Name>Sharp Smart TV</Name> + <Identification> + <Manufacturer>Sharp</Manufacturer> + <Headers> + <HttpHeaderInfo name="User-Agent" value="Sharp" match="Substring" /> + </Headers> + </Identification> + <Manufacturer>Emby</Manufacturer> + <ManufacturerUrl>http://emby.media/</ManufacturerUrl> + <ModelName>Emby Server</ModelName> + <ModelDescription>Emby</ModelDescription> + <ModelNumber>Emby</ModelNumber> + <ModelUrl>http://emby.media/</ModelUrl> + <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> + <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> + <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> + <AlbumArtPn>JPEG_SM</AlbumArtPn> + <MaxAlbumArtWidth>480</MaxAlbumArtWidth> + <MaxAlbumArtHeight>480</MaxAlbumArtHeight> + <MaxIconWidth>48</MaxIconWidth> + <MaxIconHeight>48</MaxIconHeight> + <MaxStreamingBitrate>24000000</MaxStreamingBitrate> + <MaxStaticBitrate>24000000</MaxStaticBitrate> + <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> + <MaxStaticMusicBitrate xsi:nil="true" /> + <XDlnaDoc>DMS-1.50</XDlnaDoc> + <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo> + <TimelineOffsetSeconds>0</TimelineOffsetSeconds> + <RequiresPlainVideoItems>true</RequiresPlainVideoItems> + <RequiresPlainFolders>true</RequiresPlainFolders> + <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar> + <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests> + <XmlRootAttributes /> + <DirectPlayProfiles> + <DirectPlayProfile container="m4v,mkv,avi,mov,mp4" audioCodec="aac,mp3,ac3,dts,dca" videoCodec="h264,mpeg4" type="Video" /> + <DirectPlayProfile container="asf,wmv" type="Video" /> + <DirectPlayProfile container="mpg,mpeg" audioCodec="mp3,aac" videoCodec="mpeg2video" type="Video" /> + <DirectPlayProfile container="flv" audioCodec="mp3,aac" videoCodec="h264" type="Video" /> + <DirectPlayProfile container="mp3,wav" type="Audio" /> + </DirectPlayProfiles> + <TranscodingProfiles> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" /> + <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3,dts,dca" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" /> + <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" /> + </TranscodingProfiles> + <ContainerProfiles /> + <CodecProfiles /> + <ResponseProfiles> + <ResponseProfile container="m4v" type="Video" mimeType="video/mp4"> + <Conditions /> + </ResponseProfile> + </ResponseProfiles> + <SubtitleProfiles> + <SubtitleProfile format="srt" method="Embed" /> + <SubtitleProfile format="srt" method="External" /> + </SubtitleProfiles> +</Profile>
\ No newline at end of file diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml index 154d9a68f..ee113ef63 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml +++ b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml @@ -41,7 +41,7 @@ <DirectPlayProfiles> <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3,pcm" videoCodec="mpeg1video,mpeg2video,h264" type="Video" /> <DirectPlayProfile container="mpeg" audioCodec="ac3,mp3,pcm" videoCodec="mpeg1video,mpeg2video" type="Video" /> - <DirectPlayProfile container="avi,mp4" audioCodec="ac3,aac,mp3,pcm" videoCodec="mpeg4,h264" type="Video" /> + <DirectPlayProfile container="avi,mp4,m4v" audioCodec="ac3,aac,mp3,pcm" videoCodec="mpeg4,h264" type="Video" /> <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" /> <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" /> <DirectPlayProfile container="jpeg" type="Photo" /> @@ -99,6 +99,9 @@ <ResponseProfile container="mp4" type="Video" mimeType="video/mpeg"> <Conditions /> </ResponseProfile> + <ResponseProfile container="m4v" type="Video" mimeType="video/mpeg"> + <Conditions /> + </ResponseProfile> <ResponseProfile container="mpeg" type="Video" mimeType="video/mpeg"> <Conditions /> </ResponseProfile> diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml index 39d7674a1..3b234ac36 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml +++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml @@ -41,7 +41,7 @@ <DirectPlayProfiles> <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" /> <DirectPlayProfile container="ts" audioCodec="mp3" videoCodec="mpeg2video" type="Video" /> - <DirectPlayProfile container="mp4" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg4" type="Video" /> + <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg4" type="Video" /> <DirectPlayProfile container="mpeg" audioCodec="mp3" videoCodec="mpeg2video,mpeg1video" type="Video" /> <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" videoCodec="wmv2,wmv3,vc1" type="Video" /> <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" /> @@ -129,6 +129,9 @@ <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg"> <Conditions /> </ResponseProfile> + <ResponseProfile container="m4v" type="Video" mimeType="video/mp4"> + <Conditions /> + </ResponseProfile> </ResponseProfiles> <SubtitleProfiles> <SubtitleProfile format="srt" method="Embed" /> diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml index 8b6e88702..4748b8d2a 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml +++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml @@ -41,7 +41,7 @@ <DirectPlayProfiles> <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" /> <DirectPlayProfile container="ts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" /> - <DirectPlayProfile container="mp4" audioCodec="ac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" /> + <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" /> <DirectPlayProfile container="avi" audioCodec="ac3,mp3" videoCodec="mpeg4" type="Video" /> <DirectPlayProfile container="mpeg" audioCodec="mp3,mp2" videoCodec="mpeg2video,mpeg1video" type="Video" /> <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" videoCodec="wmv2,wmv3,vc1" type="Video" /> @@ -105,6 +105,9 @@ <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg"> <Conditions /> </ResponseProfile> + <ResponseProfile container="m4v" type="Video" mimeType="video/mp4"> + <Conditions /> + </ResponseProfile> </ResponseProfiles> <SubtitleProfiles> <SubtitleProfile format="srt" method="Embed" /> diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml index e76ca2c77..d0ec30a26 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml +++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml @@ -41,7 +41,7 @@ <DirectPlayProfiles> <DirectPlayProfile container="ts" audioCodec="ac3,eac3,aac,mp3" videoCodec="h264" type="Video" /> <DirectPlayProfile container="ts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" /> - <DirectPlayProfile container="mp4" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" /> + <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" /> <DirectPlayProfile container="mov" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4,mjpeg" type="Video" /> <DirectPlayProfile container="mkv" audioCodec="ac3,eac3,aac,mp3,mp2,pcm,vorbis" videoCodec="h264,mpeg4,vp8" type="Video" /> <DirectPlayProfile container="avi" audioCodec="ac3,eac3,mp3" videoCodec="mpeg4" type="Video" /> @@ -104,6 +104,9 @@ <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg"> <Conditions /> </ResponseProfile> + <ResponseProfile container="m4v" type="Video" mimeType="video/mp4"> + <Conditions /> + </ResponseProfile> </ResponseProfiles> <SubtitleProfiles> <SubtitleProfile format="srt" method="Embed" /> diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml index ff4aa9cf8..ba83490b4 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml +++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml @@ -41,7 +41,7 @@ <DirectPlayProfiles> <DirectPlayProfile container="ts" audioCodec="ac3,eac3,aac,mp3" videoCodec="h264" type="Video" /> <DirectPlayProfile container="ts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" /> - <DirectPlayProfile container="mp4" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" /> + <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" /> <DirectPlayProfile container="mov" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4,mjpeg" type="Video" /> <DirectPlayProfile container="mkv" audioCodec="ac3,eac3,aac,mp3,mp2,pcm,vorbis" videoCodec="h264,mpeg4,vp8" type="Video" /> <DirectPlayProfile container="avi" audioCodec="ac3,eac3,mp3" videoCodec="mpeg4" type="Video" /> @@ -104,6 +104,9 @@ <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg"> <Conditions /> </ResponseProfile> + <ResponseProfile container="m4v" type="Video" mimeType="video/mp4"> + <Conditions /> + </ResponseProfile> </ResponseProfiles> <SubtitleProfiles> <SubtitleProfile format="srt" method="Embed" /> diff --git a/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml b/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml index 7ec4ccc90..22131a502 100644 --- a/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml +++ b/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml @@ -40,12 +40,12 @@ <DirectPlayProfile container="avi" audioCodec="mp2,mp3" videoCodec="mpeg4" type="Video" /> <DirectPlayProfile container="ts" audioCodec="ac3,mp2,mp3,aac" videoCodec="mpeg1video,mpeg2video,h264" type="Video" /> <DirectPlayProfile container="mpeg" audioCodec="mp2" videoCodec="mpeg1video,mpeg2video" type="Video" /> - <DirectPlayProfile container="mp4,mkv" audioCodec="aac,ac3" videoCodec="h264,mpeg4" type="Video" /> + <DirectPlayProfile container="mp4,mkv,m4v" audioCodec="aac,ac3" videoCodec="h264,mpeg4" type="Video" /> <DirectPlayProfile container="aac,mp3,wav" type="Audio" /> <DirectPlayProfile container="jpeg,png,gif,bmp,tiff" type="Photo" /> </DirectPlayProfiles> <TranscodingProfiles> - <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" /> + <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Bytes" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" /> <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" /> </TranscodingProfiles> @@ -98,6 +98,9 @@ <ResponseProfile container="wav" type="Audio" mimeType="audio/wav"> <Conditions /> </ResponseProfile> + <ResponseProfile container="m4v" type="Video" mimeType="video/mp4"> + <Conditions /> + </ResponseProfile> </ResponseProfiles> <SubtitleProfiles> <SubtitleProfile format="srt" method="Embed" /> diff --git a/Emby.Dlna/Profiles/Xml/WDTV Live.xml b/Emby.Dlna/Profiles/Xml/WDTV Live.xml index 96d37e7fb..1cf3ef597 100644 --- a/Emby.Dlna/Profiles/Xml/WDTV Live.xml +++ b/Emby.Dlna/Profiles/Xml/WDTV Live.xml @@ -40,7 +40,7 @@ <DirectPlayProfile container="mpeg" audioCodec="ac3,dca,mp2,mp3,pcm,dts" videoCodec="mpeg1video,mpeg2video" type="Video" /> <DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp2,mp3,pcm,dts" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" /> <DirectPlayProfile container="ts,m2ts" audioCodec="ac3,dca,mp2,mp3,aac,dts" videoCodec="mpeg1video,mpeg2video,h264,vc1" type="Video" /> - <DirectPlayProfile container="mp4,mov" audioCodec="ac3,aac,mp2,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" /> + <DirectPlayProfile container="mp4,mov,m4v" audioCodec="ac3,aac,mp2,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" /> <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="vc1" type="Video" /> <DirectPlayProfile container="asf" audioCodec="mp2,ac3" videoCodec="mpeg2video" type="Video" /> <DirectPlayProfile container="mp3" audioCodec="mp2,mp3" type="Audio" /> diff --git a/Emby.Dlna/Profiles/Xml/Xbox One.xml b/Emby.Dlna/Profiles/Xml/Xbox One.xml index 8c13ed8fd..6289847ca 100644 --- a/Emby.Dlna/Profiles/Xml/Xbox One.xml +++ b/Emby.Dlna/Profiles/Xml/Xbox One.xml @@ -39,7 +39,7 @@ <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg2video" type="Video" /> <DirectPlayProfile container="avi" audioCodec="ac3,mp3" videoCodec="mpeg4" type="Video" /> <DirectPlayProfile container="avi" audioCodec="aac" videoCodec="h264" type="Video" /> - <DirectPlayProfile container="mp4,mov,mkv" audioCodec="aac,ac3" videoCodec="h264,mpeg4,mpeg2video" type="Video" /> + <DirectPlayProfile container="mp4,mov,mkv,m4v" audioCodec="aac,ac3" videoCodec="h264,mpeg4,mpeg2video" type="Video" /> <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="wmv2,wmv3,vc1" type="Video" /> <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" /> <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" /> @@ -116,6 +116,9 @@ <ResponseProfile container="avi" type="Video" mimeType="video/avi"> <Conditions /> </ResponseProfile> + <ResponseProfile container="m4v" type="Video" mimeType="video/mp4"> + <Conditions /> + </ResponseProfile> </ResponseProfiles> <SubtitleProfiles> <SubtitleProfile format="srt" method="Embed" /> diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs index 1cd19d010..3f0e070ca 100644 --- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs +++ b/Emby.Dlna/Ssdp/DeviceDiscovery.cs @@ -104,7 +104,8 @@ namespace Emby.Dlna.Ssdp Argument = new UpnpDeviceInfo { Location = e.DiscoveredDevice.DescriptionLocation, - Headers = headers + Headers = headers, + LocalIpAddress = e.LocalIpAddress } }; diff --git a/Emby.Server.Core/ApplicationHost.cs b/Emby.Server.Core/ApplicationHost.cs index 215ac8492..b9412d0fb 100644 --- a/Emby.Server.Core/ApplicationHost.cs +++ b/Emby.Server.Core/ApplicationHost.cs @@ -326,6 +326,8 @@ namespace Emby.Server.Core } } + public abstract bool IsRunningAsService { get; } + private Assembly GetAssembly(Type type) { return type.GetTypeInfo().Assembly; @@ -489,7 +491,8 @@ namespace Emby.Server.Core { var migrations = new List<IVersionMigration> { - new LibraryScanMigration(ServerConfigurationManager, TaskManager) + new LibraryScanMigration(ServerConfigurationManager, TaskManager), + new GuideMigration(ServerConfigurationManager, TaskManager) }; foreach (var task in migrations) @@ -1247,7 +1250,6 @@ namespace Emby.Server.Core HasUpdateAvailable = HasUpdateAvailable, SupportsAutoRunAtStartup = SupportsAutoRunAtStartup, TranscodingTempPath = ApplicationPaths.TranscodingTempPath, - IsRunningAsService = IsRunningAsService, SupportsRunningAsService = SupportsRunningAsService, ServerName = FriendlyName, LocalAddress = localAddress, @@ -1477,6 +1479,10 @@ namespace Emby.Server.Core { AuthorizeServer(); } + catch (NotImplementedException) + { + + } catch (Exception ex) { Logger.ErrorException("Error authorizing server", ex); diff --git a/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs index b75de2ff4..eb3a71465 100644 --- a/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs +++ b/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs @@ -11,6 +11,7 @@ using MediaBrowser.Model.Events; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Threading; using Mono.Nat; +using System.Threading.Tasks; namespace Emby.Server.Core.EntryPoints { @@ -106,6 +107,11 @@ namespace Emby.Server.Core.EntryPoints private async void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e) { + if (_disposed) + { + return; + } + var info = e.Argument; string usn; @@ -169,6 +175,11 @@ namespace Emby.Server.Core.EntryPoints return; } + if (_disposed) + { + return; + } + _logger.Debug("Calling Nat.Handle on " + identifier); NatUtility.Handle(localAddress, info, endpoint, NatProtocol.Upnp); } @@ -185,6 +196,11 @@ namespace Emby.Server.Core.EntryPoints void NatUtility_DeviceFound(object sender, DeviceEventArgs e) { + if (_disposed) + { + return; + } + try { var device = e.Device; @@ -208,8 +224,13 @@ namespace Emby.Server.Core.EntryPoints private List<string> _createdRules = new List<string>(); private List<string> _usnsHandled = new List<string>(); - private void CreateRules(INatDevice device) + private async void CreateRules(INatDevice device) { + if (_disposed) + { + throw new ObjectDisposedException("PortMapper"); + } + // On some systems the device discovered event seems to fire repeatedly // This check will help ensure we're not trying to port map the same device over and over @@ -219,12 +240,16 @@ namespace Emby.Server.Core.EntryPoints { _createdRules.Add(address); - CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort); - CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort); + var success = await CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort).ConfigureAwait(false); + + if (success) + { + await CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort).ConfigureAwait(false); + } } } - private async void CreatePortMap(INatDevice device, int privatePort, int publicPort) + private async Task<bool> CreatePortMap(INatDevice device, int privatePort, int publicPort) { _logger.Debug("Creating port map on port {0}", privatePort); @@ -235,10 +260,14 @@ namespace Emby.Server.Core.EntryPoints Description = _appHost.Name }).ConfigureAwait(false); + + return true; } catch (Exception ex) { _logger.Error("Error creating port map: " + ex.Message); + + return false; } } @@ -249,8 +278,10 @@ namespace Emby.Server.Core.EntryPoints _logger.Debug("NAT device lost: {0}", device.LocalAddress.ToString()); } + private bool _disposed = false; public void Dispose() { + _disposed = true; DisposeNat(); } diff --git a/Emby.Server.Core/IO/LibraryMonitor.cs b/Emby.Server.Core/IO/LibraryMonitor.cs index 6ed096f44..f0ecb9d89 100644 --- a/Emby.Server.Core/IO/LibraryMonitor.cs +++ b/Emby.Server.Core/IO/LibraryMonitor.cs @@ -87,7 +87,7 @@ namespace Emby.Server.Core.IO public bool IsPathLocked(string path) { var lockedPaths = _tempIgnoredPaths.Keys.ToList(); - return lockedPaths.Any(i => string.Equals(i, path, StringComparison.OrdinalIgnoreCase) || _fileSystem.ContainsSubPath(i, path)); + return lockedPaths.Any(i => _fileSystem.AreEqual(i, path) || _fileSystem.ContainsSubPath(i, path)); } public async void ReportFileSystemChangeComplete(string path, bool refreshPath) @@ -288,6 +288,13 @@ namespace Emby.Server.Core.IO { try { + if (!_fileSystem.DirectoryExists(path)) + { + // Seeing a crash in the mono runtime due to an exception being thrown on a different thread + Logger.Info("Skipping realtime monitor for {0} because the path does not exist", path); + return; + } + var newWatcher = new FileSystemWatcher(path, "*") { IncludeSubdirectories = true diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index 11fd3a872..d3b2ef7ef 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -138,7 +138,7 @@ namespace Emby.Server.Implementations.Activity CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, item.Name), + Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, Notifications.Notifications.GetItemName(item)), Type = "PlaybackStopped", ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), e.ClientName, e.DeviceName), UserId = user.Id.ToString("N") @@ -170,7 +170,7 @@ namespace Emby.Server.Implementations.Activity CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, item.Name), + Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, Notifications.Notifications.GetItemName(item)), Type = "PlaybackStart", ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), e.ClientName, e.DeviceName), UserId = user.Id.ToString("N") @@ -235,10 +235,6 @@ namespace Emby.Server.Implementations.Activity }); } - void _logManager_LoggerLoaded(object sender, EventArgs e) - { - } - void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e) { CreateLogEntry(new ActivityLogEntry diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index f7dc93009..8f03fa7a4 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -343,7 +343,7 @@ namespace Emby.Server.Implementations.Channels private MediaSourceInfo GetMediaSource(BaseItem item, ChannelMediaInfo info) { - var source = info.ToMediaSource(); + var source = info.ToMediaSource(item.Id); source.RunTimeTicks = source.RunTimeTicks ?? item.RunTimeTicks; diff --git a/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs b/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs index 170ef07f3..b5639773b 100644 --- a/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs +++ b/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs @@ -9,6 +9,7 @@ using System; using System.IO; using System.Text; using System.Threading.Tasks; +using MediaBrowser.Controller.Security; using MediaBrowser.Model.IO; using MediaBrowser.Model.Threading; @@ -17,6 +18,7 @@ namespace Emby.Server.Implementations.Connect public class ConnectEntryPoint : IServerEntryPoint { private ITimer _timer; + private IpAddressInfo _cachedIpAddress; private readonly IHttpClient _httpClient; private readonly IApplicationPaths _appPaths; private readonly ILogger _logger; @@ -26,8 +28,9 @@ namespace Emby.Server.Implementations.Connect private readonly IApplicationHost _appHost; private readonly IFileSystem _fileSystem; private readonly ITimerFactory _timerFactory; + private readonly IEncryptionManager _encryption; - public ConnectEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, INetworkManager networkManager, IConnectManager connectManager, IApplicationHost appHost, IFileSystem fileSystem, ITimerFactory timerFactory) + public ConnectEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, INetworkManager networkManager, IConnectManager connectManager, IApplicationHost appHost, IFileSystem fileSystem, ITimerFactory timerFactory, IEncryptionManager encryption) { _httpClient = httpClient; _appPaths = appPaths; @@ -37,6 +40,7 @@ namespace Emby.Server.Implementations.Connect _appHost = appHost; _fileSystem = fileSystem; _timerFactory = timerFactory; + _encryption = encryption; } public void Run() @@ -143,17 +147,31 @@ namespace Emby.Server.Implementations.Connect private string CacheFilePath { - get { return Path.Combine(_appPaths.DataPath, "wan.txt"); } + get { return Path.Combine(_appPaths.DataPath, "wan.dat"); } } private void CacheAddress(IpAddressInfo address) { + if (_cachedIpAddress != null && _cachedIpAddress.Equals(address)) + { + // no need to update the file if the address has not changed + return; + } + var path = CacheFilePath; try { _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); - _fileSystem.WriteAllText(path, address.ToString(), Encoding.UTF8); + } + catch (Exception ex) + { + } + + try + { + _fileSystem.WriteAllText(path, _encryption.EncryptString(address.ToString()), Encoding.UTF8); + _cachedIpAddress = address; } catch (Exception ex) { @@ -169,11 +187,12 @@ namespace Emby.Server.Implementations.Connect try { - var endpoint = _fileSystem.ReadAllText(path, Encoding.UTF8); + var endpoint = _encryption.DecryptString(_fileSystem.ReadAllText(path, Encoding.UTF8)); IpAddressInfo ipAddress; if (_networkManager.TryParseIpAddress(endpoint, out ipAddress)) { + _cachedIpAddress = ipAddress; ((ConnectManager)_connectManager).OnWanAddressResolved(ipAddress); } } diff --git a/Emby.Server.Implementations/Connect/ConnectManager.cs b/Emby.Server.Implementations/Connect/ConnectManager.cs index 8c8b7b026..7e6755f6a 100644 --- a/Emby.Server.Implementations/Connect/ConnectManager.cs +++ b/Emby.Server.Implementations/Connect/ConnectManager.cs @@ -925,7 +925,11 @@ namespace Emby.Server.Implementations.Connect } _data.PendingAuthorizations = newPendingList; - CacheData(); + + if (!newPendingList.Select(i => i.Id).SequenceEqual(currentPendingList.Select(i => i.Id), StringComparer.Ordinal)) + { + CacheData(); + } await RefreshGuestNames(list, refreshImages).ConfigureAwait(false); } @@ -1118,7 +1122,7 @@ namespace Emby.Server.Implementations.Connect } } - public async Task Authenticate(string username, string passwordMd5) + public async Task<ConnectAuthenticationResult> Authenticate(string username, string passwordMd5) { if (string.IsNullOrWhiteSpace(username)) { @@ -1147,6 +1151,7 @@ namespace Emby.Server.Implementations.Connect // No need to examine the response using (var response = (await _httpClient.SendAsync(options, "POST").ConfigureAwait(false)).Content) { + return _json.DeserializeFromStream<ConnectAuthenticationResult>(response); } } diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs index 5bc3a625f..2819a249f 100644 --- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs +++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs @@ -137,6 +137,11 @@ namespace Emby.Server.Implementations.Data var numComplete = 0; var numItems = result.Count; + var allLibraryPaths = _libraryManager + .GetVirtualFolders() + .SelectMany(i => i.Locations) + .ToList(); + foreach (var item in result) { cancellationToken.ThrowIfCancellationRequested(); @@ -170,9 +175,8 @@ namespace Emby.Server.Implementations.Data continue; } - if (Folder.IsPathOffline(path)) + if (Folder.IsPathOffline(path, allLibraryPaths)) { - await libraryItem.UpdateIsOffline(true).ConfigureAwait(false); continue; } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 8c16216b9..c158f2e51 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -372,7 +372,7 @@ namespace Emby.Server.Implementations.Data userDataRepo.Initialize(WriteLock, _connection); - _shrinkMemoryTimer = _timerFactory.Create(OnShrinkMemoryTimerCallback, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(15)); + _shrinkMemoryTimer = _timerFactory.Create(OnShrinkMemoryTimerCallback, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(30)); } private void OnShrinkMemoryTimerCallback(object state) @@ -2384,8 +2384,17 @@ namespace Emby.Server.Implementations.Data var excludeIds = query.ExcludeItemIds.ToList(); excludeIds.Add(item.Id.ToString("N")); - query.ExcludeItemIds = excludeIds.ToArray(); + if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Trailer).Name)) + { + var hasTrailers = item as IHasTrailers; + if (hasTrailers != null) + { + excludeIds.AddRange(hasTrailers.GetTrailerIds().Select(i => i.ToString("N"))); + } + } + + query.ExcludeItemIds = excludeIds.ToArray(); query.ExcludeProviderIds = item.ProviderIds; } @@ -2548,57 +2557,53 @@ namespace Emby.Server.Implementations.Data { using (var connection = CreateConnection(true)) { - return connection.RunInTransaction(db => - { - var list = new List<BaseItem>(); + var list = new List<BaseItem>(); - using (var statement = PrepareStatementSafe(db, commandText)) + using (var statement = PrepareStatementSafe(connection, commandText)) + { + if (EnableJoinUserData(query)) { - if (EnableJoinUserData(query)) - { - statement.TryBind("@UserId", query.User.Id); - } + statement.TryBind("@UserId", query.User.Id); + } - BindSimilarParams(query, statement); + BindSimilarParams(query, statement); - // Running this again will bind the params - GetWhereClauses(query, statement); + // Running this again will bind the params + GetWhereClauses(query, statement); - foreach (var row in statement.ExecuteQuery()) + foreach (var row in statement.ExecuteQuery()) + { + var item = GetItem(row, query); + if (item != null) { - var item = GetItem(row, query); - if (item != null) - { - list.Add(item); - } + list.Add(item); } } + } - // Hack for right now since we currently don't support filtering out these duplicates within a query - if (query.EnableGroupByMetadataKey) + // Hack for right now since we currently don't support filtering out these duplicates within a query + if (query.EnableGroupByMetadataKey) + { + var limit = query.Limit ?? int.MaxValue; + limit -= 4; + var newList = new List<BaseItem>(); + + foreach (var item in list) { - var limit = query.Limit ?? int.MaxValue; - limit -= 4; - var newList = new List<BaseItem>(); + AddItem(newList, item); - foreach (var item in list) + if (newList.Count >= limit) { - AddItem(newList, item); - - if (newList.Count >= limit) - { - break; - } + break; } - - list = newList; } - LogQueryTime("GetItemList", commandText, now); + list = newList; + } - return list; + LogQueryTime("GetItemList", commandText, now); - }, ReadTransactionMode); + return list; } } } @@ -2652,7 +2657,7 @@ namespace Emby.Server.Implementations.Data { //Logger.Debug("{2} query time: {0}ms. Query: {1}", // Convert.ToInt32(elapsed), - // cmd.CommandText, + // commandText, // methodName); } } @@ -2825,8 +2830,9 @@ namespace Emby.Server.Implementations.Data { if (orderBy.Count == 0) { - orderBy.Add(new Tuple<string, SortOrder>("SimilarityScore", SortOrder.Descending)); orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending)); + orderBy.Add(new Tuple<string, SortOrder>("SimilarityScore", SortOrder.Descending)); + //orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending)); query.SortOrder = SortOrder.Descending; enableOrderInversion = false; } @@ -3212,6 +3218,11 @@ namespace Emby.Server.Implementations.Data private List<string> GetWhereClauses(InternalItemsQuery query, IStatement statement, string paramSuffix = "") { + if (query.IsResumable ?? false) + { + query.IsVirtualItem = false; + } + var whereClauses = new List<string>(); if (EnableJoinUserData(query)) @@ -3372,9 +3383,9 @@ namespace Emby.Server.Implementations.Data } } - if (query.SimilarTo != null) + if (query.SimilarTo != null && query.MinSimilarityScore > 0) { - whereClauses.Add("SimilarityScore > 0"); + whereClauses.Add("SimilarityScore > " + (query.MinSimilarityScore - 1).ToString(CultureInfo.InvariantCulture)); } if (query.IsFolder.HasValue) @@ -3616,10 +3627,12 @@ namespace Emby.Server.Implementations.Data var index = 0; foreach (var type in query.TrailerTypes) { - clauses.Add("TrailerTypes like @TrailerTypes" + index); + var paramName = "@TrailerTypes" + index; + + clauses.Add("TrailerTypes like " + paramName); if (statement != null) { - statement.TryBind("@TrailerTypes" + index, "%" + type + "%"); + statement.TryBind(paramName, "%" + type + "%"); } index++; } @@ -4085,27 +4098,6 @@ namespace Emby.Server.Implementations.Data whereClauses.Add("LocationType in (" + val + ")"); } - if (query.ExcludeLocationTypes.Length == 1) - { - if (query.ExcludeLocationTypes[0] == LocationType.Virtual && _config.Configuration.SchemaVersion >= 90) - { - query.IsVirtualItem = false; - } - else - { - whereClauses.Add("LocationType<>@ExcludeLocationTypes"); - if (statement != null) - { - statement.TryBind("@ExcludeLocationTypes", query.ExcludeLocationTypes[0].ToString()); - } - } - } - else if (query.ExcludeLocationTypes.Length > 1) - { - var val = string.Join(",", query.ExcludeLocationTypes.Select(i => "'" + i + "'").ToArray()); - - whereClauses.Add("LocationType not in (" + val + ")"); - } if (query.IsVirtualItem.HasValue) { whereClauses.Add("IsVirtualItem=@IsVirtualItem"); @@ -4221,7 +4213,7 @@ namespace Emby.Server.Implementations.Data var paramName = "@ExcludeProviderId" + index; //excludeIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = '" + pair.Key + "'), '') <> " + paramName + ")"); - excludeIds.Add("ProviderIds not like " + paramName); + excludeIds.Add("(ProviderIds is null or ProviderIds not like " + paramName + ")"); if (statement != null) { statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%"); diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 8e6c1263d..f866c34de 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -361,10 +361,7 @@ namespace Emby.Server.Implementations.Dto if (collectionFolder != null) { dto.OriginalCollectionType = collectionFolder.CollectionType; - - dto.CollectionType = user == null ? - collectionFolder.CollectionType : - collectionFolder.GetViewType(user); + dto.CollectionType = collectionFolder.CollectionType; } if (fields.Contains(ItemFields.CanDelete)) @@ -1515,7 +1512,8 @@ namespace Emby.Server.Implementations.Dto return artist; } } - return item.GetParent(); + + return item.DisplayParent ?? item.GetParent(); } private void AddInheritedImages(BaseItemDto dto, BaseItem item, DtoOptions options, BaseItem owner) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 7ee0c566f..e3cd96894 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -182,6 +182,7 @@ <Compile Include="MediaEncoder\EncodingManager.cs" /> <Compile Include="Migrations\IVersionMigration.cs" /> <Compile Include="Migrations\LibraryScanMigration.cs" /> + <Compile Include="Migrations\GuideMigration.cs" /> <Compile Include="Migrations\UpdateLevelMigration.cs" /> <Compile Include="News\NewsEntryPoint.cs" /> <Compile Include="News\NewsService.cs" /> diff --git a/Emby.Server.Implementations/EntryPoints/SystemEvents.cs b/Emby.Server.Implementations/EntryPoints/SystemEvents.cs index 021ae47ec..4ab6d32f3 100644 --- a/Emby.Server.Implementations/EntryPoints/SystemEvents.cs +++ b/Emby.Server.Implementations/EntryPoints/SystemEvents.cs @@ -6,15 +6,16 @@ using System.Threading.Tasks; using MediaBrowser.Model.System; using MediaBrowser.Controller.Plugins; using MediaBrowser.Common; +using MediaBrowser.Controller; namespace Emby.Server.Implementations.EntryPoints { public class SystemEvents : IServerEntryPoint { private readonly ISystemEvents _systemEvents; - private readonly IApplicationHost _appHost; + private readonly IServerApplicationHost _appHost; - public SystemEvents(ISystemEvents systemEvents, IApplicationHost appHost) + public SystemEvents(ISystemEvents systemEvents, IServerApplicationHost appHost) { _systemEvents = systemEvents; _appHost = appHost; diff --git a/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs index 1b897ca29..9fbe06673 100644 --- a/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs +++ b/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs @@ -10,6 +10,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; namespace Emby.Server.Implementations.EntryPoints @@ -19,7 +20,7 @@ namespace Emby.Server.Implementations.EntryPoints /// </summary> public class UsageEntryPoint : IServerEntryPoint { - private readonly IApplicationHost _applicationHost; + private readonly IServerApplicationHost _applicationHost; private readonly IHttpClient _httpClient; private readonly ILogger _logger; private readonly ISessionManager _sessionManager; @@ -28,7 +29,7 @@ namespace Emby.Server.Implementations.EntryPoints private readonly ConcurrentDictionary<Guid, ClientInfo> _apps = new ConcurrentDictionary<Guid, ClientInfo>(); - public UsageEntryPoint(ILogger logger, IApplicationHost applicationHost, IHttpClient httpClient, ISessionManager sessionManager, IUserManager userManager, IServerConfigurationManager config) + public UsageEntryPoint(ILogger logger, IServerApplicationHost applicationHost, IHttpClient httpClient, ISessionManager sessionManager, IUserManager userManager, IServerConfigurationManager config) { _logger = logger; _applicationHost = applicationHost; diff --git a/Emby.Server.Implementations/EntryPoints/UsageReporter.cs b/Emby.Server.Implementations/EntryPoints/UsageReporter.cs index be848acb7..31254c6c2 100644 --- a/Emby.Server.Implementations/EntryPoints/UsageReporter.cs +++ b/Emby.Server.Implementations/EntryPoints/UsageReporter.cs @@ -8,19 +8,20 @@ using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Controller; using MediaBrowser.Model.Logging; namespace Emby.Server.Implementations.EntryPoints { public class UsageReporter { - private readonly IApplicationHost _applicationHost; + private readonly IServerApplicationHost _applicationHost; private readonly IHttpClient _httpClient; private readonly IUserManager _userManager; private readonly ILogger _logger; private const string MbAdminUrl = "https://www.mb3admin.com/admin/"; - public UsageReporter(IApplicationHost applicationHost, IHttpClient httpClient, IUserManager userManager, ILogger logger) + public UsageReporter(IServerApplicationHost applicationHost, IHttpClient httpClient, IUserManager userManager, ILogger logger) { _applicationHost = applicationHost; _httpClient = httpClient; diff --git a/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs index 5bb21d02a..f841b8b6b 100644 --- a/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs +++ b/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs @@ -61,92 +61,92 @@ namespace Emby.Server.Implementations.FileOrganization }; try - { - if (_libraryMonitor.IsPathLocked(path)) { - result.Status = FileSortingStatus.Failure; - result.StatusMessage = "Path is locked by other processes. Please try again later."; - return result; - } + if (_libraryMonitor.IsPathLocked(path)) + { + result.Status = FileSortingStatus.Failure; + result.StatusMessage = "Path is locked by other processes. Please try again later."; + return result; + } - var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(); - var resolver = new EpisodeResolver(namingOptions, new NullLogger()); + var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(); + var resolver = new EpisodeResolver(namingOptions, new NullLogger()); - var episodeInfo = resolver.Resolve(path, false) ?? - new MediaBrowser.Naming.TV.EpisodeInfo(); + var episodeInfo = resolver.Resolve(path, false) ?? + new MediaBrowser.Naming.TV.EpisodeInfo(); - var seriesName = episodeInfo.SeriesName; + var seriesName = episodeInfo.SeriesName; - if (!string.IsNullOrEmpty(seriesName)) - { - var seasonNumber = episodeInfo.SeasonNumber; + if (!string.IsNullOrEmpty(seriesName)) + { + var seasonNumber = episodeInfo.SeasonNumber; - result.ExtractedSeasonNumber = seasonNumber; + result.ExtractedSeasonNumber = seasonNumber; - // Passing in true will include a few extra regex's - var episodeNumber = episodeInfo.EpisodeNumber; + // Passing in true will include a few extra regex's + var episodeNumber = episodeInfo.EpisodeNumber; - result.ExtractedEpisodeNumber = episodeNumber; + result.ExtractedEpisodeNumber = episodeNumber; - var premiereDate = episodeInfo.IsByDate ? - new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value) : - (DateTime?)null; + var premiereDate = episodeInfo.IsByDate ? + new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value) : + (DateTime?)null; - if (episodeInfo.IsByDate || (seasonNumber.HasValue && episodeNumber.HasValue)) - { - if (episodeInfo.IsByDate) + if (episodeInfo.IsByDate || (seasonNumber.HasValue && episodeNumber.HasValue)) { - _logger.Debug("Extracted information from {0}. Series name {1}, Date {2}", path, seriesName, premiereDate.Value); + if (episodeInfo.IsByDate) + { + _logger.Debug("Extracted information from {0}. Series name {1}, Date {2}", path, seriesName, premiereDate.Value); + } + else + { + _logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, seasonNumber, episodeNumber); + } + + var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber; + + result.ExtractedEndingEpisodeNumber = endingEpisodeNumber; + + await OrganizeEpisode(path, + seriesName, + seasonNumber, + episodeNumber, + endingEpisodeNumber, + premiereDate, + options, + overwriteExisting, + false, + result, + cancellationToken).ConfigureAwait(false); } else { - _logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, seasonNumber, episodeNumber); + var msg = string.Format("Unable to determine episode number from {0}", path); + result.Status = FileSortingStatus.Failure; + result.StatusMessage = msg; + _logger.Warn(msg); } - - var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber; - - result.ExtractedEndingEpisodeNumber = endingEpisodeNumber; - - await OrganizeEpisode(path, - seriesName, - seasonNumber, - episodeNumber, - endingEpisodeNumber, - premiereDate, - options, - overwriteExisting, - false, - result, - cancellationToken).ConfigureAwait(false); } else { - var msg = string.Format("Unable to determine episode number from {0}", path); + var msg = string.Format("Unable to determine series name from {0}", path); result.Status = FileSortingStatus.Failure; result.StatusMessage = msg; _logger.Warn(msg); } - } - else - { - var msg = string.Format("Unable to determine series name from {0}", path); - result.Status = FileSortingStatus.Failure; - result.StatusMessage = msg; - _logger.Warn(msg); - } - var previousResult = _organizationService.GetResultBySourcePath(path); + var previousResult = _organizationService.GetResultBySourcePath(path); - if (previousResult != null) - { - // Don't keep saving the same result over and over if nothing has changed - if (previousResult.Status == result.Status && previousResult.StatusMessage == result.StatusMessage && result.Status != FileSortingStatus.Success) + if (previousResult != null) { - return previousResult; + // Don't keep saving the same result over and over if nothing has changed + if (previousResult.Status == result.Status && previousResult.StatusMessage == result.StatusMessage && result.Status != FileSortingStatus.Success) + { + return previousResult; + } } - } - await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false); + await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { @@ -162,58 +162,60 @@ namespace Emby.Server.Implementations.FileOrganization var result = _organizationService.GetResult(request.ResultId); try - { - Series series = null; - - if (request.NewSeriesProviderIds.Count > 0) { - // We're having a new series here - SeriesInfo seriesRequest = new SeriesInfo(); - seriesRequest.ProviderIds = request.NewSeriesProviderIds; - - var refreshOptions = new MetadataRefreshOptions(_fileSystem); - series = new Series(); - series.Id = Guid.NewGuid(); - series.Name = request.NewSeriesName; + Series series = null; - int year; - if (int.TryParse(request.NewSeriesYear, out year)) + if (request.NewSeriesProviderIds.Count > 0) { - series.ProductionYear = year; - } + // We're having a new series here + SeriesInfo seriesRequest = new SeriesInfo(); + seriesRequest.ProviderIds = request.NewSeriesProviderIds; - var seriesFolderName = series.Name; - if (series.ProductionYear.HasValue) - { - seriesFolderName = string.Format("{0} ({1})", seriesFolderName, series.ProductionYear); - } + var refreshOptions = new MetadataRefreshOptions(_fileSystem); + series = new Series(); + series.Id = Guid.NewGuid(); + series.Name = request.NewSeriesName; + + int year; + if (int.TryParse(request.NewSeriesYear, out year)) + { + series.ProductionYear = year; + } - series.Path = Path.Combine(request.TargetFolder, seriesFolderName); + var seriesFolderName = series.Name; + if (series.ProductionYear.HasValue) + { + seriesFolderName = string.Format("{0} ({1})", seriesFolderName, series.ProductionYear); + } - series.ProviderIds = request.NewSeriesProviderIds; + seriesFolderName = _fileSystem.GetValidFilename(seriesFolderName); - await series.RefreshMetadata(refreshOptions, cancellationToken); - } + series.Path = Path.Combine(request.TargetFolder, seriesFolderName); - if (series == null) - { - // Existing Series - series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId)); - } + series.ProviderIds = request.NewSeriesProviderIds; - await OrganizeEpisode(result.OriginalPath, - series, - request.SeasonNumber, - request.EpisodeNumber, - request.EndingEpisodeNumber, - null, - options, - true, - request.RememberCorrection, - result, - cancellationToken).ConfigureAwait(false); + await series.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); + } + + if (series == null) + { + // Existing Series + series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId)); + } + + await OrganizeEpisode(result.OriginalPath, + series, + request.SeasonNumber, + request.EpisodeNumber, + request.EndingEpisodeNumber, + null, + options, + true, + request.RememberCorrection, + result, + cancellationToken).ConfigureAwait(false); - await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false); + await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { @@ -287,91 +289,91 @@ namespace Emby.Server.Implementations.FileOrganization { throw new Exception("File is currently processed otherwise. Please try again later."); } - - try - { - // Proceed to sort the file - var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options.TvOptions, cancellationToken).ConfigureAwait(false); - - if (string.IsNullOrEmpty(newPath)) - { - var msg = string.Format("Unable to sort {0} because target path could not be determined.", sourcePath); - throw new Exception(msg); - } - - _logger.Info("Sorting file {0} to new path {1}", sourcePath, newPath); - result.TargetPath = newPath; - - var fileExists = _fileSystem.FileExists(result.TargetPath); - var otherDuplicatePaths = GetOtherDuplicatePaths(result.TargetPath, series, seasonNumber, episodeNumber, endingEpiosdeNumber); - if (!overwriteExisting) + try { - if (options.TvOptions.CopyOriginalFile && fileExists && IsSameEpisode(sourcePath, newPath)) - { - var msg = string.Format("File '{0}' already copied to new path '{1}', stopping organization", sourcePath, newPath); - _logger.Info(msg); - result.Status = FileSortingStatus.SkippedExisting; - result.StatusMessage = msg; - return; - } + // Proceed to sort the file + var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options.TvOptions, cancellationToken).ConfigureAwait(false); - if (fileExists) + if (string.IsNullOrEmpty(newPath)) { - var msg = string.Format("File '{0}' already exists as '{1}', stopping organization", sourcePath, newPath); - _logger.Info(msg); - result.Status = FileSortingStatus.SkippedExisting; - result.StatusMessage = msg; - result.TargetPath = newPath; - return; + var msg = string.Format("Unable to sort {0} because target path could not be determined.", sourcePath); + throw new Exception(msg); } - if (otherDuplicatePaths.Count > 0) - { - var msg = string.Format("File '{0}' already exists as these:'{1}'. Stopping organization", sourcePath, string.Join("', '", otherDuplicatePaths)); - _logger.Info(msg); - result.Status = FileSortingStatus.SkippedExisting; - result.StatusMessage = msg; - result.DuplicatePaths = otherDuplicatePaths; - return; - } - } + _logger.Info("Sorting file {0} to new path {1}", sourcePath, newPath); + result.TargetPath = newPath; - PerformFileSorting(options.TvOptions, result); - - if (overwriteExisting) - { - var hasRenamedFiles = false; + var fileExists = _fileSystem.FileExists(result.TargetPath); + var otherDuplicatePaths = GetOtherDuplicatePaths(result.TargetPath, series, seasonNumber, episodeNumber, endingEpiosdeNumber); - foreach (var path in otherDuplicatePaths) + if (!overwriteExisting) { - _logger.Debug("Removing duplicate episode {0}", path); - - _libraryMonitor.ReportFileSystemChangeBeginning(path); - - var renameRelatedFiles = !hasRenamedFiles && - string.Equals(Path.GetDirectoryName(path), Path.GetDirectoryName(result.TargetPath), StringComparison.OrdinalIgnoreCase); - - if (renameRelatedFiles) + if (options.TvOptions.CopyOriginalFile && fileExists && IsSameEpisode(sourcePath, newPath)) { - hasRenamedFiles = true; + var msg = string.Format("File '{0}' already copied to new path '{1}', stopping organization", sourcePath, newPath); + _logger.Info(msg); + result.Status = FileSortingStatus.SkippedExisting; + result.StatusMessage = msg; + return; } - try + if (fileExists) { - DeleteLibraryFile(path, renameRelatedFiles, result.TargetPath); + var msg = string.Format("File '{0}' already exists as '{1}', stopping organization", sourcePath, newPath); + _logger.Info(msg); + result.Status = FileSortingStatus.SkippedExisting; + result.StatusMessage = msg; + result.TargetPath = newPath; + return; } - catch (IOException ex) + + if (otherDuplicatePaths.Count > 0) { - _logger.ErrorException("Error removing duplicate episode", ex, path); + var msg = string.Format("File '{0}' already exists as these:'{1}'. Stopping organization", sourcePath, string.Join("', '", otherDuplicatePaths)); + _logger.Info(msg); + result.Status = FileSortingStatus.SkippedExisting; + result.StatusMessage = msg; + result.DuplicatePaths = otherDuplicatePaths; + return; } - finally + } + + PerformFileSorting(options.TvOptions, result); + + if (overwriteExisting) + { + var hasRenamedFiles = false; + + foreach (var path in otherDuplicatePaths) { - _libraryMonitor.ReportFileSystemChangeComplete(path, true); + _logger.Debug("Removing duplicate episode {0}", path); + + _libraryMonitor.ReportFileSystemChangeBeginning(path); + + var renameRelatedFiles = !hasRenamedFiles && + string.Equals(Path.GetDirectoryName(path), Path.GetDirectoryName(result.TargetPath), StringComparison.OrdinalIgnoreCase); + + if (renameRelatedFiles) + { + hasRenamedFiles = true; + } + + try + { + DeleteLibraryFile(path, renameRelatedFiles, result.TargetPath); + } + catch (IOException ex) + { + _logger.ErrorException("Error removing duplicate episode", ex, path); + } + finally + { + _libraryMonitor.ReportFileSystemChangeComplete(path, true); + } } } } - } catch (Exception ex) { result.Status = FileSortingStatus.Failure; diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs index ec3dfeb60..ede85fb67 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs @@ -49,6 +49,7 @@ namespace Emby.Server.Implementations.HttpServer.Security string device = null; string client = null; string version = null; + string token = null; if (auth != null) { @@ -56,9 +57,13 @@ namespace Emby.Server.Implementations.HttpServer.Security auth.TryGetValue("Device", out device); auth.TryGetValue("Client", out client); auth.TryGetValue("Version", out version); + auth.TryGetValue("Token", out token); } - var token = httpReq.Headers["X-Emby-Token"]; + if (string.IsNullOrWhiteSpace(token)) + { + token = httpReq.Headers["X-Emby-Token"]; + } if (string.IsNullOrWhiteSpace(token)) { @@ -156,8 +161,10 @@ namespace Emby.Server.Implementations.HttpServer.Security // There should be at least to parts if (parts.Length != 2) return null; + var acceptedNames = new[] { "MediaBrowser", "Emby"}; + // It has to be a digest request - if (!string.Equals(parts[0], "MediaBrowser", StringComparison.OrdinalIgnoreCase)) + if (!acceptedNames.Contains(parts[0] ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { return null; } @@ -174,7 +181,7 @@ namespace Emby.Server.Implementations.HttpServer.Security if (param.Length == 2) { - var value = NormalizeValue (param[1].Trim(new[] { '"' })); + var value = NormalizeValue(param[1].Trim(new[] { '"' })); result.Add(param[0], value); } } @@ -182,14 +189,14 @@ namespace Emby.Server.Implementations.HttpServer.Security return result; } - private string NormalizeValue(string value) - { - if (string.IsNullOrWhiteSpace (value)) - { - return value; - } + private string NormalizeValue(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + return value; + } - return System.Net.WebUtility.HtmlEncode(value); - } + return System.Net.WebUtility.HtmlEncode(value); + } } } diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs index 39033249f..d7f2ffa43 100644 --- a/Emby.Server.Implementations/IO/FileRefresher.cs +++ b/Emby.Server.Implementations/IO/FileRefresher.cs @@ -261,10 +261,11 @@ namespace Emby.Server.Implementations.IO // In order to determine if the file is being written to, we have to request write access // But if the server only has readonly access, this is going to cause this entire algorithm to fail // So we'll take a best guess about our access level - var requestedFileAccess = ConfigurationManager.Configuration.SaveLocalMeta - ? FileAccessMode.ReadWrite - : FileAccessMode.Read; + //var requestedFileAccess = ConfigurationManager.Configuration.SaveLocalMeta + // ? FileAccessMode.ReadWrite + // : FileAccessMode.Read; + var requestedFileAccess = FileAccessMode.Read; try { using (_fileSystem.GetFileStream(path, FileOpenMode.Open, requestedFileAccess, FileShareMode.ReadWrite)) diff --git a/Emby.Server.Implementations/Intros/DefaultIntroProvider.cs b/Emby.Server.Implementations/Intros/DefaultIntroProvider.cs index 180f6aba7..4d19a0e9b 100644 --- a/Emby.Server.Implementations/Intros/DefaultIntroProvider.cs +++ b/Emby.Server.Implementations/Intros/DefaultIntroProvider.cs @@ -100,20 +100,30 @@ namespace Emby.Server.Implementations.Intros if (trailerTypes.Count > 0) { + if (trailerTypes.Count >= 5) + { + trailerTypes.Clear(); + } + + // hack - can't filter by user library because local trailers get TopParentId =null in the db. + // for now we have to use a post-query filter afterwards to solve that var trailerResult = _libraryManager.GetItemList(new InternalItemsQuery { IncludeItemTypes = new[] { typeof(Trailer).Name }, TrailerTypes = trailerTypes.ToArray(), SimilarTo = item, - IsPlayed = config.EnableIntrosForWatchedContent ? (bool?)null : false, + //IsPlayed = config.EnableIntrosForWatchedContent ? (bool?)null : false, MaxParentalRating = config.EnableIntrosParentalControl ? ratingLevel : null, BlockUnratedItems = config.EnableIntrosParentalControl ? new[] { UnratedItem.Trailer } : new UnratedItem[] { }, // Account for duplicates by imdb id, since the database doesn't support this yet - Limit = config.TrailerLimit * 2, + Limit = config.TrailerLimit * 4, SourceTypes = sourceTypes.ToArray() - - }).Where(i => string.IsNullOrWhiteSpace(i.GetProviderId(MetadataProviders.Imdb)) || !string.Equals(i.GetProviderId(MetadataProviders.Imdb), item.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)).Take(config.TrailerLimit); + }) + .Where(i => string.IsNullOrWhiteSpace(i.GetProviderId(MetadataProviders.Imdb)) || !string.Equals(i.GetProviderId(MetadataProviders.Imdb), item.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)) + .Where(i => i.IsVisibleStandalone(user)) + .Where(i => config.EnableIntrosForWatchedContent || !i.IsPlayed(user)) + .Take(config.TrailerLimit); candidates.AddRange(trailerResult.Select(i => new ItemWithTrailer { diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 4c788a2ab..c59a22884 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1956,30 +1956,6 @@ namespace Emby.Server.Implementations.Library var options = collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions(); - if (options.SchemaVersion < 3) - { - options.SaveLocalMetadata = ConfigurationManager.Configuration.SaveLocalMeta; - options.EnableInternetProviders = ConfigurationManager.Configuration.EnableInternetProviders; - } - - if (options.SchemaVersion < 2) - { - var chapterOptions = ConfigurationManager.GetConfiguration<ChapterOptions>("chapters"); - options.ExtractChapterImagesDuringLibraryScan = chapterOptions.ExtractDuringLibraryScan; - - if (collectionFolder != null) - { - if (string.Equals(collectionFolder.CollectionType, "movies", StringComparison.OrdinalIgnoreCase)) - { - options.EnableChapterImageExtraction = chapterOptions.EnableMovieChapterImageExtraction; - } - else if (string.Equals(collectionFolder.CollectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) - { - options.EnableChapterImageExtraction = chapterOptions.EnableEpisodeChapterImageExtraction; - } - } - } - return options; } @@ -2034,7 +2010,7 @@ namespace Emby.Server.Implementations.Library private string GetContentTypeOverride(string path, bool inherit) { - var nameValuePair = ConfigurationManager.Configuration.ContentTypes.FirstOrDefault(i => string.Equals(i.Name, path, StringComparison.OrdinalIgnoreCase) || (inherit && !string.IsNullOrWhiteSpace(i.Name) && _fileSystem.ContainsSubPath(i.Name, path))); + var nameValuePair = ConfigurationManager.Configuration.ContentTypes.FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path) || (inherit && !string.IsNullOrWhiteSpace(i.Name) && _fileSystem.ContainsSubPath(i.Name, path))); if (nameValuePair != null) { return nameValuePair.Value; @@ -2505,6 +2481,8 @@ namespace Emby.Server.Implementations.Library options.VideoFileExtensions.Remove(".zip"); } + options.VideoFileExtensions.Add(".tp"); + return options; } @@ -2615,7 +2593,7 @@ namespace Emby.Server.Implementations.Library { foreach (var pathInfo in libraryOptions.PathInfos) { - if (string.IsNullOrWhiteSpace(pathInfo.NetworkPath)) + if (string.IsNullOrWhiteSpace(pathInfo.Path) || string.IsNullOrWhiteSpace(pathInfo.NetworkPath)) { continue; } @@ -2643,10 +2621,13 @@ namespace Emby.Server.Implementations.Library foreach (var map in ConfigurationManager.Configuration.PathSubstitutions) { - var substitutionResult = SubstitutePathInternal(path, map.From, map.To); - if (substitutionResult.Item2) + if (!string.IsNullOrWhiteSpace(map.From)) { - return substitutionResult.Item1; + var substitutionResult = SubstitutePathInternal(path, map.From, map.To); + if (substitutionResult.Item2) + { + return substitutionResult.Item1; + } } } @@ -3088,7 +3069,7 @@ namespace Emby.Server.Implementations.Library { removeList.Add(contentType); } - else if (string.Equals(path, contentType.Name, StringComparison.OrdinalIgnoreCase) + else if (_fileSystem.AreEqual(path, contentType.Name) || _fileSystem.ContainsSubPath(path, contentType.Name)) { removeList.Add(contentType); diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 93c406ebc..c1bd8fe91 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -199,6 +199,8 @@ namespace Emby.Server.Implementations.Library foreach (var mediaSource in list) { + mediaSource.InferTotalBitrate(); + SetKeyProperties(provider, mediaSource); } diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs index 7669dd0bf..9d07837c6 100644 --- a/Emby.Server.Implementations/Library/MusicManager.cs +++ b/Emby.Server.Implementations/Library/MusicManager.cs @@ -105,11 +105,10 @@ namespace Emby.Server.Implementations.Library return inputItems .Cast<Audio>() .Select(i => new Tuple<Audio, int>(i, i.Genres.Count(genresDictionary.ContainsKey))) - .Where(i => i.Item2 > 0) .OrderByDescending(i => i.Item2) .ThenBy(i => Guid.NewGuid()) .Select(i => i.Item1) - .Take(100) + .Take(200) .OrderBy(i => Guid.NewGuid()); } diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index 871b2d46d..b3d6d4ad7 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -142,12 +142,14 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio } } } - - var fullName = fileSystemInfo.FullName; - - if (libraryManager.IsAudioFile(fullName, libraryOptions)) + else { - return true; + var fullName = fileSystemInfo.FullName; + + if (libraryManager.IsAudioFile(fullName, libraryOptions)) + { + return true; + } } } diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs index afdf65c06..e6c88aa1a 100644 --- a/Emby.Server.Implementations/Library/SearchEngine.cs +++ b/Emby.Server.Implementations/Library/SearchEngine.cs @@ -157,6 +157,7 @@ namespace Emby.Server.Implementations.Library } AddIfMissing(excludeItemTypes, typeof(CollectionFolder).Name); + AddIfMissing(excludeItemTypes, typeof(Folder).Name); var mediaItems = _libraryManager.GetItemList(new InternalItemsQuery(user) { @@ -164,8 +165,7 @@ namespace Emby.Server.Implementations.Library ExcludeItemTypes = excludeItemTypes.ToArray(), IncludeItemTypes = includeItemTypes.ToArray(), Limit = query.Limit, - IncludeItemsByName = true, - IsVirtualItem = false + IncludeItemsByName = true }); // Add search hints based on item name diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 2a5706b3b..eb0d0cf9b 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -236,29 +236,63 @@ namespace Emby.Server.Implementations.Library var user = Users .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase)); - if (user == null) - { - throw new SecurityException("Invalid username or password entered."); - } + var success = false; - if (user.Policy.IsDisabled) + if (user != null) { - throw new SecurityException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name)); - } + // Authenticate using local credentials if not a guest + if (!user.ConnectLinkType.HasValue || user.ConnectLinkType.Value != UserLinkType.Guest) + { + success = string.Equals(GetPasswordHash(user), passwordSha1.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); - var success = false; + if (!success && _networkManager.IsInLocalNetwork(remoteEndPoint) && user.Configuration.EnableLocalPassword) + { + success = string.Equals(GetLocalPasswordHash(user), passwordSha1.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); + } + } - // Authenticate using local credentials if not a guest - if (!user.ConnectLinkType.HasValue || user.ConnectLinkType.Value != UserLinkType.Guest) + // Maybe user accidently entered connect credentials. let's be flexible + if (!success && user.ConnectLinkType.HasValue && !string.IsNullOrWhiteSpace(passwordMd5) && !string.IsNullOrWhiteSpace(user.ConnectUserName)) + { + try + { + await _connectFactory().Authenticate(user.ConnectUserName, passwordMd5).ConfigureAwait(false); + success = true; + } + catch + { + + } + } + } + + // Try originally entered username + if (!success && (user == null || !string.Equals(user.ConnectUserName, username, StringComparison.OrdinalIgnoreCase))) { - success = string.Equals(GetPasswordHash(user), passwordSha1.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); + try + { + var connectAuthResult = await _connectFactory().Authenticate(username, passwordMd5).ConfigureAwait(false); - if (!success && _networkManager.IsInLocalNetwork(remoteEndPoint) && user.Configuration.EnableLocalPassword) + user = Users.FirstOrDefault(i => string.Equals(i.ConnectUserId, connectAuthResult.User.Id, StringComparison.OrdinalIgnoreCase)); + + success = user != null; + } + catch { - success = string.Equals(GetLocalPasswordHash(user), passwordSha1.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase); + } } + if (user == null) + { + throw new SecurityException("Invalid username or password entered."); + } + + if (user.Policy.IsDisabled) + { + throw new SecurityException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name)); + } + // Update LastActivityDate and LastLoginDate, then save if (success) { diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index f7cc8bb73..f11cbd498 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -55,8 +55,6 @@ namespace Emby.Server.Implementations.Library }).ToList(); } - var plainFolderIds = user.Configuration.PlainFolderViews.Select(i => new Guid(i)).ToList(); - var groupedFolders = new List<ICollectionFolder>(); var list = new List<Folder>(); @@ -72,12 +70,6 @@ namespace Emby.Server.Implementations.Library continue; } - if (plainFolderIds.Contains(folder.Id) && UserView.IsEligibleForEnhancedView(folderViewType)) - { - list.Add(folder); - continue; - } - if (collectionFolder != null && UserView.IsEligibleForGrouping(folder) && user.IsFolderGrouped(folder.Id)) { groupedFolders.Add(collectionFolder); @@ -287,7 +279,7 @@ namespace Emby.Server.Implementations.Library SortBy = new[] { ItemSortBy.DateCreated }, IsFolder = includeItemTypes.Length == 0 ? false : (bool?)null, ExcludeItemTypes = excludeItemTypes, - ExcludeLocationTypes = new[] { LocationType.Virtual }, + IsVirtualItem = false, Limit = limit * 5, SourceTypes = parents.Count == 0 ? new[] { SourceType.Library } : new SourceType[] { }, IsPlayed = request.IsPlayed diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 2e591711b..5e0b4ff34 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -150,7 +150,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV foreach (var recordingFolder in recordingFolders) { var pathsToCreate = recordingFolder.Locations - .Where(i => !allExistingPaths.Contains(i, StringComparer.OrdinalIgnoreCase)) + .Where(i => !allExistingPaths.Any(p => _fileSystem.AreEqual(p, i))) .ToList(); if (pathsToCreate.Count == 0) @@ -1370,13 +1370,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV ActiveRecordingInfo removed; _activeRecordings.TryRemove(timer.Id, out removed); - if (recordingStatus != RecordingStatus.Completed && DateTime.UtcNow < timer.EndDate) + if (recordingStatus != RecordingStatus.Completed && DateTime.UtcNow < timer.EndDate && timer.RetryCount < 10) { const int retryIntervalSeconds = 60; _logger.Info("Retrying recording in {0} seconds.", retryIntervalSeconds); timer.Status = RecordingStatus.New; timer.StartDate = DateTime.UtcNow.AddSeconds(retryIntervalSeconds); + timer.RetryCount++; _timerProvider.AddOrUpdate(timer); } else if (_fileSystem.FileExists(recordPath)) @@ -2106,13 +2107,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { return true; } - - if (!seriesTimer.Days.Contains(timer.StartDate.ToLocalTime().DayOfWeek)) - { - return true; - } } + //if (!seriesTimer.Days.Contains(timer.StartDate.ToLocalTime().DayOfWeek)) + //{ + // return true; + //} + if (seriesTimer.RecordNewOnly && timer.IsRepeat) { return true; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 5e55b893f..beb08cc25 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -240,14 +240,49 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { try { - _logger.Info("Killing ffmpeg recording process for {0}", _targetPath); + _logger.Info("Stopping ffmpeg recording process for {0}", _targetPath); //process.Kill(); _process.StandardInput.WriteLine("q"); } catch (Exception ex) { - _logger.ErrorException("Error killing transcoding job for {0}", ex, _targetPath); + _logger.ErrorException("Error stopping recording transcoding job for {0}", ex, _targetPath); + } + + if (_hasExited) + { + return; + } + + try + { + _logger.Info("Calling recording process.WaitForExit for {0}", _targetPath); + + if (_process.WaitForExit(5000)) + { + return; + } + } + catch (Exception ex) + { + _logger.ErrorException("Error waiting for recording process to exit for {0}", ex, _targetPath); + } + + if (_hasExited) + { + return; + } + + try + { + _logger.Info("Killing ffmpeg recording process for {0}", _targetPath); + + _process.Kill(); + } + catch (Exception ex) + { + _logger.ErrorException("Error killing recording transcoding job for {0}", ex, _targetPath); } } } diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index e2446b16f..46b914232 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -182,7 +182,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings programsInfo.Add(GetProgram(channelNumber, schedule, programDict[schedule.programID])); } - _logger.Info("Finished with EPGData"); } } @@ -322,8 +321,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings using (var response = await Get(httpOptions, true, info).ConfigureAwait(false)) { var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response); - _logger.Info("Found " + root.map.Count + " channels on the lineup on ScheduleDirect"); - _logger.Info("Mapping Stations to Channel"); + foreach (ScheduleDirect.Map map in root.map) { var channelNumber = map.logicalChannelNumber; @@ -353,7 +351,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings }); } } - _logger.Info("Added " + GetChannelPairCacheCount(listingsId) + " channels to the dictionary"); foreach (ChannelInfo channel in channels) { diff --git a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs index a338ae23a..0313e6fde 100644 --- a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs @@ -96,15 +96,7 @@ namespace Emby.Server.Implementations.LiveTv } // Try to estimate this - if (!mediaSource.Bitrate.HasValue) - { - var total = mediaSource.MediaStreams.Select(i => i.BitRate ?? 0).Sum(); - - if (total > 0) - { - mediaSource.Bitrate = total; - } - } + mediaSource.InferTotalBitrate(true); } } } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 62a0738c7..ff76f6bef 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -459,15 +459,7 @@ namespace Emby.Server.Implementations.LiveTv } // Set the total bitrate if not already supplied - if (!mediaSource.Bitrate.HasValue) - { - var total = mediaSource.MediaStreams.Select(i => i.BitRate ?? 0).Sum(); - - if (total > 0) - { - mediaSource.Bitrate = total; - } - } + mediaSource.InferTotalBitrate(); if (!(service is EmbyTV.EmbyTV)) { @@ -1602,7 +1594,7 @@ namespace Emby.Server.Implementations.LiveTv Recursive = true, AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(), IsFolder = false, - ExcludeLocationTypes = new[] { LocationType.Virtual }, + IsVirtualItem = false, Limit = query.Limit, SortBy = new[] { ItemSortBy.DateCreated }, SortOrder = SortOrder.Descending, diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index e0a35686e..dd95660c7 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -200,15 +200,7 @@ namespace Emby.Server.Implementations.LiveTv } // Try to estimate this - if (!mediaSource.Bitrate.HasValue) - { - var total = mediaSource.MediaStreams.Select(i => i.BitRate ?? 0).Sum(); - - if (total > 0) - { - mediaSource.Bitrate = total; - } - } + mediaSource.InferTotalBitrate(); } public Task CloseMediaSource(string liveStreamId) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs index f2e48fbc0..336469c50 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs @@ -104,7 +104,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { Type = HdHomerunHost.DeviceType, Url = url, - DataVersion = 1, DeviceId = response.DeviceID }).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 77efe8585..1c7c0828c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -61,10 +61,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { var id = ChannelIdPrefix + i.GuideNumber; - if (info.DataVersion >= 1) - { - id += '_' + (i.GuideName ?? string.Empty).GetMD5().ToString("N"); - } + id += '_' + (i.GuideName ?? string.Empty).GetMD5().ToString("N"); return id; } @@ -103,7 +100,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun TunerHostId = info.Id, IsHD = i.HD == 1, AudioCodec = i.AudioCodec, - VideoCodec = i.VideoCodec + VideoCodec = i.VideoCodec, + ChannelType = ChannelType.TV }); } @@ -430,6 +428,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun IsInfiniteStream = true }; + mediaSource.InferTotalBitrate(); + return mediaSource; } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs index 4852270d5..625e4457d 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs @@ -25,7 +25,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>(); private readonly MulticastStream _multicastStream; - public HdHomerunLiveStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost) : base(mediaSource) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index 8027ce2dd..601cb2666 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken) { - return await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).Parse(info.Url, ChannelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false); + return await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).Parse(info.Url, ChannelIdPrefix, info.Id, !info.EnableTvgId, cancellationToken).ConfigureAwait(false); } public Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken) @@ -127,6 +127,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { protocol = MediaProtocol.Udp; } + else if (path.StartsWith("rtp", StringComparison.OrdinalIgnoreCase)) + { + protocol = MediaProtocol.Rtmp; + } var mediaSource = new MediaSourceInfo { @@ -155,9 +159,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts ReadAtNativeFramerate = false, Id = channel.Path.GetMD5().ToString("N"), - IsInfiniteStream = true + IsInfiniteStream = true, + SupportsDirectStream = false, + IsRemote = true }; + mediaSource.InferTotalBitrate(); + return new List<MediaSourceInfo> { mediaSource }; } return new List<MediaSourceInfo>(); @@ -168,4 +176,4 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return Task.FromResult(true); } } -} +}
\ No newline at end of file diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index e0f040281..5e191ada9 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -14,6 +14,7 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Extensions; namespace Emby.Server.Implementations.LiveTv.TunerHosts { @@ -32,14 +33,25 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts _appHost = appHost; } - public async Task<List<M3UChannel>> Parse(string url, string channelIdPrefix, string tunerHostId, CancellationToken cancellationToken) + public async Task<List<M3UChannel>> Parse(string url, string channelIdPrefix, string tunerHostId, bool enableStreamUrlAsIdentifier, CancellationToken cancellationToken) { var urlHash = url.GetMD5().ToString("N"); // Read the file and display it line by line. using (var reader = new StreamReader(await GetListingsStream(url, cancellationToken).ConfigureAwait(false))) { - return GetChannels(reader, urlHash, channelIdPrefix, tunerHostId); + return GetChannels(reader, urlHash, channelIdPrefix, tunerHostId, enableStreamUrlAsIdentifier); + } + } + + public List<M3UChannel> ParseString(string text, string channelIdPrefix, string tunerHostId) + { + var urlHash = "text".GetMD5().ToString("N"); + + // Read the file and display it line by line. + using (var reader = new StringReader(text)) + { + return GetChannels(reader, urlHash, channelIdPrefix, tunerHostId, false); } } @@ -59,7 +71,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts } const string ExtInfPrefix = "#EXTINF:"; - private List<M3UChannel> GetChannels(StreamReader reader, string urlHash, string channelIdPrefix, string tunerHostId) + private List<M3UChannel> GetChannels(TextReader reader, string urlHash, string channelIdPrefix, string tunerHostId, bool enableStreamUrlAsIdentifier) { var channels = new List<M3UChannel>(); string line; @@ -85,7 +97,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts else if (!string.IsNullOrWhiteSpace(extInf) && !line.StartsWith("#", StringComparison.OrdinalIgnoreCase)) { var channel = GetChannelnfo(extInf, tunerHostId, line); - channel.Id = channelIdPrefix + urlHash + line.GetMD5().ToString("N"); + if (string.IsNullOrWhiteSpace(channel.Id) || enableStreamUrlAsIdentifier) + { + channel.Id = channelIdPrefix + urlHash + line.GetMD5().ToString("N"); + } + else + { + channel.Id = channelIdPrefix + urlHash + channel.Id.GetMD5().ToString("N"); + } + channel.Path = line; channels.Add(channel); extInf = ""; @@ -114,6 +134,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts channel.Name = GetChannelName(extInf, attributes); channel.Number = GetChannelNumber(extInf, attributes, mediaUrl); + if (attributes.TryGetValue("tvg-id", out value)) + { + channel.Id = value; + } + return channel; } @@ -122,18 +147,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts var nameParts = extInf.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var nameInExtInf = nameParts.Length > 1 ? nameParts.Last().Trim() : null; - var numberString = nameParts[0]; + string numberString = null; - //Check for channel number with the format from SatIp - int number; + // Check for channel number with the format from SatIp + // #EXTINF:0,84. VOX Schweiz + // #EXTINF:0,84.0 - VOX Schweiz if (!string.IsNullOrWhiteSpace(nameInExtInf)) { - var numberIndex = nameInExtInf.IndexOf('.'); + var numberIndex = nameInExtInf.IndexOf(' '); if (numberIndex > 0) { - if (int.TryParse(nameInExtInf.Substring(0, numberIndex), out number)) + var numberPart = nameInExtInf.Substring(0, numberIndex).Trim(new[] { ' ', '.' }); + + double number; + if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out number)) { - numberString = number.ToString(); + numberString = numberPart; } } } @@ -150,7 +179,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts string value; if (attributes.TryGetValue("tvg-id", out value)) { - numberString = value; + double doubleValue; + if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out doubleValue)) + { + numberString = value; + } } } @@ -208,17 +241,21 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts var nameParts = extInf.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var nameInExtInf = nameParts.Length > 1 ? nameParts.Last().Trim() : null; - //Check for channel number with the format from SatIp - int number; + // Check for channel number with the format from SatIp + // #EXTINF:0,84. VOX Schweiz + // #EXTINF:0,84.0 - VOX Schweiz if (!string.IsNullOrWhiteSpace(nameInExtInf)) { - var numberIndex = nameInExtInf.IndexOf('.'); + var numberIndex = nameInExtInf.IndexOf(' '); if (numberIndex > 0) { - if (int.TryParse(nameInExtInf.Substring(0, numberIndex), out number)) + var numberPart = nameInExtInf.Substring(0, numberIndex).Trim(new[] { ' ', '.' }); + + double number; + if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out number)) { //channel.Number = number.ToString(); - nameInExtInf = nameInExtInf.Substring(numberIndex + 1); + nameInExtInf = nameInExtInf.Substring(numberIndex + 1).Trim(new[] { ' ', '-' }); } } } @@ -250,20 +287,18 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts var reg = new Regex(@"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase); var matches = reg.Matches(line); - var minIndex = int.MaxValue; + + remaining = line; + foreach (Match match in matches) { - dict[match.Groups[1].Value] = match.Groups[2].Value; - minIndex = Math.Min(minIndex, match.Index); - } + var key = match.Groups[1].Value; + var value = match.Groups[2].Value; - if (minIndex > 0 && minIndex < line.Length) - { - line = line.Substring(0, minIndex); + dict[match.Groups[1].Value] = match.Groups[2].Value; + remaining = remaining.Replace(key + "=\"" + value + "\"", string.Empty, StringComparison.OrdinalIgnoreCase); } - remaining = line; - return dict; } } diff --git a/Emby.Server.Implementations/Localization/iso6392.txt b/Emby.Server.Implementations/Localization/iso6392.txt index 665a5375e..5616d41bc 100644 --- a/Emby.Server.Implementations/Localization/iso6392.txt +++ b/Emby.Server.Implementations/Localization/iso6392.txt @@ -137,6 +137,7 @@ fon|||Fon|fon fre|fra|fr|French|français frm|||French, Middle (ca.1400-1600)|français moyen (1400-1600) fro|||French, Old (842-ca.1400)|français ancien (842-ca.1400) +frc||fr-ca|French (Canada)|french frr|||Northern Frisian|frison septentrional frs|||Eastern Frisian|frison oriental fry||fy|Western Frisian|frison occidental diff --git a/Emby.Server.Implementations/Migrations/GuideMigration.cs b/Emby.Server.Implementations/Migrations/GuideMigration.cs new file mode 100644 index 000000000..71286b282 --- /dev/null +++ b/Emby.Server.Implementations/Migrations/GuideMigration.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Common.Updates; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Tasks; +using MediaBrowser.Model.Updates; +using System.Linq; + +namespace Emby.Server.Implementations.Migrations +{ + public class GuideMigration : IVersionMigration + { + private readonly IServerConfigurationManager _config; + private readonly ITaskManager _taskManager; + + public GuideMigration(IServerConfigurationManager config, ITaskManager taskManager) + { + _config = config; + _taskManager = taskManager; + } + + public async Task Run() + { + var name = "GuideRefresh2"; + + if (!_config.Configuration.Migrations.Contains(name, StringComparer.OrdinalIgnoreCase)) + { + Task.Run(() => + { + _taskManager.QueueScheduledTask(_taskManager.ScheduledTasks.Select(i => i.ScheduledTask) + .First(i => string.Equals(i.Key, "RefreshGuide", StringComparison.OrdinalIgnoreCase))); + }); + + var list = _config.Configuration.Migrations.ToList(); + list.Add(name); + _config.Configuration.Migrations = list.ToArray(); + _config.SaveConfiguration(); + } + } + } +} diff --git a/Emby.Server.Implementations/Migrations/LibraryScanMigration.cs b/Emby.Server.Implementations/Migrations/LibraryScanMigration.cs index 600dd8033..bd185bc9c 100644 --- a/Emby.Server.Implementations/Migrations/LibraryScanMigration.cs +++ b/Emby.Server.Implementations/Migrations/LibraryScanMigration.cs @@ -35,8 +35,6 @@ namespace Emby.Server.Implementations.Migrations { _taskManager.QueueScheduledTask(_taskManager.ScheduledTasks.Select(i => i.ScheduledTask) .First(i => string.Equals(i.Key, "RefreshLibrary", StringComparison.OrdinalIgnoreCase))); - _taskManager.QueueScheduledTask(_taskManager.ScheduledTasks.Select(i => i.ScheduledTask) - .First(i => string.Equals(i.Key, "RefreshGuide", StringComparison.OrdinalIgnoreCase))); }); var list = _config.Configuration.Migrations.ToList(); diff --git a/Emby.Server.Implementations/Notifications/Notifications.cs b/Emby.Server.Implementations/Notifications/Notifications.cs index 2d441c18c..8f24dfe1a 100644 --- a/Emby.Server.Implementations/Notifications/Notifications.cs +++ b/Emby.Server.Implementations/Notifications/Notifications.cs @@ -430,6 +430,23 @@ namespace Emby.Server.Implementations.Notifications return name; } + public static string GetItemName(BaseItemInfo item) + { + var name = item.Name; + + if (!string.IsNullOrWhiteSpace(item.SeriesName)) + { + name = item.SeriesName + " - " + name; + } + + if (item.Artists != null && item.Artists.Count > 0) + { + name = item.Artists[0] + " - " + name; + } + + return name; + } + async void _userManager_UserCreated(object sender, GenericEventArgs<User> e) { var notification = new NotificationRequest diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 9583141e0..386da73c6 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -100,7 +100,7 @@ namespace Emby.Server.Implementations.Playlists if (string.IsNullOrWhiteSpace(options.MediaType)) { - throw new ArgumentException("A playlist media type is required."); + options.MediaType = "Audio"; } var user = _userManager.GetUserById(options.UserId); diff --git a/Emby.Server.Implementations/Sync/SyncHelper.cs b/Emby.Server.Implementations/Sync/SyncHelper.cs index da475f003..7fe703796 100644 --- a/Emby.Server.Implementations/Sync/SyncHelper.cs +++ b/Emby.Server.Implementations/Sync/SyncHelper.cs @@ -4,7 +4,7 @@ namespace Emby.Server.Implementations.Sync { public class SyncHelper { - public static int? AdjustBitrate(int? profileBitrate, string quality) + public static long? AdjustBitrate(long? profileBitrate, string quality) { if (profileBitrate.HasValue) { diff --git a/Emby.Server.Implementations/Sync/SyncJobProcessor.cs b/Emby.Server.Implementations/Sync/SyncJobProcessor.cs index b1adc64df..17cdef5fc 100644 --- a/Emby.Server.Implementations/Sync/SyncJobProcessor.cs +++ b/Emby.Server.Implementations/Sync/SyncJobProcessor.cs @@ -59,15 +59,8 @@ namespace Emby.Server.Implementations.Sync _mediaSourceManager = mediaSourceManager; } - public async Task EnsureJobItems(SyncJob job) + public async Task EnsureJobItems(SyncJob job, User user) { - var user = _userManager.GetUserById(job.UserId); - - if (user == null) - { - throw new InvalidOperationException("Cannot proceed with sync because user no longer exists."); - } - var items = (await GetItemsForSync(job.Category, job.ParentId, job.RequestedItemIds, user, job.UnwatchedOnly).ConfigureAwait(false)) .ToList(); @@ -385,7 +378,16 @@ namespace Emby.Server.Implementations.Sync if (job.SyncNewContent) { - await EnsureJobItems(job).ConfigureAwait(false); + var user = _userManager.GetUserById(job.UserId); + + if (user == null) + { + await _syncManager.CancelJob(job.Id).ConfigureAwait(false); + } + else + { + await EnsureJobItems(job, user).ConfigureAwait(false); + } } } } diff --git a/Emby.Server.Implementations/Sync/SyncManager.cs b/Emby.Server.Implementations/Sync/SyncManager.cs index 310b35afe..418d42c9a 100644 --- a/Emby.Server.Implementations/Sync/SyncManager.cs +++ b/Emby.Server.Implementations/Sync/SyncManager.cs @@ -181,7 +181,7 @@ namespace Emby.Server.Implementations.Sync await _repo.Create(job).ConfigureAwait(false); - await processor.EnsureJobItems(job).ConfigureAwait(false); + await processor.EnsureJobItems(job, user).ConfigureAwait(false); // If it already has a converting status then is must have been aborted during conversion var jobItemsResult = GetJobItems(new SyncJobItemQuery @@ -560,6 +560,12 @@ namespace Emby.Server.Implementations.Sync { var jobItem = _repo.GetJobItem(id); + if (jobItem == null) + { + _logger.Debug("ReportSyncJobItemTransferred: SyncJobItem {0} doesn't exist anymore", id); + return; + } + jobItem.Status = SyncJobItemStatus.Synced; jobItem.Progress = 100; diff --git a/Emby.Server.Implementations/Sync/TargetDataProvider.cs b/Emby.Server.Implementations/Sync/TargetDataProvider.cs index fbd82aa7a..cac8f0cd8 100644 --- a/Emby.Server.Implementations/Sync/TargetDataProvider.cs +++ b/Emby.Server.Implementations/Sync/TargetDataProvider.cs @@ -19,6 +19,7 @@ namespace Emby.Server.Implementations.Sync private readonly IServerSyncProvider _provider; private readonly SemaphoreSlim _dataLock = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim _remoteDataLock = new SemaphoreSlim(1, 1); private List<LocalItem> _items; private readonly ILogger _logger; @@ -63,15 +64,24 @@ namespace Emby.Server.Implementations.Sync { _logger.Debug("Getting {0} from {1}", string.Join(MediaSync.PathSeparatorString, GetRemotePath().ToArray()), _provider.Name); - var fileResult = await _provider.GetFiles(GetRemotePath().ToArray(), _target, cancellationToken).ConfigureAwait(false); + await _remoteDataLock.WaitAsync(cancellationToken).ConfigureAwait(false); - if (fileResult.Items.Length > 0) + try { - using (var stream = await _provider.GetFile(fileResult.Items[0].FullName, _target, new Progress<double>(), cancellationToken)) + var fileResult = await _provider.GetFiles(GetRemotePath().ToArray(), _target, cancellationToken).ConfigureAwait(false); + + if (fileResult.Items.Length > 0) { - return _json.DeserializeFromStream<List<LocalItem>>(stream); + using (var stream = await _provider.GetFile(fileResult.Items[0].FullName, _target, new Progress<double>(), cancellationToken)) + { + return _json.DeserializeFromStream<List<LocalItem>>(stream); + } } } + finally + { + _remoteDataLock.Release(); + } return new List<LocalItem>(); } @@ -93,9 +103,19 @@ namespace Emby.Server.Implementations.Sync // Save to sync provider stream.Position = 0; var remotePath = GetRemotePath(); - _logger.Debug("Saving data.json to {0}. Remote path: {1}", _provider.Name, string.Join("/", remotePath)); - await _provider.SendFile(stream, remotePath, _target, new Progress<double>(), cancellationToken).ConfigureAwait(false); + await _remoteDataLock.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + _logger.Debug("Saving data.json to {0}. Remote path: {1}", _provider.Name, string.Join("/", remotePath)); + + await _provider.SendFile(stream, remotePath, _target, new Progress<double>(), cancellationToken).ConfigureAwait(false); + } + finally + { + _remoteDataLock.Release(); + } } } diff --git a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs index bef964c6f..b8d03db3d 100644 --- a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs +++ b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs @@ -151,7 +151,9 @@ namespace Emby.Server.Implementations.UserViews string[] collectionStripViewTypes = { CollectionType.Movies, - CollectionType.TvShows + CollectionType.TvShows, + CollectionType.Playlists, + CollectionType.Photos }; return collectionStripViewTypes.Contains(view.ViewType ?? string.Empty); diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 15c1cbe82..e654cc8f6 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -666,7 +666,7 @@ namespace MediaBrowser.Api.Library IncludeItemTypes = new[] { type.Name }, Limit = 0, Recursive = true, - ExcludeLocationTypes = new[] { LocationType.Virtual }, + IsVirtualItem = false, SourceTypes = new[] { SourceType.Library }, IsFavorite = request.IsFavorite }; diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 7f568183e..33f687ce7 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -205,7 +205,8 @@ namespace MediaBrowser.Api.Playback } else { - args += "-map -0:v"; + // No known video stream + args += "-vn"; } if (state.AudioStream != null) @@ -395,8 +396,6 @@ namespace MediaBrowser.Api.Playback { param += " -crf 23"; } - - param += " -tune zerolatency"; } else if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) @@ -536,6 +535,11 @@ namespace MediaBrowser.Api.Playback } } + if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)) + { + param += " -x264opts:0 subme=0:rc_lookahead=10:me_range=4:me=dia:no_chroma_me:8x8dct=0:partitions=none"; + } + if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) @@ -1492,8 +1496,16 @@ namespace MediaBrowser.Api.Playback return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture)); } + if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) + { + // h264 + return string.Format(" -maxrate {0} -bufsize {1}", + bitrate.Value.ToString(UsCulture), + (bitrate.Value * 2).ToString(UsCulture)); + } + // h264 - return string.Format(" -maxrate {0} -bufsize {1}", + return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}", bitrate.Value.ToString(UsCulture), (bitrate.Value * 2).ToString(UsCulture)); } @@ -1873,7 +1885,7 @@ namespace MediaBrowser.Api.Playback request.AudioCodec = InferAudioCodec(url); } - var state = new StreamState(MediaSourceManager, Logger) + var state = new StreamState(MediaSourceManager, Logger, TranscodingJobType) { Request = request, RequestedUrl = url, @@ -1975,7 +1987,7 @@ namespace MediaBrowser.Api.Playback if (state.OutputVideoBitrate.HasValue && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { var resolution = ResolutionNormalizer.Normalize( - state.VideoStream == null ? (int?) null : state.VideoStream.BitRate, + state.VideoStream == null ? (int?)null : state.VideoStream.BitRate, state.OutputVideoBitrate.Value, state.VideoStream == null ? null : state.VideoStream.Codec, state.OutputVideoCodec, @@ -2691,11 +2703,52 @@ namespace MediaBrowser.Api.Playback { //inputModifier += " -noaccurate_seek"; } + + foreach (var stream in state.MediaSource.MediaStreams) + { + if (!stream.IsExternal && stream.Type != MediaStreamType.Subtitle) + { + if (!string.IsNullOrWhiteSpace(stream.Codec) && stream.Index != -1) + { + var decoder = GetDecoderFromCodec(stream.Codec); + + if (!string.IsNullOrWhiteSpace(decoder)) + { + inputModifier += " -codec:" + stream.Index.ToString(UsCulture) + " " + decoder; + } + } + } + } + //var videoStream = state.VideoStream; + //if (videoStream != null && !string.IsNullOrWhiteSpace(videoStream.Codec)) + //{ + // inputModifier += " -codec:0 " + videoStream.Codec; + + // var audioStream = state.AudioStream; + // if (audioStream != null && !string.IsNullOrWhiteSpace(audioStream.Codec)) + // { + // inputModifier += " -codec:1 " + audioStream.Codec; + // } + //} } return inputModifier; } + private string GetDecoderFromCodec(string codec) + { + if (string.Equals(codec, "mp2", StringComparison.OrdinalIgnoreCase)) + { + return null; + } + if (string.Equals(codec, "aac_latm", StringComparison.OrdinalIgnoreCase)) + { + return null; + } + + return codec; + } + /// <summary> /// Infers the audio codec based on the url /// </summary> diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 84f395518..97072115d 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -103,7 +103,7 @@ namespace MediaBrowser.Api.Playback.Hls throw; } - var waitForSegments = state.SegmentLength >= 10 ? 2 : (state.SegmentLength > 3 || !isLive ? 3 : 3); + var waitForSegments = state.SegmentLength >= 10 ? 2 : 3; await WaitForMinimumSegmentCount(playlist, waitForSegments, cancellationTokenSource.Token).ConfigureAwait(false); } } @@ -223,6 +223,7 @@ namespace MediaBrowser.Api.Playback.Hls protected Stream GetPlaylistFileStream(string path) { var tmpPath = path + ".tmp"; + tmpPath = path; try { @@ -242,7 +243,7 @@ namespace MediaBrowser.Api.Playback.Hls var threads = GetNumberOfThreads(state, false); - var inputModifier = GetInputModifier(state); + var inputModifier = GetInputModifier(state, true); // If isEncoding is true we're actually starting ffmpeg var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0"; @@ -255,10 +256,32 @@ namespace MediaBrowser.Api.Playback.Hls "hls/" + Path.GetFileNameWithoutExtension(outputPath)); } + var useGenericSegmenter = false; + if (useGenericSegmenter) + { + var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state); + + var timeDeltaParam = String.Empty; + + return string.Format("{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0 -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", + inputModifier, + GetInputArgument(state), + threads, + GetMapArgs(state), + GetVideoArguments(state), + GetAudioArguments(state), + state.SegmentLength.ToString(UsCulture), + startNumberParam, + outputPath, + outputTsArg, + timeDeltaParam + ).Trim(); + } + // add when stream copying? // -avoid_negative_ts make_zero -fflags +genpts - var args = string.Format("{0} {1} {2} -map_metadata -1 -map_chapters -1 -threads {3} {4} {5} -fflags +genpts -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9}{10} -y \"{11}\"", + var args = string.Format("{0} {1} {2} -map_metadata -1 -map_chapters -1 -threads {3} {4} {5} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero {6} -hls_time {7} -individual_header_trailer 0 -start_number {8} -hls_list_size {9}{10} -y \"{11}\"", itsOffset, inputModifier, GetInputArgument(state), @@ -286,13 +309,6 @@ namespace MediaBrowser.Api.Playback.Hls return 0; } - protected bool IsLiveStream(StreamState state) - { - var isLiveStream = (state.RunTimeTicks ?? 0) == 0; - - return isLiveStream; - } - public BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext) { } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 171c971f1..e922094ad 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -265,9 +265,15 @@ namespace MediaBrowser.Api.Playback.Hls double startSeconds = 0; var lengths = GetSegmentLengths(state); + if (requestedIndex >= lengths.Length) + { + var msg = string.Format("Invalid segment index requested: {0} - Segment count: {1}", requestedIndex, lengths.Length); + throw new ArgumentException(msg); + } + for (var i = 0; i < requestedIndex; i++) { - startSeconds += lengths[requestedIndex]; + startSeconds += lengths[i]; } var position = TimeSpan.FromSeconds(startSeconds).Ticks; @@ -279,9 +285,15 @@ namespace MediaBrowser.Api.Playback.Hls double startSeconds = 0; var lengths = GetSegmentLengths(state); + if (requestedIndex >= lengths.Length) + { + var msg = string.Format("Invalid segment index requested: {0} - Segment count: {1}", requestedIndex, lengths.Length); + throw new ArgumentException(msg); + } + for (var i = 0; i <= requestedIndex; i++) { - startSeconds += lengths[requestedIndex]; + startSeconds += lengths[i]; } var position = TimeSpan.FromSeconds(startSeconds).Ticks; @@ -348,6 +360,8 @@ namespace MediaBrowser.Api.Playback.Hls return; } + Logger.Debug("Deleting partial HLS file {0}", file.FullName); + try { FileSystem.DeleteFile(file.FullName); @@ -507,7 +521,7 @@ namespace MediaBrowser.Api.Playback.Hls builder.AppendLine("#EXTM3U"); - var isLiveStream = IsLiveStream(state); + var isLiveStream = state.IsSegmentedLiveStream; var queryStringIndex = Request.RawUrl.IndexOf('?'); var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex); @@ -723,7 +737,7 @@ namespace MediaBrowser.Api.Playback.Hls foreach (var length in segmentLengths) { - builder.AppendLine("#EXTINF:" + length.ToString("0.0000", UsCulture) + ","); + builder.AppendLine("#EXTINF:" + length.ToString("0.0000", UsCulture) + ", nodesc"); builder.AppendLine(string.Format("hls1/{0}/{1}{2}{3}", @@ -826,7 +840,7 @@ namespace MediaBrowser.Api.Playback.Hls args += " -bsf:v h264_mp4toannexb"; } - args += " -flags -global_header"; + //args += " -flags -global_header"; } else { @@ -851,7 +865,7 @@ namespace MediaBrowser.Api.Playback.Hls args += GetGraphicalSubtitleParam(state, codec); } - args += " -flags -global_header"; + //args += " -flags -global_header"; } if (EnableCopyTs(state) && args.IndexOf("-copyts", StringComparison.OrdinalIgnoreCase) == -1) @@ -875,27 +889,25 @@ namespace MediaBrowser.Api.Playback.Hls var inputModifier = GetInputModifier(state, false); // If isEncoding is true we're actually starting ffmpeg - var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0"; - - var toTimeParam = string.Empty; - var timestampOffsetParam = string.Empty; - - if (state.IsOutputVideo && !EnableCopyTs(state) && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && (state.Request.StartTimeTicks ?? 0) > 0) - { - timestampOffsetParam = " -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0); - } + var startNumber = GetStartNumber(state); + var startNumberParam = isEncoding ? startNumber.ToString(UsCulture) : "0"; var mapArgs = state.IsOutputVideo ? GetMapArgs(state) : string.Empty; - var enableSplittingOnNonKeyFrames = string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && false; + var useGenericSegmenter = true; - // TODO: check libavformat version for 57 50.100 and use -hls_flags split_by_time - var hlsProtocolSupportsSplittingByTime = false; - - if (enableSplittingOnNonKeyFrames && !hlsProtocolSupportsSplittingByTime) + if (useGenericSegmenter) { var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state); - return string.Format("{0} {10} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} -break_non_keyframes 1 -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", + var timeDeltaParam = String.Empty; + + if (isEncoding && startNumber > 0) + { + var startTime = state.SegmentLength * startNumber; + timeDeltaParam = string.Format("-segment_time_delta -{0}", startTime); + } + + return string.Format("{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0 -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", inputModifier, GetInputArgument(state), threads, @@ -906,27 +918,21 @@ namespace MediaBrowser.Api.Playback.Hls startNumberParam, outputPath, outputTsArg, - toTimeParam - ).Trim(); + timeDeltaParam + ).Trim(); } - var splitByTime = hlsProtocolSupportsSplittingByTime && enableSplittingOnNonKeyFrames; - var splitByTimeArg = splitByTime ? " -hls_flags split_by_time" : ""; - - return string.Format("{0}{12} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4}{5} {6} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {7}{8} -start_number {9} -hls_list_size {10} -y \"{11}\"", + return string.Format("{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {6} -individual_header_trailer 0 -start_number {7} -hls_list_size {8} -y \"{9}\"", inputModifier, GetInputArgument(state), threads, mapArgs, GetVideoArguments(state), - timestampOffsetParam, GetAudioArguments(state), state.SegmentLength.ToString(UsCulture), - splitByTimeArg, startNumberParam, state.HlsListSize.ToString(UsCulture), - outputPath, - toTimeParam + outputPath ).Trim(); } diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 8fb78b6e5..fcb8c34f3 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -175,6 +175,15 @@ namespace MediaBrowser.Api.Playback return ToOptimizedResult(info); } + private T Clone<T>(T obj) + { + // Since we're going to be setting properties on MediaSourceInfos that come out of _mediaSourceManager, we should clone it + // Should we move this directly into MediaSourceManager? + + var json = _json.SerializeToString(obj); + return _json.DeserializeFromString<T>(json); + } + private async Task<PlaybackInfoResponse> GetPlaybackInfo(string id, string userId, string[] supportedLiveMediaTypes, string mediaSourceId = null, string liveStreamId = null) { var result = new PlaybackInfoResponse(); @@ -217,6 +226,8 @@ namespace MediaBrowser.Api.Playback } else { + result.MediaSources = Clone(result.MediaSources); + result.PlaySessionId = Guid.NewGuid().ToString("N"); } @@ -227,7 +238,7 @@ namespace MediaBrowser.Api.Playback PlaybackInfoResponse result, DeviceProfile profile, AuthorizationInfo auth, - int? maxBitrate, + long? maxBitrate, long startTimeTicks, string mediaSourceId, int? audioStreamIndex, @@ -249,7 +260,7 @@ namespace MediaBrowser.Api.Playback MediaSourceInfo mediaSource, DeviceProfile profile, AuthorizationInfo auth, - int? maxBitrate, + long? maxBitrate, long startTimeTicks, string mediaSourceId, int? audioStreamIndex, @@ -383,7 +394,7 @@ namespace MediaBrowser.Api.Playback } } - private int? GetMaxBitrate(int? clientMaxBitrate) + private long? GetMaxBitrate(long? clientMaxBitrate) { var maxBitrate = clientMaxBitrate; var remoteClientMaxBitrate = _config.Configuration.RemoteClientBitrateLimit; @@ -425,7 +436,7 @@ namespace MediaBrowser.Api.Playback } } - private void SortMediaSources(PlaybackInfoResponse result, int? maxBitrate) + private void SortMediaSources(PlaybackInfoResponse result, long? maxBitrate) { var originalList = result.MediaSources.ToList(); diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index d1ef996ae..ab0bfd502 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -89,21 +89,25 @@ namespace MediaBrowser.Api.Playback return 10; } - if (!RunTimeTicks.HasValue) + if (IsSegmentedLiveStream) { return 3; } return 6; } - if (!RunTimeTicks.HasValue) - { - return 3; - } return 3; } } + public bool IsSegmentedLiveStream + { + get + { + return TranscodingType != TranscodingJobType.Progressive && !RunTimeTicks.HasValue; + } + } + public int HlsListSize { get @@ -123,8 +127,9 @@ namespace MediaBrowser.Api.Playback public List<string> SupportedAudioCodecs { get; set; } public List<string> SupportedVideoCodecs { get; set; } public string UserAgent { get; set; } + public TranscodingJobType TranscodingType { get; set; } - public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger) + public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger, TranscodingJobType transcodingType) { _mediaSourceManager = mediaSourceManager; _logger = logger; @@ -132,6 +137,7 @@ namespace MediaBrowser.Api.Playback SupportedVideoCodecs = new List<string>(); PlayableStreamFileNames = new List<string>(); RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + TranscodingType = transcodingType; } public string InputAudioSync { get; set; } diff --git a/MediaBrowser.Api/Reports/ReportsService.cs b/MediaBrowser.Api/Reports/ReportsService.cs index efbfa1bdf..cb275fa29 100644 --- a/MediaBrowser.Api/Reports/ReportsService.cs +++ b/MediaBrowser.Api/Reports/ReportsService.cs @@ -293,7 +293,11 @@ namespace MediaBrowser.Api.Reports // ExcludeLocationTypes if (!string.IsNullOrEmpty(request.ExcludeLocationTypes)) { - query.ExcludeLocationTypes = request.ExcludeLocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray(); + var excludeLocationTypes = request.ExcludeLocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray(); + if (excludeLocationTypes.Contains(LocationType.Virtual)) + { + query.IsVirtualItem = false; + } } if (!string.IsNullOrEmpty(request.LocationTypes)) diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs index 9c107bdff..a42441268 100644 --- a/MediaBrowser.Api/Session/SessionsService.cs +++ b/MediaBrowser.Api/Session/SessionsService.cs @@ -243,6 +243,8 @@ namespace MediaBrowser.Api.Session [ApiMember(Name = "SupportsPersistentIdentifier", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] public bool SupportsPersistentIdentifier { get; set; } + public bool SupportsContentUploading { get; set; } + public PostCapabilities() { SupportsPersistentIdentifier = true; @@ -559,6 +561,8 @@ namespace MediaBrowser.Api.Session SupportsSync = request.SupportsSync, + SupportsContentUploading = request.SupportsContentUploading, + SupportsPersistentIdentifier = request.SupportsPersistentIdentifier }); } diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 049a7b1c6..8300beac3 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -318,7 +318,11 @@ namespace MediaBrowser.Api.UserLibrary // ExcludeLocationTypes if (!string.IsNullOrEmpty(request.ExcludeLocationTypes)) { - query.ExcludeLocationTypes = request.ExcludeLocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray(); + var excludeLocationTypes = request.ExcludeLocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray(); + if (excludeLocationTypes.Contains(LocationType.Virtual)) + { + query.IsVirtualItem = false; + } } if (!string.IsNullOrEmpty(request.LocationTypes)) diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index 1ac98d165..08aba90d2 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -405,13 +405,6 @@ namespace MediaBrowser.Api.UserLibrary /// <returns>System.Object.</returns> public object Get(GetLocalTrailers request) { - var result = GetAsync(request); - - return ToOptimizedSerializedResultUsingCache(result); - } - - private List<BaseItemDto> GetAsync(GetLocalTrailers request) - { var user = _userManager.GetUserById(request.UserId); var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id); @@ -430,7 +423,7 @@ namespace MediaBrowser.Api.UserLibrary .Select(_libraryManager.GetItemById) .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)); - return dtos.ToList(); + return ToOptimizedSerializedResultUsingCache(dtos); } /// <summary> diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index 8353eccdd..35d6dca3d 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -37,12 +37,6 @@ namespace MediaBrowser.Common event EventHandler<GenericEventArgs<PackageVersionInfo>> ApplicationUpdated; /// <summary> - /// Gets a value indicating whether this instance is running as service. - /// </summary> - /// <value><c>true</c> if this instance is running as service; otherwise, <c>false</c>.</value> - bool IsRunningAsService { get; } - - /// <summary> /// Gets or sets a value indicating whether this instance has pending kernel reload. /// </summary> /// <value><c>true</c> if this instance has pending kernel reload; otherwise, <c>false</c>.</value> diff --git a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs index 1216ae352..ba20395d1 100644 --- a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs +++ b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs @@ -49,9 +49,11 @@ namespace MediaBrowser.Controller.Channels SupportsDirectPlay = true; } - public MediaSourceInfo ToMediaSource() + public MediaSourceInfo ToMediaSource(Guid itemId) { - var id = Path.GetMD5().ToString("N"); + var id = string.IsNullOrWhiteSpace(Path) ? + itemId.ToString("N") : + Path.GetMD5().ToString("N"); var source = new MediaSourceInfo { @@ -65,16 +67,12 @@ namespace MediaBrowser.Controller.Channels Name = id, Id = id, ReadAtNativeFramerate = ReadAtNativeFramerate, - SupportsDirectStream = Protocol == MediaProtocol.File, - SupportsDirectPlay = SupportsDirectPlay + SupportsDirectStream = false, + SupportsDirectPlay = SupportsDirectPlay, + IsRemote = true }; - var bitrate = (AudioBitrate ?? 0) + (VideoBitrate ?? 0); - - if (bitrate > 0) - { - source.Bitrate = bitrate; - } + source.InferTotalBitrate(); return source; } diff --git a/MediaBrowser.Controller/Chapters/IChapterManager.cs b/MediaBrowser.Controller/Chapters/IChapterManager.cs index 4b39e66cc..05517ebcd 100644 --- a/MediaBrowser.Controller/Chapters/IChapterManager.cs +++ b/MediaBrowser.Controller/Chapters/IChapterManager.cs @@ -26,11 +26,5 @@ namespace MediaBrowser.Controller.Chapters /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> Task SaveChapters(string itemId, List<ChapterInfo> chapters, CancellationToken cancellationToken); - - /// <summary> - /// Gets the configuration. - /// </summary> - /// <returns>ChapterOptions.</returns> - ChapterOptions GetConfiguration(); } } diff --git a/MediaBrowser.Controller/Connect/IConnectManager.cs b/MediaBrowser.Controller/Connect/IConnectManager.cs index e004eaccf..f899c7262 100644 --- a/MediaBrowser.Controller/Connect/IConnectManager.cs +++ b/MediaBrowser.Controller/Connect/IConnectManager.cs @@ -61,7 +61,7 @@ namespace MediaBrowser.Controller.Connect /// <param name="username">The username.</param> /// <param name="passwordMd5">The password MD5.</param> /// <returns>Task.</returns> - Task Authenticate(string username, string passwordMd5); + Task<ConnectAuthenticationResult> Authenticate(string username, string passwordMd5); /// <summary> /// Gets the local user. diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 3a6a7765b..4cc6a7c7e 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -267,15 +267,8 @@ namespace MediaBrowser.Controller.Entities.Audio } } - var bitrate = i.TotalBitrate ?? - info.MediaStreams.Where(m => m.Type == MediaStreamType.Audio) - .Select(m => m.BitRate ?? 0) - .Sum(); - - if (bitrate > 0) - { - info.Bitrate = bitrate; - } + info.Bitrate = i.TotalBitrate; + info.InferTotalBitrate(); return info; } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 1d2716b64..37631bbe8 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -45,6 +45,15 @@ namespace MediaBrowser.Controller.Entities.Audio } [IgnoreDataMember] + public override bool IsDisplayedAsFolder + { + get + { + return true; + } + } + + [IgnoreDataMember] public override bool SupportsAddingToPlaylist { get { return true; } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs index d75b31f6a..bbe1a54a4 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -40,6 +40,15 @@ namespace MediaBrowser.Controller.Entities.Audio } } + [IgnoreDataMember] + public override bool IsDisplayedAsFolder + { + get + { + return true; + } + } + /// <summary> /// Returns the folder containing the item. /// If the item is a folder, it returns the folder itself diff --git a/MediaBrowser.Controller/Entities/AudioBook.cs b/MediaBrowser.Controller/Entities/AudioBook.cs index efeb9b497..c15cae8b1 100644 --- a/MediaBrowser.Controller/Entities/AudioBook.cs +++ b/MediaBrowser.Controller/Entities/AudioBook.cs @@ -17,6 +17,15 @@ namespace MediaBrowser.Controller.Entities } [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return true; + } + } + + [IgnoreDataMember] public string SeriesPresentationUniqueKey { get; set; } [IgnoreDataMember] public string SeriesName { get; set; } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 2aa53d651..4cfea4c70 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -53,6 +53,7 @@ namespace MediaBrowser.Controller.Entities ImageInfos = new List<ItemImageInfo>(); InheritedTags = new List<string>(); ProductionLocations = new List<string>(); + SourceType = SourceType.Library; } public static readonly char[] SlugReplaceChars = { '?', '/', '&' }; @@ -272,9 +273,6 @@ namespace MediaBrowser.Controller.Entities public virtual string Path { get; set; } [IgnoreDataMember] - public bool IsOffline { get; set; } - - [IgnoreDataMember] public virtual SourceType SourceType { get; set; } /// <summary> @@ -338,20 +336,6 @@ 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> /// Gets or sets the type of the location. /// </summary> @@ -1355,6 +1339,11 @@ namespace MediaBrowser.Controller.Entities if (string.IsNullOrWhiteSpace(lang)) { + lang = LibraryManager.GetLibraryOptions(this).PreferredMetadataLanguage; + } + + if (string.IsNullOrWhiteSpace(lang)) + { lang = ConfigurationManager.Configuration.PreferredMetadataLanguage; } @@ -1385,6 +1374,11 @@ namespace MediaBrowser.Controller.Entities if (string.IsNullOrWhiteSpace(lang)) { + lang = LibraryManager.GetLibraryOptions(this).MetadataCountryCode; + } + + if (string.IsNullOrWhiteSpace(lang)) + { lang = ConfigurationManager.Configuration.MetadataCountryCode; } @@ -1606,12 +1600,15 @@ namespace MediaBrowser.Controller.Entities return true; } - var userCollectionFolders = user.RootFolder.GetChildren(user, true).Select(i => i.Id).ToList(); - var itemCollectionFolders = LibraryManager.GetCollectionFolders(this).Select(i => i.Id); + var itemCollectionFolders = LibraryManager.GetCollectionFolders(this).Select(i => i.Id).ToList(); - if (!itemCollectionFolders.Any(userCollectionFolders.Contains)) + if (itemCollectionFolders.Count > 0) { - return false; + var userCollectionFolders = user.RootFolder.GetChildren(user, true).Select(i => i.Id).ToList(); + if (!itemCollectionFolders.Any(userCollectionFolders.Contains)) + { + return false; + } } } @@ -1631,6 +1628,15 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public virtual bool IsDisplayedAsFolder + { + get + { + return false; + } + } + public virtual string GetClientTypeName() { if (IsFolder && SourceType == SourceType.Channel && !(this is Channel)) diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 681f16f07..62ea21a79 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -121,7 +121,6 @@ namespace MediaBrowser.Controller.Entities { LibraryOptions[path] = options; - options.SchemaVersion = 3; XmlSerializer.SerializeToFile(options, GetLibraryOptionsPath(path)); } } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 943c9d882..cdd503bba 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -86,6 +86,15 @@ namespace MediaBrowser.Controller.Entities } [IgnoreDataMember] + public override bool IsDisplayedAsFolder + { + get + { + return true; + } + } + + [IgnoreDataMember] public virtual bool SupportsCumulativeRunTimeTicks { get @@ -360,6 +369,11 @@ namespace MediaBrowser.Controller.Entities var validChildren = new List<BaseItem>(); + var allLibraryPaths = LibraryManager + .GetVirtualFolders() + .SelectMany(i => i.Locations) + .ToList(); + if (locationType != LocationType.Remote && locationType != LocationType.Virtual) { IEnumerable<BaseItem> nonCachedChildren; @@ -393,7 +407,6 @@ namespace MediaBrowser.Controller.Entities if (currentChildren.TryGetValue(child.Id, out currentChild) && IsValidFromResolver(currentChild, child)) { - await currentChild.UpdateIsOffline(false).ConfigureAwait(false); validChildren.Add(currentChild); continue; @@ -420,9 +433,8 @@ namespace MediaBrowser.Controller.Entities { } - else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path)) + else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path, allLibraryPaths)) { - await item.UpdateIsOffline(true).ConfigureAwait(false); } else { @@ -437,7 +449,6 @@ namespace MediaBrowser.Controller.Entities Logger.Debug("Removed item: " + item.Path); item.SetParent(null); - item.IsOffline = false; await LibraryManager.DeleteItem(item, new DeleteOptions { DeleteFileLocation = false }).ConfigureAwait(false); LibraryManager.ReportItemRemoved(item); } @@ -603,6 +614,11 @@ namespace MediaBrowser.Controller.Entities /// <returns><c>true</c> if the specified path is offline; otherwise, <c>false</c>.</returns> public static bool IsPathOffline(string path) { + return IsPathOffline(path, LibraryManager.GetVirtualFolders().SelectMany(i => i.Locations).ToList()); + } + + public static bool IsPathOffline(string path, List<string> allLibraryPaths) + { if (FileSystem.FileExists(path)) { return false; @@ -618,31 +634,20 @@ namespace MediaBrowser.Controller.Entities return false; } - path = System.IO.Path.GetDirectoryName(path); - } + if (allLibraryPaths.Contains(path, StringComparer.OrdinalIgnoreCase)) + { + return true; + } - if (ContainsPath(LibraryManager.GetVirtualFolders(), originalPath)) - { - return true; + path = System.IO.Path.GetDirectoryName(path); } - return false; - } - - /// <summary> - /// Determines whether the specified folders contains path. - /// </summary> - /// <param name="folders">The folders.</param> - /// <param name="path">The path.</param> - /// <returns><c>true</c> if the specified folders contains path; otherwise, <c>false</c>.</returns> - private static bool ContainsPath(IEnumerable<VirtualFolderInfo> folders, string path) - { - return folders.SelectMany(i => i.Locations).Any(i => ContainsPath(i, path)); + return allLibraryPaths.Any(i => ContainsPath(i, originalPath)); } private static bool ContainsPath(string parent, string path) { - return string.Equals(parent, path, StringComparison.OrdinalIgnoreCase) || FileSystem.ContainsSubPath(parent, path); + return FileSystem.AreEqual(parent, path) || FileSystem.ContainsSubPath(parent, path); } /// <summary> @@ -1327,7 +1332,7 @@ namespace MediaBrowser.Controller.Entities if (!user.Configuration.DisplayMissingEpisodes || !user.Configuration.DisplayUnairedEpisodes) { - query.ExcludeLocationTypes = new[] { LocationType.Virtual }; + query.IsVirtualItem = false; } var itemsResult = await GetItems(query).ConfigureAwait(false); @@ -1366,7 +1371,7 @@ namespace MediaBrowser.Controller.Entities { Recursive = true, IsFolder = false, - ExcludeLocationTypes = new[] { LocationType.Virtual }, + IsVirtualItem = false, EnableTotalRecordCount = false }).Result; diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs index da4ee352f..1b746ae51 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -41,6 +41,15 @@ namespace MediaBrowser.Controller.Entities } [IgnoreDataMember] + public override bool IsDisplayedAsFolder + { + get + { + return true; + } + } + + [IgnoreDataMember] public override bool SupportsAncestors { get diff --git a/MediaBrowser.Controller/Entities/ICollectionFolder.cs b/MediaBrowser.Controller/Entities/ICollectionFolder.cs index f4544f173..d8b02034c 100644 --- a/MediaBrowser.Controller/Entities/ICollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/ICollectionFolder.cs @@ -20,17 +20,4 @@ namespace MediaBrowser.Controller.Entities { bool EnableUserSpecificView { get; } } - - public static class CollectionFolderExtensions - { - public static string GetViewType(this ICollectionFolder folder, User user) - { - if (user.Configuration.PlainFolderViews.Contains(folder.Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) - { - return null; - } - - return folder.CollectionType; - } - } } diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 15af0888d..9c5730d05 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -130,7 +130,6 @@ namespace MediaBrowser.Controller.Entities public string[] TopParentIds { get; set; } public LocationType[] LocationTypes { get; set; } - public LocationType[] ExcludeLocationTypes { get; set; } public string[] PresetViews { get; set; } public SourceType[] SourceTypes { get; set; } public SourceType[] ExcludeSourceTypes { get; set; } @@ -160,6 +159,7 @@ namespace MediaBrowser.Controller.Entities public DateTime? MinDateLastSaved { get; set; } public DtoOptions DtoOptions { get; set; } + public int MinSimilarityScore { get; set; } public bool HasField(ItemFields name) { @@ -197,6 +197,8 @@ namespace MediaBrowser.Controller.Entities public InternalItemsQuery() { + MinSimilarityScore = 20; + GroupByPresentationUniqueKey = true; EnableTotalRecordCount = true; @@ -230,7 +232,6 @@ namespace MediaBrowser.Controller.Entities ExcludeTags = new string[] { }; ExcludeInheritedTags = new string[] { }; LocationTypes = new LocationType[] { }; - ExcludeLocationTypes = new LocationType[] { }; PresetViews = new string[] { }; SourceTypes = new SourceType[] { }; ExcludeSourceTypes = new SourceType[] { }; diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index c14dd70bf..dbd9d1cef 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -40,6 +40,15 @@ namespace MediaBrowser.Controller.Entities } [IgnoreDataMember] + public override bool IsDisplayedAsFolder + { + get + { + return true; + } + } + + [IgnoreDataMember] public override bool SupportsAncestors { get diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 58d2cdc6c..a880b6d77 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -647,7 +647,7 @@ namespace MediaBrowser.Controller.Entities query.SetUser(user); query.Limit = GetSpecialItemsLimit(); query.IncludeItemTypes = new[] { typeof(Episode).Name }; - query.ExcludeLocationTypes = new[] { LocationType.Virtual }; + query.IsVirtualItem = false; return ConvertToResult(_libraryManager.GetItemList(query)); } @@ -1199,7 +1199,7 @@ namespace MediaBrowser.Controller.Entities return false; } - if (query.ExcludeLocationTypes.Length > 0 && query.ExcludeLocationTypes.Contains(item.LocationType)) + if (query.IsVirtualItem.HasValue && item.IsVirtualItem != query.IsVirtualItem.Value) { return false; } diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 47df12e1b..fb9c3d213 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -649,22 +649,8 @@ namespace MediaBrowser.Controller.Entities } } - try - { - var bitrate = i.TotalBitrate ?? - info.MediaStreams.Where(m => m.Type != MediaStreamType.Subtitle && !string.Equals(m.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase)) - .Select(m => m.BitRate ?? 0) - .Sum(); - - if (bitrate > 0) - { - info.Bitrate = bitrate; - } - } - catch (OverflowException ex) - { - Logger.ErrorException("Error calculating total bitrate", ex); - } + info.Bitrate = i.TotalBitrate; + info.InferTotalBitrate(); return info; } diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 44c003197..86b52bd77 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -22,6 +22,12 @@ namespace MediaBrowser.Controller Task<SystemInfo> GetSystemInfo(); /// <summary> + /// Gets a value indicating whether this instance is running as service. + /// </summary> + /// <value><c>true</c> if this instance is running as service; otherwise, <c>false</c>.</value> + bool IsRunningAsService { get; } + + /// <summary> /// Gets a value indicating whether [supports automatic run at startup]. /// </summary> /// <value><c>true</c> if [supports automatic run at startup]; otherwise, <c>false</c>.</value> diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index ffb6a7555..5a139e09d 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -15,6 +15,11 @@ namespace MediaBrowser.Controller.LiveTv { public class LiveTvProgram : BaseItem, IHasLookupInfo<LiveTvProgramLookupInfo>, IHasStartDate, IHasProgramAttributes { + public LiveTvProgram() + { + IsVirtualItem = true; + } + public override List<string> GetUserDataKeys() { var list = base.GetUserDataKeys(); diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs index 10ed95fe5..3c935f924 100644 --- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/TimerInfo.cs @@ -94,6 +94,7 @@ namespace MediaBrowser.Controller.LiveTv /// <value>The priority.</value> public int Priority { get; set; } + public int RetryCount { get; set; } // Program properties public int? SeasonNumber { get; set; } diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index 80bbc87e3..7654ec1d7 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -827,8 +827,16 @@ namespace MediaBrowser.MediaEncoding.Encoder return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture)); } + if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) + { + // h264 + return string.Format(" -maxrate {0} -bufsize {1}", + bitrate.Value.ToString(UsCulture), + (bitrate.Value * 2).ToString(UsCulture)); + } + // h264 - return string.Format(" -maxrate {0} -bufsize {1}", + return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}", bitrate.Value.ToString(UsCulture), (bitrate.Value * 2).ToString(UsCulture)); } @@ -896,7 +904,8 @@ namespace MediaBrowser.MediaEncoding.Encoder } else { - args += "-map -0:v"; + // No known video stream + args += "-vn"; } if (state.AudioStream != null) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs index cec272b39..e547f2fae 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs @@ -8,25 +8,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { public static string GetInputArgument(List<string> inputFiles, MediaProtocol protocol) { - if (protocol == MediaProtocol.Http) - { - var url = inputFiles.First(); - - return string.Format("\"{0}\"", url); - } - if (protocol == MediaProtocol.Rtmp) - { - var url = inputFiles.First(); - - return string.Format("\"{0}\"", url); - } - if (protocol == MediaProtocol.Rtsp) - { - var url = inputFiles.First(); - - return string.Format("\"{0}\"", url); - } - if (protocol == MediaProtocol.Udp) + if (protocol != MediaProtocol.File) { var url = inputFiles.First(); diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 89730a11f..6a5945b76 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -533,8 +533,10 @@ namespace MediaBrowser.MediaEncoding.Encoder probeSize = probeSize + " " + analyzeDuration; probeSize = probeSize.Trim(); + var forceEnableLogging = request.Protocol != MediaProtocol.File; + return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters, - probeSize, request.MediaType == DlnaProfileType.Audio, request.VideoType, cancellationToken); + probeSize, request.MediaType == DlnaProfileType.Audio, request.VideoType, forceEnableLogging, cancellationToken); } /// <summary> @@ -577,14 +579,6 @@ namespace MediaBrowser.MediaEncoding.Encoder /// <summary> /// Gets the media info internal. /// </summary> - /// <param name="inputPath">The input path.</param> - /// <param name="primaryPath">The primary path.</param> - /// <param name="protocol">The protocol.</param> - /// <param name="extractChapters">if set to <c>true</c> [extract chapters].</param> - /// <param name="probeSizeArgument">The probe size argument.</param> - /// <param name="isAudio">if set to <c>true</c> [is audio].</param> - /// <param name="videoType">Type of the video.</param> - /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{MediaInfoResult}.</returns> private async Task<MediaInfo> GetMediaInfoInternal(string inputPath, string primaryPath, @@ -593,6 +587,7 @@ namespace MediaBrowser.MediaEncoding.Encoder string probeSizeArgument, bool isAudio, VideoType videoType, + bool forceEnableLogging, CancellationToken cancellationToken) { var args = extractChapters @@ -614,7 +609,14 @@ namespace MediaBrowser.MediaEncoding.Encoder EnableRaisingEvents = true }); - _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); + if (forceEnableLogging) + { + _logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); + } + else + { + _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); + } using (var processWrapper = new ProcessWrapper(process, this, _logger)) { @@ -1248,8 +1250,8 @@ namespace MediaBrowser.MediaEncoding.Encoder lock (_runningProcesses) { proceses = _runningProcesses.ToList(); + _runningProcesses.Clear(); } - _runningProcesses.Clear(); foreach (var process in proceses) { @@ -1319,16 +1321,16 @@ namespace MediaBrowser.MediaEncoding.Encoder { } - lock (_mediaEncoder._runningProcesses) - { - _mediaEncoder._runningProcesses.Remove(this); - } - DisposeProcess(process); } private void DisposeProcess(IProcess process) { + lock (_mediaEncoder._runningProcesses) + { + _mediaEncoder._runningProcesses.Remove(this); + } + try { process.Dispose(); diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index c16d3afbd..2f8ecaece 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -47,6 +47,8 @@ namespace MediaBrowser.MediaEncoding.Probing info.MediaStreams = internalStreams.Select(s => GetMediaStream(isAudio, s, data.format)) .Where(i => i != null) + // Drop subtitle streams if we don't know the codec because it will just cause failures if we don't know how to handle them + .Where(i => i.Type != MediaStreamType.Subtitle || !string.IsNullOrWhiteSpace(i.Codec)) .ToList(); if (data.format != null) @@ -185,11 +187,7 @@ namespace MediaBrowser.MediaEncoding.Probing // If ffprobe reported the container bitrate as being the same as the video stream bitrate, then it's wrong if (videoStreamsBitrate == (info.Bitrate ?? 0)) { - var streamBitrates = info.MediaStreams.Where(i => !i.IsExternal).Select(i => i.BitRate ?? 0).Sum(); - if (streamBitrates > (info.Bitrate ?? 0)) - { - info.Bitrate = streamBitrates; - } + info.InferTotalBitrate(true); } } diff --git a/MediaBrowser.Model/Configuration/ChapterOptions.cs b/MediaBrowser.Model/Configuration/ChapterOptions.cs deleted file mode 100644 index 1c2b0c527..000000000 --- a/MediaBrowser.Model/Configuration/ChapterOptions.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MediaBrowser.Model.Configuration -{ - public class ChapterOptions - { - public bool EnableMovieChapterImageExtraction { get; set; } - public bool EnableEpisodeChapterImageExtraction { get; set; } - public bool EnableOtherVideoChapterImageExtraction { get; set; } - - public bool ExtractDuringLibraryScan { get; set; } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs index e79253d19..24bc5dd11 100644 --- a/MediaBrowser.Model/Configuration/LibraryOptions.cs +++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs @@ -5,7 +5,6 @@ public bool EnableArchiveMediaFiles { get; set; } public bool EnablePhotos { get; set; } public bool EnableRealtimeMonitor { get; set; } - public int SchemaVersion { get; set; } public bool EnableChapterImageExtraction { get; set; } public bool ExtractChapterImagesDuringLibraryScan { get; set; } public bool DownloadImagesInAdvance { get; set; } @@ -16,6 +15,18 @@ public bool ImportMissingEpisodes { get; set; } public bool EnableAutomaticSeriesGrouping { get; set; } + /// <summary> + /// Gets or sets the preferred metadata language. + /// </summary> + /// <value>The preferred metadata language.</value> + public string PreferredMetadataLanguage { get; set; } + + /// <summary> + /// Gets or sets the metadata country code. + /// </summary> + /// <value>The metadata country code.</value> + public string MetadataCountryCode { get; set; } + public LibraryOptions() { EnablePhotos = true; diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 22713b94f..bd0a53cc0 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -55,12 +55,6 @@ namespace MediaBrowser.Model.Configuration public string CertificatePath { get; set; } /// <summary> - /// Gets or sets a value indicating whether [enable internet providers]. - /// </summary> - /// <value><c>true</c> if [enable internet providers]; otherwise, <c>false</c>.</value> - public bool EnableInternetProviders { get; set; } - - /// <summary> /// Gets or sets a value indicating whether this instance is port authorized. /// </summary> /// <value><c>true</c> if this instance is port authorized; otherwise, <c>false</c>.</value> @@ -88,12 +82,6 @@ namespace MediaBrowser.Model.Configuration public string SeasonZeroDisplayName { get; set; } /// <summary> - /// Gets or sets a value indicating whether [save local meta]. - /// </summary> - /// <value><c>true</c> if [save local meta]; otherwise, <c>false</c>.</value> - public bool SaveLocalMeta { get; set; } - - /// <summary> /// Gets or sets the preferred metadata language. /// </summary> /// <value>The preferred metadata language.</value> @@ -243,8 +231,6 @@ namespace MediaBrowser.Model.Configuration LibraryMonitorDelay = 60; - EnableInternetProviders = true; - PathSubstitutions = new PathSubstitution[] { }; ContentTypes = new NameValuePair[] { }; diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index efd3d6b67..5567063fe 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -37,7 +37,6 @@ namespace MediaBrowser.Model.Configuration public string[] OrderedViews { get; set; } public string[] LatestItemsExcludes { get; set; } - public string[] PlainFolderViews { get; set; } public bool HidePlayedInLatest { get; set; } @@ -61,8 +60,6 @@ namespace MediaBrowser.Model.Configuration LatestItemsExcludes = new string[] { }; OrderedViews = new string[] { }; - PlainFolderViews = new string[] { }; - GroupedFolders = new string[] { }; } } diff --git a/MediaBrowser.Model/Dlna/AudioOptions.cs b/MediaBrowser.Model/Dlna/AudioOptions.cs index f3b6df861..24c7aef98 100644 --- a/MediaBrowser.Model/Dlna/AudioOptions.cs +++ b/MediaBrowser.Model/Dlna/AudioOptions.cs @@ -41,7 +41,7 @@ namespace MediaBrowser.Model.Dlna /// <summary> /// The application's configured quality setting /// </summary> - public int? MaxBitrate { get; set; } + public long? MaxBitrate { get; set; } /// <summary> /// Gets or sets the context. @@ -59,7 +59,7 @@ namespace MediaBrowser.Model.Dlna /// Gets the maximum bitrate. /// </summary> /// <returns>System.Nullable<System.Int32>.</returns> - public int? GetMaxBitrate(bool isAudio) + public long? GetMaxBitrate(bool isAudio) { if (MaxBitrate.HasValue) { diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 821531ed0..d99a21e6d 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -51,8 +51,8 @@ namespace MediaBrowser.Model.Dlna public int? MaxIconWidth { get; set; } public int? MaxIconHeight { get; set; } - public int? MaxStreamingBitrate { get; set; } - public int? MaxStaticBitrate { get; set; } + public long? MaxStreamingBitrate { get; set; } + public long? MaxStaticBitrate { get; set; } public int? MusicStreamingTranscodingBitrate { get; set; } public int? MaxStaticMusicBitrate { get; set; } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index bc9a3488f..262964404 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -93,7 +93,7 @@ namespace MediaBrowser.Model.Dlna return GetOptimalStream(streams, options.GetMaxBitrate(false)); } - private StreamInfo GetOptimalStream(List<StreamInfo> streams, int? maxBitrate) + private StreamInfo GetOptimalStream(List<StreamInfo> streams, long? maxBitrate) { streams = StreamInfoSorter.SortMediaSources(streams, maxBitrate); @@ -277,25 +277,26 @@ namespace MediaBrowser.Model.Dlna playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue); } - int transcodingBitrate = options.AudioTranscodingBitrate ?? + long transcodingBitrate = options.AudioTranscodingBitrate ?? options.Profile.MusicStreamingTranscodingBitrate ?? 128000; - int? configuredBitrate = options.GetMaxBitrate(true); + var configuredBitrate = options.GetMaxBitrate(true); if (configuredBitrate.HasValue) { transcodingBitrate = Math.Min(configuredBitrate.Value, transcodingBitrate); } - playlistItem.AudioBitrate = Math.Min(transcodingBitrate, playlistItem.AudioBitrate ?? transcodingBitrate); + var longBitrate = Math.Min(transcodingBitrate, playlistItem.AudioBitrate ?? transcodingBitrate); + playlistItem.AudioBitrate = longBitrate > int.MaxValue ? int.MaxValue : Convert.ToInt32(longBitrate); } return playlistItem; } - private int? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options, bool isAudio) + private long? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options, bool isAudio) { if (item.Protocol == MediaProtocol.File) { @@ -583,11 +584,11 @@ namespace MediaBrowser.Model.Dlna int audioBitrate = GetAudioBitrate(playlistItem.SubProtocol, options.GetMaxBitrate(false), playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec, audioStream); playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate); - int? maxBitrateSetting = options.GetMaxBitrate(false); + var maxBitrateSetting = options.GetMaxBitrate(false); // Honor max rate if (maxBitrateSetting.HasValue) { - int videoBitrate = maxBitrateSetting.Value; + var videoBitrate = maxBitrateSetting.Value; if (playlistItem.AudioBitrate.HasValue) { @@ -595,15 +596,16 @@ namespace MediaBrowser.Model.Dlna } // Make sure the video bitrate is lower than bitrate settings but at least 64k - int currentValue = playlistItem.VideoBitrate ?? videoBitrate; - playlistItem.VideoBitrate = Math.Max(Math.Min(videoBitrate, currentValue), 64000); + long currentValue = playlistItem.VideoBitrate ?? videoBitrate; + var longBitrate = Math.Max(Math.Min(videoBitrate, currentValue), 64000); + playlistItem.VideoBitrate = longBitrate > int.MaxValue ? int.MaxValue : Convert.ToInt32(longBitrate); } } return playlistItem; } - private int GetAudioBitrate(string subProtocol, int? maxTotalBitrate, int? targetAudioChannels, string targetAudioCodec, MediaStream audioStream) + private int GetAudioBitrate(string subProtocol, long? maxTotalBitrate, int? targetAudioChannels, string targetAudioCodec, MediaStream audioStream) { int defaultBitrate = audioStream == null ? 192000 : audioStream.BitRate ?? 192000; // Reduce the bitrate if we're downmixing @@ -865,7 +867,7 @@ namespace MediaBrowser.Model.Dlna } private bool IsEligibleForDirectPlay(MediaSourceInfo item, - int? maxBitrate, + long? maxBitrate, MediaStream subtitleStream, VideoOptions options, PlayMethod playMethod) @@ -960,7 +962,7 @@ namespace MediaBrowser.Model.Dlna return null; } - private bool IsAudioEligibleForDirectPlay(MediaSourceInfo item, int? maxBitrate) + private bool IsAudioEligibleForDirectPlay(MediaSourceInfo item, long? maxBitrate) { if (!maxBitrate.HasValue) { diff --git a/MediaBrowser.Model/Dlna/StreamInfoSorter.cs b/MediaBrowser.Model/Dlna/StreamInfoSorter.cs index 293054e5b..425fb9c93 100644 --- a/MediaBrowser.Model/Dlna/StreamInfoSorter.cs +++ b/MediaBrowser.Model/Dlna/StreamInfoSorter.cs @@ -7,7 +7,7 @@ namespace MediaBrowser.Model.Dlna { public class StreamInfoSorter { - public static List<StreamInfo> SortMediaSources(List<StreamInfo> streams, int? maxBitrate) + public static List<StreamInfo> SortMediaSources(List<StreamInfo> streams, long? maxBitrate) { return streams.OrderBy(i => { diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index 814368d32..d20911a7f 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -1,8 +1,8 @@ -using System; -using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Extensions; using MediaBrowser.Model.MediaInfo; using System.Collections.Generic; +using System.Linq; using MediaBrowser.Model.Serialization; namespace MediaBrowser.Model.Dto @@ -21,6 +21,11 @@ namespace MediaBrowser.Model.Dto public string Name { get; set; } + /// <summary> + /// Differentiate internet url vs local network + /// </summary> + public bool IsRemote { get; set; } + public string ETag { get; set; } public long? RunTimeTicks { get; set; } public bool ReadAtNativeFramerate { get; set; } @@ -67,6 +72,37 @@ namespace MediaBrowser.Model.Dto SupportsProbing = true; } + public void InferTotalBitrate(bool force = false) + { + if (MediaStreams == null) + { + return; + } + + if (!force && Bitrate.HasValue) + { + return; + } + + var internalStreams = MediaStreams + .Where(i => !i.IsExternal) + .ToList(); + + if (internalStreams.Count == 0) + { + return; + } + + var bitrate = internalStreams + .Select(m => m.BitRate ?? 0) + .Sum(); + + if (bitrate > 0) + { + Bitrate = bitrate; + } + } + public int? DefaultAudioStreamIndex { get; set; } public int? DefaultSubtitleStreamIndex { get; set; } diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 85f475ca2..0eb9e2730 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -92,6 +92,11 @@ namespace MediaBrowser.Model.Entities { attributes.Add(StringHelper.FirstToUpper(Language)); } + else + { + attributes.Add("Und"); + } + if (IsDefault) { attributes.Add("Default"); diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs index 62bb66ea8..f6d1bb351 100644 --- a/MediaBrowser.Model/IO/IFileSystem.cs +++ b/MediaBrowser.Model/IO/IFileSystem.cs @@ -120,6 +120,8 @@ namespace MediaBrowser.Model.IO /// <param name="file2">The file2.</param> void SwapFiles(string file1, string file2); + bool AreEqual(string path1, string path2); + /// <summary> /// Determines whether [contains sub path] [the specified parent path]. /// </summary> diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index c5e140032..3c2ace896 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -11,7 +11,6 @@ namespace MediaBrowser.Model.LiveTv public string RecordingPath { get; set; } public string MovieRecordingPath { get; set; } public string SeriesRecordingPath { get; set; } - public bool EnableAutoOrganize { get; set; } public bool EnableRecordingEncoding { get; set; } public string RecordingEncodingFormat { get; set; } public bool EnableRecordingSubfolders { get; set; } @@ -58,8 +57,7 @@ namespace MediaBrowser.Model.LiveTv public string SourceB { get; set; } public string SourceC { get; set; } public string SourceD { get; set; } - - public int DataVersion { get; set; } + public bool EnableTvgId { get; set; } public TunerHostInfo() { diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index f36519e27..4457c6dc2 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -78,7 +78,6 @@ <Compile Include="Collections\CollectionCreationResult.cs" /> <Compile Include="Configuration\AccessSchedule.cs" /> <Compile Include="Configuration\ChannelOptions.cs" /> - <Compile Include="Configuration\ChapterOptions.cs" /> <Compile Include="Configuration\CinemaModeConfiguration.cs" /> <Compile Include="Configuration\EncodingOptions.cs" /> <Compile Include="Configuration\FanartOptions.cs" /> diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs index b6e2a96aa..913967352 100644 --- a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs +++ b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs @@ -7,7 +7,7 @@ namespace MediaBrowser.Model.MediaInfo public string OpenToken { get; set; } public string UserId { get; set; } public string PlaySessionId { get; set; } - public int? MaxStreamingBitrate { get; set; } + public long? MaxStreamingBitrate { get; set; } public long? StartTimeTicks { get; set; } public int? AudioStreamIndex { get; set; } public int? SubtitleStreamIndex { get; set; } diff --git a/MediaBrowser.Model/MediaInfo/MediaProtocol.cs b/MediaBrowser.Model/MediaInfo/MediaProtocol.cs index 1474e6d96..efb31790b 100644 --- a/MediaBrowser.Model/MediaInfo/MediaProtocol.cs +++ b/MediaBrowser.Model/MediaInfo/MediaProtocol.cs @@ -6,6 +6,7 @@ namespace MediaBrowser.Model.MediaInfo Http = 1, Rtmp = 2, Rtsp = 3, - Udp = 4 + Udp = 4, + Rtp = 5 } }
\ No newline at end of file diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs index a2b85d121..fc4714be3 100644 --- a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs +++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs @@ -8,7 +8,7 @@ namespace MediaBrowser.Model.MediaInfo public string UserId { get; set; } - public int? MaxStreamingBitrate { get; set; } + public long? MaxStreamingBitrate { get; set; } public long? StartTimeTicks { get; set; } diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs index d5e54ae11..222c1bd64 100644 --- a/MediaBrowser.Model/Session/ClientCapabilities.cs +++ b/MediaBrowser.Model/Session/ClientCapabilities.cs @@ -10,7 +10,7 @@ namespace MediaBrowser.Model.Session public List<string> SupportedCommands { get; set; } public bool SupportsMediaControl { get; set; } - + public bool SupportsContentUploading { get; set; } public string MessageCallbackUrl { get; set; } public bool SupportsPersistentIdentifier { get; set; } diff --git a/MediaBrowser.Model/Session/GeneralCommandType.cs b/MediaBrowser.Model/Session/GeneralCommandType.cs index 64b61678e..6cceb162a 100644 --- a/MediaBrowser.Model/Session/GeneralCommandType.cs +++ b/MediaBrowser.Model/Session/GeneralCommandType.cs @@ -36,6 +36,7 @@ DisplayMessage = 28, SetRepeatMode = 29, ChannelUp = 30, - ChannelDown = 31 + ChannelDown = 31, + SetMaxStreamingBitrate = 31 } }
\ No newline at end of file diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index 6145c7b61..4154093cb 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -17,12 +17,6 @@ namespace MediaBrowser.Model.System public string OperatingSystemDisplayName { get; set; } /// <summary> - /// Gets or sets a value indicating whether this instance is running as service. - /// </summary> - /// <value><c>true</c> if this instance is running as service; otherwise, <c>false</c>.</value> - public bool IsRunningAsService { get; set; } - - /// <summary> /// Gets or sets a value indicating whether [supports running as service]. /// </summary> /// <value><c>true</c> if [supports running as service]; otherwise, <c>false</c>.</value> diff --git a/MediaBrowser.Providers/Chapters/ChapterManager.cs b/MediaBrowser.Providers/Chapters/ChapterManager.cs index 87aaafb39..5fea2c3cd 100644 --- a/MediaBrowser.Providers/Chapters/ChapterManager.cs +++ b/MediaBrowser.Providers/Chapters/ChapterManager.cs @@ -43,25 +43,5 @@ namespace MediaBrowser.Providers.Chapters { return _itemRepo.SaveChapters(new Guid(itemId), chapters, cancellationToken); } - - public ChapterOptions GetConfiguration() - { - return _config.GetConfiguration<ChapterOptions>("chapters"); - } - } - - public class ChapterConfigurationStore : IConfigurationFactory - { - public IEnumerable<ConfigurationStore> GetConfigurations() - { - return new List<ConfigurationStore> - { - new ConfigurationStore - { - Key = "chapters", - ConfigurationType = typeof (ChapterOptions) - } - }; - } } } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index a2f3d0459..003e7b9fa 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -237,17 +237,17 @@ namespace MediaBrowser.Providers.Manager }); } - public IEnumerable<IImageProvider> GetImageProviders(IHasImages item, ImageRefreshOptions refreshOptions) + public IEnumerable<IImageProvider> GetImageProviders(IHasImages item, ImageRefreshOptions refreshOptions) { return GetImageProviders(item, GetMetadataOptions(item), refreshOptions, false); } - private IEnumerable<IImageProvider> GetImageProviders(IHasImages item, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled) + private IEnumerable<IImageProvider> GetImageProviders(IHasImages item, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled) { // Avoid implicitly captured closure var currentOptions = options; - return ImageProviders.Where(i => CanRefresh(i, item, options, refreshOptions, includeDisabled)) + return ImageProviders.Where(i => CanRefresh(i, item, options, refreshOptions, includeDisabled)) .OrderBy(i => { // See if there's a user-defined order @@ -291,7 +291,7 @@ namespace MediaBrowser.Providers.Manager { var options = GetMetadataOptions(item); - return GetImageProviders(item, options, new ImageRefreshOptions(new DirectoryService(_logger, _fileSystem)), includeDisabled).OfType<IRemoteImageProvider>(); + return GetImageProviders(item, options, new ImageRefreshOptions(new DirectoryService(_logger, _fileSystem)), includeDisabled).OfType<IRemoteImageProvider>(); } private bool CanRefresh(IMetadataProvider provider, IHasMetadata item, MetadataOptions options, bool includeDisabled, bool forceEnableInternetMetadata, bool checkIsOwnedItem) @@ -335,17 +335,17 @@ namespace MediaBrowser.Providers.Manager return true; } - private bool CanRefresh(IImageProvider provider, IHasImages item, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled) + private bool CanRefresh(IImageProvider provider, IHasImages item, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled) { if (!includeDisabled) { // If locked only allow local providers if (item.IsLocked && !(provider is ILocalImageProvider)) { - if (refreshOptions.ImageRefreshMode != ImageRefreshMode.FullRefresh) - { - return false; - } + if (refreshOptions.ImageRefreshMode != ImageRefreshMode.FullRefresh) + { + return false; + } } if (provider is IRemoteImageProvider || provider is IDynamicImageProvider) @@ -481,7 +481,7 @@ namespace MediaBrowser.Providers.Manager ItemType = typeof(T).Name }; - var imageProviders = GetImageProviders(dummy, options, new ImageRefreshOptions(new DirectoryService(_logger, _fileSystem)), true).ToList(); + var imageProviders = GetImageProviders(dummy, options, new ImageRefreshOptions(new DirectoryService(_logger, _fileSystem)), true).ToList(); AddMetadataPlugins(summary.Plugins, dummy, options); AddImagePlugins(summary.Plugins, dummy, imageProviders); @@ -510,25 +510,19 @@ namespace MediaBrowser.Providers.Manager Type = MetadataPluginType.LocalMetadataProvider })); - if (item.IsInternetMetadataEnabled()) + // Fetchers + list.AddRange(providers.Where(i => (i is IRemoteMetadataProvider)).Select(i => new MetadataPlugin { - // Fetchers - list.AddRange(providers.Where(i => (i is IRemoteMetadataProvider)).Select(i => new MetadataPlugin - { - Name = i.Name, - Type = MetadataPluginType.MetadataFetcher - })); - } + Name = i.Name, + Type = MetadataPluginType.MetadataFetcher + })); - if (item.IsSaveLocalMetadataEnabled()) + // Savers + list.AddRange(_savers.Where(i => IsSaverEnabledForItem(i, item, ItemUpdateType.MetadataEdit, true)).OrderBy(i => i.Name).Select(i => new MetadataPlugin { - // Savers - list.AddRange(_savers.Where(i => IsSaverEnabledForItem(i, item, ItemUpdateType.MetadataEdit, true)).OrderBy(i => i.Name).Select(i => new MetadataPlugin - { - Name = i.Name, - Type = MetadataPluginType.MetadataSaver - })); - } + Name = i.Name, + Type = MetadataPluginType.MetadataSaver + })); } private void AddImagePlugins<T>(List<MetadataPlugin> list, T item, List<IImageProvider> imageProviders) @@ -584,7 +578,7 @@ namespace MediaBrowser.Providers.Manager return SaveMetadata(item, updateType, _savers.Where(i => savers.Contains(i.Name, StringComparer.OrdinalIgnoreCase))); } - private readonly SemaphoreSlim _saveLock = new SemaphoreSlim(1,1); + private readonly SemaphoreSlim _saveLock = new SemaphoreSlim(1, 1); /// <summary> /// Saves the metadata. /// </summary> @@ -927,19 +921,19 @@ namespace MediaBrowser.Providers.Manager { await item.RefreshMetadata(options, CancellationToken.None).ConfigureAwait(false); - if (item.IsFolder) + // Collection folders don't validate their children so we'll have to simulate that here + var collectionFolder = item as CollectionFolder; + + if (collectionFolder != null) { - // Collection folders don't validate their children so we'll have to simulate that here - var collectionFolder = item as CollectionFolder; + await RefreshCollectionFolderChildren(options, collectionFolder).ConfigureAwait(false); + } + else + { + var folder = item as Folder; - if (collectionFolder != null) + if (folder != null) { - await RefreshCollectionFolderChildren(options, collectionFolder).ConfigureAwait(false); - } - else - { - var folder = (Folder)item; - await folder.ValidateChildren(new Progress<double>(), cancellationToken, options).ConfigureAwait(false); } } @@ -964,11 +958,14 @@ namespace MediaBrowser.Providers.Manager { var cancellationToken = CancellationToken.None; - var albums = _libraryManagerFactory().RootFolder - .GetRecursiveChildren() - .OfType<MusicAlbum>() - .Where(i => i.HasAnyArtist(item.Name)) - .ToList(); + var albums = _libraryManagerFactory() + .GetItemList(new InternalItemsQuery + { + IncludeItemTypes = new[] { typeof(MusicAlbum).Name }, + ArtistIds = new[] { item.Id.ToString("N") } + }) + .OfType<MusicAlbum>() + .ToList(); var musicArtists = albums .Select(i => i.MusicArtist) diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index eaffc12d7..15f9853ec 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -20,9 +20,6 @@ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Globalization; namespace MediaBrowser.Providers.MediaInfo @@ -35,6 +32,8 @@ namespace MediaBrowser.Providers.MediaInfo ICustomMetadataProvider<Trailer>, ICustomMetadataProvider<Video>, ICustomMetadataProvider<Audio>, + ICustomMetadataProvider<AudioPodcast>, + ICustomMetadataProvider<AudioBook>, IHasItemChangeMonitor, IHasOrder, IForcedProvider, @@ -100,6 +99,16 @@ namespace MediaBrowser.Providers.MediaInfo return FetchAudioInfo(item, cancellationToken); } + public Task<ItemUpdateType> FetchAsync(AudioPodcast item, MetadataRefreshOptions options, CancellationToken cancellationToken) + { + return FetchAudioInfo(item, cancellationToken); + } + + public Task<ItemUpdateType> FetchAsync(AudioBook item, MetadataRefreshOptions options, CancellationToken cancellationToken) + { + return FetchAudioInfo(item, cancellationToken); + } + public FFProbeProvider(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager, IChapterManager chapterManager, ILibraryManager libraryManager) { _logger = logger; diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index c1bae4b3c..c4ea13063 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -417,7 +417,7 @@ namespace MediaBrowser.Providers.MediaInfo } if (!string.IsNullOrWhiteSpace(data.Name)) { - if (string.IsNullOrWhiteSpace(video.Name) || string.Equals(video.Name, Path.GetFileNameWithoutExtension(video.Path), StringComparison.OrdinalIgnoreCase)) + if (string.IsNullOrWhiteSpace(video.Name) || (string.Equals(video.Name, Path.GetFileNameWithoutExtension(video.Path), StringComparison.OrdinalIgnoreCase) && !video.ProviderIds.Any())) { // Don't use the embedded name for extras because it will often be the same name as the movie if (!video.ExtraType.HasValue && !video.IsOwnedItem) diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs index c47b6ab93..0e842cdb4 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs @@ -82,10 +82,10 @@ namespace MediaBrowser.Providers.MediaInfo { MediaTypes = new string[] { MediaType.Video }, IsVirtualItem = false, - ExcludeLocationTypes = new LocationType[] { LocationType.Remote, LocationType.Virtual }, IncludeItemTypes = types.ToArray() }).OfType<Video>() + .Where(i => i.LocationType != LocationType.Remote) .ToList(); if (videos.Count == 0) diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs index f9f247999..fd4d041b2 100644 --- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs +++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs @@ -66,29 +66,39 @@ namespace MediaBrowser.Providers.TV .Distinct() .ToList()) { - var hasSeason = series.Children.OfType<Season>() - .Any(i => i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber); + var existingSeason = series.Children.OfType<Season>() + .FirstOrDefault(i => i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber); - if (!hasSeason) + if (existingSeason == null) { await AddSeason(series, seasonNumber, false, cancellationToken).ConfigureAwait(false); hasChanges = true; } + else if (existingSeason.IsVirtualItem) + { + existingSeason.IsVirtualItem = false; + await existingSeason.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); + } } // Unknown season - create a dummy season to put these under if (episodesInSeriesFolder.Any(i => !i.ParentIndexNumber.HasValue)) { - var hasSeason = series.Children.OfType<Season>() - .Any(i => !i.IndexNumber.HasValue); + var existingSeason = series.Children.OfType<Season>() + .FirstOrDefault(i => !i.IndexNumber.HasValue); - if (!hasSeason) + if (existingSeason == null) { await AddSeason(series, null, false, cancellationToken).ConfigureAwait(false); hasChanges = true; } + else if (existingSeason.IsVirtualItem) + { + existingSeason.IsVirtualItem = false; + await existingSeason.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); + } } return hasChanges; diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 22e7e753c..1fd04070b 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -93,6 +93,12 @@ namespace MediaBrowser.Providers.TV return; } + // Check this in order to avoid logging an exception due to directory not existing + if (!_fileSystem.DirectoryExists(seriesDataPath)) + { + return; + } + var episodeFiles = _fileSystem.GetFilePaths(seriesDataPath) .Where(i => string.Equals(Path.GetExtension(i), ".xml", StringComparison.OrdinalIgnoreCase)) .Select(Path.GetFileNameWithoutExtension) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs index 8488c5669..f6af365fd 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs @@ -143,7 +143,8 @@ namespace MediaBrowser.Providers.TV Url = ServerTimeUrl, CancellationToken = cancellationToken, EnableHttpCompression = true, - ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool + ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool, + BufferContent = false }).ConfigureAwait(false)) { @@ -239,7 +240,8 @@ namespace MediaBrowser.Providers.TV Url = string.Format(UpdatesUrl, lastUpdateTime), CancellationToken = cancellationToken, EnableHttpCompression = true, - ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool + ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool, + BufferContent = false }).ConfigureAwait(false)) { diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index cc8a90fe3..1798299e8 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -237,7 +237,8 @@ namespace MediaBrowser.Providers.TV { Url = url, ResourcePool = TvDbResourcePool, - CancellationToken = cancellationToken + CancellationToken = cancellationToken, + BufferContent = false }).ConfigureAwait(false)) { @@ -281,7 +282,8 @@ namespace MediaBrowser.Providers.TV { Url = url, ResourcePool = TvDbResourcePool, - CancellationToken = cancellationToken + CancellationToken = cancellationToken, + BufferContent = false }).ConfigureAwait(false)) { @@ -535,7 +537,8 @@ namespace MediaBrowser.Providers.TV { Url = url, ResourcePool = TvDbResourcePool, - CancellationToken = cancellationToken + CancellationToken = cancellationToken, + BufferContent = false }).ConfigureAwait(false)) { @@ -1664,7 +1667,8 @@ namespace MediaBrowser.Providers.TV { CancellationToken = cancellationToken, Url = url, - ResourcePool = TvDbResourcePool + ResourcePool = TvDbResourcePool, + BufferContent = false }); } } diff --git a/MediaBrowser.Providers/TV/TvExternalIds.cs b/MediaBrowser.Providers/TV/TvExternalIds.cs index 2ba3b6ff6..d2860a622 100644 --- a/MediaBrowser.Providers/TV/TvExternalIds.cs +++ b/MediaBrowser.Providers/TV/TvExternalIds.cs @@ -119,28 +119,4 @@ namespace MediaBrowser.Providers.TV return item is Series; } } - - public class TvComPersonExternalId : IExternalId - { - public string Name - { - get { return "TV.com"; } - } - - public string Key - { - get { return MetadataProviders.Tvcom.ToString(); } - } - - public string UrlFormatString - { - get { return null; } - } - - public bool Supports(IHasProviderIds item) - { - return item is Person; - } - } - } diff --git a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj index 857a8e15f..85c7533a7 100644 --- a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj +++ b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj @@ -58,6 +58,7 @@ <TlsProvider>Default</TlsProvider>
<LinkMode>None</LinkMode>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <I18n>west</I18n>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'AppStore|AnyCPU' ">
<DebugType>full</DebugType>
@@ -404,9 +405,6 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\manifest.json">
<Link>Resources\dashboard-ui\manifest.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\metadata.html">
- <Link>Resources\dashboard-ui\metadata.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\metadataimages.html">
<Link>Resources\dashboard-ui\metadataimages.html</Link>
</BundleResource>
@@ -548,6 +546,9 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\userprofiles.html">
<Link>Resources\dashboard-ui\userprofiles.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\videoosd.html">
+ <Link>Resources\dashboard-ui\videoosd.html</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html">
<Link>Resources\dashboard-ui\wizardagreement.html</Link>
</BundleResource>
@@ -914,6 +915,9 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\apiclient.js">
<Link>Resources\dashboard-ui\bower_components\emby-apiclient\apiclient.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\apiclientex.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-apiclient\apiclientex.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\appstorage-cache.js">
<Link>Resources\dashboard-ui\bower_components\emby-apiclient\appstorage-cache.js</Link>
</BundleResource>
@@ -1013,6 +1017,9 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\browserdeviceprofile.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\browserdeviceprofile.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\chromecastplayer.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\chromecastplayer.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\clearbutton.css">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\clearbutton.css</Link>
</BundleResource>
@@ -1061,15 +1068,24 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\multidownload.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\multidownload.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\packagemanager.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\packagemanager.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playmenu.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playmenu.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\pluginmanager.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\pluginmanager.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\qualityoptions.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\qualityoptions.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\router.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\router.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\sanitizefilename.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\sanitizefilename.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\scrollhelper.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\scrollhelper.js</Link>
</BundleResource>
@@ -1079,6 +1095,9 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\servernotifications.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\servernotifications.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\sessionplayer.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\sessionplayer.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\shell.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\shell.js</Link>
</BundleResource>
@@ -1223,194 +1242,92 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\emby-toggle\emby-toggle.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\emby-toggle\emby-toggle.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\material-icons\2fcrYFNaTjcS6g4U3t-Y5ZjZjT5FdEJ140U2DJYC3mY.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\material-icons\2fcrYFNaTjcS6g4U3t-Y5ZjZjT5FdEJ140U2DJYC3mY.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\material-icons\2fcryfnatjcs6g4u3t-y5ewrjpiaoeww8aihgqwrjao.woff">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\material-icons\2fcryfnatjcs6g4u3t-y5ewrjpiaoeww8aihgqwrjao.woff</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\material-icons\2fcrYFNaTjcS6g4U3t-Y5ewrjPiaoEww8AihgqWRJAo.woff">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\material-icons\2fcrYFNaTjcS6g4U3t-Y5ewrjPiaoEww8AihgqWRJAo.woff</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\material-icons\2fcryfnatjcs6g4u3t-y5zjzjt5fdej140u2djyc3my.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\material-icons\2fcryfnatjcs6g4u3t-y5zjzjt5fdej140u2djyc3my.woff2</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\material-icons\style.css">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\material-icons\style.css</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\montserrat\IQHow_FEYlDC4Gzy_m8fcgFhaRv2pGgT5Kf0An0s4MM.woff">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\montserrat\IQHow_FEYlDC4Gzy_m8fcgFhaRv2pGgT5Kf0An0s4MM.woff</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\montserrat\IQHow_FEYlDC4Gzy_m8fcoWiMMZ7xLd792ULpGE4W_Y.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\montserrat\IQHow_FEYlDC4Gzy_m8fcoWiMMZ7xLd792ULpGE4W_Y.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\montserrat\style.css">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\montserrat\style.css</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\montserrat\zhcz-_WihjSQC0oHJ9TCYBsxEYwM7FgeyaSgU71cLG0.woff">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\montserrat\zhcz-_WihjSQC0oHJ9TCYBsxEYwM7FgeyaSgU71cLG0.woff</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\montserrat\zhcz-_WihjSQC0oHJ9TCYPk_vArhqVIZ0nv9q090hN8.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\montserrat\zhcz-_WihjSQC0oHJ9TCYPk_vArhqVIZ0nv9q090hN8.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\59ZRklaO5bWGqF5A9baEERJtnKITppOI_IvcXXDNrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\59ZRklaO5bWGqF5A9baEERJtnKITppOI_IvcXXDNrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\DXI1ORHCpsQm3Vp6mXoaTRWV49_lSm1NYrwo-zkhivY.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\DXI1ORHCpsQm3Vp6mXoaTRWV49_lSm1NYrwo-zkhivY.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\DXI1ORHCpsQm3Vp6mXoaTT0LW-43aMEzIO6XUTLjad8.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\DXI1ORHCpsQm3Vp6mXoaTT0LW-43aMEzIO6XUTLjad8.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\DXI1ORHCpsQm3Vp6mXoaTZX5f-9o1vgP2EXwfjgl7AY.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\DXI1ORHCpsQm3Vp6mXoaTZX5f-9o1vgP2EXwfjgl7AY.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\DXI1ORHCpsQm3Vp6mXoaTa-j2U0lmluP9RWlSytm3ho.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\DXI1ORHCpsQm3Vp6mXoaTa-j2U0lmluP9RWlSytm3ho.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\DXI1ORHCpsQm3Vp6mXoaTaaRobkAwv3vxw3jMhVENGA.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\DXI1ORHCpsQm3Vp6mXoaTaaRobkAwv3vxw3jMhVENGA.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\DXI1ORHCpsQm3Vp6mXoaTegdm0LZdjqr5-oayXSOefg.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\DXI1ORHCpsQm3Vp6mXoaTegdm0LZdjqr5-oayXSOefg.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\DXI1ORHCpsQm3Vp6mXoaTf8zf_FOSsgRmwsS7Aa9k2w.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\DXI1ORHCpsQm3Vp6mXoaTf8zf_FOSsgRmwsS7Aa9k2w.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\K88pR3goAWT7BTt32Z01mxJtnKITppOI_IvcXXDNrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\K88pR3goAWT7BTt32Z01mxJtnKITppOI_IvcXXDNrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\LWCjsQkB6EMdfHrEVqA1KRJtnKITppOI_IvcXXDNrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\LWCjsQkB6EMdfHrEVqA1KRJtnKITppOI_IvcXXDNrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\MTP_ySUJH_bn48VBG8sNShWV49_lSm1NYrwo-zkhivY.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\MTP_ySUJH_bn48VBG8sNShWV49_lSm1NYrwo-zkhivY.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\MTP_ySUJH_bn48VBG8sNSj0LW-43aMEzIO6XUTLjad8.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\MTP_ySUJH_bn48VBG8sNSj0LW-43aMEzIO6XUTLjad8.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\MTP_ySUJH_bn48VBG8sNSpX5f-9o1vgP2EXwfjgl7AY.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\MTP_ySUJH_bn48VBG8sNSpX5f-9o1vgP2EXwfjgl7AY.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\MTP_ySUJH_bn48VBG8sNSq-j2U0lmluP9RWlSytm3ho.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\MTP_ySUJH_bn48VBG8sNSq-j2U0lmluP9RWlSytm3ho.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\MTP_ySUJH_bn48VBG8sNSqaRobkAwv3vxw3jMhVENGA.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\MTP_ySUJH_bn48VBG8sNSqaRobkAwv3vxw3jMhVENGA.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\MTP_ySUJH_bn48VBG8sNSugdm0LZdjqr5-oayXSOefg.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\MTP_ySUJH_bn48VBG8sNSugdm0LZdjqr5-oayXSOefg.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\MTP_ySUJH_bn48VBG8sNSv8zf_FOSsgRmwsS7Aa9k2w.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\MTP_ySUJH_bn48VBG8sNSv8zf_FOSsgRmwsS7Aa9k2w.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\RjgO7rYTmqiVp7vzi-Q5URJtnKITppOI_IvcXXDNrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\RjgO7rYTmqiVp7vzi-Q5URJtnKITppOI_IvcXXDNrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\cJZKeOuBrn4kERxqtaUH3VtXRa8TVwTICgirnJhmVJw.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\cJZKeOuBrn4kERxqtaUH3VtXRa8TVwTICgirnJhmVJw.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\k3k702ZOKiLJc3WVjuplzBWV49_lSm1NYrwo-zkhivY.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\k3k702ZOKiLJc3WVjuplzBWV49_lSm1NYrwo-zkhivY.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\k3k702ZOKiLJc3WVjuplzD0LW-43aMEzIO6XUTLjad8.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\k3k702ZOKiLJc3WVjuplzD0LW-43aMEzIO6XUTLjad8.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\k3k702ZOKiLJc3WVjuplzJX5f-9o1vgP2EXwfjgl7AY.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\k3k702ZOKiLJc3WVjuplzJX5f-9o1vgP2EXwfjgl7AY.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\k3k702ZOKiLJc3WVjuplzK-j2U0lmluP9RWlSytm3ho.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\k3k702ZOKiLJc3WVjuplzK-j2U0lmluP9RWlSytm3ho.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\k3k702ZOKiLJc3WVjuplzKaRobkAwv3vxw3jMhVENGA.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\k3k702ZOKiLJc3WVjuplzKaRobkAwv3vxw3jMhVENGA.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\-l14jk06m6puhb-5mxqqnrjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\-l14jk06m6puhb-5mxqqnrjtnkitppoi_ivcxxdnrsc.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\k3k702ZOKiLJc3WVjuplzOgdm0LZdjqr5-oayXSOefg.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\k3k702ZOKiLJc3WVjuplzOgdm0LZdjqr5-oayXSOefg.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\0ec6fl06luxeywpbsjvxcbjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\0ec6fl06luxeywpbsjvxcbjtnkitppoi_ivcxxdnrsc.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\k3k702ZOKiLJc3WVjuplzP8zf_FOSsgRmwsS7Aa9k2w.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\k3k702ZOKiLJc3WVjuplzP8zf_FOSsgRmwsS7Aa9k2w.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\2tsd397wlxj96qwhynikxpeszw2xoq-xsnqo47m55da.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\2tsd397wlxj96qwhynikxpeszw2xoq-xsnqo47m55da.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\style.css">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\style.css</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\97uahxiqzroncbacei3awxjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\97uahxiqzroncbacei3awxjtnkitppoi_ivcxxdnrsc.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\u-WUoqrET9fUeobQW7jkRRJtnKITppOI_IvcXXDNrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\u-WUoqrET9fUeobQW7jkRRJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\azmswpodyevhtrvuabjwvbtbgvql8ndjpwnre27mub0.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\azmswpodyevhtrvuabjwvbtbgvql8ndjpwnre27mub0.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\xozscpT2726on7jbcb_pAhJtnKITppOI_IvcXXDNrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\opensans\xozscpT2726on7jbcb_pAhJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\cwb0xya8bzo0ksthx0utua.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\cwb0xya8bzo0ksthx0utua.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\-L14Jk06m6pUHB-5mXQQnRJtnKITppOI_IvcXXDNrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\-L14Jk06m6pUHB-5mXQQnRJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\d-6iyplofoccackzxwxsoftxra8tvwticgirnjhmvjw.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\d-6iyplofoccackzxwxsoftxra8tvwticgirnjhmvjw.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\0eC6fl06luXEYWpBSJvXCBJtnKITppOI_IvcXXDNrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\0eC6fl06luXEYWpBSJvXCBJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\e7mevayvogmqfwwl61pkhbtbgvql8ndjpwnre27mub0.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\e7mevayvogmqfwwl61pkhbtbgvql8ndjpwnre27mub0.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\2tsd397wLxj96qwHyNIkxPesZW2xOQ-xsNqO47m55DA.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\2tsd397wLxj96qwHyNIkxPesZW2xOQ-xsNqO47m55DA.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\fcx7wwv8ozt71a3e1xoajveszw2xoq-xsnqo47m55da.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\fcx7wwv8ozt71a3e1xoajveszw2xoq-xsnqo47m55da.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\97uahxiqZRoncBaCEI3aWxJtnKITppOI_IvcXXDNrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\97uahxiqZRoncBaCEI3aWxJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\fl4y0qdoxyythegmxx8kcrjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\fl4y0qdoxyythegmxx8kcrjtnkitppoi_ivcxxdnrsc.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\CWB0XYA8bzo0kSThX0UTuA.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\CWB0XYA8bzo0kSThX0UTuA.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\frnv30oaydlfrth2vnzzdhtbgvql8ndjpwnre27mub0.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\frnv30oaydlfrth2vnzzdhtbgvql8ndjpwnre27mub0.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\Fcx7Wwv8OzT71A3E1XOAjvesZW2xOQ-xsNqO47m55DA.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\Fcx7Wwv8OzT71A3E1XOAjvesZW2xOQ-xsNqO47m55DA.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\gwvjdern2amz39wrsoz7fxtbgvql8ndjpwnre27mub0.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\gwvjdern2amz39wrsoz7fxtbgvql8ndjpwnre27mub0.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\Fl4y0QdOxyyTHEGMXX8kcRJtnKITppOI_IvcXXDNrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\Fl4y0QdOxyyTHEGMXX8kcRJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\hgo13k-tfspn0qi1sfdufvtxra8tvwticgirnjhmvjw.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\hgo13k-tfspn0qi1sfdufvtxra8tvwticgirnjhmvjw.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\Hgo13k-tfSpn0qi1SFdUfVtXRa8TVwTICgirnJhmVJw.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\Hgo13k-tfSpn0qi1SFdUfVtXRa8TVwTICgirnJhmVJw.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\i3s1wsgsg9ycurv6puktorjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\i3s1wsgsg9ycurv6puktorjtnkitppoi_ivcxxdnrsc.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\I3S1wsgSg9YCurV6PUkTORJtnKITppOI_IvcXXDNrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\I3S1wsgSg9YCurV6PUkTORJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\nydwbdd4giq26g5xybhsfbjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\nydwbdd4giq26g5xybhsfbjtnkitppoi_ivcxxdnrsc.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\NYDWBdD4gIq26G5XYbHsFBJtnKITppOI_IvcXXDNrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\NYDWBdD4gIq26G5XYbHsFBJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\ooefwznlrtefzlymlvv1ubjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\ooefwznlrtefzlymlvv1ubjtnkitppoi_ivcxxdnrsc.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\Pru33qjShpZSmG3z6VYwnRJtnKITppOI_IvcXXDNrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\Pru33qjShpZSmG3z6VYwnRJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\pru33qjshpzsmg3z6vywnrjtnkitppoi_ivcxxdnrsc.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\pru33qjshpzsmg3z6vywnrjtnkitppoi_ivcxxdnrsc.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\RobotoBold.woff">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\RobotoBold.woff</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotobold.woff">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotobold.woff</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\RobotoLight.woff">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\RobotoLight.woff</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotolight.woff">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotolight.woff</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\RobotoMedium.woff">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\RobotoMedium.woff</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotomedium.woff">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotomedium.woff</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\RobotoRegular.woff">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\RobotoRegular.woff</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotoregular.woff">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotoregular.woff</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\RobotoThin.woff">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\RobotoThin.woff</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotothin.woff">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\robotothin.woff</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\RxZJdnzeo3R5zSexge8UUVtXRa8TVwTICgirnJhmVJw.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\RxZJdnzeo3R5zSexge8UUVtXRa8TVwTICgirnJhmVJw.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\VvXUGKZXbHtX_S_VCTLpGhTbgVql8nDJpwnrE27mub0.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\VvXUGKZXbHtX_S_VCTLpGhTbgVql8nDJpwnrE27mub0.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\aZMswpodYeVhtRvuABJWvBTbgVql8nDJpwnrE27mub0.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\aZMswpodYeVhtRvuABJWvBTbgVql8nDJpwnrE27mub0.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\d-6IYplOFocCacKzxwXSOFtXRa8TVwTICgirnJhmVJw.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\d-6IYplOFocCacKzxwXSOFtXRa8TVwTICgirnJhmVJw.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\e7MeVAyvogMqFwwl61PKhBTbgVql8nDJpwnrE27mub0.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\e7MeVAyvogMqFwwl61PKhBTbgVql8nDJpwnrE27mub0.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\frNV30OaYdlFRtH2VnZZdhTbgVql8nDJpwnrE27mub0.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\frNV30OaYdlFRtH2VnZZdhTbgVql8nDJpwnrE27mub0.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\gwVJDERN2Amz39wrSoZ7FxTbgVql8nDJpwnrE27mub0.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\gwVJDERN2Amz39wrSoZ7FxTbgVql8nDJpwnrE27mub0.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\oOeFwZNlrTefzLYmlVV1UBJtnKITppOI_IvcXXDNrsc.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\oOeFwZNlrTefzLYmlVV1UBJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\rxzjdnzeo3r5zsexge8uuvtxra8tvwticgirnjhmvjw.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\rxzjdnzeo3r5zsexge8uuvtxra8tvwticgirnjhmvjw.woff2</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\style.css">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\style.css</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\ty9dfvLAziwdqQ2dHoyjphTbgVql8nDJpwnrE27mub0.woff2">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\ty9dfvLAziwdqQ2dHoyjphTbgVql8nDJpwnrE27mub0.woff2</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\ty9dfvlaziwdqq2dhoyjphtbgvql8ndjpwnre27mub0.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\ty9dfvlaziwdqq2dhoyjphtbgvql8ndjpwnre27mub0.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\vvxugkzxbhtx_s_vctlpghtbgvql8ndjpwnre27mub0.woff2">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fonts\roboto\vvxugkzxbhtx_s_vctlpghtbgvql8ndjpwnre27mub0.woff2</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fullscreen\fullscreen-doubleclick.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fullscreen\fullscreen-doubleclick.js</Link>
@@ -1436,6 +1353,18 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\guide\tvguide.template.html">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\guide\tvguide.template.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\htmlaudioplayer\blank.mp3">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\htmlaudioplayer\blank.mp3</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\htmlaudioplayer\plugin.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\htmlaudioplayer\plugin.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\htmlvideoplayer\plugin.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\htmlvideoplayer\plugin.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\htmlvideoplayer\style.css">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\htmlvideoplayer\style.css</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\imageeditor\imageeditor.css">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\imageeditor\imageeditor.css</Link>
</BundleResource>
@@ -1466,6 +1395,12 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\input\api.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\input\api.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\input\gamepadtokey.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\input\gamepadtokey.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\input\mouse.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\input\mouse.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\itemhovermenu\itemhovermenu.css">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\itemhovermenu\itemhovermenu.css</Link>
</BundleResource>
@@ -1553,6 +1488,24 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\pagejs\page.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\pagejs\page.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\nowplayinghelper.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\nowplayinghelper.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\playbackmanager.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\playbackmanager.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\playbackvalidation.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\playbackvalidation.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\playerselection.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\playerselection.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\playersettingsmenu.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\playersettingsmenu.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\remotecontrolautoplay.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\remotecontrolautoplay.js</Link>
+ </BundleResource>
<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>
</BundleResource>
@@ -1631,6 +1584,12 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\require\requiretext.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\require\requiretext.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\resourcelocks\nullresourcelock.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\resourcelocks\nullresourcelock.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\resourcelocks\resourcelockmanager.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\resourcelocks\resourcelockmanager.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\scroller\smoothscroller.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\scroller\smoothscroller.js</Link>
</BundleResource>
@@ -1682,8 +1641,8 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\ar.json">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\ar.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\bg-BG.json">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\bg-BG.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\bg-bg.json">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\bg-bg.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\ca.json">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\ca.json</Link>
@@ -1700,17 +1659,17 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\el.json">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\el.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\en-GB.json">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\en-GB.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\en-gb.json">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\en-gb.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\en-US.json">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\en-US.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\en-us.json">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\en-us.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\es-AR.json">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\es-AR.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\es-ar.json">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\es-ar.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\es-MX.json">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\es-MX.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\es-mx.json">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\es-mx.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\es.json">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\es.json</Link>
@@ -1718,8 +1677,8 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\fi.json">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\fi.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\fr-CA.json">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\fr-CA.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\fr-ca.json">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\fr-ca.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\fr.json">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\fr.json</Link>
@@ -1760,11 +1719,11 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\pl.json">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\pl.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\pt-BR.json">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\pt-BR.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\pt-br.json">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\pt-br.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\pt-PT.json">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\pt-PT.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\pt-pt.json">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\pt-pt.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\ro.json">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\ro.json</Link>
@@ -1775,8 +1734,8 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\sk.json">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\sk.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\sl-SI.json">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\sl-SI.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\sl-si.json">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\sl-si.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\sv.json">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\sv.json</Link>
@@ -1790,14 +1749,14 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\vi.json">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\vi.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\zh-CN.json">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\zh-CN.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\zh-cn.json">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\zh-cn.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\zh-HK.json">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\zh-HK.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\zh-hk.json">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\zh-hk.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\zh-TW.json">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\zh-TW.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\strings\zh-tw.json">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\strings\zh-tw.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\subtitleeditor\subtitleeditor.css">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\subtitleeditor\subtitleeditor.css</Link>
@@ -1895,6 +1854,12 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\voice\grammar\grammar.json">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\voice\grammar\grammar.json</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\youtubeplayer\plugin.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\youtubeplayer\plugin.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\youtubeplayer\style.css">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\youtubeplayer\style.css</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fetch\.bower.json">
<Link>Resources\dashboard-ui\bower_components\fetch\.bower.json</Link>
</BundleResource>
@@ -2600,9 +2565,6 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\remotecontrol.js">
<Link>Resources\dashboard-ui\components\remotecontrol.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\remotecontrolautoplay.js">
- <Link>Resources\dashboard-ui\components\remotecontrolautoplay.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\viewcontainer-lite.js">
<Link>Resources\dashboard-ui\components\viewcontainer-lite.js</Link>
</BundleResource>
@@ -2729,9 +2691,6 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\livetv.css">
<Link>Resources\dashboard-ui\css\livetv.css</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\mediaplayer-video.css">
- <Link>Resources\dashboard-ui\css\mediaplayer-video.css</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\metadataeditor.css">
<Link>Resources\dashboard-ui\css\metadataeditor.css</Link>
</BundleResource>
@@ -3056,9 +3015,6 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\channelslatest.js">
<Link>Resources\dashboard-ui\scripts\channelslatest.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\chromecast.js">
- <Link>Resources\dashboard-ui\scripts\chromecast.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\connectlogin.js">
<Link>Resources\dashboard-ui\scripts\connectlogin.js</Link>
</BundleResource>
@@ -3092,9 +3048,6 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\episodes.js">
<Link>Resources\dashboard-ui\scripts\episodes.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\externalplayer.js">
- <Link>Resources\dashboard-ui\scripts\externalplayer.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\favorites.js">
<Link>Resources\dashboard-ui\scripts\favorites.js</Link>
</BundleResource>
@@ -3128,9 +3081,6 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\homeupcoming.js">
<Link>Resources\dashboard-ui\scripts\homeupcoming.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\htmlmediarenderer.js">
- <Link>Resources\dashboard-ui\scripts\htmlmediarenderer.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\indexpage.js">
<Link>Resources\dashboard-ui\scripts\indexpage.js</Link>
</BundleResource>
@@ -3197,21 +3147,9 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\loginpage.js">
<Link>Resources\dashboard-ui\scripts\loginpage.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mediacontroller.js">
- <Link>Resources\dashboard-ui\scripts\mediacontroller.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\medialibrarypage.js">
<Link>Resources\dashboard-ui\scripts\medialibrarypage.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mediaplayer-video.js">
- <Link>Resources\dashboard-ui\scripts\mediaplayer-video.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mediaplayer.js">
- <Link>Resources\dashboard-ui\scripts\mediaplayer.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\metadataconfigurationpage.js">
- <Link>Resources\dashboard-ui\scripts\metadataconfigurationpage.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\metadataimagespage.js">
<Link>Resources\dashboard-ui\scripts\metadataimagespage.js</Link>
</BundleResource>
@@ -3311,9 +3249,6 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\pluginspage.js">
<Link>Resources\dashboard-ui\scripts\pluginspage.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\remotecontrol.js">
- <Link>Resources\dashboard-ui\scripts\remotecontrol.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\reports.js">
<Link>Resources\dashboard-ui\scripts\reports.js</Link>
</BundleResource>
@@ -3401,6 +3336,9 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\userprofilespage.js">
<Link>Resources\dashboard-ui\scripts\userprofilespage.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\videoosd.js">
+ <Link>Resources\dashboard-ui\scripts\videoosd.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardagreement.js">
<Link>Resources\dashboard-ui\scripts\wizardagreement.js</Link>
</BundleResource>
@@ -3425,11 +3363,11 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\ar.json">
<Link>Resources\dashboard-ui\strings\ar.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\be-BY.json">
- <Link>Resources\dashboard-ui\strings\be-BY.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\be-by.json">
+ <Link>Resources\dashboard-ui\strings\be-by.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\bg-BG.json">
- <Link>Resources\dashboard-ui\strings\bg-BG.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\bg-bg.json">
+ <Link>Resources\dashboard-ui\strings\bg-bg.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\ca.json">
<Link>Resources\dashboard-ui\strings\ca.json</Link>
@@ -3440,8 +3378,8 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\da.json">
<Link>Resources\dashboard-ui\strings\da.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\de-DE.json">
- <Link>Resources\dashboard-ui\strings\de-DE.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\de-de.json">
+ <Link>Resources\dashboard-ui\strings\de-de.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\de.json">
<Link>Resources\dashboard-ui\strings\de.json</Link>
@@ -3449,20 +3387,20 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\el.json">
<Link>Resources\dashboard-ui\strings\el.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\en-GB.json">
- <Link>Resources\dashboard-ui\strings\en-GB.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\en-gb.json">
+ <Link>Resources\dashboard-ui\strings\en-gb.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\en-US.json">
- <Link>Resources\dashboard-ui\strings\en-US.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\en-us.json">
+ <Link>Resources\dashboard-ui\strings\en-us.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\es-AR.json">
- <Link>Resources\dashboard-ui\strings\es-AR.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\es-ar.json">
+ <Link>Resources\dashboard-ui\strings\es-ar.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\es-ES.json">
- <Link>Resources\dashboard-ui\strings\es-ES.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\es-es.json">
+ <Link>Resources\dashboard-ui\strings\es-es.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\es-MX.json">
- <Link>Resources\dashboard-ui\strings\es-MX.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\es-mx.json">
+ <Link>Resources\dashboard-ui\strings\es-mx.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\es.json">
<Link>Resources\dashboard-ui\strings\es.json</Link>
@@ -3470,11 +3408,11 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\fi.json">
<Link>Resources\dashboard-ui\strings\fi.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\fr-CA.json">
- <Link>Resources\dashboard-ui\strings\fr-CA.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\fr-ca.json">
+ <Link>Resources\dashboard-ui\strings\fr-ca.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\fr-FR.json">
- <Link>Resources\dashboard-ui\strings\fr-FR.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\fr-fr.json">
+ <Link>Resources\dashboard-ui\strings\fr-fr.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\fr.json">
<Link>Resources\dashboard-ui\strings\fr.json</Link>
@@ -3503,8 +3441,8 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\ko.json">
<Link>Resources\dashboard-ui\strings\ko.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\lt-LT.json">
- <Link>Resources\dashboard-ui\strings\lt-LT.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\lt-lt.json">
+ <Link>Resources\dashboard-ui\strings\lt-lt.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\ms.json">
<Link>Resources\dashboard-ui\strings\ms.json</Link>
@@ -3518,11 +3456,11 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\pl.json">
<Link>Resources\dashboard-ui\strings\pl.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\pt-BR.json">
- <Link>Resources\dashboard-ui\strings\pt-BR.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\pt-br.json">
+ <Link>Resources\dashboard-ui\strings\pt-br.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\pt-PT.json">
- <Link>Resources\dashboard-ui\strings\pt-PT.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\pt-pt.json">
+ <Link>Resources\dashboard-ui\strings\pt-pt.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\ro.json">
<Link>Resources\dashboard-ui\strings\ro.json</Link>
@@ -3533,8 +3471,8 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\sk.json">
<Link>Resources\dashboard-ui\strings\sk.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\sl-SI.json">
- <Link>Resources\dashboard-ui\strings\sl-SI.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\sl-si.json">
+ <Link>Resources\dashboard-ui\strings\sl-si.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\sv.json">
<Link>Resources\dashboard-ui\strings\sv.json</Link>
@@ -3548,14 +3486,14 @@ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\vi.json">
<Link>Resources\dashboard-ui\strings\vi.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\zh-CN.json">
- <Link>Resources\dashboard-ui\strings\zh-CN.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\zh-cn.json">
+ <Link>Resources\dashboard-ui\strings\zh-cn.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\zh-HK.json">
- <Link>Resources\dashboard-ui\strings\zh-HK.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\zh-hk.json">
+ <Link>Resources\dashboard-ui\strings\zh-hk.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\zh-TW.json">
- <Link>Resources\dashboard-ui\strings\zh-TW.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\zh-tw.json">
+ <Link>Resources\dashboard-ui\strings\zh-tw.json</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\themes\halloween\bg.jpg">
<Link>Resources\dashboard-ui\themes\halloween\bg.jpg</Link>
diff --git a/MediaBrowser.Server.Mac/Main.cs b/MediaBrowser.Server.Mac/Main.cs index 6d02c7a1a..8c5ca6c1f 100644 --- a/MediaBrowser.Server.Mac/Main.cs +++ b/MediaBrowser.Server.Mac/Main.cs @@ -99,7 +99,7 @@ namespace MediaBrowser.Server.Mac // Allow all https requests ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); - var fileSystem = new MonoFileSystem(logManager.GetLogger("FileSystem"), false, false); + var fileSystem = new MonoFileSystem(logManager.GetLogger("FileSystem"), false, false, appPaths.TempDirectory); fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); var environmentInfo = GetEnvironmentInfo(); diff --git a/MediaBrowser.Server.Mac/Native/MonoFileSystem.cs b/MediaBrowser.Server.Mac/Native/MonoFileSystem.cs index 3fb07b8d8..daf2b90e6 100644 --- a/MediaBrowser.Server.Mac/Native/MonoFileSystem.cs +++ b/MediaBrowser.Server.Mac/Native/MonoFileSystem.cs @@ -6,7 +6,8 @@ namespace Emby.Server.Mac.Native { public class MonoFileSystem : ManagedFileSystem { - public MonoFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars) : base(logger, supportsAsyncFileStreams, enableManagedInvalidFileNameChars, true) + public MonoFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars, string tempPath) + : base(logger, supportsAsyncFileStreams, enableManagedInvalidFileNameChars, true, tempPath) { } diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj index 24da2fd59..325011adf 100644 --- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj +++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj @@ -227,10 +227,6 @@ <SubType>Designer</SubType> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> - <None Include="System.Data.SQLite.dll.config"> - <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> - <SubType>Designer</SubType> - </None> </ItemGroup> <ItemGroup /> </Project>
\ No newline at end of file diff --git a/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs b/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs index 3d6a3e35d..a5dc691a7 100644 --- a/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs +++ b/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs @@ -6,8 +6,8 @@ namespace MediaBrowser.Server.Mono.Native { public class MonoFileSystem : ManagedFileSystem { - public MonoFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars) - : base(logger, supportsAsyncFileStreams, enableManagedInvalidFileNameChars, true) + public MonoFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars, string tempPath) + : base(logger, supportsAsyncFileStreams, enableManagedInvalidFileNameChars, true, tempPath) { } diff --git a/MediaBrowser.Server.Mono/Program.cs b/MediaBrowser.Server.Mono/Program.cs index b3aeb2027..8100dec8d 100644 --- a/MediaBrowser.Server.Mono/Program.cs +++ b/MediaBrowser.Server.Mono/Program.cs @@ -108,7 +108,7 @@ namespace MediaBrowser.Server.Mono // Allow all https requests ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); - var fileSystem = new MonoFileSystem(logManager.GetLogger("FileSystem"), false, false); + var fileSystem = new MonoFileSystem(logManager.GetLogger("FileSystem"), false, false, appPaths.TempDirectory); fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); var environmentInfo = GetEnvironmentInfo(); diff --git a/MediaBrowser.Server.Mono/System.Data.SQLite.dll.config b/MediaBrowser.Server.Mono/System.Data.SQLite.dll.config deleted file mode 100644 index 83a6cd9f3..000000000 --- a/MediaBrowser.Server.Mono/System.Data.SQLite.dll.config +++ /dev/null @@ -1,3 +0,0 @@ -<configuration> - <dllmap dll="sqlite3" target="libsqlite3.so" os="linux"/> -</configuration>
\ No newline at end of file diff --git a/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs index 8ecdca46b..fded98862 100644 --- a/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs +++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs @@ -113,7 +113,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp Type = SatIpHost.DeviceType, Url = deviceUrl, InfoUrl = infoUrl, - DataVersion = 1, DeviceId = info.DeviceId, FriendlyName = info.FriendlyName, Tuners = info.Tuners, diff --git a/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/SatIpHost.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/SatIpHost.cs index 55101ce10f..fffca4ca9 100644 --- a/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/SatIpHost.cs +++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/SatIpHost.cs @@ -44,7 +44,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp { if (!string.IsNullOrWhiteSpace(tuner.M3UUrl)) { - return await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).Parse(tuner.M3UUrl, ChannelIdPrefix, tuner.Id, cancellationToken).ConfigureAwait(false); + return await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).Parse(tuner.M3UUrl, ChannelIdPrefix, tuner.Id, false, cancellationToken).ConfigureAwait(false); } var channels = await new ChannelScan(Logger).Scan(tuner, cancellationToken).ConfigureAwait(false); @@ -115,6 +115,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp RequiresClosing = false }; + mediaSource.InferTotalBitrate(true); + return new List<MediaSourceInfo> { mediaSource }; } return new List<MediaSourceInfo>(); @@ -179,4 +181,4 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp return streamPath; } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index c34940141..70b86c4a6 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -324,7 +324,7 @@ namespace MediaBrowser.ServerApplication /// <param name="options">The options.</param> private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, bool runService, StartupOptions options) { - var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), true, true, true); + var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), true, true, true, appPaths.TempDirectory); fileSystem.AddShortcutHandler(new LnkShortcutHandler()); fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); @@ -601,6 +601,12 @@ namespace MediaBrowser.ServerApplication /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> private static bool PerformUpdateIfNeeded(ServerApplicationPaths appPaths, ILogger logger) { + // Not supported + if (IsRunningAsService) + { + return false; + } + // Look for the existence of an update archive var updateArchive = Path.Combine(appPaths.TempUpdatePath, "MBServer" + ".zip"); if (File.Exists(updateArchive)) diff --git a/MediaBrowser.Tests/M3uParserTest.cs b/MediaBrowser.Tests/M3uParserTest.cs new file mode 100644 index 000000000..3320d8794 --- /dev/null +++ b/MediaBrowser.Tests/M3uParserTest.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Emby.Common.Implementations.Cryptography; +using Emby.Server.Implementations.LiveTv.TunerHosts; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Model.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MediaBrowser.Tests +{ + [TestClass] + public class M3uParserTest + { + [TestMethod] + public void TestFormat1() + { + BaseExtensions.CryptographyProvider = new CryptographyProvider(); + + var result = new M3uParser(new NullLogger(), null, null, null).ParseString("#EXTINF:0,84. VOX Schweiz\nhttp://mystream", "-", "-"); + Assert.AreEqual(1, result.Count); + + Assert.AreEqual("VOX Schweiz", result[0].Name); + Assert.AreEqual("84", result[0].Number); + } + [TestMethod] + public void TestFormat2() + { + BaseExtensions.CryptographyProvider = new CryptographyProvider(); + + var input = "#EXTINF:-1 tvg-id=\"\" tvg-name=\"ABC News 04\" tvg-logo=\"\" group-title=\"ABC Group\",ABC News 04"; + input += "\n"; + input += "http://mystream"; + + var result = new M3uParser(new NullLogger(), null, null, null).ParseString(input, "-", "-"); + Assert.AreEqual(1, result.Count); + + Assert.AreEqual("ABC News 04", result[0].Name); + Assert.IsNull(result[0].Number); + } + + [TestMethod] + public void TestFormat3() + { + BaseExtensions.CryptographyProvider = new CryptographyProvider(); + + var result = new M3uParser(new NullLogger(), null, null, null).ParseString("#EXTINF:0, 3.2 - Movies!\nhttp://mystream", "-", "-"); + Assert.AreEqual(1, result.Count); + + Assert.AreEqual("Movies!", result[0].Name); + Assert.AreEqual("3.2", result[0].Number); + } + + [TestMethod] + public void TestFormat4() + { + BaseExtensions.CryptographyProvider = new CryptographyProvider(); + + var result = new M3uParser(new NullLogger(), null, null, null).ParseString("#EXTINF:0 tvg-id=\"abckabclosangeles.path.to\" tvg-logo=\"path.to / channel_logos / abckabclosangeles.png\", ABC KABC Los Angeles\nhttp://mystream", "-", "-"); + Assert.AreEqual(1, result.Count); + + Assert.IsNull(result[0].Number); + Assert.AreEqual("ABC KABC Los Angeles", result[0].Name); + } + + [TestMethod] + public void TestFormat5() + { + BaseExtensions.CryptographyProvider = new CryptographyProvider(); + + var result = new M3uParser(new NullLogger(), null, null, null).ParseString("#EXTINF:-1 channel-id=\"2101\" tvg-id=\"I69387.json.schedulesdirect.org\" group-title=\"Entertainment\",BBC 1 HD\nhttp://mystream", "-", "-"); + Assert.AreEqual(1, result.Count); + + Assert.AreEqual("BBC 1 HD", result[0].Name); + Assert.AreEqual("2101", result[0].Number); + } + + [TestMethod] + public void TestFormat6() + { + BaseExtensions.CryptographyProvider = new CryptographyProvider(); + + var result = new M3uParser(new NullLogger(), null, null, null).ParseString("#EXTINF:-1 tvg-id=\"2101\" group-title=\"Entertainment\",BBC 1 HD\nhttp://mystream", "-", "-"); + Assert.AreEqual(1, result.Count); + + Assert.AreEqual("BBC 1 HD", result[0].Name); + Assert.AreEqual("2101", result[0].Number); + } + } +} diff --git a/MediaBrowser.Tests/MediaBrowser.Tests.csproj b/MediaBrowser.Tests/MediaBrowser.Tests.csproj index 4ea2cb0c0..8ba828d85 100644 --- a/MediaBrowser.Tests/MediaBrowser.Tests.csproj +++ b/MediaBrowser.Tests/MediaBrowser.Tests.csproj @@ -37,6 +37,9 @@ <WarningLevel>4</WarningLevel> </PropertyGroup> <ItemGroup> + <Reference Include="Emby.Common.Implementations"> + <HintPath>..\ThirdParty\emby\Emby.Common.Implementations.dll</HintPath> + </Reference> <Reference Include="System" /> <Reference Include="System.XML" /> </ItemGroup> @@ -58,12 +61,21 @@ <Compile Include="ConsistencyTests\TextIndexing\WordIndex.cs" /> <Compile Include="ConsistencyTests\TextIndexing\WordOccurrence.cs" /> <Compile Include="ConsistencyTests\TextIndexing\WordOccurrences.cs" /> + <Compile Include="M3uParserTest.cs" /> <Compile Include="MediaEncoding\Subtitles\AssParserTests.cs" /> <Compile Include="MediaEncoding\Subtitles\SrtParserTests.cs" /> <Compile Include="MediaEncoding\Subtitles\VttWriterTest.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\Emby.Server.Implementations\Emby.Server.Implementations.csproj"> + <Project>{e383961b-9356-4d5d-8233-9a1079d03055}</Project> + <Name>Emby.Server.Implementations</Name> + </ProjectReference> + <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj"> + <Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project> + <Name>MediaBrowser.Common</Name> + </ProjectReference> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj"> <Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project> <Name>MediaBrowser.Controller</Name> diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 0a67cbdb0..5b0ef735e 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -159,9 +159,6 @@ <Content Include="dashboard-ui\components\remotecontrol.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\components\remotecontrolautoplay.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\components\tvproviders\xmltv.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -255,6 +252,9 @@ <Content Include="dashboard-ui\css\images\empty.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\scripts\videoosd.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\touchicon144.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -417,9 +417,6 @@ <Content Include="dashboard-ui\devices\ios\ios.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\htmlmediarenderer.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\sections.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -645,9 +642,6 @@ <Content Include="dashboard-ui\css\livetv.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\css\mediaplayer-video.css">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\css\nowplaying.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -813,9 +807,6 @@ <Content Include="dashboard-ui\scripts\channelslatest.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\chromecast.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\dashboard\cinemamodeconfiguration.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -855,9 +846,6 @@ <Content Include="dashboard-ui\dashboard\autoorganizelog.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\externalplayer.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\favorites.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -897,12 +885,6 @@ <Content Include="dashboard-ui\scripts\editorsidebar.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\mediacontroller.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\scripts\mediaplayer-video.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\metadatasubtitles.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -975,6 +957,9 @@ <Content Include="dashboard-ui\userpassword.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\videoosd.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\wizardagreement.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -987,9 +972,6 @@ <Content Include="dashboard-ui\wizardlivetvtuner.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\wizardsettings.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\index.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1024,9 +1006,6 @@ <Content Include="dashboard-ui\scripts\notifications.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\remotecontrol.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\moviecollections.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1093,11 +1072,6 @@ </Content>
</ItemGroup>
<ItemGroup>
- <Content Include="dashboard-ui\metadata.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- </ItemGroup>
- <ItemGroup>
<Content Include="dashboard-ui\scripts\tvgenres.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1194,11 +1168,6 @@ </Content>
</ItemGroup>
<ItemGroup>
- <Content Include="dashboard-ui\scripts\metadataconfigurationpage.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- </ItemGroup>
- <ItemGroup>
<Content Include="dashboard-ui\scripts\loginpage.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1249,6 +1218,9 @@ </Content>
</ItemGroup>
<ItemGroup>
+ <Content Include="dashboard-ui\wizardsettings.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\wizardstart.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1385,146 +1357,152 @@ <Content Include="dashboard-ui\css\images\clients\dlna.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\mediaplayer.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
</ItemGroup>
<ItemGroup>
- <Content Include="dashboard-ui\strings\ar.json">
+ <None Include="dashboard-ui\manifest.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\bg-BG.json">
+ </None>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.table.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\ca.json">
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.popup.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\strings\cs.json">
+ <None Include="dashboard-ui\strings\ar.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\da.json">
+ </None>
+ <None Include="dashboard-ui\strings\be-by.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\de.json">
+ </None>
+ <None Include="dashboard-ui\strings\bg-bg.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\el.json">
+ </None>
+ <None Include="dashboard-ui\strings\ca.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\en-GB.json">
+ </None>
+ <None Include="dashboard-ui\strings\cs.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\en-US.json">
+ </None>
+ <None Include="dashboard-ui\strings\da.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\es-AR.json">
+ </None>
+ <None Include="dashboard-ui\strings\de-de.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\es-MX.json">
+ </None>
+ <None Include="dashboard-ui\strings\de.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\es.json">
+ </None>
+ <None Include="dashboard-ui\strings\el.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\fi.json">
+ </None>
+ <None Include="dashboard-ui\strings\en-gb.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\fr.json">
+ </None>
+ <None Include="dashboard-ui\strings\en-us.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\gsw.json">
+ </None>
+ <None Include="dashboard-ui\strings\es-ar.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\he.json">
+ </None>
+ <None Include="dashboard-ui\strings\es-es.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\hr.json">
+ </None>
+ <None Include="dashboard-ui\strings\es-mx.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\it.json">
+ </None>
+ <None Include="dashboard-ui\strings\es.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\kk.json">
+ </None>
+ <None Include="dashboard-ui\strings\fi.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\ko.json">
+ </None>
+ <None Include="dashboard-ui\strings\fr-ca.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\ms.json">
+ </None>
+ <None Include="dashboard-ui\strings\fr-fr.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\nb.json">
+ </None>
+ <None Include="dashboard-ui\strings\fr.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\nl.json">
+ </None>
+ <None Include="dashboard-ui\strings\gsw.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\pl.json">
+ </None>
+ <None Include="dashboard-ui\strings\he.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\pt-BR.json">
+ </None>
+ <None Include="dashboard-ui\strings\hr.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\pt-PT.json">
+ </None>
+ <None Include="dashboard-ui\strings\hu.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\ro.json">
+ </None>
+ <None Include="dashboard-ui\strings\id.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\ru.json">
+ </None>
+ <None Include="dashboard-ui\strings\it.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\sl-SI.json">
+ </None>
+ <None Include="dashboard-ui\strings\kk.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\sv.json">
+ </None>
+ <None Include="dashboard-ui\strings\ko.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\tr.json">
+ </None>
+ <None Include="dashboard-ui\strings\lt-lt.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\uk.json">
+ </None>
+ <None Include="dashboard-ui\strings\ms.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\vi.json">
+ </None>
+ <None Include="dashboard-ui\strings\nb.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\zh-CN.json">
+ </None>
+ <None Include="dashboard-ui\strings\nl.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\strings\zh-TW.json">
+ </None>
+ <None Include="dashboard-ui\strings\pl.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <None Include="dashboard-ui\manifest.json">
+ </None>
+ <None Include="dashboard-ui\strings\pt-br.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
- <None Include="dashboard-ui\strings\be-BY.json">
+ <None Include="dashboard-ui\strings\pt-pt.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
- <None Include="dashboard-ui\strings\fr-CA.json">
+ <None Include="dashboard-ui\strings\ro.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
- <None Include="dashboard-ui\strings\fr-FR.json">
+ <None Include="dashboard-ui\strings\ru.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
- <None Include="dashboard-ui\strings\hu.json">
+ <None Include="dashboard-ui\strings\sk.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
- <None Include="dashboard-ui\strings\id.json">
+ <None Include="dashboard-ui\strings\sl-si.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
- <None Include="dashboard-ui\strings\sk.json">
+ <None Include="dashboard-ui\strings\sv.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
- <None Include="dashboard-ui\strings\zh-HK.json">
+ <None Include="dashboard-ui\strings\tr.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.table.js">
+ <None Include="dashboard-ui\strings\uk.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.popup.js">
+ </None>
+ <None Include="dashboard-ui\strings\vi.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
+ </None>
+ <None Include="dashboard-ui\strings\zh-cn.json">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ <None Include="dashboard-ui\strings\zh-hk.json">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ <None Include="dashboard-ui\strings\zh-tw.json">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index dba6d96ac..e4037f2ef 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -964,7 +964,20 @@ namespace MediaBrowser.XbmcMetadata.Parsers } default: - reader.Skip(); + string readerName = reader.Name; + string providerIdValue; + if (_validProviderIds.TryGetValue(readerName, out providerIdValue)) + { + var id = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(id)) + { + item.SetProviderId(providerIdValue, id); + } + } + else + { + reader.Skip(); + } break; } } diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index 18936df01..057522e9b 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -316,11 +316,11 @@ namespace MediaBrowser.XbmcMetadata.Savers if ((stream.CodecTag ?? string.Empty).IndexOf("xvid", StringComparison.OrdinalIgnoreCase) != -1) { - codec = "xvid;"; + codec = "xvid"; } else if ((stream.CodecTag ?? string.Empty).IndexOf("divx", StringComparison.OrdinalIgnoreCase) != -1) { - codec = "divx;"; + codec = "divx"; } writer.WriteElementString("codec", codec); @@ -846,7 +846,7 @@ namespace MediaBrowser.XbmcMetadata.Savers AddUserData(item, writer, userManager, userDataRepo, options); - AddActors(people, writer, libraryManager, fileSystem, config); + AddActors(people, writer, libraryManager, fileSystem, config, options.SaveImagePathsInNfo); var folder = item as BoxSet; if (folder != null) @@ -974,7 +974,7 @@ namespace MediaBrowser.XbmcMetadata.Savers writer.WriteEndElement(); } - private static void AddActors(List<PersonInfo> people, XmlWriter writer, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager config) + private static void AddActors(List<PersonInfo> people, XmlWriter writer, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager config, bool saveImagePath) { var actors = people .Where(i => !IsPersonType(i, PersonType.Director) && !IsPersonType(i, PersonType.Writer)) @@ -1004,20 +1004,23 @@ namespace MediaBrowser.XbmcMetadata.Savers writer.WriteElementString("sortorder", person.SortOrder.Value.ToString(UsCulture)); } - try + if (saveImagePath) { - var personEntity = libraryManager.GetPerson(person.Name); - var image = personEntity.GetImageInfo(ImageType.Primary, 0); + try + { + var personEntity = libraryManager.GetPerson(person.Name); + var image = personEntity.GetImageInfo(ImageType.Primary, 0); - if (image != null) + if (image != null) + { + writer.WriteElementString("thumb", GetImagePathToSave(image, libraryManager, config)); + } + } + catch (Exception) { - writer.WriteElementString("thumb", GetImagePathToSave(image, libraryManager, config)); + // Already logged in core } } - catch (Exception) - { - // Already logged in core - } writer.WriteEndElement(); } diff --git a/Mono.Nat/Pmp/PmpNatDevice.cs b/Mono.Nat/Pmp/PmpNatDevice.cs index 93007cb8a..10ebbdc2c 100644 --- a/Mono.Nat/Pmp/PmpNatDevice.cs +++ b/Mono.Nat/Pmp/PmpNatDevice.cs @@ -136,34 +136,36 @@ namespace Mono.Nat.Pmp { while (!cancellationToken.IsCancellationRequested) { - var result = await udpClient.ReceiveAsync().ConfigureAwait(false); - var endPoint = result.RemoteEndPoint; - byte[] data = data = result.Buffer; + try + { + var result = await udpClient.ReceiveAsync().ConfigureAwait(false); + var endPoint = result.RemoteEndPoint; + byte[] data = data = result.Buffer; - if (data.Length < 16) - continue; + if (data.Length < 16) + continue; - if (data[0] != PmpConstants.Version) - continue; + if (data[0] != PmpConstants.Version) + continue; - var opCode = (byte)(data[1] & 127); + var opCode = (byte)(data[1] & 127); - var protocol = Protocol.Tcp; - if (opCode == PmpConstants.OperationCodeUdp) - protocol = Protocol.Udp; + var protocol = Protocol.Tcp; + if (opCode == PmpConstants.OperationCodeUdp) + protocol = Protocol.Udp; - short resultCode = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 2)); - int epoch = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 4)); + short resultCode = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 2)); + int epoch = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 4)); - short privatePort = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 8)); - short publicPort = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 10)); + short privatePort = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 8)); + short publicPort = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 10)); - var lifetime = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 12)); + var lifetime = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 12)); - if (privatePort < 0 || publicPort < 0 || resultCode != PmpConstants.ResultCodeSuccess) - { - var errors = new[] - { + if (privatePort < 0 || publicPort < 0 || resultCode != PmpConstants.ResultCodeSuccess) + { + var errors = new[] + { "Success", "Unsupported Version", "Not Authorized/Refused (e.g. box supports mapping, but user has turned feature off)" @@ -173,19 +175,25 @@ namespace Mono.Nat.Pmp "Unsupported opcode" }; - var errorMsg = errors[resultCode]; - NatUtility.Log("Error in CreatePortMapListen: " + errorMsg); - return; - } + var errorMsg = errors[resultCode]; + NatUtility.Log("Error in CreatePortMapListen: " + errorMsg); + return; + } - if (lifetime == 0) return; //mapping was deleted + if (lifetime == 0) return; //mapping was deleted - //mapping was created - //TODO: verify that the private port+protocol are a match - mapping.PublicPort = publicPort; - mapping.Protocol = protocol; - mapping.Expiration = DateTime.Now.AddSeconds(lifetime); - return; + //mapping was created + //TODO: verify that the private port+protocol are a match + mapping.PublicPort = publicPort; + mapping.Protocol = protocol; + mapping.Expiration = DateTime.Now.AddSeconds(lifetime); + return; + } + catch (Exception ex) + { + NatUtility.Logger.ErrorException("Error in CreatePortMapListen", ex); + return; + } } } diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 6b5895720..4bb58cd73 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common</id> - <version>3.0.692</version> + <version>3.0.694</version> <title>Emby.Common</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 36ff96226..c475a4c91 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <metadata> <id>MediaBrowser.Server.Core</id> - <version>3.0.692</version> + <version>3.0.694</version> <title>Emby.Server.Core</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains core components required to build plugins for Emby Server.</description> <copyright>Copyright © Emby 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.692" /> + <dependency id="MediaBrowser.Common" version="3.0.694" /> </dependencies> </metadata> <files> diff --git a/OpenSubtitlesHandler/Utilities.cs b/OpenSubtitlesHandler/Utilities.cs index c012da462..f8cfa5c4f 100644 --- a/OpenSubtitlesHandler/Utilities.cs +++ b/OpenSubtitlesHandler/Utilities.cs @@ -37,8 +37,8 @@ namespace OpenSubtitlesHandler public static IHttpClient HttpClient { get; set; } public static ITextEncoding EncodingHelper { get; set; } - //private static string XML_RPC_SERVER = "https://api.opensubtitles.org/xml-rpc"; - private static string XML_RPC_SERVER = "https://92.240.234.122/xml-rpc"; + private static string XML_RPC_SERVER = "https://api.opensubtitles.org/xml-rpc"; + //private static string XML_RPC_SERVER = "https://92.240.234.122/xml-rpc"; private static string HostHeader = "api.opensubtitles.org:443"; /// <summary> diff --git a/RSSDP/DeviceAvailableEventArgs.cs b/RSSDP/DeviceAvailableEventArgs.cs index 39f07e1d7..046c10524 100644 --- a/RSSDP/DeviceAvailableEventArgs.cs +++ b/RSSDP/DeviceAvailableEventArgs.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using MediaBrowser.Model.Net; namespace Rssdp { @@ -11,10 +12,11 @@ namespace Rssdp /// </summary> public sealed class DeviceAvailableEventArgs : EventArgs { + public IpAddressInfo LocalIpAddress { get; set; } - #region Fields + #region Fields - private readonly DiscoveredSsdpDevice _DiscoveredDevice; + private readonly DiscoveredSsdpDevice _DiscoveredDevice; private readonly bool _IsNewlyDiscovered; #endregion diff --git a/RSSDP/ResponseReceivedEventArgs.cs b/RSSDP/ResponseReceivedEventArgs.cs index f7dc5813d..c983fa204 100644 --- a/RSSDP/ResponseReceivedEventArgs.cs +++ b/RSSDP/ResponseReceivedEventArgs.cs @@ -15,9 +15,11 @@ namespace Rssdp.Infrastructure public sealed class ResponseReceivedEventArgs : EventArgs { - #region Fields + public IpAddressInfo LocalIpAddress { get; set; } - private readonly HttpResponseMessage _Message; + #region Fields + + private readonly HttpResponseMessage _Message; private readonly IpEndPointInfo _ReceivedFrom; #endregion diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index 97f5ebbd0..c4959c1f2 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -454,7 +454,7 @@ namespace Rssdp.Infrastructure } if (responseMessage != null) - OnResponseReceived(responseMessage, endPoint); + OnResponseReceived(responseMessage, endPoint, receivedOnLocalIpAddress); } else { @@ -490,11 +490,14 @@ namespace Rssdp.Infrastructure handlers(this, new RequestReceivedEventArgs(data, remoteEndPoint, receivedOnLocalIpAddress)); } - private void OnResponseReceived(HttpResponseMessage data, IpEndPointInfo endPoint) + private void OnResponseReceived(HttpResponseMessage data, IpEndPointInfo endPoint, IpAddressInfo localIpAddress) { var handlers = this.ResponseReceived; if (handlers != null) - handlers(this, new ResponseReceivedEventArgs(data, endPoint)); + handlers(this, new ResponseReceivedEventArgs(data, endPoint) + { + LocalIpAddress = localIpAddress + }); } #endregion diff --git a/RSSDP/SsdpDeviceLocatorBase.cs b/RSSDP/SsdpDeviceLocatorBase.cs index b6276e499..60a792425 100644 --- a/RSSDP/SsdpDeviceLocatorBase.cs +++ b/RSSDP/SsdpDeviceLocatorBase.cs @@ -7,6 +7,7 @@ using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Net; using MediaBrowser.Model.Threading; using RSSDP; @@ -163,7 +164,7 @@ namespace Rssdp.Infrastructure { foreach (var device in GetUnexpiredDevices().Where(NotificationTypeMatchesFilter)) { - DeviceFound(device, false); + DeviceFound(device, false, null); } } @@ -237,16 +238,17 @@ namespace Rssdp.Infrastructure /// <summary> /// Raises the <see cref="DeviceAvailable"/> event. /// </summary> - /// <param name="device">A <see cref="DiscoveredSsdpDevice"/> representing the device that is now available.</param> - /// <param name="isNewDevice">True if the device was not currently in the cahce before this event was raised.</param> /// <seealso cref="DeviceAvailable"/> - protected virtual void OnDeviceAvailable(DiscoveredSsdpDevice device, bool isNewDevice) + protected virtual void OnDeviceAvailable(DiscoveredSsdpDevice device, bool isNewDevice, IpAddressInfo localIpAddress) { if (this.IsDisposed) return; var handlers = this.DeviceAvailable; if (handlers != null) - handlers(this, new DeviceAvailableEventArgs(device, isNewDevice)); + handlers(this, new DeviceAvailableEventArgs(device, isNewDevice) + { + LocalIpAddress = localIpAddress + }); } /// <summary> @@ -335,7 +337,7 @@ namespace Rssdp.Infrastructure #region Discovery/Device Add - private void AddOrUpdateDiscoveredDevice(DiscoveredSsdpDevice device) + private void AddOrUpdateDiscoveredDevice(DiscoveredSsdpDevice device, IpAddressInfo localIpAddress) { bool isNewDevice = false; lock (_Devices) @@ -353,10 +355,10 @@ namespace Rssdp.Infrastructure } } - DeviceFound(device, isNewDevice); + DeviceFound(device, isNewDevice, localIpAddress); } - private void DeviceFound(DiscoveredSsdpDevice device, bool isNewDevice) + private void DeviceFound(DiscoveredSsdpDevice device, bool isNewDevice, IpAddressInfo localIpAddress) { // Don't raise the event if we've already done it for a cached // version of this device, and the cached version isn't @@ -391,7 +393,7 @@ namespace Rssdp.Infrastructure } if (raiseEvent) - OnDeviceAvailable(device, isNewDevice); + OnDeviceAvailable(device, isNewDevice, localIpAddress); } private bool NotificationTypeMatchesFilter(DiscoveredSsdpDevice device) @@ -428,7 +430,7 @@ namespace Rssdp.Infrastructure return _CommunicationsServer.SendMulticastMessage(message); } - private void ProcessSearchResponseMessage(HttpResponseMessage message) + private void ProcessSearchResponseMessage(HttpResponseMessage message, IpAddressInfo localIpAddress) { if (!message.IsSuccessStatusCode) return; @@ -445,22 +447,22 @@ namespace Rssdp.Infrastructure ResponseHeaders = message.Headers }; - AddOrUpdateDiscoveredDevice(device); + AddOrUpdateDiscoveredDevice(device, localIpAddress); } } - private void ProcessNotificationMessage(HttpRequestMessage message) + private void ProcessNotificationMessage(HttpRequestMessage message, IpAddressInfo localIpAddress) { if (String.Compare(message.Method.Method, "Notify", StringComparison.OrdinalIgnoreCase) != 0) return; var notificationType = GetFirstHeaderStringValue("NTS", message); if (String.Compare(notificationType, SsdpConstants.SsdpKeepAliveNotification, StringComparison.OrdinalIgnoreCase) == 0) - ProcessAliveNotification(message); + ProcessAliveNotification(message, localIpAddress); else if (String.Compare(notificationType, SsdpConstants.SsdpByeByeNotification, StringComparison.OrdinalIgnoreCase) == 0) ProcessByeByeNotification(message); } - private void ProcessAliveNotification(HttpRequestMessage message) + private void ProcessAliveNotification(HttpRequestMessage message, IpAddressInfo localIpAddress) { var location = GetFirstHeaderUriValue("Location", message); if (location != null) @@ -475,7 +477,7 @@ namespace Rssdp.Infrastructure ResponseHeaders = message.Headers }; - AddOrUpdateDiscoveredDevice(device); + AddOrUpdateDiscoveredDevice(device, localIpAddress); ResetExpireCachedDevicesTimer(); } @@ -702,12 +704,12 @@ namespace Rssdp.Infrastructure private void CommsServer_ResponseReceived(object sender, ResponseReceivedEventArgs e) { - ProcessSearchResponseMessage(e.Message); + ProcessSearchResponseMessage(e.Message, e.LocalIpAddress); } private void CommsServer_RequestReceived(object sender, RequestReceivedEventArgs e) { - ProcessNotificationMessage(e.Message); + ProcessNotificationMessage(e.Message, e.LocalIpAddress); } #endregion diff --git a/RSSDP/SsdpDevicePublisherBase.cs b/RSSDP/SsdpDevicePublisherBase.cs index 2543632b6..c0ae3955d 100644 --- a/RSSDP/SsdpDevicePublisherBase.cs +++ b/RSSDP/SsdpDevicePublisherBase.cs @@ -245,7 +245,7 @@ namespace Rssdp.Infrastructure return; } - WriteTrace(String.Format("Search Request Received From {0}, Target = {1}", remoteEndPoint.ToString(), searchTarget)); + //WriteTrace(String.Format("Search Request Received From {0}, Target = {1}", remoteEndPoint.ToString(), searchTarget)); if (IsDuplicateSearchRequest(searchTarget, remoteEndPoint)) { diff --git a/src/Emby.Server/Program.cs b/src/Emby.Server/Program.cs index 26141a0ce..fde2ab7b2 100644 --- a/src/Emby.Server/Program.cs +++ b/src/Emby.Server/Program.cs @@ -193,7 +193,7 @@ namespace Emby.Server /// <param name="options">The options.</param> private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, StartupOptions options, EnvironmentInfo environmentInfo) { - var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), true, true, true); + var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), true, true, true, appPaths.TempDirectory); fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); diff --git a/src/Emby.Server/project.json b/src/Emby.Server/project.json index e77506e92..83c9ce876 100644 --- a/src/Emby.Server/project.json +++ b/src/Emby.Server/project.json @@ -1,5 +1,5 @@ { - "version": "3.1.0-*", + "version": "3.1.0.1", "buildOptions": { "emitEntryPoint": true }, |
