From 0d28929e171992e55ab220b878edaec59e81b0ae Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 3 Nov 2017 14:11:04 -0400 Subject: update download progress reporting --- Emby.Server.Implementations/IO/ProgressStream.cs | 240 ----------------------- 1 file changed, 240 deletions(-) delete mode 100644 Emby.Server.Implementations/IO/ProgressStream.cs (limited to 'Emby.Server.Implementations/IO') diff --git a/Emby.Server.Implementations/IO/ProgressStream.cs b/Emby.Server.Implementations/IO/ProgressStream.cs deleted file mode 100644 index be1ff72f8e..0000000000 --- a/Emby.Server.Implementations/IO/ProgressStream.cs +++ /dev/null @@ -1,240 +0,0 @@ -using System; -using System.IO; - -namespace Emby.Server.Implementations.IO -{ - /// - /// Measures progress when reading from a stream or writing to one - /// - public class ProgressStream : Stream - { - /// - /// Gets the base stream. - /// - /// The base stream. - public Stream BaseStream { get; private set; } - - /// - /// Gets or sets the bytes processed. - /// - /// The bytes processed. - private long BytesProcessed { get; set; } - /// - /// Gets or sets the length of the write. - /// - /// The length of the write. - private long WriteLength { get; set; } - - /// - /// Gets or sets the length of the read. - /// - /// The length of the read. - private long? ReadLength { get; set; } - - /// - /// Gets or sets the progress action. - /// - /// The progress action. - private Action ProgressAction { get; set; } - - /// - /// Creates the read progress stream. - /// - /// The base stream. - /// The progress action. - /// Length of the read. - /// ProgressStream. - public static ProgressStream CreateReadProgressStream(Stream baseStream, Action progressAction, long? readLength = null) - { - return new ProgressStream - { - BaseStream = baseStream, - ProgressAction = progressAction, - ReadLength = readLength - }; - } - - /// - /// Creates the write progress stream. - /// - /// The base stream. - /// The progress action. - /// Length of the write. - /// ProgressStream. - public static ProgressStream CreateWriteProgressStream(Stream baseStream, Action progressAction, long writeLength) - { - return new ProgressStream - { - BaseStream = baseStream, - ProgressAction = progressAction, - WriteLength = writeLength - }; - } - - /// - /// When overridden in a derived class, gets a value indicating whether the current stream supports reading. - /// - /// true if this instance can read; otherwise, false. - /// true if the stream supports reading; otherwise, false. - public override bool CanRead - { - get { return BaseStream.CanRead; } - } - - /// - /// When overridden in a derived class, gets a value indicating whether the current stream supports seeking. - /// - /// true if this instance can seek; otherwise, false. - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek - { - get { return BaseStream.CanSeek; } - } - - /// - /// When overridden in a derived class, gets a value indicating whether the current stream supports writing. - /// - /// true if this instance can write; otherwise, false. - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite - { - get { return BaseStream.CanWrite; } - } - - /// - /// When overridden in a derived class, clears all buffers for this stream and causes any buffered data to be written to the underlying device. - /// - public override void Flush() - { - BaseStream.Flush(); - } - - /// - /// When overridden in a derived class, gets the length in bytes of the stream. - /// - /// The length. - /// A long value representing the length of the stream in bytes. - public override long Length - { - get { return BaseStream.Length; } - } - - /// - /// When overridden in a derived class, gets or sets the position within the current stream. - /// - /// The position. - /// The current position within the stream. - public override long Position - { - get { return BaseStream.Position; } - set - { - BaseStream.Position = value; - } - } - - /// - /// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - public override int Read(byte[] buffer, int offset, int count) - { - var read = BaseStream.Read(buffer, offset, count); - - BytesProcessed += read; - - double percent = BytesProcessed; - percent /= ReadLength ?? BaseStream.Length; - percent *= 100; - - ProgressAction(percent); - - return read; - } - - public override int EndRead(IAsyncResult asyncResult) - { - var read = base.EndRead(asyncResult); - - BytesProcessed += read; - - double percent = BytesProcessed; - percent /= ReadLength ?? BaseStream.Length; - percent *= 100; - - ProgressAction(percent); - - return read; - } - - /// - /// When overridden in a derived class, sets the position within the current stream. - /// - /// A byte offset relative to the parameter. - /// A value of type indicating the reference point used to obtain the new position. - /// The new position within the current stream. - public override long Seek(long offset, SeekOrigin origin) - { - return BaseStream.Seek(offset, origin); - } - - /// - /// When overridden in a derived class, sets the length of the current stream. - /// - /// The desired length of the current stream in bytes. - public override void SetLength(long value) - { - BaseStream.SetLength(value); - } - - /// - /// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// An array of bytes. This method copies bytes from to the current stream. - /// The zero-based byte offset in at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - public override void Write(byte[] buffer, int offset, int count) - { - BaseStream.Write(buffer, offset, count); - - BytesProcessed += count; - - double percent = BytesProcessed; - percent /= WriteLength; - percent *= 100; - - ProgressAction(percent); - } - - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - var result = base.BeginWrite(buffer, offset, count, callback, state); - - BytesProcessed += count; - - double percent = BytesProcessed; - percent /= WriteLength; - percent *= 100; - - ProgressAction(percent); - - return result; - } - - /// - /// Releases the unmanaged resources used by the and optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected override void Dispose(bool disposing) - { - if (disposing) - { - BaseStream.Dispose(); - } - base.Dispose(disposing); - } - } -} -- cgit v1.2.3 From 5cb74690284105db70a467ab77c2af3f44e42348 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 5 Nov 2017 16:51:23 -0500 Subject: support track selection before playback --- Emby.Dlna/DlnaManager.cs | 1 - Emby.Dlna/Emby.Dlna.csproj | 2 - Emby.Dlna/Profiles/DefaultProfile.cs | 6 +- Emby.Dlna/Profiles/Xbox360Profile.cs | 326 --------------------- Emby.Dlna/Profiles/XboxOneProfile.cs | 4 +- Emby.Dlna/Profiles/Xml/Default.xml | 4 +- Emby.Dlna/Profiles/Xml/Xbox 360.xml | 116 -------- Emby.Dlna/Profiles/Xml/Xbox One.xml | 4 +- Emby.Server.Implementations/ApplicationHost.cs | 2 +- .../Channels/ChannelManager.cs | 16 +- Emby.Server.Implementations/Dto/DtoService.cs | 2 + .../IO/SharpCifsFileSystem.cs | 42 ++- .../Library/Resolvers/BaseVideoResolver.cs | 2 +- .../LiveTv/EmbyTV/EmbyTV.cs | 4 + .../LiveTv/LiveTvManager.cs | 22 +- .../TunerHosts/HdHomerun/HdHomerunHttpStream.cs | 5 +- .../TunerHosts/HdHomerun/HdHomerunUdpStream.cs | 5 +- .../LiveTv/TunerHosts/LiveStream.cs | 11 +- .../Localization/Core/bg-BG.json | 46 +-- .../Localization/Core/sk.json | 36 +-- .../Localization/Core/sv.json | 2 +- .../Localization/LocalizationManager.cs | 28 +- .../MediaEncoder/EncodingManager.cs | 9 +- .../ScheduledTasks/ChapterImagesTask.cs | 5 +- MediaBrowser.Controller/Entities/Audio/Audio.cs | 5 + MediaBrowser.Controller/Entities/BaseItem.cs | 2 +- .../Entities/IHasMediaSources.cs | 1 + MediaBrowser.Controller/Entities/Video.cs | 5 + MediaBrowser.Controller/LiveTv/LiveTvChannel.cs | 5 + .../MediaEncoding/EncodingHelper.cs | 16 +- .../MediaEncoding/IEncodingManager.cs | 3 +- .../Providers/DirectoryService.cs | 52 ++-- .../Providers/IDirectoryService.cs | 3 + MediaBrowser.Model/Dto/BaseItemDto.cs | 3 +- MediaBrowser.Model/Providers/SubtitleOptions.cs | 2 + MediaBrowser.Model/Session/GeneralCommandType.cs | 3 +- MediaBrowser.Providers/Manager/MetadataService.cs | 6 +- .../MediaInfo/FFProbeVideoInfo.cs | 6 +- .../MediaInfo/SubtitleResolver.cs | 115 ++++---- .../Subtitles/SubtitleManager.cs | 97 ++++-- SharedVersion.cs | 2 +- 41 files changed, 353 insertions(+), 673 deletions(-) delete mode 100644 Emby.Dlna/Profiles/Xbox360Profile.cs delete mode 100644 Emby.Dlna/Profiles/Xml/Xbox 360.xml (limited to 'Emby.Server.Implementations/IO') diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index bdc523c8bc..8caa086ee2 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -554,7 +554,6 @@ namespace Emby.Dlna var list = new List { new SamsungSmartTvProfile(), - new Xbox360Profile(), new XboxOneProfile(), new SonyPs3Profile(), new SonyPs4Profile(), diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 365774b0d9..eb8b178a85 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -105,7 +105,6 @@ - @@ -175,7 +174,6 @@ - diff --git a/Emby.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs index 46639ee424..75204b234d 100644 --- a/Emby.Dlna/Profiles/DefaultProfile.cs +++ b/Emby.Dlna/Profiles/DefaultProfile.cs @@ -65,13 +65,15 @@ namespace Emby.Dlna.Profiles { new DirectPlayProfile { - Container = "m4v,mpegts,ts,3gp,mov,xvid,vob,mkv,wmv,asf,ogm,ogv,m2v,avi,mpg,mpeg,mp4,webm,wtv,m2ts,dvr-ms", + // play all + Container = "", Type = DlnaProfileType.Video }, new DirectPlayProfile { - Container = "aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus,flac,m4a", + // play all + Container = "", Type = DlnaProfileType.Audio } }; diff --git a/Emby.Dlna/Profiles/Xbox360Profile.cs b/Emby.Dlna/Profiles/Xbox360Profile.cs deleted file mode 100644 index 7bdcd2a6f8..0000000000 --- a/Emby.Dlna/Profiles/Xbox360Profile.cs +++ /dev/null @@ -1,326 +0,0 @@ -using MediaBrowser.Model.Dlna; -using System.Xml.Serialization; - -namespace Emby.Dlna.Profiles -{ - /// - /// Good info on xbox 360 requirements: https://code.google.com/p/jems/wiki/XBox360Notes - /// - [XmlRoot("Profile")] - public class Xbox360Profile : DefaultProfile - { - public Xbox360Profile() - { - Name = "Xbox 360"; - - // Required according to above - ModelName = "Windows Media Player Sharing"; - - ModelNumber = "12.0"; - - FriendlyName = "${HostName}: 1"; - - ModelUrl = "http://go.microsoft.com/fwlink/?LinkId=105926"; - Manufacturer = "Microsoft Corporation"; - ManufacturerUrl = "http://www.microsoft.com"; - XDlnaDoc = "DMS-1.50"; - ModelDescription = "Emby : UPnP Media Server"; - - TimelineOffsetSeconds = 40; - RequiresPlainFolders = true; - RequiresPlainVideoItems = true; - EnableMSMediaReceiverRegistrar = true; - - Identification = new DeviceIdentification - { - ModelName = "Xbox 360", - - Headers = new[] - { - new HttpHeaderInfo {Name = "User-Agent", Value = "Xbox", Match = HeaderMatchType.Substring}, - new HttpHeaderInfo {Name = "User-Agent", Value = "Xenon", Match = HeaderMatchType.Substring} - } - }; - - TranscodingProfiles = new[] - { - new TranscodingProfile - { - Container = "mp3", - AudioCodec = "mp3", - Type = DlnaProfileType.Audio - }, - new TranscodingProfile - { - Container = "asf", - VideoCodec = "wmv2", - AudioCodec = "wmav2", - Type = DlnaProfileType.Video, - TranscodeSeekInfo = TranscodeSeekInfo.Bytes, - EstimateContentLength = true - }, - new TranscodingProfile - { - Container = "jpeg", - Type = DlnaProfileType.Photo - } - }; - - DirectPlayProfiles = new[] - { - new DirectPlayProfile - { - Container = "avi", - VideoCodec = "mpeg4", - AudioCodec = "ac3,mp3", - Type = DlnaProfileType.Video - }, - new DirectPlayProfile - { - Container = "avi", - VideoCodec = "h264", - AudioCodec = "aac", - Type = DlnaProfileType.Video - }, - new DirectPlayProfile - { - Container = "mp4,mov", - VideoCodec = "h264,mpeg4", - AudioCodec = "aac,ac3", - Type = DlnaProfileType.Video - }, - new DirectPlayProfile - { - Container = "asf", - VideoCodec = "wmv2,wmv3,vc1", - AudioCodec = "wmav2,wmapro", - Type = DlnaProfileType.Video - }, - new DirectPlayProfile - { - Container = "asf", - AudioCodec = "wmav2,wmapro,wmavoice", - Type = DlnaProfileType.Audio - }, - new DirectPlayProfile - { - Container = "mp3", - AudioCodec = "mp3", - Type = DlnaProfileType.Audio - }, - new DirectPlayProfile - { - Container = "jpeg", - Type = DlnaProfileType.Photo - } - }; - - ResponseProfiles = new[] - { - new ResponseProfile - { - Container = "avi", - MimeType = "video/avi", - Type = DlnaProfileType.Video - } - }; - - ContainerProfiles = new[] - { - new ContainerProfile - { - Type = DlnaProfileType.Video, - Container = "mp4,mov", - - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.Equals, - Property = ProfileConditionValue.Has64BitOffsets, - Value = "false", - IsRequired = false - } - } - }, - - new ContainerProfile - { - Type = DlnaProfileType.Photo, - - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Width, - Value = "1920" - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Height, - Value = "1080" - } - } - } - }; - - CodecProfiles = new[] - { - new CodecProfile - { - Type = CodecType.Video, - Codec = "mpeg4", - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Width, - Value = "1280" - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Height, - Value = "720" - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoFramerate, - Value = "30", - IsRequired = false - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoBitrate, - Value = "5120000", - IsRequired = false - } - } - }, - - new CodecProfile - { - Type = CodecType.Video, - Codec = "h264", - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Width, - Value = "1920" - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Height, - Value = "1080" - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoLevel, - Value = "41", - IsRequired = false - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoBitrate, - Value = "10240000", - IsRequired = false - } - } - }, - - new CodecProfile - { - Type = CodecType.Video, - Codec = "wmv2,wmv3,vc1", - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Width, - Value = "1920" - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Height, - Value = "1080" - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoFramerate, - Value = "30", - IsRequired = false - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoBitrate, - Value = "15360000", - IsRequired = false - } - } - }, - - new CodecProfile - { - Type = CodecType.VideoAudio, - Codec = "ac3,wmav2,wmapro", - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.AudioChannels, - Value = "6", - IsRequired = false - } - } - }, - - new CodecProfile - { - Type = CodecType.VideoAudio, - Codec = "aac", - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.AudioChannels, - Value = "2", - IsRequired = false - }, - new ProfileCondition - { - Condition = ProfileConditionType.Equals, - Property = ProfileConditionValue.AudioProfile, - Value = "lc", - IsRequired = false - } - } - } - }; - - SubtitleProfiles = new[] - { - new SubtitleProfile - { - Format = "srt", - Method = SubtitleDeliveryMethod.Embed - } - }; - } - } -} diff --git a/Emby.Dlna/Profiles/XboxOneProfile.cs b/Emby.Dlna/Profiles/XboxOneProfile.cs index e17640f2fa..99e52510f7 100644 --- a/Emby.Dlna/Profiles/XboxOneProfile.cs +++ b/Emby.Dlna/Profiles/XboxOneProfile.cs @@ -60,7 +60,7 @@ namespace Emby.Dlna.Profiles new DirectPlayProfile { Container = "ts", - VideoCodec = "h264,mpeg2video", + VideoCodec = "h264,mpeg2video,hevc", AudioCodec = "ac3,aac,mp3", Type = DlnaProfileType.Video }, @@ -81,7 +81,7 @@ namespace Emby.Dlna.Profiles new DirectPlayProfile { Container = "mp4,mov,mkv,m4v", - VideoCodec = "h264,mpeg4,mpeg2video", + VideoCodec = "h264,mpeg4,mpeg2video,hevc", AudioCodec = "aac,ac3", Type = DlnaProfileType.Video }, diff --git a/Emby.Dlna/Profiles/Xml/Default.xml b/Emby.Dlna/Profiles/Xml/Default.xml index b07a2f7c26..133f4abf1a 100644 --- a/Emby.Dlna/Profiles/Xml/Default.xml +++ b/Emby.Dlna/Profiles/Xml/Default.xml @@ -29,8 +29,8 @@ false - - + + diff --git a/Emby.Dlna/Profiles/Xml/Xbox 360.xml b/Emby.Dlna/Profiles/Xml/Xbox 360.xml deleted file mode 100644 index 3b7d2963f2..0000000000 --- a/Emby.Dlna/Profiles/Xml/Xbox 360.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - Xbox 360 - - Xbox 360 - - - - - - ${HostName}: 1 - Microsoft Corporation - http://www.microsoft.com - Windows Media Player Sharing - Emby : UPnP Media Server - 12.0 - http://go.microsoft.com/fwlink/?LinkId=105926 - false - false - false - Audio,Photo,Video - JPEG_SM - 480 - 480 - 48 - 48 - 40000000 - 40000000 - 192000 - - DMS-1.50 - 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 - 40 - true - true - true - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Emby.Dlna/Profiles/Xml/Xbox One.xml b/Emby.Dlna/Profiles/Xml/Xbox One.xml index 423327a389..6354232342 100644 --- a/Emby.Dlna/Profiles/Xml/Xbox One.xml +++ b/Emby.Dlna/Profiles/Xml/Xbox One.xml @@ -36,10 +36,10 @@ false - + - + diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c1ab9ec222..2aeb4b9714 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -996,7 +996,7 @@ namespace Emby.Server.Implementations NotificationManager = new NotificationManager(LogManager, UserManager, ServerConfigurationManager); RegisterSingleInstance(NotificationManager); - SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, MediaSourceManager); + SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, MediaSourceManager, ServerConfigurationManager); RegisterSingleInstance(SubtitleManager); RegisterSingleInstance(new DeviceDiscovery(LogManager.GetLogger("IDeviceDiscovery"), ServerConfigurationManager, SocketFactory, TimerFactory)); diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index c42f22804b..c566ca25bd 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -296,7 +296,7 @@ namespace Emby.Server.Implementations.Channels if (requiresCallback != null) { - results = await GetChannelItemMediaSourcesInternal(requiresCallback, GetItemExternalId(item), cancellationToken) + results = await GetChannelItemMediaSourcesInternal(requiresCallback, item.ExternalId, cancellationToken) .ConfigureAwait(false); } else @@ -990,18 +990,6 @@ namespace Emby.Server.Implementations.Channels return result; } - private string GetItemExternalId(BaseItem item) - { - var externalId = item.ExternalId; - - if (string.IsNullOrWhiteSpace(externalId)) - { - externalId = item.GetProviderId("ProviderExternalId"); - } - - return externalId; - } - private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1); private async Task GetChannelItems(IChannel channel, User user, @@ -1080,7 +1068,7 @@ namespace Emby.Server.Implementations.Channels { var categoryItem = _libraryManager.GetItemById(new Guid(folderId)); - query.FolderId = GetItemExternalId(categoryItem); + query.FolderId = categoryItem.ExternalId; } var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 0a316fcf1a..2fa09c2b1c 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -373,6 +373,8 @@ namespace Emby.Server.Implementations.Dto } NormalizeMediaSourceContainers(dto); + + dto.SupportsMediaSourceSelection = hasMediaSources.SupportsMediaSourceSelection(); } } diff --git a/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs b/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs index 1d21796b1a..0e1f6bb008 100644 --- a/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs +++ b/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs @@ -487,14 +487,17 @@ namespace Emby.Server.Implementations.IO AssertDirectoryExists(dir, path); var list = ListFiles(dir, recursive); + var result = new List(); foreach (var file in list) { if (file.IsDirectory()) { - yield return ToMetadata(file); + result.Add(ToMetadata(file)); } } + + return result; } public IEnumerable GetFiles(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false) @@ -503,6 +506,7 @@ namespace Emby.Server.Implementations.IO AssertDirectoryExists(dir, path); var list = ListFiles(dir, recursive); + var result = new List(); foreach (var file in list) { @@ -513,10 +517,12 @@ namespace Emby.Server.Implementations.IO if (extensions == null || extensions.Length == 0 || extensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { - yield return ToMetadata(file); + result.Add(ToMetadata(file)); } } } + + return result; } public IEnumerable GetFileSystemEntries(string path, bool recursive = false) @@ -525,15 +531,19 @@ namespace Emby.Server.Implementations.IO AssertDirectoryExists(dir, path); var list = ListFiles(dir, recursive); + var result = new List(); foreach (var file in list) { - yield return ToMetadata(file); + result.Add(ToMetadata(file)); } + + return result; } - public IEnumerable GetFileSystemEntryPaths(string path, bool recursive = false) + public List GetFileSystemEntryPaths(string path, bool recursive = false) { + var result = new List(); var dir = CreateSmbDirectoryForListFiles(path); AssertDirectoryExists(dir, path); @@ -541,16 +551,18 @@ namespace Emby.Server.Implementations.IO foreach (var file in list) { - yield return GetReturnPath(file); + result.Add(GetReturnPath(file)); } + return result; } - public IEnumerable GetFilePaths(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false) + public List GetFilePaths(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false) { var dir = CreateSmbDirectoryForListFiles(path); AssertDirectoryExists(dir, path); var list = ListFiles(dir, recursive); + var result = new List(); foreach (var file in list) { @@ -561,44 +573,52 @@ namespace Emby.Server.Implementations.IO if (extensions == null || extensions.Length == 0 || extensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { - yield return filePath; + result.Add(filePath); } } } + + return result; } - public IEnumerable GetDirectoryPaths(string path, bool recursive = false) + public List GetDirectoryPaths(string path, bool recursive = false) { var dir = CreateSmbDirectoryForListFiles(path); AssertDirectoryExists(dir, path); var list = ListFiles(dir, recursive); + var result = new List(); foreach (var file in list) { if (file.IsDirectory()) { - yield return GetReturnPath(file); + result.Add(GetReturnPath(file)); } } + + return result; } private IEnumerable ListFiles(SmbFile dir, bool recursive) { var list = dir.ListFiles(); + var result = new List(); foreach (var file in list) { - yield return file; + result.Add(file); if (recursive && file.IsDirectory()) { foreach (var subFile in ListFiles(file, recursive)) { - yield return subFile; + result.Add(subFile); } } } + + return result; } } } diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index e3200a0995..7e960f85e6 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -274,7 +274,7 @@ namespace Emby.Server.Implementations.Library.Resolvers return false; } - return FileSystem.GetFilePaths(fullPath).Any(i => string.Equals(Path.GetExtension(i), ".vob", StringComparison.OrdinalIgnoreCase)); + return directoryService.GetFilePaths(fullPath).Any(i => string.Equals(Path.GetExtension(i), ".vob", StringComparison.OrdinalIgnoreCase)); } /// diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index be5e57539b..b469966f5c 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -2445,6 +2445,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { existingTimer.Status = RecordingStatus.Cancelled; } + else if (!existingTimer.IsManual) + { + existingTimer.Status = RecordingStatus.New; + } if (existingTimer.Status != RecordingStatus.Cancelled) { diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 718620ab57..7e72d1b1a1 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1232,6 +1232,8 @@ namespace Emby.Server.Implementations.LiveTv var newChannelIdList = new List(); var newProgramIdList = new List(); + var cleanDatabase = true; + foreach (var service in _services) { cancellationToken.ThrowIfCancellationRequested(); @@ -1254,6 +1256,7 @@ namespace Emby.Server.Implementations.LiveTv } catch (Exception ex) { + cleanDatabase = false; _logger.ErrorException("Error refreshing channels for service", ex); } @@ -1264,8 +1267,11 @@ namespace Emby.Server.Implementations.LiveTv progress.Report(100 * percent); } - await CleanDatabaseInternal(newChannelIdList.ToArray(), new[] { typeof(LiveTvChannel).Name }, progress, cancellationToken).ConfigureAwait(false); - await CleanDatabaseInternal(newProgramIdList.ToArray(), new[] { typeof(LiveTvProgram).Name }, progress, cancellationToken).ConfigureAwait(false); + if (cleanDatabase) + { + await CleanDatabaseInternal(newChannelIdList.ToArray(), new[] { typeof(LiveTvChannel).Name }, progress, cancellationToken).ConfigureAwait(false); + await CleanDatabaseInternal(newProgramIdList.ToArray(), new[] { typeof(LiveTvProgram).Name }, progress, cancellationToken).ConfigureAwait(false); + } var coreService = _services.OfType().FirstOrDefault(); @@ -1291,8 +1297,9 @@ namespace Emby.Server.Implementations.LiveTv { progress.Report(10); - var allChannels = await GetChannels(service, cancellationToken).ConfigureAwait(false); - var allChannelsList = allChannels.ToList(); + var allChannelsList = (await service.GetChannelsAsync(cancellationToken).ConfigureAwait(false)) + .Select(i => new Tuple(service.Name, i)) + .ToList(); var list = new List(); @@ -1507,13 +1514,6 @@ namespace Emby.Server.Implementations.LiveTv return 7; } - private async Task>> GetChannels(ILiveTvService service, CancellationToken cancellationToken) - { - var channels = await service.GetChannelsAsync(cancellationToken).ConfigureAwait(false); - - return channels.Select(i => new Tuple(service.Name, i)); - } - private DateTime _lastRecordingRefreshTime; private async Task RefreshRecordings(Guid internalLiveTvFolderId, CancellationToken cancellationToken) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs index 7e0e5fc5c9..1ea77d505e 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs @@ -73,9 +73,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun //OpenedMediaSource.SupportsTranscoding = true; } - public override void Close() + protected override void CloseInternal() { - Logger.Info("Closing HDHR live stream"); LiveStreamCancellationTokenSource.Cancel(); } @@ -106,7 +105,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { Logger.ErrorException("Error copying live stream.", ex); } - + EnableStreamSharing = false; await DeleteTempFile(TempFilePath).ConfigureAwait(false); }); } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index 06326d26c6..8b46b78be9 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -105,9 +105,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun await taskCompletionSource.Task.ConfigureAwait(false); } - public override void Close() + protected override void CloseInternal() { - Logger.Info("Closing HDHR UDP live stream"); LiveStreamCancellationTokenSource.Cancel(); } @@ -134,6 +133,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun openTaskCompletionSource.TrySetException(ex); } + EnableStreamSharing = false; + try { await hdHomerunManager.StopStreaming().ConfigureAwait(false); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index 8620987355..cead1def03 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -52,7 +52,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return Task.FromResult(true); } - public virtual void Close() + public void Close() + { + EnableStreamSharing = false; + + Logger.Info("Closing " + GetType().Name); + + CloseInternal(); + } + + protected virtual void CloseInternal() { } diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json index cc7611bf4a..02c03f5781 100644 --- a/Emby.Server.Implementations/Localization/Core/bg-BG.json +++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json @@ -1,7 +1,7 @@ { "Latest": "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438", "ValueSpecialEpisodeName": "\u0421\u043f\u0435\u0446\u0438\u0430\u043b\u043d\u0438 - {0}", - "Inherit": "Inherit", + "Inherit": "\u041d\u0430\u0441\u043b\u0435\u0434\u044f\u0432\u0430\u043d\u0435", "Books": "\u041a\u043d\u0438\u0433\u0438", "Music": "\u041c\u0443\u0437\u0438\u043a\u0430", "Games": "\u0418\u0433\u0440\u0438", @@ -35,7 +35,7 @@ "UserDownloadingItemWithValues": "{0} is downloading {1}", "HeaderLiveTV": "\u0422\u0435\u043b\u0435\u0432\u0438\u0437\u0438\u044f \u043d\u0430 \u0436\u0438\u0432\u043e", "ChapterNameValue": "\u0413\u043b\u0430\u0432\u0430 {0}", - "ScheduledTaskFailedWithName": "{0} failed", + "ScheduledTaskFailedWithName": "{0} \u0441\u0435 \u043f\u0440\u043e\u0432\u0430\u043b\u0438", "LabelRunningTimeValue": "Running time: {0}", "ScheduledTaskStartedWithName": "{0} \u0437\u0430\u043f\u043e\u0447\u043d\u0430", "VersionNumber": "\u0412\u0435\u0440\u0441\u0438\u044f {0}", @@ -49,40 +49,40 @@ "DeviceOnlineWithName": "{0} \u0435 \u0441\u0432\u044a\u0440\u0437\u0430\u043d", "UserOnlineFromDevice": "{0} \u0435 \u043d\u0430 \u043b\u0438\u043d\u0438\u044f \u043e\u0442 {1}", "ProviderValue": "\u0414\u043e\u0441\u0442\u0430\u0432\u0447\u0438\u043a: {0}", - "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", - "UserCreatedWithName": "User {0} has been created", - "UserPasswordChangedWithName": "Password has been changed for user {0}", - "UserDeletedWithName": "User {0} has been deleted", + "SubtitlesDownloadedForItem": "\u0418\u0437\u0442\u0435\u0433\u043b\u0435\u043d\u0438 \u0441\u0430 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u0438 \u0437\u0430 {0}", + "UserCreatedWithName": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f\u0442 {0} \u0435 \u0441\u044a\u0437\u0434\u0430\u0434\u0435\u043d", + "UserPasswordChangedWithName": "\u041f\u0430\u0440\u043e\u043b\u0430\u0442\u0430 \u043d\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f {0} \u0435 \u043f\u0440\u043e\u043c\u0435\u043d\u0435\u043d\u0430", + "UserDeletedWithName": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f\u0442 {0} \u0435 \u0438\u0437\u0442\u0440\u0438\u0442", "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}", "MessageServerConfigurationUpdated": "Server configuration has been updated", "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", - "MessageApplicationUpdated": "Emby Server has been updated", + "MessageApplicationUpdated": "\u0421\u044a\u0440\u0432\u044a\u0440\u044a\u0442 \u0435 \u043e\u0431\u043d\u043e\u0432\u0435\u043d", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", - "UserOfflineFromDevice": "{0} has disconnected from {1}", - "DeviceOfflineWithName": "{0} has disconnected", + "AuthenticationSucceededWithUserName": "{0} \u0441\u0435 \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "UserOfflineFromDevice": "{0} \u0441\u0435 \u0440\u0430\u0437\u043a\u0430\u0447\u0438 \u043e\u0442 {1}", + "DeviceOfflineWithName": "{0} \u0441\u0435 \u0440\u0430\u0437\u043a\u0430\u0447\u0438", "UserStartedPlayingItemWithValues": "{0} has started playing {1}", "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}", - "NotificationOptionPluginError": "Plugin failure", - "NotificationOptionApplicationUpdateAvailable": "Application update available", - "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginError": "\u0413\u0440\u0435\u0448\u043a\u0430 \u0432 \u043f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0430", + "NotificationOptionApplicationUpdateAvailable": "\u041d\u0430\u043b\u0438\u0447\u043d\u043e \u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u0430\u0442\u0430", + "NotificationOptionApplicationUpdateInstalled": "\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\u0442\u043e \u043d\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u0430\u0442\u0430 \u0435 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u043e", "NotificationOptionPluginUpdateInstalled": "\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\u0442\u043e \u043d\u0430 \u043f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0430\u0442\u0430 \u0435 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u043e", "NotificationOptionPluginInstalled": "\u041f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0430\u0442\u0430 \u0435 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0430", "NotificationOptionPluginUninstalled": "\u041f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0430\u0442\u0430 \u0435 \u0434\u0435\u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0430", - "NotificationOptionVideoPlayback": "Video playback started", - "NotificationOptionAudioPlayback": "Audio playback started", - "NotificationOptionGamePlayback": "Game playback started", - "NotificationOptionVideoPlaybackStopped": "Video playback stopped", - "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", - "NotificationOptionGamePlaybackStopped": "Game playback stopped", - "NotificationOptionTaskFailed": "Scheduled task failure", - "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionVideoPlayback": "\u0412\u044a\u0437\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0436\u0434\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0432\u0438\u0434\u0435\u043e \u0437\u0430\u043f\u043e\u0447\u043d\u0430", + "NotificationOptionAudioPlayback": "\u0412\u044a\u0437\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0436\u0434\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0437\u0432\u0443\u043a \u0437\u0430\u043f\u043e\u0447\u043d\u0430", + "NotificationOptionGamePlayback": "\u0412\u044a\u0437\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0436\u0434\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0438\u0433\u0440\u0430\u0442\u0430 \u0437\u0430\u043f\u043e\u0447\u043d\u0430", + "NotificationOptionVideoPlaybackStopped": "\u0412\u044a\u0437\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0436\u0434\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0432\u0438\u0434\u0435\u043e \u0435 \u0441\u043f\u0440\u044f\u043d\u043e", + "NotificationOptionAudioPlaybackStopped": "\u0412\u044a\u0437\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0436\u0434\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0437\u0432\u0443\u043a \u0435 \u0441\u043f\u0440\u044f\u043d\u043e", + "NotificationOptionGamePlaybackStopped": "\u0412\u044a\u0437\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0436\u0434\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0438\u0433\u0440\u0430\u0442\u0430 \u0435 \u0441\u043f\u0440\u044f\u043d\u0430", + "NotificationOptionTaskFailed": "\u0413\u0440\u0435\u0448\u043a\u0430 \u0432 \u043f\u043b\u0430\u043d\u0438\u0440\u0430\u043d\u0430 \u0437\u0430\u0434\u0430\u0447\u0430", + "NotificationOptionInstallationFailed": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0435", "NotificationOptionNewLibraryContent": "\u0414\u043e\u0431\u0430\u0432\u0435\u043d\u043e \u0435 \u043d\u043e\u0432\u043e \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435", - "NotificationOptionCameraImageUploaded": "Camera image uploaded", + "NotificationOptionCameraImageUploaded": "\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u0442\u043e \u043e\u0442 \u0444\u043e\u0442\u043e\u0430\u043f\u0430\u0440\u0430\u0442\u0430 \u0435 \u043a\u0430\u0447\u0435\u043d\u043e", "NotificationOptionUserLockedOut": "User locked out", "NotificationOptionServerRestartRequired": "\u041d\u0443\u0436\u043d\u043e \u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u043f\u0443\u0441\u043a\u0430\u043d\u0435 \u043d\u0430 \u0441\u044a\u0440\u0432\u044a\u0440\u0430", "UserLockedOutWithName": "User {0} has been locked out", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", + "SubtitleDownloadFailureForItem": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0438\u0437\u0442\u0435\u0433\u043b\u044f\u043d\u0435 \u043d\u0430 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u0438 \u0437\u0430 {0}", "Sync": "\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u0430\u043d\u0435", "User": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b", "System": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430", diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json index aa6ada9123..0c7bce1daa 100644 --- a/Emby.Server.Implementations/Localization/Core/sk.json +++ b/Emby.Server.Implementations/Localization/Core/sk.json @@ -2,13 +2,13 @@ "Latest": "Latest", "ValueSpecialEpisodeName": "Special - {0}", "Inherit": "Inherit", - "Books": "Books", - "Music": "Music", - "Games": "Games", - "Photos": "Photos", - "MixedContent": "Mixed content", - "MusicVideos": "Music videos", - "HomeVideos": "Home videos", + "Books": "Knihy", + "Music": "Hudba", + "Games": "Hry", + "Photos": "Fotky", + "MixedContent": "Zmie\u0161an\u00fd obsah", + "MusicVideos": "Hudobn\u00e9 vide\u00e1", + "HomeVideos": "Dom\u00e1ce vide\u00e1", "Playlists": "Playlists", "HeaderRecordingGroups": "Recording Groups", "HeaderContinueWatching": "Continue Watching", @@ -16,20 +16,20 @@ "HeaderFavoriteSongs": "Ob\u013e\u00faben\u00e9 pesni\u010dky", "HeaderAlbumArtists": "Album Artists", "HeaderFavoriteAlbums": "Favorite Albums", - "HeaderFavoriteEpisodes": "Favorite Episodes", + "HeaderFavoriteEpisodes": "Ob\u013e\u00faben\u00e9 epiz\u00f3dy", "HeaderFavoriteShows": "Ob\u013e\u00faben\u00e9 seri\u00e1ly", - "HeaderNextUp": "Next Up", + "HeaderNextUp": "Nasleduje", "Favorites": "Ob\u013e\u00faben\u00e9", - "Collections": "Collections", - "Channels": "Channels", - "Movies": "Movies", - "Albums": "Albums", - "Artists": "Artists", - "Folders": "Folders", - "Songs": "Songs", + "Collections": "Zbierky", + "Channels": "Kan\u00e1ly", + "Movies": "Filmy", + "Albums": "Albumy", + "Artists": "Umelci", + "Folders": "Prie\u010dinky", + "Songs": "Skladby", "TvShows": "TV Shows", "Shows": "Series", - "Genres": "Genres", + "Genres": "\u017d\u00e1nre", "NameSeasonNumber": "Season {0}", "AppDeviceValues": "App: {0}, Device: {1}", "UserDownloadingItemWithValues": "{0} is downloading {1}", @@ -86,6 +86,6 @@ "Sync": "Sync", "User": "User", "System": "System", - "Application": "Application", + "Application": "Aplik\u00e1cia", "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json index c43bd79a6d..ed18670249 100644 --- a/Emby.Server.Implementations/Localization/Core/sv.json +++ b/Emby.Server.Implementations/Localization/Core/sv.json @@ -27,7 +27,7 @@ "Artists": "Artister", "Folders": "Mappar", "Songs": "L\u00e5tar", - "TvShows": "TV Shows", + "TvShows": "TV-serier", "Shows": "Serier", "Genres": "Genrer", "NameSeasonNumber": "S\u00e4song {0}", diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 2eb4743cd0..6d271c0e1c 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -64,7 +64,7 @@ namespace Emby.Server.Implementations.Localization var localizationPath = LocalizationPath; - _fileSystem.CreateDirectory(localizationPath); + _fileSystem.CreateDirectory(localizationPath); var existingFiles = GetRatingsFiles(localizationPath) .Select(Path.GetFileName) @@ -91,7 +91,7 @@ namespace Emby.Server.Implementations.Localization } } } - + foreach (var file in GetRatingsFiles(localizationPath)) { LoadRatings(file); @@ -128,12 +128,20 @@ namespace Emby.Server.Implementations.Localization return _textLocalizer.NormalizeFormKD(text); } + private CultureDto[] _cultures; + /// /// Gets the cultures. /// /// IEnumerable{CultureDto}. public CultureDto[] GetCultures() { + var result = _cultures; + if (result != null) + { + return result; + } + var type = GetType(); var path = type.Namespace + ".iso6392.txt"; @@ -166,10 +174,14 @@ namespace Emby.Server.Implementations.Localization } } - return list.Where(i => !string.IsNullOrWhiteSpace(i.Name) && - !string.IsNullOrWhiteSpace(i.DisplayName) && - !string.IsNullOrWhiteSpace(i.ThreeLetterISOLanguageName) && - !string.IsNullOrWhiteSpace(i.TwoLetterISOLanguageName)).ToArray(); + result = list.Where(i => !string.IsNullOrWhiteSpace(i.Name) && + !string.IsNullOrWhiteSpace(i.DisplayName) && + !string.IsNullOrWhiteSpace(i.ThreeLetterISOLanguageName) && + !string.IsNullOrWhiteSpace(i.TwoLetterISOLanguageName)).ToArray(); + + _cultures = result; + + return result; } /// @@ -239,7 +251,7 @@ namespace Emby.Server.Implementations.Localization /// Dictionary{System.StringParentalRating}. private void LoadRatings(string file) { - var dict = _fileSystem.ReadAllLines(file).Select(i => + var dict = _fileSystem.ReadAllLines(file).Select(i => { if (!string.IsNullOrWhiteSpace(i)) { @@ -269,7 +281,7 @@ namespace Emby.Server.Implementations.Localization _allParentalRatings.TryAdd(countryCode, dict); } - private readonly string[] _unratedValues = {"n/a", "unrated", "not rated"}; + private readonly string[] _unratedValues = { "n/a", "unrated", "not rated" }; /// /// Gets the rating level. diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs index 9a9e619a62..6e0e55bef4 100644 --- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -17,6 +17,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; namespace Emby.Server.Implementations.MediaEncoder { @@ -89,7 +90,7 @@ namespace Emby.Server.Implementations.MediaEncoder /// private static readonly long FirstChapterTicks = TimeSpan.FromSeconds(15).Ticks; - public async Task RefreshChapterImages(Video video, List chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken) + public async Task RefreshChapterImages(Video video, IDirectoryService directoryService, List chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken) { if (!IsEligibleForChapterImageExtraction(video)) { @@ -101,7 +102,7 @@ namespace Emby.Server.Implementations.MediaEncoder var runtimeTicks = video.RunTimeTicks ?? 0; - var currentImages = GetSavedChapterImages(video); + var currentImages = GetSavedChapterImages(video, directoryService); foreach (var chapter in chapters) { @@ -194,13 +195,13 @@ namespace Emby.Server.Implementations.MediaEncoder return Path.Combine(GetChapterImagesPath(video), filename); } - private List GetSavedChapterImages(Video video) + private List GetSavedChapterImages(Video video, IDirectoryService directoryService) { var path = GetChapterImagesPath(video); try { - return _fileSystem.GetFilePaths(path) + return directoryService.GetFilePaths(path) .ToList(); } catch (IOException) diff --git a/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs index bf7bf9ff8b..fe0652f668 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs @@ -16,6 +16,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Extensions; +using MediaBrowser.Controller.Providers; namespace Emby.Server.Implementations.ScheduledTasks { @@ -120,6 +121,8 @@ namespace Emby.Server.Implementations.ScheduledTasks previouslyFailedImages = new List(); } + var directoryService = new DirectoryService(_fileSystem); + foreach (var video in videos) { cancellationToken.ThrowIfCancellationRequested(); @@ -132,7 +135,7 @@ namespace Emby.Server.Implementations.ScheduledTasks { var chapters = _itemRepo.GetChapters(video.Id); - var success = await _encodingManager.RefreshChapterImages(video, chapters, extract, true, CancellationToken.None); + var success = await _encodingManager.RefreshChapterImages(video, directoryService, chapters, extract, true, CancellationToken.None); if (!success) { diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 16fd75d2e9..8ef1060d1a 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -51,6 +51,11 @@ namespace MediaBrowser.Controller.Entities.Audio return 1; } + public bool SupportsMediaSourceSelection() + { + return false; + } + [IgnoreDataMember] public override bool SupportsPlayedStatus { diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 7f6949feba..f6a8f1d5a4 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2040,7 +2040,7 @@ namespace MediaBrowser.Controller.Entities .Where(i => i.IsLocalFile) .Select(i => FileSystem.GetDirectoryName(i.Path)) .Distinct(StringComparer.OrdinalIgnoreCase) - .SelectMany(i => FileSystem.GetFilePaths(i)) + .SelectMany(i => directoryService.GetFilePaths(i)) .ToList(); var deletedImages = ImageInfos diff --git a/MediaBrowser.Controller/Entities/IHasMediaSources.cs b/MediaBrowser.Controller/Entities/IHasMediaSources.cs index 54786134f3..399fe9f5a3 100644 --- a/MediaBrowser.Controller/Entities/IHasMediaSources.cs +++ b/MediaBrowser.Controller/Entities/IHasMediaSources.cs @@ -13,5 +13,6 @@ namespace MediaBrowser.Controller.Entities /// Task{IEnumerable{MediaSourceInfo}}. List GetMediaSources(bool enablePathSubstitution); List GetMediaStreams(); + bool SupportsMediaSourceSelection(); } } diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 52f1dd0514..60db53f790 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -109,6 +109,11 @@ namespace MediaBrowser.Controller.Entities get { return true; } } + public bool SupportsMediaSourceSelection() + { + return SourceType == SourceType.Library; + } + /// /// Gets or sets the timestamp. /// diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 16010b7f5a..ac961d757e 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -24,6 +24,11 @@ namespace MediaBrowser.Controller.LiveTv return list; } + public bool SupportsMediaSourceSelection() + { + return false; + } + public override UnratedItem GetBlockUnratedType() { return UnratedItem.LiveTvChannel; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index bddafe9a6a..8f8791922d 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1325,6 +1325,8 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue) { videoSizeParam = string.Format("scale={0}:{1}", state.VideoStream.Width.Value.ToString(_usCulture), state.VideoStream.Height.Value.ToString(_usCulture)); + + videoSizeParam += ":force_original_aspect_ratio=decrease"; } var mapPrefix = state.SubtitleStream.IsExternal ? @@ -1335,7 +1337,7 @@ namespace MediaBrowser.Controller.MediaEncoding ? 0 : state.SubtitleStream.Index; - return string.Format(" -filter_complex \"[{0}:{1}]{4}[sub] ; [0:{2}] [sub] overlay{3}\"", + return string.Format(" -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay{3}\"", mapPrefix.ToString(_usCulture), subtitleStreamIndex.ToString(_usCulture), state.VideoStream.Index.ToString(_usCulture), @@ -2094,6 +2096,12 @@ namespace MediaBrowser.Controller.MediaEncoding args += " -avoid_negative_ts disabled -start_at_zero"; } + // This is for internal graphical subs + if (hasGraphicalSubs) + { + args += GetGraphicalSubtitleParam(state, encodingOptions, videoCodec); + } + var qualityParam = GetVideoQualityParam(state, videoCodec, encodingOptions, defaultH264Preset); if (!string.IsNullOrEmpty(qualityParam)) @@ -2101,12 +2109,6 @@ namespace MediaBrowser.Controller.MediaEncoding args += " " + qualityParam.Trim(); } - // This is for internal graphical subs - if (hasGraphicalSubs) - { - args += GetGraphicalSubtitleParam(state, encodingOptions, videoCodec); - } - if (!state.RunTimeTicks.HasValue) { args += " -flags -global_header"; diff --git a/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs b/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs index 81269fe3fa..7d50efd5ed 100644 --- a/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs +++ b/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Entities; +using MediaBrowser.Controller.Providers; namespace MediaBrowser.Controller.MediaEncoding { @@ -11,6 +12,6 @@ namespace MediaBrowser.Controller.MediaEncoding /// /// Refreshes the chapter images. /// - Task RefreshChapterImages(Video video, List chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken); + Task RefreshChapterImages(Video video, IDirectoryService directoryService, List chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs index d957470d3b..d673198fdc 100644 --- a/MediaBrowser.Controller/Providers/DirectoryService.cs +++ b/MediaBrowser.Controller/Providers/DirectoryService.cs @@ -14,11 +14,11 @@ namespace MediaBrowser.Controller.Providers private readonly ILogger _logger; private readonly IFileSystem _fileSystem; - private readonly ConcurrentDictionary _cache = - new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary _cache = new Dictionary(StringComparer.OrdinalIgnoreCase); - private readonly ConcurrentDictionary _fileCache = - new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary _fileCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + + private readonly Dictionary> _filePathCache = new Dictionary>(StringComparer.OrdinalIgnoreCase); public DirectoryService(ILogger logger, IFileSystem fileSystem) { @@ -32,11 +32,6 @@ namespace MediaBrowser.Controller.Providers } public FileSystemMetadata[] GetFileSystemEntries(string path) - { - return GetFileSystemEntries(path, false); - } - - private FileSystemMetadata[] GetFileSystemEntries(string path, bool clearCache) { if (string.IsNullOrWhiteSpace(path)) { @@ -45,13 +40,6 @@ namespace MediaBrowser.Controller.Providers FileSystemMetadata[] entries; - if (clearCache) - { - FileSystemMetadata[] removed; - - _cache.TryRemove(path, out removed); - } - if (!_cache.TryGetValue(path, out entries)) { //_logger.Debug("Getting files for " + path); @@ -66,21 +54,17 @@ namespace MediaBrowser.Controller.Providers entries = new FileSystemMetadata[] { }; } - _cache.TryAdd(path, entries); + //_cache.TryAdd(path, entries); + _cache[path] = entries; } return entries; } public List GetFiles(string path) - { - return GetFiles(path, false); - } - - public List GetFiles(string path, bool clearCache) { var list = new List(); - var items = GetFileSystemEntries(path, clearCache); + var items = GetFileSystemEntries(path); foreach (var item in items) { if (!item.IsDirectory) @@ -100,7 +84,8 @@ namespace MediaBrowser.Controller.Providers if (file != null && file.Exists) { - _fileCache.TryAdd(path, file); + //_fileCache.TryAdd(path, file); + _fileCache[path] = file; } else { @@ -111,5 +96,24 @@ namespace MediaBrowser.Controller.Providers return file; //return _fileSystem.GetFileInfo(path); } + + public List GetFilePaths(string path) + { + return GetFilePaths(path, false); + } + + public List GetFilePaths(string path, bool clearCache) + { + List result; + if (clearCache || !_filePathCache.TryGetValue(path, out result)) + { + result = _fileSystem.GetFilePaths(path).ToList(); + + _filePathCache[path] = result; + } + + return result; + } + } } diff --git a/MediaBrowser.Controller/Providers/IDirectoryService.cs b/MediaBrowser.Controller/Providers/IDirectoryService.cs index 6f864f4be2..0b4574f6e5 100644 --- a/MediaBrowser.Controller/Providers/IDirectoryService.cs +++ b/MediaBrowser.Controller/Providers/IDirectoryService.cs @@ -8,5 +8,8 @@ namespace MediaBrowser.Controller.Providers FileSystemMetadata[] GetFileSystemEntries(string path); List GetFiles(string path); FileSystemMetadata GetFile(string path); + + List GetFilePaths(string path); + List GetFilePaths(string path, bool clearCache); } } \ No newline at end of file diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index 8bad650b53..7cacb96eea 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -75,7 +75,8 @@ namespace MediaBrowser.Model.Dto public bool? CanDownload { get; set; } public bool? HasSubtitles { get; set; } - + public bool? SupportsMediaSourceSelection { get; set; } + public string PreferredMetadataLanguage { get; set; } public string PreferredMetadataCountryCode { get; set; } diff --git a/MediaBrowser.Model/Providers/SubtitleOptions.cs b/MediaBrowser.Model/Providers/SubtitleOptions.cs index 5587e897fe..d0d81e1fb2 100644 --- a/MediaBrowser.Model/Providers/SubtitleOptions.cs +++ b/MediaBrowser.Model/Providers/SubtitleOptions.cs @@ -13,6 +13,7 @@ namespace MediaBrowser.Model.Providers public bool IsOpenSubtitleVipAccount { get; set; } public bool RequirePerfectMatch { get; set; } + public bool SaveSubtitlesInMediaFolders { get; set; } public SubtitleOptions() { @@ -20,6 +21,7 @@ namespace MediaBrowser.Model.Providers SkipIfAudioTrackMatches = true; RequirePerfectMatch = true; + SaveSubtitlesInMediaFolders = true; } } } \ No newline at end of file diff --git a/MediaBrowser.Model/Session/GeneralCommandType.cs b/MediaBrowser.Model/Session/GeneralCommandType.cs index 8dd0c29e43..616d5f9b41 100644 --- a/MediaBrowser.Model/Session/GeneralCommandType.cs +++ b/MediaBrowser.Model/Session/GeneralCommandType.cs @@ -39,6 +39,7 @@ ChannelDown = 31, SetMaxStreamingBitrate = 31, Guide = 32, - ToggleStats = 33 + ToggleStats = 33, + PlayMediaSource = 34 } } \ No newline at end of file diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index af03e21b2d..32b9a9ae2a 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -27,6 +27,7 @@ namespace MediaBrowser.Providers.Manager protected readonly IFileSystem FileSystem; protected readonly IUserDataManager UserDataManager; protected readonly ILibraryManager LibraryManager; + private readonly SubtitleResolver _subtitleResolver; protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) { @@ -36,6 +37,8 @@ namespace MediaBrowser.Providers.Manager FileSystem = fileSystem; UserDataManager = userDataManager; LibraryManager = libraryManager; + + _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager, fileSystem); } public async Task RefreshMetadata(IHasMetadata item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) @@ -76,8 +79,7 @@ namespace MediaBrowser.Providers.Manager if (video != null && !video.IsPlaceHolder) { requiresRefresh = !video.SubtitleFiles - .SequenceEqual(SubtitleResolver.GetSubtitleFiles(video, refreshOptions.DirectoryService, FileSystem, false) - .OrderBy(i => i), StringComparer.OrdinalIgnoreCase); + .SequenceEqual(_subtitleResolver.GetExternalSubtitleFiles(video, refreshOptions.DirectoryService, false), StringComparer.Ordinal); } } } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index a6a363ffdf..1582385571 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -229,7 +229,7 @@ namespace MediaBrowser.Providers.MediaInfo extractDuringScan = libraryOptions.ExtractChapterImagesDuringLibraryScan; } - await _encodingManager.RefreshChapterImages(video, chapters, extractDuringScan, false, cancellationToken).ConfigureAwait(false); + await _encodingManager.RefreshChapterImages(video, options.DirectoryService, chapters, extractDuringScan, false, cancellationToken).ConfigureAwait(false); _chapterManager.SaveChapters(video.Id.ToString(), chapters); } @@ -472,7 +472,7 @@ namespace MediaBrowser.Providers.MediaInfo var subtitleResolver = new SubtitleResolver(_localization, _fileSystem); var startIndex = currentStreams.Count == 0 ? 0 : (currentStreams.Select(i => i.Index).Max() + 1); - var externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, startIndex, options.DirectoryService, false).ToList(); + var externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, startIndex, options.DirectoryService, false); var enableSubtitleDownloading = options.MetadataRefreshMode == MetadataRefreshMode.Default || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh; @@ -497,7 +497,7 @@ namespace MediaBrowser.Providers.MediaInfo // Rescan if (downloadedLanguages.Count > 0) { - externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, startIndex, options.DirectoryService, true).ToList(); + externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, startIndex, options.DirectoryService, true); } } diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs index a2c10d6a86..4e264bd223 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs @@ -16,29 +16,91 @@ namespace MediaBrowser.Providers.MediaInfo private readonly ILocalizationManager _localization; private readonly IFileSystem _fileSystem; + private string[] SubtitleExtensions = new[] + { + ".srt", + ".ssa", + ".ass", + ".sub", + ".smi", + ".sami", + ".vtt" + }; + public SubtitleResolver(ILocalizationManager localization, IFileSystem fileSystem) { _localization = localization; _fileSystem = fileSystem; } - public IEnumerable GetExternalSubtitleStreams(Video video, + public List GetExternalSubtitleStreams(Video video, int startIndex, IDirectoryService directoryService, bool clearCache) { - var files = GetSubtitleFiles(video, directoryService, _fileSystem, clearCache); - var streams = new List(); - var videoFileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(video.Path); + GetExternalSubtitleStreams(streams, video.ContainingFolderPath, video.Path, startIndex, directoryService, clearCache); + + startIndex += streams.Count; + + try + { + GetExternalSubtitleStreams(streams, video.GetInternalMetadataPath(), video.Path, startIndex, directoryService, clearCache); + } + catch (IOException) + { + + } + + return streams; + } + + public List GetExternalSubtitleFiles(Video video, + IDirectoryService directoryService, + bool clearCache) + { + var streams = GetExternalSubtitleStreams(video, 0, directoryService, clearCache); + + var list = new List(); + + foreach (var stream in streams) + { + list.Add(stream.Path); + } + + return list; + } + + private void GetExternalSubtitleStreams(List streams, string folder, + string videoPath, + int startIndex, + IDirectoryService directoryService, + bool clearCache) + { + var videoFileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(videoPath); videoFileNameWithoutExtension = NormalizeFilenameForSubtitleComparison(videoFileNameWithoutExtension); + var files = directoryService.GetFilePaths(folder, clearCache); + foreach (var fullName in files) { + var extension = Path.GetExtension(fullName); + + if (!SubtitleExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) + { + continue; + } + var fileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(fullName); fileNameWithoutExtension = NormalizeFilenameForSubtitleComparison(fileNameWithoutExtension); + if (!string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase) && + !fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension + ".", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + var codec = Path.GetExtension(fullName).ToLower().TrimStart('.'); if (string.Equals(codec, "txt", StringComparison.OrdinalIgnoreCase)) @@ -98,8 +160,6 @@ namespace MediaBrowser.Providers.MediaInfo }); } } - - return streams; } private string NormalizeFilenameForSubtitleComparison(string filename) @@ -115,48 +175,5 @@ namespace MediaBrowser.Providers.MediaInfo return filename; } - - private static IEnumerable SubtitleExtensions - { - get - { - return new[] { ".srt", ".ssa", ".ass", ".sub", ".smi", ".sami", ".vtt" }; - } - } - - public static IEnumerable GetSubtitleFiles(Video video, IDirectoryService directoryService, IFileSystem fileSystem, bool clearCache) - { - var containingPath = video.ContainingFolderPath; - - if (string.IsNullOrEmpty(containingPath)) - { - throw new ArgumentException(string.Format("Cannot search for items that don't have a path: {0} {1}", video.Name, video.Id)); - } - - var files = fileSystem.GetFilePaths(containingPath, clearCache); - - var videoFileNameWithoutExtension = fileSystem.GetFileNameWithoutExtension(video.Path); - - return files.Where(i => - { - var extension = Path.GetExtension(i); - - if (SubtitleExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) - { - var fileNameWithoutExtension = fileSystem.GetFileNameWithoutExtension(i); - - if (string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - if (fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension + ".", StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - }); - } } } diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index fe655759e1..945498966a 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -16,8 +16,8 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; - -using MediaBrowser.Controller.IO; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Subtitles @@ -30,17 +30,19 @@ namespace MediaBrowser.Providers.Subtitles private readonly ILibraryMonitor _monitor; private readonly ILibraryManager _libraryManager; private readonly IMediaSourceManager _mediaSourceManager; + private readonly IServerConfigurationManager _config; public event EventHandler SubtitlesDownloaded; public event EventHandler SubtitleDownloadFailure; - public SubtitleManager(ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager) + public SubtitleManager(ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager, IServerConfigurationManager config) { _logger = logger; _fileSystem = fileSystem; _monitor = monitor; _libraryManager = libraryManager; _mediaSourceManager = mediaSourceManager; + _config = config; } public void AddParts(IEnumerable subtitleProviders) @@ -102,6 +104,11 @@ namespace MediaBrowser.Providers.Subtitles return results.SelectMany(i => i).ToArray(); } + private SubtitleOptions GetOptions() + { + return _config.GetConfiguration("subtitles"); + } + public async Task DownloadSubtitles(Video video, string subtitleId, CancellationToken cancellationToken) @@ -109,49 +116,37 @@ namespace MediaBrowser.Providers.Subtitles var parts = subtitleId.Split(new[] { '_' }, 2); var provider = GetProvider(parts.First()); + var saveInMediaFolder = GetOptions().SaveSubtitlesInMediaFolders && video.SupportsLocalMetadata; + try { var response = await GetRemoteSubtitles(subtitleId, cancellationToken).ConfigureAwait(false); using (var stream = response.Stream) { - var savePath = Path.Combine(_fileSystem.GetDirectoryName(video.Path), - _fileSystem.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower()); - - if (response.IsForced) + using (var memoryStream = new MemoryStream()) { - savePath += ".forced"; - } - - savePath += "." + response.Format.ToLower(); + await stream.CopyToAsync(memoryStream).ConfigureAwait(false); + memoryStream.Position = 0; - _logger.Info("Saving subtitles to {0}", savePath); + var savePaths = new List(); + var saveFileName = _fileSystem.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower(); - _monitor.ReportFileSystemChangeBeginning(savePath); + if (response.IsForced) + { + saveFileName += ".forced"; + } - try - { - //var isText = MediaStream.IsTextFormat(response.Format); + saveFileName += "." + response.Format.ToLower(); - using (var fs = _fileSystem.GetFileStream(savePath, FileOpenMode.Create, FileAccessMode.Write, - FileShareMode.Read, true)) + if (saveInMediaFolder) { - await stream.CopyToAsync(fs).ConfigureAwait(false); + savePaths.Add(Path.Combine(video.ContainingFolderPath, saveFileName)); } - EventHelper.FireEventIfNotNull(SubtitlesDownloaded, this, new SubtitleDownloadEventArgs - { - Item = video, - Format = response.Format, - Language = response.Language, - IsForced = response.IsForced, - Provider = provider.Name + savePaths.Add(Path.Combine(video.GetInternalMetadataPath(), saveFileName)); - }, _logger); - } - finally - { - _monitor.ReportFileSystemChangeComplete(savePath, false); + await TrySaveToFiles(memoryStream, savePaths).ConfigureAwait(false); } } } @@ -173,6 +168,46 @@ namespace MediaBrowser.Providers.Subtitles } } + private async Task TrySaveToFiles(Stream stream, List savePaths) + { + Exception exceptionToThrow = null; + + foreach (var savePath in savePaths) + { + _logger.Info("Saving subtitles to {0}", savePath); + + _monitor.ReportFileSystemChangeBeginning(savePath); + + try + { + using (var fs = _fileSystem.GetFileStream(savePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) + { + await stream.CopyToAsync(fs).ConfigureAwait(false); + } + + return; + } + catch (Exception ex) + { + if (exceptionToThrow == null) + { + exceptionToThrow = ex; + } + } + finally + { + _monitor.ReportFileSystemChangeComplete(savePath, false); + } + + stream.Position = 0; + } + + if (exceptionToThrow != null) + { + throw exceptionToThrow; + } + } + public Task SearchSubtitles(Video video, string language, bool? isPerfectMatch, CancellationToken cancellationToken) { if (video.LocationType != LocationType.FileSystem || diff --git a/SharedVersion.cs b/SharedVersion.cs index 74a0759e3c..0750f469e3 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.36.1")] +[assembly: AssemblyVersion("3.2.36.2")] -- cgit v1.2.3