From 0d28929e171992e55ab220b878edaec59e81b0ae Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 3 Nov 2017 14:11:04 -0400 Subject: [PATCH 01/21] update download progress reporting --- .../Emby.Server.Implementations.csproj | 1 - .../HttpClientManager/HttpClientManager.cs | 8 +- .../IO/ProgressStream.cs | 240 ------------------ .../Localization/Core/ar.json | 7 +- .../Localization/Core/bg-BG.json | 45 ++-- .../Localization/Core/ca.json | 7 +- .../Localization/Core/cs.json | 7 +- .../Localization/Core/da.json | 7 +- .../Localization/Core/de.json | 7 +- .../Localization/Core/en-GB.json | 7 +- .../Localization/Core/es-AR.json | 7 +- .../Localization/Core/es-MX.json | 7 +- .../Localization/Core/es.json | 13 +- .../Localization/Core/fr-CA.json | 7 +- .../Localization/Core/fr.json | 7 +- .../Localization/Core/he.json | 7 +- .../Localization/Core/hr.json | 7 +- .../Localization/Core/hu.json | 7 +- .../Localization/Core/it.json | 7 +- .../Localization/Core/kk.json | 7 +- .../Localization/Core/ko.json | 7 +- .../Localization/Core/lt-LT.json | 7 +- .../Localization/Core/ms.json | 7 +- .../Localization/Core/nb.json | 7 +- .../Localization/Core/nl.json | 7 +- .../Localization/Core/pl.json | 9 +- .../Localization/Core/pt-BR.json | 7 +- .../Localization/Core/pt-PT.json | 7 +- .../Localization/Core/ru.json | 7 +- .../Localization/Core/sk.json | 7 +- .../Localization/Core/sl-SI.json | 7 +- .../Localization/Core/sv.json | 7 +- .../Localization/Core/tr.json | 7 +- .../Localization/Core/zh-CN.json | 7 +- .../Localization/Core/zh-HK.json | 7 +- .../ScheduledTasks/ScheduledTaskWorker.cs | 2 + MediaBrowser.Controller/Entities/BaseItem.cs | 5 - .../Entities/CollectionFolder.cs | 15 -- MediaBrowser.Controller/Entities/Folder.cs | 2 +- MediaBrowser.Controller/IO/StreamHelper.cs | 23 ++ 40 files changed, 84 insertions(+), 482 deletions(-) delete mode 100644 Emby.Server.Implementations/IO/ProgressStream.cs diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 8ff1b63c0..6b2a005f9 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -117,7 +117,6 @@ - diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index cd7c98dc8..4a9e417f2 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -16,6 +16,7 @@ using MediaBrowser.Common.Net; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; +using MediaBrowser.Controller.IO; namespace Emby.Server.Implementations.HttpClientManager { @@ -633,12 +634,9 @@ namespace Emby.Server.Implementations.HttpClientManager } else { - using (var stream = ProgressStream.CreateReadProgressStream(httpResponse.GetResponseStream(), options.Progress.Report, contentLength.Value)) + using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) { - using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) - { - await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); - } + await StreamHelper.CopyToAsync(httpResponse.GetResponseStream(), fs, StreamDefaults.DefaultCopyToBufferSize, options.Progress, contentLength.Value, options.CancellationToken).ConfigureAwait(false); } } diff --git a/Emby.Server.Implementations/IO/ProgressStream.cs b/Emby.Server.Implementations/IO/ProgressStream.cs deleted file mode 100644 index be1ff72f8..000000000 --- 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); - } - } -} diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index 0c2ab1f6d..54200605d 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -87,10 +87,5 @@ "User": "\u0627\u0644\u0645\u0633\u062a\u062e\u062f\u0645", "System": "\u0627\u0644\u0646\u0638\u0627\u0645", "Application": "\u0627\u0644\u062a\u0637\u0628\u064a\u0642", - "Plugin": "\u0627\u0644\u0645\u0644\u062d\u0642", - "LabelExit": "\u062e\u0631\u0648\u062c", - "LabelVisitCommunity": "\u0632\u064a\u0627\u0631\u0629 \u0627\u0644\u0645\u062c\u062a\u0645\u0639", - "LabelBrowseLibrary": "\u062a\u0635\u0641\u062d \u0627\u0644\u0645\u0643\u062a\u0628\u0629", - "LabelConfigureServer": "\u0636\u0628\u0637 \u0625\u0639\u062f\u0627\u062f\u0627\u062a \u0623\u0645\u0628\u064a", - "LabelRestartServer": "\u0625\u0639\u0627\u062f\u0629 \u062a\u0634\u063a\u064a\u0644 \u0627\u0644\u062e\u0627\u062f\u0645" + "Plugin": "\u0627\u0644\u0645\u0644\u062d\u0642" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json index 40899caab..cc7611bf4 100644 --- a/Emby.Server.Implementations/Localization/Core/bg-BG.json +++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json @@ -6,7 +6,7 @@ "Music": "\u041c\u0443\u0437\u0438\u043a\u0430", "Games": "\u0418\u0433\u0440\u0438", "Photos": "\u0421\u043d\u0438\u043c\u043a\u0438", - "MixedContent": "Mixed content", + "MixedContent": "\u0421\u043c\u0435\u0441\u0435\u043d\u043e \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435", "MusicVideos": "\u041c\u0443\u0437\u0438\u043a\u0430\u043b\u043d\u0438 \u043a\u043b\u0438\u043f\u043e\u0432\u0435", "HomeVideos": "\u0414\u043e\u043c\u0430\u0448\u043d\u0438 \u043a\u043b\u0438\u043f\u043e\u0432\u0435", "Playlists": "\u0421\u043f\u0438\u0441\u044a\u0446\u0438", @@ -17,8 +17,8 @@ "HeaderAlbumArtists": "\u0418\u0437\u043f\u044a\u043b\u043d\u0438\u0442\u0435\u043b\u0438 \u043d\u0430 \u0430\u043b\u0431\u0443\u043c\u0438", "HeaderFavoriteAlbums": "\u041b\u044e\u0431\u0438\u043c\u0438 \u0430\u043b\u0431\u0443\u043c\u0438", "HeaderFavoriteEpisodes": "\u041b\u044e\u0431\u0438\u043c\u0438 \u0435\u043f\u0438\u0437\u043e\u0434\u0438", - "HeaderFavoriteShows": "Favorite Shows", - "HeaderNextUp": "Next Up", + "HeaderFavoriteShows": "\u041b\u044e\u0431\u0438\u043c\u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u0438", + "HeaderNextUp": "\u0421\u043b\u0435\u0434\u0432\u0430", "Favorites": "\u041b\u044e\u0431\u0438\u043c\u0438", "Collections": "\u041a\u043e\u043b\u0435\u043a\u0446\u0438\u0438", "Channels": "\u041a\u0430\u043d\u0430\u043b\u0438", @@ -27,28 +27,28 @@ "Artists": "\u0418\u0437\u043f\u044a\u043b\u043d\u0438\u0442\u0435\u043b\u0438", "Folders": "\u041f\u0430\u043f\u043a\u0438", "Songs": "\u041f\u0435\u0441\u043d\u0438", - "TvShows": "TV Shows", + "TvShows": "\u0422\u0435\u043b\u0435\u0432\u0438\u0437\u0438\u043e\u043d\u043d\u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u0438", "Shows": "\u0421\u0435\u0440\u0438\u0430\u043b\u0438", "Genres": "\u0416\u0430\u043d\u0440\u043e\u0432\u0435", "NameSeasonNumber": "\u0421\u0435\u0437\u043e\u043d {0}", - "AppDeviceValues": "App: {0}, Device: {1}", + "AppDeviceValues": "\u041f\u0440\u043e\u0433\u0440\u0430\u043c\u0430: {0}, \u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e: {1}", "UserDownloadingItemWithValues": "{0} is downloading {1}", - "HeaderLiveTV": "Live TV", - "ChapterNameValue": "Chapter {0}", + "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", "LabelRunningTimeValue": "Running time: {0}", "ScheduledTaskStartedWithName": "{0} \u0437\u0430\u043f\u043e\u0447\u043d\u0430", - "VersionNumber": "Version {0}", - "PluginInstalledWithName": "{0} was installed", - "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly.", - "PluginUpdatedWithName": "{0} was updated", + "VersionNumber": "\u0412\u0435\u0440\u0441\u0438\u044f {0}", + "PluginInstalledWithName": "{0} \u0435 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u043e", + "StartupEmbyServerIsLoading": "\u0421\u044a\u0440\u0432\u044a\u0440\u044a\u0442 \u0437\u0430\u0440\u0435\u0436\u0434\u0430. \u041c\u043e\u043b\u044f, \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e \u0441\u043b\u0435\u0434 \u043c\u0430\u043b\u043a\u043e.", + "PluginUpdatedWithName": "{0} \u0435 \u043e\u0431\u043d\u043e\u0432\u0435\u043d\u043e", "PluginUninstalledWithName": "{0} \u0435 \u0434\u0435\u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u043e", - "ItemAddedWithName": "{0} was added to the library", - "ItemRemovedWithName": "{0} was removed from the library", - "LabelIpAddressValue": "Ip address: {0}", - "DeviceOnlineWithName": "{0} is connected", - "UserOnlineFromDevice": "{0} is online from {1}", - "ProviderValue": "Provider: {0}", + "ItemAddedWithName": "{0} \u0435 \u0434\u043e\u0431\u0430\u0432\u0435\u043d\u043e \u043a\u044a\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u0442\u0430", + "ItemRemovedWithName": "{0} \u0435 \u043f\u0440\u0435\u043c\u0430\u0445\u043d\u0430\u0442\u043e \u043e\u0442 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u0442\u0430", + "LabelIpAddressValue": "\u0418\u041f \u0430\u0434\u0440\u0435\u0441: {0}", + "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}", @@ -77,7 +77,7 @@ "NotificationOptionGamePlaybackStopped": "Game playback stopped", "NotificationOptionTaskFailed": "Scheduled task failure", "NotificationOptionInstallationFailed": "Installation failure", - "NotificationOptionNewLibraryContent": "New content added", + "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", "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", @@ -86,11 +86,6 @@ "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", - "Application": "Application", - "Plugin": "Plugin", - "LabelExit": "Exit", - "LabelVisitCommunity": "\u041f\u043e\u0441\u0435\u0442\u0435\u0442\u0435 \u043e\u0431\u0449\u043d\u043e\u0441\u0442\u0442\u0430", - "LabelBrowseLibrary": "\u0420\u0430\u0437\u0433\u043b\u0435\u0436\u0434\u0430\u043d\u0435 \u043d\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u0442\u0430", - "LabelConfigureServer": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0415\u043c\u0431\u0438", - "LabelRestartServer": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u043f\u0443\u0441\u043a\u0430\u043d\u0435 \u043d\u0430 \u0441\u044a\u0440\u0432\u044a\u0440\u0430" + "Application": "\u041f\u0440\u043e\u0433\u0440\u0430\u043c\u0430", + "Plugin": "\u041f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0430" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json index cc2c00234..f21a0b295 100644 --- a/Emby.Server.Implementations/Localization/Core/ca.json +++ b/Emby.Server.Implementations/Localization/Core/ca.json @@ -87,10 +87,5 @@ "User": "User", "System": "System", "Application": "Application", - "Plugin": "Plugin", - "LabelExit": "Exit", - "LabelVisitCommunity": "Visit Community", - "LabelBrowseLibrary": "Browse Library", - "LabelConfigureServer": "Configure Emby", - "LabelRestartServer": "Restart Server" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json index abbfde380..82d8699a7 100644 --- a/Emby.Server.Implementations/Localization/Core/cs.json +++ b/Emby.Server.Implementations/Localization/Core/cs.json @@ -87,10 +87,5 @@ "User": "U\u017eivatel", "System": "Syst\u00e9m", "Application": "Aplikace", - "Plugin": "Z\u00e1suvn\u00fd modul", - "LabelExit": "Uko\u010dit", - "LabelVisitCommunity": "Nav\u0161t\u00edvit komunitu", - "LabelBrowseLibrary": "Proch\u00e1zet knihovnu", - "LabelConfigureServer": "Konfigurovat Emby", - "LabelRestartServer": "Restartovat Server" + "Plugin": "Z\u00e1suvn\u00fd modul" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json index ed9652c33..eb05943f9 100644 --- a/Emby.Server.Implementations/Localization/Core/da.json +++ b/Emby.Server.Implementations/Localization/Core/da.json @@ -87,10 +87,5 @@ "User": "Bruger", "System": "System", "Application": "Applikation", - "Plugin": "Plugin", - "LabelExit": "Afslut", - "LabelVisitCommunity": "Bes\u00f8g F\u00e6llesskab", - "LabelBrowseLibrary": "Gennemse Bibliotek", - "LabelConfigureServer": "Konfigurer Emby", - "LabelRestartServer": "Genstart Server" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index 183b4aaf8..7bab689eb 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -87,10 +87,5 @@ "User": "Benutzer", "System": "System", "Application": "Anwendung", - "Plugin": "Plugin", - "LabelExit": "Beenden", - "LabelVisitCommunity": "Besuche die Community", - "LabelBrowseLibrary": "Bibliothek durchsuchen", - "LabelConfigureServer": "Konfiguriere Emby", - "LabelRestartServer": "Server neustarten" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json index 8dc9abf01..62db5a358 100644 --- a/Emby.Server.Implementations/Localization/Core/en-GB.json +++ b/Emby.Server.Implementations/Localization/Core/en-GB.json @@ -87,10 +87,5 @@ "User": "User", "System": "System", "Application": "Application", - "Plugin": "Plugin", - "LabelExit": "Exit", - "LabelVisitCommunity": "Visit Community", - "LabelBrowseLibrary": "Browse Library", - "LabelConfigureServer": "Configure Emby", - "LabelRestartServer": "Restart Server" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json index e4a49abf6..c48042d9a 100644 --- a/Emby.Server.Implementations/Localization/Core/es-AR.json +++ b/Emby.Server.Implementations/Localization/Core/es-AR.json @@ -87,10 +87,5 @@ "User": "User", "System": "System", "Application": "Application", - "Plugin": "Plugin", - "LabelExit": "Exit", - "LabelVisitCommunity": "Visit Community", - "LabelBrowseLibrary": "Browse Library", - "LabelConfigureServer": "Configure Emby", - "LabelRestartServer": "Restart Server" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json index 45cc2c69b..303df142a 100644 --- a/Emby.Server.Implementations/Localization/Core/es-MX.json +++ b/Emby.Server.Implementations/Localization/Core/es-MX.json @@ -87,10 +87,5 @@ "User": "Usuario", "System": "Sistema", "Application": "Aplicaci\u00f3n", - "Plugin": "Complemento", - "LabelExit": "Salir", - "LabelVisitCommunity": "Visitar la Comunidad", - "LabelBrowseLibrary": "Explorar Biblioteca", - "LabelConfigureServer": "Configurar Emby", - "LabelRestartServer": "Reiniciar el Servidor" + "Plugin": "Complemento" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json index 68975d632..c7fa51c03 100644 --- a/Emby.Server.Implementations/Localization/Core/es.json +++ b/Emby.Server.Implementations/Localization/Core/es.json @@ -27,7 +27,7 @@ "Artists": "Artistas", "Folders": "Carpetas", "Songs": "Canciones", - "TvShows": "TV Shows", + "TvShows": "Series TV", "Shows": "Series", "Genres": "G\u00e9neros", "NameSeasonNumber": "Temporada {0}", @@ -61,8 +61,8 @@ "AuthenticationSucceededWithUserName": "{0} autenticado correctamente", "UserOfflineFromDevice": "{0} se ha desconectado de {1}", "DeviceOfflineWithName": "{0} se ha desconectado", - "UserStartedPlayingItemWithValues": "{0} ha comenzado jugando {1}", - "UserStoppedPlayingItemWithValues": "{0} ha dejado de reproducir {1}", + "UserStartedPlayingItemWithValues": "{0} ha comenzado reproducir {1}", + "UserStoppedPlayingItemWithValues": "{0} ha parado de reproducir {1}", "NotificationOptionPluginError": "Error en plugin", "NotificationOptionApplicationUpdateAvailable": "Actualizaci\u00f3n de la aplicaci\u00f3n disponible", "NotificationOptionApplicationUpdateInstalled": "Actualizaci\u00f3n de la aplicaci\u00f3n instalada", @@ -87,10 +87,5 @@ "User": "Usuario", "System": "Sistema", "Application": "Aplicaci\u00f3n", - "Plugin": "Plugin", - "LabelExit": "Salida", - "LabelVisitCommunity": "Visita la Comunidad", - "LabelBrowseLibrary": "Navegar la biblioteca", - "LabelConfigureServer": "Configurar Emby", - "LabelRestartServer": "Configurar Emby" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/fr-CA.json b/Emby.Server.Implementations/Localization/Core/fr-CA.json index 795bc98cc..7743905f0 100644 --- a/Emby.Server.Implementations/Localization/Core/fr-CA.json +++ b/Emby.Server.Implementations/Localization/Core/fr-CA.json @@ -87,10 +87,5 @@ "User": "User", "System": "System", "Application": "Application", - "Plugin": "Plugin", - "LabelExit": "Exit", - "LabelVisitCommunity": "Visit Community", - "LabelBrowseLibrary": "Browse Library", - "LabelConfigureServer": "Configure Emby", - "LabelRestartServer": "Restart Server" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index 5ea01fcdb..953d7f434 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -87,10 +87,5 @@ "User": "Utilisateur", "System": "Syst\u00e8me", "Application": "Application", - "Plugin": "Extension", - "LabelExit": "Quitter", - "LabelVisitCommunity": "Visiter la communaut\u00e9", - "LabelBrowseLibrary": "Parcourir la m\u00e9diath\u00e8que", - "LabelConfigureServer": "Configurer Emby", - "LabelRestartServer": "Red\u00e9marrer le serveur" + "Plugin": "Extension" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index f287dc422..c679ed289 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -87,10 +87,5 @@ "User": "User", "System": "System", "Application": "Application", - "Plugin": "Plugin", - "LabelExit": "Exit", - "LabelVisitCommunity": "Visit Community", - "LabelBrowseLibrary": "Browse Library", - "LabelConfigureServer": "Configure Emby", - "LabelRestartServer": "Restart Server" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json index 143e3ab82..c807e53b4 100644 --- a/Emby.Server.Implementations/Localization/Core/hr.json +++ b/Emby.Server.Implementations/Localization/Core/hr.json @@ -87,10 +87,5 @@ "User": "Korisnik", "System": "Sistem", "Application": "Aplikacija", - "Plugin": "Dodatak", - "LabelExit": "Izlaz", - "LabelVisitCommunity": "Posjeti zajednicu", - "LabelBrowseLibrary": "Pregledaj biblioteku", - "LabelConfigureServer": "Podesi Emby", - "LabelRestartServer": "Restartiraj Server" + "Plugin": "Dodatak" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json index 39d0f8061..dc8f2b702 100644 --- a/Emby.Server.Implementations/Localization/Core/hu.json +++ b/Emby.Server.Implementations/Localization/Core/hu.json @@ -87,10 +87,5 @@ "User": "User", "System": "System", "Application": "Application", - "Plugin": "Plugin", - "LabelExit": "Kil\u00e9p\u00e9s", - "LabelVisitCommunity": "K\u00f6z\u00f6ss\u00e9g", - "LabelBrowseLibrary": "M\u00e9diat\u00e1r tall\u00f3z\u00e1sa", - "LabelConfigureServer": "Emby konfigur\u00e1l\u00e1sa", - "LabelRestartServer": "Szerver \u00fajraindit\u00e1sa" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index 4ff0d98d8..42605acdb 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -87,10 +87,5 @@ "User": "Utente", "System": "Sistema", "Application": "Applicazione", - "Plugin": "Plug-in", - "LabelExit": "Esci", - "LabelVisitCommunity": "Visita il forum di discussione", - "LabelBrowseLibrary": "Esplora la libreria", - "LabelConfigureServer": "Configura Emby", - "LabelRestartServer": "Riavvia Server" + "Plugin": "Plug-in" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json index c24ee3d13..a991fe363 100644 --- a/Emby.Server.Implementations/Localization/Core/kk.json +++ b/Emby.Server.Implementations/Localization/Core/kk.json @@ -87,10 +87,5 @@ "User": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b", "System": "\u0416\u04af\u0439\u0435", "Application": "\u049a\u043e\u043b\u0434\u0430\u043d\u0431\u0430", - "Plugin": "\u041f\u043b\u0430\u0433\u0438\u043d", - "LabelExit": "\u0428\u044b\u0493\u0443", - "LabelVisitCommunity": "\u049a\u0430\u0443\u044b\u043c\u0434\u0430\u0441\u0442\u044b\u049b\u049b\u0430 \u0431\u0430\u0440\u0443", - "LabelBrowseLibrary": "\u0422\u0430\u0441\u044b\u0493\u044b\u0448\u0445\u0430\u043d\u0430\u043d\u044b \u0448\u043e\u043b\u0443", - "LabelConfigureServer": "Emby \u0442\u0435\u04a3\u0448\u0435\u0443", - "LabelRestartServer": "\u0421\u0435\u0440\u0432\u0435\u0440\u0434\u0456 \u049b\u0430\u0439\u0442\u0430 \u0456\u0441\u043a\u0435 \u049b\u043e\u0441\u0443" + "Plugin": "\u041f\u043b\u0430\u0433\u0438\u043d" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index 6c5ed0260..0f99c8432 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -87,10 +87,5 @@ "User": "User", "System": "System", "Application": "Application", - "Plugin": "Plugin", - "LabelExit": "Exit", - "LabelVisitCommunity": "Visit Community", - "LabelBrowseLibrary": "Browse Library", - "LabelConfigureServer": "Configure Emby", - "LabelRestartServer": "Restart Server" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json index 21ed36d7c..9e1fede1d 100644 --- a/Emby.Server.Implementations/Localization/Core/lt-LT.json +++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json @@ -87,10 +87,5 @@ "User": "User", "System": "System", "Application": "Application", - "Plugin": "Plugin", - "LabelExit": "Exit", - "LabelVisitCommunity": "Visit Community", - "LabelBrowseLibrary": "Browse Library", - "LabelConfigureServer": "Configure Emby", - "LabelRestartServer": "Restart Server" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json index e4a49abf6..c48042d9a 100644 --- a/Emby.Server.Implementations/Localization/Core/ms.json +++ b/Emby.Server.Implementations/Localization/Core/ms.json @@ -87,10 +87,5 @@ "User": "User", "System": "System", "Application": "Application", - "Plugin": "Plugin", - "LabelExit": "Exit", - "LabelVisitCommunity": "Visit Community", - "LabelBrowseLibrary": "Browse Library", - "LabelConfigureServer": "Configure Emby", - "LabelRestartServer": "Restart Server" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json index d0482315d..5cd9894be 100644 --- a/Emby.Server.Implementations/Localization/Core/nb.json +++ b/Emby.Server.Implementations/Localization/Core/nb.json @@ -87,10 +87,5 @@ "User": "Bruker", "System": "System", "Application": "Applikasjon", - "Plugin": "Plugin", - "LabelExit": "Avslutt", - "LabelVisitCommunity": "Bes\u00f8k Samfunnet", - "LabelBrowseLibrary": "Bla i biblioteket", - "LabelConfigureServer": "Konfigurere Emby", - "LabelRestartServer": "Start om serveren" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index daba9bed5..d79fcf747 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -87,10 +87,5 @@ "User": "Gebruiker", "System": "Systeem", "Application": "Toepassing", - "Plugin": "Plug-in", - "LabelExit": "Afsluiten", - "LabelVisitCommunity": "Bezoek Gemeenschap", - "LabelBrowseLibrary": "Bekijk bibliotheek", - "LabelConfigureServer": "Emby Configureren", - "LabelRestartServer": "Server herstarten" + "Plugin": "Plug-in" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json index 24ac0d973..896df24dd 100644 --- a/Emby.Server.Implementations/Localization/Core/pl.json +++ b/Emby.Server.Implementations/Localization/Core/pl.json @@ -27,7 +27,7 @@ "Artists": "Wykonawcy", "Folders": "Foldery", "Songs": "Utwory", - "TvShows": "TV Shows", + "TvShows": "Seriale", "Shows": "Seriale", "Genres": "Gatunki", "NameSeasonNumber": "Sezon {0}", @@ -87,10 +87,5 @@ "User": "U\u017cytkownik", "System": "System", "Application": "Aplikacja", - "Plugin": "Wtyczka", - "LabelExit": "Wyj\u015bcie", - "LabelVisitCommunity": "Odwied\u017a spo\u0142eczno\u015b\u0107", - "LabelBrowseLibrary": "Przegl\u0105daj bibliotek\u0119", - "LabelConfigureServer": "Konfiguracja Emby", - "LabelRestartServer": "Uruchom serwer ponownie" + "Plugin": "Wtyczka" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json index 196a6b78b..fa4eac1c4 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-BR.json +++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json @@ -87,10 +87,5 @@ "User": "Usu\u00e1rio", "System": "Sistema", "Application": "Aplicativo", - "Plugin": "Plugin", - "LabelExit": "Sair", - "LabelVisitCommunity": "Visite a Comunidade", - "LabelBrowseLibrary": "Explorar Biblioteca", - "LabelConfigureServer": "Configurar Emby", - "LabelRestartServer": "Reiniciar Servidor" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json index 7a6b8f419..ac20fa1e5 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-PT.json +++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json @@ -87,10 +87,5 @@ "User": "User", "System": "System", "Application": "Aplica\u00e7\u00e3o", - "Plugin": "Plugin", - "LabelExit": "Exit", - "LabelVisitCommunity": "Visit Community", - "LabelBrowseLibrary": "Browse Library", - "LabelConfigureServer": "Configure Emby", - "LabelRestartServer": "Restart Server" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json index d27b1ca93..12345ca14 100644 --- a/Emby.Server.Implementations/Localization/Core/ru.json +++ b/Emby.Server.Implementations/Localization/Core/ru.json @@ -87,10 +87,5 @@ "User": "\u041f\u043e\u043b\u044c\u0437-\u043b\u044c", "System": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430", "Application": "\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435", - "Plugin": "\u041f\u043b\u0430\u0433\u0438\u043d", - "LabelExit": "\u0412\u044b\u0445\u043e\u0434", - "LabelVisitCommunity": "\u041f\u043e\u0441\u0435\u0449\u0435\u043d\u0438\u0435 \u0421\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u0430", - "LabelBrowseLibrary": "\u041d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044f \u043f\u043e \u043c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0435", - "LabelConfigureServer": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Emby", - "LabelRestartServer": "\u041f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0430" + "Plugin": "\u041f\u043b\u0430\u0433\u0438\u043d" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json index 98f60e5e1..aa6ada912 100644 --- a/Emby.Server.Implementations/Localization/Core/sk.json +++ b/Emby.Server.Implementations/Localization/Core/sk.json @@ -87,10 +87,5 @@ "User": "User", "System": "System", "Application": "Application", - "Plugin": "Plugin", - "LabelExit": "Exit", - "LabelVisitCommunity": "Visit Community", - "LabelBrowseLibrary": "Browse Library", - "LabelConfigureServer": "Configure Emby", - "LabelRestartServer": "Restart Server" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json index cb8e080e4..27561a890 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -87,10 +87,5 @@ "User": "User", "System": "System", "Application": "Application", - "Plugin": "Plugin", - "LabelExit": "Exit", - "LabelVisitCommunity": "Visit Community", - "LabelBrowseLibrary": "Browse Library", - "LabelConfigureServer": "Configure Emby", - "LabelRestartServer": "Restart Server" + "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 a03881183..c43bd79a6 100644 --- a/Emby.Server.Implementations/Localization/Core/sv.json +++ b/Emby.Server.Implementations/Localization/Core/sv.json @@ -87,10 +87,5 @@ "User": "Anv\u00e4ndare", "System": "System", "Application": "App", - "Plugin": "Till\u00e4gg", - "LabelExit": "Avsluta", - "LabelVisitCommunity": "Bes\u00f6k v\u00e5rt diskussionsforum", - "LabelBrowseLibrary": "Bl\u00e4ddra i biblioteket", - "LabelConfigureServer": "Konfigurera Emby", - "LabelRestartServer": "Starta om servern" + "Plugin": "Till\u00e4gg" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json index a0a515170..71af4110d 100644 --- a/Emby.Server.Implementations/Localization/Core/tr.json +++ b/Emby.Server.Implementations/Localization/Core/tr.json @@ -87,10 +87,5 @@ "User": "User", "System": "System", "Application": "Application", - "Plugin": "Plugin", - "LabelExit": "Exit", - "LabelVisitCommunity": "Visit Community", - "LabelBrowseLibrary": "Browse Library", - "LabelConfigureServer": "Configure Emby", - "LabelRestartServer": "Restart Server" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index 42126ca1b..0f248f3cd 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -87,10 +87,5 @@ "User": "\u7528\u6237", "System": "\u7cfb\u7edf", "Application": "\u5e94\u7528\u7a0b\u5e8f", - "Plugin": "\u63d2\u4ef6", - "LabelExit": "\u9000\u51fa", - "LabelVisitCommunity": "\u8bbf\u95ee\u793e\u533a", - "LabelBrowseLibrary": "\u6d4f\u89c8\u5a92\u4f53\u5e93", - "LabelConfigureServer": "\u914d\u7f6e Emby", - "LabelRestartServer": "\u91cd\u542f\u670d\u52a1\u5668" + "Plugin": "\u63d2\u4ef6" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index 37afc47e6..b60edb176 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -87,10 +87,5 @@ "User": "User", "System": "System", "Application": "Application", - "Plugin": "Plugin", - "LabelExit": "Exit", - "LabelVisitCommunity": "\u8a2a\u554f\u8a0e\u8ad6\u5340", - "LabelBrowseLibrary": "Browse Library", - "LabelConfigureServer": "Configure Emby", - "LabelRestartServer": "Restart Server" + "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index d7d048110..bdc29c16b 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -464,6 +464,8 @@ namespace Emby.Server.Implementations.ScheduledTasks /// The e. void progress_ProgressChanged(object sender, double e) { + e = Math.Min(e, 100); + CurrentProgress = e; EventHelper.FireEventIfNotNull(TaskProgress, this, new GenericEventArgs diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 5b4cd5900..7f6949feb 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1376,11 +1376,6 @@ namespace MediaBrowser.Controller.Entities return list; } - internal virtual bool IsValidFromResolver(BaseItem newItem) - { - return true; - } - internal virtual ItemUpdateType UpdateFromResolvedItem(BaseItem newItem) { var updateType = ItemUpdateType.None; diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 5fb9e517c..03fca60c8 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -265,21 +265,6 @@ namespace MediaBrowser.Controller.Entities return changed; } - internal override bool IsValidFromResolver(BaseItem newItem) - { - var newCollectionFolder = newItem as CollectionFolder; - - if (newCollectionFolder != null) - { - if (!string.Equals(CollectionType, newCollectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - } - - return base.IsValidFromResolver(newItem); - } - private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService, bool setPhysicalLocations) { var path = ContainingFolderPath; diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 12183aec2..fb283067f 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -416,7 +416,7 @@ namespace MediaBrowser.Controller.Entities { BaseItem currentChild; - if (currentChildren.TryGetValue(child.Id, out currentChild) && currentChild.IsValidFromResolver(child)) + if (currentChildren.TryGetValue(child.Id, out currentChild)) { validChildren.Add(currentChild); diff --git a/MediaBrowser.Controller/IO/StreamHelper.cs b/MediaBrowser.Controller/IO/StreamHelper.cs index 106fec41f..5aec9a182 100644 --- a/MediaBrowser.Controller/IO/StreamHelper.cs +++ b/MediaBrowser.Controller/IO/StreamHelper.cs @@ -1,6 +1,7 @@ using System.IO; using System.Threading; using System; +using System.Threading.Tasks; namespace MediaBrowser.Controller.IO { @@ -23,5 +24,27 @@ namespace MediaBrowser.Controller.IO } } } + + public static async Task CopyToAsync(Stream source, Stream destination, int bufferSize, IProgress progress, long contentLength, CancellationToken cancellationToken) + { + byte[] buffer = new byte[bufferSize]; + int read; + long totalRead = 0; + + while ((read = source.Read(buffer, 0, buffer.Length)) != 0) + { + cancellationToken.ThrowIfCancellationRequested(); + + destination.Write(buffer, 0, read); + + totalRead += read; + + double pct = totalRead; + pct /= contentLength; + pct *= 100; + + progress.Report(pct); + } + } } } From b9c1f61681de23d95de7c6b392eb3e55670991da Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 3 Nov 2017 14:11:21 -0400 Subject: [PATCH 02/21] update support for moviedb urls in nfo files --- Emby.Server.Implementations/Networking/NetworkManager.cs | 2 +- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 4 ++-- SharedVersion.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index 72d0154aa..f8f7da78a 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/Emby.Server.Implementations/Networking/NetworkManager.cs @@ -106,7 +106,7 @@ namespace Emby.Server.Implementations.Networking endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) || endpoint.StartsWith("192.168", StringComparison.OrdinalIgnoreCase) || endpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase) || - endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) || + //endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) || IsInPrivateAddressSpaceAndLocalSubnet(endpoint); } diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index c1c2e7d9d..689f175f3 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -243,13 +243,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers } // Support Tmdb - // http://www.themoviedb.org/movie/36557 + // https://www.themoviedb.org/movie/30287-fallo var srch = MovieDbParserSearchString; var index = xml.IndexOf(srch, StringComparison.OrdinalIgnoreCase); if (index != -1) { - var tmdbId = xml.Substring(index + srch.Length).TrimEnd('/'); + var tmdbId = xml.Substring(index + srch.Length).TrimEnd('/').Split('-')[0]; int value; if (!string.IsNullOrWhiteSpace(tmdbId) && int.TryParse(tmdbId, NumberStyles.Any, CultureInfo.InvariantCulture, out value)) { diff --git a/SharedVersion.cs b/SharedVersion.cs index e61a50da2..74a0759e3 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.35.2")] +[assembly: AssemblyVersion("3.2.36.1")] From 5cb74690284105db70a467ab77c2af3f44e42348 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 5 Nov 2017 16:51:23 -0500 Subject: [PATCH 03/21] 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 +- .../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 +- .../HdHomerun/HdHomerunHttpStream.cs | 5 +- .../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 +- .../Entities/Audio/Audio.cs | 5 + MediaBrowser.Controller/Entities/BaseItem.cs | 2 +- .../Entities/IHasMediaSources.cs | 1 + MediaBrowser.Controller/Entities/Video.cs | 5 + .../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 +- .../Providers/SubtitleOptions.cs | 2 + .../Session/GeneralCommandType.cs | 3 +- .../Manager/MetadataService.cs | 6 +- .../MediaInfo/FFProbeVideoInfo.cs | 6 +- .../MediaInfo/SubtitleResolver.cs | 115 +++--- .../Subtitles/SubtitleManager.cs | 101 ++++-- SharedVersion.cs | 2 +- 41 files changed, 355 insertions(+), 675 deletions(-) delete mode 100644 Emby.Dlna/Profiles/Xbox360Profile.cs delete mode 100644 Emby.Dlna/Profiles/Xml/Xbox 360.xml diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index bdc523c8b..8caa086ee 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 365774b0d..eb8b178a8 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 46639ee42..75204b234 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 7bdcd2a6f..000000000 --- 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 e17640f2f..99e52510f 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 b07a2f7c2..133f4abf1 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 3b7d2963f..000000000 --- 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 423327a38..635423234 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 c1ab9ec22..2aeb4b971 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 c42f22804..c566ca25b 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 0a316fcf1..2fa09c2b1 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 1d21796b1..0e1f6bb00 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 e3200a099..7e960f85e 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 be5e57539..b469966f5 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 718620ab5..7e72d1b1a 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 7e0e5fc5c..1ea77d505 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 06326d26c..8b46b78be 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 862098735..cead1def0 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 cc7611bf4..02c03f578 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 aa6ada912..0c7bce1da 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 c43bd79a6..ed1867024 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 2eb4743cd..6d271c0e1 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 9a9e619a6..6e0e55bef 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 bf7bf9ff8..fe0652f66 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 16fd75d2e..8ef1060d1 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 7f6949feb..f6a8f1d5a 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 54786134f..399fe9f5a 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 52f1dd051..60db53f79 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 16010b7f5..ac961d757 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 bddafe9a6..8f8791922 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 81269fe3f..7d50efd5e 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 d957470d3..d673198fd 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 6f864f4be..0b4574f6e 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 8bad650b5..7cacb96ee 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 5587e897f..d0d81e1fb 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 8dd0c29e4..616d5f9b4 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 af03e21b2..32b9a9ae2 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 a6a363ffd..158238557 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 a2c10d6a8..4e264bd22 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 fe655759e..945498966 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"; - } + await stream.CopyToAsync(memoryStream).ConfigureAwait(false); + memoryStream.Position = 0; - savePath += "." + response.Format.ToLower(); + var savePaths = new List(); + var saveFileName = _fileSystem.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower(); - _logger.Info("Saving subtitles to {0}", savePath); - - _monitor.ReportFileSystemChangeBeginning(savePath); - - try - { - //var isText = MediaStream.IsTextFormat(response.Format); - - using (var fs = _fileSystem.GetFileStream(savePath, FileOpenMode.Create, FileAccessMode.Write, - FileShareMode.Read, true)) + if (response.IsForced) { - await stream.CopyToAsync(fs).ConfigureAwait(false); + saveFileName += ".forced"; } - EventHelper.FireEventIfNotNull(SubtitlesDownloaded, this, new SubtitleDownloadEventArgs - { - Item = video, - Format = response.Format, - Language = response.Language, - IsForced = response.IsForced, - Provider = provider.Name + saveFileName += "." + response.Format.ToLower(); - }, _logger); - } - finally - { - _monitor.ReportFileSystemChangeComplete(savePath, false); + if (saveInMediaFolder) + { + savePaths.Add(Path.Combine(video.ContainingFolderPath, saveFileName)); + } + + savePaths.Add(Path.Combine(video.GetInternalMetadataPath(), saveFileName)); + + 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 74a0759e3..0750f469e 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")] From 83a43121af862051c0d414b1ca7ef6c389088e0b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 6 Nov 2017 16:32:44 -0500 Subject: [PATCH 04/21] fix hdhr stream returning too quickly --- Emby.Server.Implementations/Dto/DtoService.cs | 2 -- .../TunerHosts/HdHomerun/HdHomerunHttpStream.cs | 14 ++++++-------- MediaBrowser.Controller/Entities/Audio/Audio.cs | 5 ----- .../Entities/IHasMediaSources.cs | 1 - MediaBrowser.Controller/Entities/Video.cs | 5 ----- MediaBrowser.Controller/LiveTv/LiveTvChannel.cs | 5 ----- MediaBrowser.Model/Dto/BaseItemDto.cs | 1 - SharedVersion.cs | 2 +- 8 files changed, 7 insertions(+), 28 deletions(-) diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 2fa09c2b1..0a316fcf1 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -373,8 +373,6 @@ namespace Emby.Server.Implementations.Dto } NormalizeMediaSourceContainers(dto); - - dto.SupportsMediaSourceSelection = hasMediaSources.SupportsMediaSourceSelection(); } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs index 1ea77d505..ddbbda737 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs @@ -57,7 +57,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun Logger.Info("Opened HDHR stream from {0}", url); - StartStreaming(response, LiveStreamCancellationTokenSource.Token); + var taskCompletionSource = new TaskCompletionSource(); + StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token); //OpenedMediaSource.Protocol = MediaProtocol.File; //OpenedMediaSource.Path = tempFile; @@ -71,6 +72,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun //OpenedMediaSource.SupportsDirectPlay = false; //OpenedMediaSource.SupportsDirectStream = true; //OpenedMediaSource.SupportsTranscoding = true; + await taskCompletionSource.Task.ConfigureAwait(false); } protected override void CloseInternal() @@ -78,7 +80,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun LiveStreamCancellationTokenSource.Cancel(); } - private Task StartStreaming(HttpResponseInfo response, CancellationToken cancellationToken) + private Task StartStreaming(HttpResponseInfo response, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) { return Task.Run(async () => { @@ -90,10 +92,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { Logger.Info("Beginning HdHomerunHttpStream stream to file"); - FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath)); using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None)) { - StreamHelper.CopyTo(stream, fileStream, 81920, null, cancellationToken); + StreamHelper.CopyTo(stream, fileStream, 81920, () => Resolve(openTaskCompletionSource), cancellationToken); } } } @@ -112,10 +113,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private void Resolve(TaskCompletionSource openTaskCompletionSource) { - Task.Run(() => - { - openTaskCompletionSource.TrySetResult(true); - }); + openTaskCompletionSource.TrySetResult(true); } } } diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 8ef1060d1..16fd75d2e 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -51,11 +51,6 @@ namespace MediaBrowser.Controller.Entities.Audio return 1; } - public bool SupportsMediaSourceSelection() - { - return false; - } - [IgnoreDataMember] public override bool SupportsPlayedStatus { diff --git a/MediaBrowser.Controller/Entities/IHasMediaSources.cs b/MediaBrowser.Controller/Entities/IHasMediaSources.cs index 399fe9f5a..54786134f 100644 --- a/MediaBrowser.Controller/Entities/IHasMediaSources.cs +++ b/MediaBrowser.Controller/Entities/IHasMediaSources.cs @@ -13,6 +13,5 @@ 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 60db53f79..52f1dd051 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -109,11 +109,6 @@ 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 ac961d757..16010b7f5 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -24,11 +24,6 @@ namespace MediaBrowser.Controller.LiveTv return list; } - public bool SupportsMediaSourceSelection() - { - return false; - } - public override UnratedItem GetBlockUnratedType() { return UnratedItem.LiveTvChannel; diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index 7cacb96ee..ba975db44 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -75,7 +75,6 @@ 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/SharedVersion.cs b/SharedVersion.cs index 0750f469e..82dd0b72e 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.36.2")] +[assembly: AssemblyVersion("3.2.36.3")] From 50d322cbb7413b63147713d16ee603abcc51910f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 7 Nov 2017 12:43:59 -0500 Subject: [PATCH 05/21] 3.2.36.4 --- Emby.Dlna/Profiles/WdtvLiveProfile.cs | 10 +++++----- Emby.Dlna/Profiles/Xml/WDTV Live.xml | 10 +++++----- SharedVersion.cs | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Emby.Dlna/Profiles/WdtvLiveProfile.cs b/Emby.Dlna/Profiles/WdtvLiveProfile.cs index 01e77baa2..61819c57d 100644 --- a/Emby.Dlna/Profiles/WdtvLiveProfile.cs +++ b/Emby.Dlna/Profiles/WdtvLiveProfile.cs @@ -58,7 +58,7 @@ namespace Emby.Dlna.Profiles Container = "avi", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1", - AudioCodec = "ac3,dca,mp2,mp3,pcm,dts" + AudioCodec = "ac3,eac3,dca,mp2,mp3,pcm,dts" }, new DirectPlayProfile @@ -66,7 +66,7 @@ namespace Emby.Dlna.Profiles Container = "mpeg", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video", - AudioCodec = "ac3,dca,mp2,mp3,pcm,dts" + AudioCodec = "ac3,eac3,dca,mp2,mp3,pcm,dts" }, new DirectPlayProfile @@ -74,7 +74,7 @@ namespace Emby.Dlna.Profiles Container = "mkv", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1", - AudioCodec = "ac3,dca,aac,mp2,mp3,pcm,dts" + AudioCodec = "ac3,eac3,dca,aac,mp2,mp3,pcm,dts" }, new DirectPlayProfile @@ -82,7 +82,7 @@ namespace Emby.Dlna.Profiles Container = "ts,m2ts", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video,h264,vc1", - AudioCodec = "ac3,dca,mp2,mp3,aac,dts" + AudioCodec = "ac3,eac3,dca,mp2,mp3,aac,dts" }, new DirectPlayProfile @@ -90,7 +90,7 @@ namespace Emby.Dlna.Profiles Container = "mp4,mov,m4v", Type = DlnaProfileType.Video, VideoCodec = "h264,mpeg4", - AudioCodec = "ac3,aac,mp2,mp3,dca,dts" + AudioCodec = "ac3,eac3,aac,mp2,mp3,dca,dts" }, new DirectPlayProfile diff --git a/Emby.Dlna/Profiles/Xml/WDTV Live.xml b/Emby.Dlna/Profiles/Xml/WDTV Live.xml index 71b346318..44d130e92 100644 --- a/Emby.Dlna/Profiles/Xml/WDTV Live.xml +++ b/Emby.Dlna/Profiles/Xml/WDTV Live.xml @@ -36,11 +36,11 @@ true - - - - - + + + + + diff --git a/SharedVersion.cs b/SharedVersion.cs index 82dd0b72e..6403bf1cb 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.36.3")] +[assembly: AssemblyVersion("3.2.36.4")] From 25138bbeb74a7f02a07ea98d4f5e64b76eb00bbf Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 8 Nov 2017 15:23:39 -0500 Subject: [PATCH 06/21] update subtitle downloading to use local metadata settings --- .../TunerHosts/HdHomerun/HdHomerunHost.cs | 110 +++++++++++++++--- MediaBrowser.Api/StartupWizardService.cs | 35 +++++- MediaBrowser.Model/Entities/MediaStream.cs | 82 ++++++++++++- .../Providers/SubtitleOptions.cs | 2 - .../Subtitles/SubtitleManager.cs | 2 +- SharedVersion.cs | 2 +- 6 files changed, 204 insertions(+), 29 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 59346cdec..8cfe9de39 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -105,7 +105,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private class HdHomerunChannelInfo : ChannelInfo { public bool IsLegacyTuner { get; set; } - public string Url { get; set; } } protected override async Task> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken) @@ -124,7 +123,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun VideoCodec = i.VideoCodec, ChannelType = ChannelType.TV, IsLegacyTuner = (i.URL ?? string.Empty).StartsWith("hdhomerun", StringComparison.OrdinalIgnoreCase), - Url = i.URL + Path = i.URL }).Cast().ToList(); } @@ -148,7 +147,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { using (var response = await _httpClient.SendAsync(new HttpRequestOptions() { - Url = string.Format("{0}/discover.json", GetApiUrl(info, false)), + Url = string.Format("{0}/discover.json", GetApiUrl(info)), CancellationToken = cancellationToken, TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds), BufferContent = false @@ -195,13 +194,80 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } } - private async Task> GetTunerInfos(TunerHostInfo info, CancellationToken cancellationToken) + private async Task> GetTunerInfosHttp(TunerHostInfo info, CancellationToken cancellationToken) + { + var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false); + + using (var stream = await _httpClient.Get(new HttpRequestOptions() + { + Url = string.Format("{0}/tuners.html", GetApiUrl(info)), + CancellationToken = cancellationToken, + TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds), + BufferContent = false + })) + { + var tuners = new List(); + using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8)) + { + while (!sr.EndOfStream) + { + string line = StripXML(sr.ReadLine()); + if (line.Contains("Channel")) + { + LiveTvTunerStatus status; + var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase); + var name = line.Substring(0, index - 1); + var currentChannel = line.Substring(index + 7); + if (currentChannel != "none") { status = LiveTvTunerStatus.LiveTv; } else { status = LiveTvTunerStatus.Available; } + tuners.Add(new LiveTvTunerInfo + { + Name = name, + SourceType = string.IsNullOrWhiteSpace(model.ModelNumber) ? Name : model.ModelNumber, + ProgramName = currentChannel, + Status = status + }); + } + } + } + return tuners; + } + } + + private static string StripXML(string source) + { + char[] buffer = new char[source.Length]; + int bufferIndex = 0; + bool inside = false; + + for (int i = 0; i < source.Length; i++) + { + char let = source[i]; + if (let == '<') + { + inside = true; + continue; + } + if (let == '>') + { + inside = false; + continue; + } + if (!inside) + { + buffer[bufferIndex] = let; + bufferIndex++; + } + } + return new string(buffer, 0, bufferIndex); + } + + private async Task> GetTunerInfosUdp(TunerHostInfo info, CancellationToken cancellationToken) { var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false); var tuners = new List(); - var uri = new Uri(GetApiUrl(info, false)); + var uri = new Uri(GetApiUrl(info)); using (var manager = new HdHomerunManager(_socketFactory, Logger)) { @@ -246,7 +312,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun return list; } - private string GetApiUrl(TunerHostInfo info, bool isPlayback) + public async Task> GetTunerInfos(TunerHostInfo info, CancellationToken cancellationToken) + { + // TODO Need faster way to determine UDP vs HTTP + var channels = await GetChannels(info, true, cancellationToken); + + var hdHomerunChannelInfo = channels.FirstOrDefault() as HdHomerunChannelInfo; + + if (hdHomerunChannelInfo == null || hdHomerunChannelInfo.IsLegacyTuner) + { + return await GetTunerInfosUdp(info, cancellationToken).ConfigureAwait(false); + } + + return await GetTunerInfosHttp(info, cancellationToken).ConfigureAwait(false); + } + + private string GetApiUrl(TunerHostInfo info) { var url = info.Url; @@ -260,16 +341,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun url = "http://" + url; } - var uri = new Uri(url); - - if (isPlayback) - { - var builder = new UriBuilder(uri); - builder.Port = 5004; - uri = builder.Uri; - } - - return uri.AbsoluteUri.TrimEnd('/'); + return new Uri(url).AbsoluteUri.TrimEnd('/'); } private class Channels @@ -392,7 +464,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun nal = "0"; } - var url = GetApiUrl(info, false); + var url = GetApiUrl(info); var id = profile; if (string.IsNullOrWhiteSpace(id)) @@ -526,7 +598,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun if (hdhomerunChannel != null && hdhomerunChannel.IsLegacyTuner) { - return new HdHomerunUdpStream(mediaSource, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Url), modelInfo.TunerCount, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager, _environment); + return new HdHomerunUdpStream(mediaSource, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Path), modelInfo.TunerCount, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager, _environment); } // The UDP method is not working reliably on OSX, and on BSD it hasn't been tested yet @@ -537,7 +609,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { mediaSource.Protocol = MediaProtocol.Http; - var httpUrl = GetApiUrl(info, true) + "/auto/v" + hdhrId; + var httpUrl = channelInfo.Path; // If raw was used, the tuner doesn't support params if (!string.IsNullOrWhiteSpace(profile) && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase)) diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index 1b185b073..54e4657c1 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -9,6 +9,8 @@ using System.Linq; using System.Threading.Tasks; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Services; +using MediaBrowser.Common.Net; +using System.Threading; namespace MediaBrowser.Api { @@ -50,14 +52,16 @@ namespace MediaBrowser.Api private readonly IUserManager _userManager; private readonly IConnectManager _connectManager; private readonly IMediaEncoder _mediaEncoder; + private readonly IHttpClient _httpClient; - public StartupWizardService(IServerConfigurationManager config, IServerApplicationHost appHost, IUserManager userManager, IConnectManager connectManager, IMediaEncoder mediaEncoder) + public StartupWizardService(IServerConfigurationManager config, IHttpClient httpClient, IServerApplicationHost appHost, IUserManager userManager, IConnectManager connectManager, IMediaEncoder mediaEncoder) { _config = config; _appHost = appHost; _userManager = userManager; _connectManager = connectManager; _mediaEncoder = mediaEncoder; + _httpClient = httpClient; } public void Post(ReportStartupWizardComplete request) @@ -65,6 +69,35 @@ namespace MediaBrowser.Api _config.Configuration.IsStartupWizardCompleted = true; _config.SetOptimalValues(); _config.SaveConfiguration(); + + Task.Run(UpdateStats); + } + + private async Task UpdateStats() + { + try + { + var url = string.Format("http://www.mb3admin.com/admin/service/package/installed?mac={0}&product=MBServer&operation=Install&version={1}", + _appHost.SystemId, + _appHost.ApplicationVersion.ToString()); + + using (var response = await _httpClient.SendAsync(new HttpRequestOptions + { + + Url = url, + CancellationToken = CancellationToken.None, + LogErrors = false, + LogRequest = false + + }, "GET").ConfigureAwait(false)) + { + + } + } + catch + { + + } } public object Get(GetStartupInfo request) diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 747528cbf..4cdcde412 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -46,13 +46,13 @@ namespace MediaBrowser.Model.Entities { get { - if (!string.IsNullOrEmpty(Title)) - { - return AddLanguageIfNeeded(Title); - } - if (Type == MediaStreamType.Audio) { + if (!string.IsNullOrEmpty(Title)) + { + return AddLanguageIfNeeded(Title); + } + List attributes = new List(); if (!string.IsNullOrEmpty(Language)) @@ -84,8 +84,32 @@ namespace MediaBrowser.Model.Entities return string.Join(" ", attributes.ToArray(attributes.Count)); } + if (Type == MediaStreamType.Video) + { + List attributes = new List(); + + var resolutionText = GetResolutionText(); + + if (!string.IsNullOrEmpty(resolutionText)) + { + attributes.Add(resolutionText); + } + + if (!string.IsNullOrEmpty(Codec)) + { + attributes.Add(Codec.ToUpper()); + } + + return string.Join(" ", attributes.ToArray(attributes.Count)); + } + if (Type == MediaStreamType.Subtitle) { + if (!string.IsNullOrEmpty(Title)) + { + return AddLanguageIfNeeded(Title); + } + List attributes = new List(); if (!string.IsNullOrEmpty(Language)) @@ -121,6 +145,54 @@ namespace MediaBrowser.Model.Entities } } + private string GetResolutionText() + { + var i = this; + + if (i.Width.HasValue) + { + if (i.Width >= 3800) + { + return "4K"; + } + if (i.Width >= 2500) + { + if (i.IsInterlaced) + { + return "1440I"; + } + return "1440P"; + } + if (i.Width >= 1900) + { + if (i.IsInterlaced) + { + return "1080I"; + } + return "1080P"; + } + if (i.Width >= 1260) + { + if (i.IsInterlaced) + { + return "720I"; + } + return "720P"; + } + if (i.Width >= 700) + { + + if (i.IsInterlaced) + { + return "480I"; + } + return "480P"; + } + + } + return null; + } + private string AddLanguageIfNeeded(string title) { if (!string.IsNullOrEmpty(Language) && diff --git a/MediaBrowser.Model/Providers/SubtitleOptions.cs b/MediaBrowser.Model/Providers/SubtitleOptions.cs index d0d81e1fb..5587e897f 100644 --- a/MediaBrowser.Model/Providers/SubtitleOptions.cs +++ b/MediaBrowser.Model/Providers/SubtitleOptions.cs @@ -13,7 +13,6 @@ namespace MediaBrowser.Model.Providers public bool IsOpenSubtitleVipAccount { get; set; } public bool RequirePerfectMatch { get; set; } - public bool SaveSubtitlesInMediaFolders { get; set; } public SubtitleOptions() { @@ -21,7 +20,6 @@ namespace MediaBrowser.Model.Providers SkipIfAudioTrackMatches = true; RequirePerfectMatch = true; - SaveSubtitlesInMediaFolders = true; } } } \ No newline at end of file diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index 945498966..6a9b7136c 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -116,7 +116,7 @@ namespace MediaBrowser.Providers.Subtitles var parts = subtitleId.Split(new[] { '_' }, 2); var provider = GetProvider(parts.First()); - var saveInMediaFolder = GetOptions().SaveSubtitlesInMediaFolders && video.SupportsLocalMetadata; + var saveInMediaFolder = video.IsSaveLocalMetadataEnabled(); try { diff --git a/SharedVersion.cs b/SharedVersion.cs index 6403bf1cb..f0a2b930d 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.36.4")] +[assembly: AssemblyVersion("3.2.36.5")] From 901d7b509c712ababc489166e05bdfea1908fa0f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 9 Nov 2017 15:58:09 -0500 Subject: [PATCH 07/21] update play media source feature --- Emby.Dlna/PlayTo/PlayToController.cs | 2 +- .../Localization/Core/cs.json | 2 +- .../Localization/Core/fr.json | 2 +- .../Localization/Core/sk.json | 100 +++++++++--------- .../Session/HttpSessionController.cs | 12 +++ .../Session/SessionManager.cs | 11 +- MediaBrowser.Api/Session/SessionsService.cs | 33 +----- MediaBrowser.Model/Session/PlayRequest.cs | 10 +- SharedVersion.cs | 2 +- 9 files changed, 82 insertions(+), 92 deletions(-) diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index ba1d3a6de..bd0a9e1f4 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -328,7 +328,7 @@ namespace Emby.Dlna.PlayTo { if (isFirst && command.StartPositionTicks.HasValue) { - playlist.Add(CreatePlaylistItem(item, user, command.StartPositionTicks.Value, null, null, null)); + playlist.Add(CreatePlaylistItem(item, user, command.StartPositionTicks.Value, command.MediaSourceId, command.AudioStreamIndex, command.SubtitleStreamIndex)); isFirst = false; } else diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json index 82d8699a7..d59e40ed3 100644 --- a/Emby.Server.Implementations/Localization/Core/cs.json +++ b/Emby.Server.Implementations/Localization/Core/cs.json @@ -27,7 +27,7 @@ "Artists": "Um\u011blci", "Folders": "Slo\u017eky", "Songs": "Skladby", - "TvShows": "TV Shows", + "TvShows": "TV seri\u00e1ly", "Shows": "Seri\u00e1ly", "Genres": "\u017d\u00e1nry", "NameSeasonNumber": "Sez\u00f3na {0}", diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index 953d7f434..6552a47ab 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -27,7 +27,7 @@ "Artists": "Artistes", "Folders": "Dossiers", "Songs": "Chansons", - "TvShows": "TV Shows", + "TvShows": "S\u00e9ries T\u00e9l\u00e9", "Shows": "\u00c9missions", "Genres": "Genres", "NameSeasonNumber": "Saison {0}", diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json index 0c7bce1da..f09eb1506 100644 --- a/Emby.Server.Implementations/Localization/Core/sk.json +++ b/Emby.Server.Implementations/Localization/Core/sk.json @@ -1,7 +1,7 @@ { - "Latest": "Latest", - "ValueSpecialEpisodeName": "Special - {0}", - "Inherit": "Inherit", + "Latest": "Najnov\u0161ie", + "ValueSpecialEpisodeName": "\u0160peci\u00e1l - {0}", + "Inherit": "Zdedi\u0165", "Books": "Knihy", "Music": "Hudba", "Games": "Hry", @@ -9,13 +9,13 @@ "MixedContent": "Zmie\u0161an\u00fd obsah", "MusicVideos": "Hudobn\u00e9 vide\u00e1", "HomeVideos": "Dom\u00e1ce vide\u00e1", - "Playlists": "Playlists", + "Playlists": "Zoznamy skladieb", "HeaderRecordingGroups": "Recording Groups", - "HeaderContinueWatching": "Continue Watching", - "HeaderFavoriteArtists": "Favorite Artists", + "HeaderContinueWatching": "Pokra\u010dujte v pozeran\u00ed", + "HeaderFavoriteArtists": "Ob\u013e\u00faben\u00ed umelci", "HeaderFavoriteSongs": "Ob\u013e\u00faben\u00e9 pesni\u010dky", "HeaderAlbumArtists": "Album Artists", - "HeaderFavoriteAlbums": "Favorite Albums", + "HeaderFavoriteAlbums": "Ob\u013e\u00faben\u00e9 albumy", "HeaderFavoriteEpisodes": "Ob\u013e\u00faben\u00e9 epiz\u00f3dy", "HeaderFavoriteShows": "Ob\u013e\u00faben\u00e9 seri\u00e1ly", "HeaderNextUp": "Nasleduje", @@ -30,62 +30,62 @@ "TvShows": "TV Shows", "Shows": "Series", "Genres": "\u017d\u00e1nre", - "NameSeasonNumber": "Season {0}", - "AppDeviceValues": "App: {0}, Device: {1}", - "UserDownloadingItemWithValues": "{0} is downloading {1}", + "NameSeasonNumber": "Sez\u00f3na {0}", + "AppDeviceValues": "Aplik\u00e1cia: {0}, Zariadenie: {1}", + "UserDownloadingItemWithValues": "{0} s\u0165ahuje {1}", "HeaderLiveTV": "Live TV", - "ChapterNameValue": "Chapter {0}", - "ScheduledTaskFailedWithName": "{0} failed", - "LabelRunningTimeValue": "Running time: {0}", + "ChapterNameValue": "Kapitola {0}", + "ScheduledTaskFailedWithName": "{0} zlyhalo", + "LabelRunningTimeValue": "D\u013a\u017eka: {0}", "ScheduledTaskStartedWithName": "{0} started", - "VersionNumber": "Version {0}", - "PluginInstalledWithName": "{0} was installed", - "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly.", - "PluginUpdatedWithName": "{0} was updated", - "PluginUninstalledWithName": "{0} was uninstalled", - "ItemAddedWithName": "{0} was added to the library", - "ItemRemovedWithName": "{0} was removed from the library", - "LabelIpAddressValue": "Ip address: {0}", - "DeviceOnlineWithName": "{0} is connected", - "UserOnlineFromDevice": "{0} is online from {1}", + "VersionNumber": "Verzia {0}", + "PluginInstalledWithName": "{0} bol nain\u0161talovan\u00fd", + "StartupEmbyServerIsLoading": "Emby Server sa sp\u00fa\u0161\u0165a. Sk\u00faste to pros\u00edm o chv\u00ed\u013eu znova.", + "PluginUpdatedWithName": "{0} bol aktualizovan\u00fd", + "PluginUninstalledWithName": "{0} bol odin\u0161talovan\u00fd", + "ItemAddedWithName": "{0} bol pridan\u00fd do kni\u017enice", + "ItemRemovedWithName": "{0} bol odstr\u00e1nen\u00fd z kni\u017enice", + "LabelIpAddressValue": "IP adresa: {0}", + "DeviceOnlineWithName": "{0} je pripojen\u00fd", + "UserOnlineFromDevice": "{0} je online z {1}", "ProviderValue": "Provider: {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": "Titulky pre {0} stiahnut\u00e9", + "UserCreatedWithName": "Pou\u017e\u00edvate\u013e {0} bol vytvoren\u00fd", + "UserPasswordChangedWithName": "Heslo pou\u017e\u00edvate\u013ea {0} zmenen\u00e9", + "UserDeletedWithName": "Pou\u017e\u00edvate\u013e {0} bol vymazan\u00fd", "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", - "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", - "UserOfflineFromDevice": "{0} has disconnected from {1}", - "DeviceOfflineWithName": "{0} has disconnected", - "UserStartedPlayingItemWithValues": "{0} has started playing {1}", - "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}", + "MessageServerConfigurationUpdated": "Konfigur\u00e1cia servera aktualizovan\u00e1", + "MessageNamedServerConfigurationUpdatedWithValue": "Sekcia {0} konfigur\u00e1cie servera bola aktualizovan\u00e1", + "MessageApplicationUpdated": "Emby Server bol aktualizovan\u00fd", + "FailedLoginAttemptWithUserName": "Ne\u00faspe\u0161n\u00fd pokus o prihl\u00e1senie z {0}", + "AuthenticationSucceededWithUserName": "{0} \u00faspe\u0161ne overen\u00fd", + "UserOfflineFromDevice": "{0} sa odpojil od {1}", + "DeviceOfflineWithName": "{0} je odpojen\u00fd", + "UserStartedPlayingItemWithValues": "{0} spustil prehr\u00e1vanie {1}", + "UserStoppedPlayingItemWithValues": "{0} zastavil prehr\u00e1vanie {1}", "NotificationOptionPluginError": "Plugin failure", - "NotificationOptionApplicationUpdateAvailable": "Application update available", - "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionApplicationUpdateAvailable": "Je dostupn\u00e1 aktualiz\u00e1cia aplik\u00e1cie", + "NotificationOptionApplicationUpdateInstalled": "Aktualiz\u00e1cia aplik\u00e1cie nain\u0161talovan\u00e1", "NotificationOptionPluginUpdateInstalled": "Plugin update installed", "NotificationOptionPluginInstalled": "Plugin installed", "NotificationOptionPluginUninstalled": "Plugin uninstalled", - "NotificationOptionVideoPlayback": "Video playback started", - "NotificationOptionAudioPlayback": "Audio playback started", + "NotificationOptionVideoPlayback": "Spusten\u00e9 prehr\u00e1vanie videa", + "NotificationOptionAudioPlayback": "Spusten\u00e9 prehr\u00e1vanie audia", "NotificationOptionGamePlayback": "Game playback started", - "NotificationOptionVideoPlaybackStopped": "Video playback stopped", - "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", - "NotificationOptionGamePlaybackStopped": "Game playback stopped", - "NotificationOptionTaskFailed": "Scheduled task failure", - "NotificationOptionInstallationFailed": "Installation failure", - "NotificationOptionNewLibraryContent": "New content added", + "NotificationOptionVideoPlaybackStopped": "Zastaven\u00e9 prehr\u00e1vanie videa", + "NotificationOptionAudioPlaybackStopped": "Zastaven\u00e9 prehr\u00e1vanie audia", + "NotificationOptionGamePlaybackStopped": "Hra ukon\u010den\u00e1", + "NotificationOptionTaskFailed": "Napl\u00e1novan\u00e1 \u00faloha zlyhala", + "NotificationOptionInstallationFailed": "Chyba in\u0161tal\u00e1cie", + "NotificationOptionNewLibraryContent": "Pridan\u00fd nov\u00fd obsah", "NotificationOptionCameraImageUploaded": "Camera image uploaded", "NotificationOptionUserLockedOut": "User locked out", - "NotificationOptionServerRestartRequired": "Server restart required", + "NotificationOptionServerRestartRequired": "Vy\u017eaduje sa re\u0161tart servera", "UserLockedOutWithName": "User {0} has been locked out", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", + "SubtitleDownloadFailureForItem": "S\u0165ahovanie titulkov pre {0} zlyhalo", "Sync": "Sync", - "User": "User", - "System": "System", + "User": "Pou\u017e\u00edvate\u013e", + "System": "Syst\u00e9m", "Application": "Aplik\u00e1cia", "Plugin": "Plugin" } \ No newline at end of file diff --git a/Emby.Server.Implementations/Session/HttpSessionController.cs b/Emby.Server.Implementations/Session/HttpSessionController.cs index 940c821e2..e1c1bbe2b 100644 --- a/Emby.Server.Implementations/Session/HttpSessionController.cs +++ b/Emby.Server.Implementations/Session/HttpSessionController.cs @@ -109,6 +109,18 @@ namespace Emby.Server.Implementations.Session { dict["StartPositionTicks"] = command.StartPositionTicks.Value.ToString(CultureInfo.InvariantCulture); } + if (command.AudioStreamIndex.HasValue) + { + dict["AudioStreamIndex"] = command.AudioStreamIndex.Value.ToString(CultureInfo.InvariantCulture); + } + if (command.SubtitleStreamIndex.HasValue) + { + dict["SubtitleStreamIndex"] = command.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture); + } + if (!string.IsNullOrWhiteSpace(command.MediaSourceId)) + { + dict["MediaSourceId"] = command.MediaSourceId; + } return SendMessage(command.PlayCommand.ToString(), dict, cancellationToken); } diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 2c1535165..30f6e6521 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -972,7 +972,6 @@ namespace Emby.Server.Implementations.Session if (command.PlayCommand == PlayCommand.PlayInstantMix) { items = command.ItemIds.SelectMany(i => TranslateItemForInstantMix(i, user)) - .Where(i => i.LocationType != LocationType.Virtual) .ToList(); command.PlayCommand = PlayCommand.PlayNow; @@ -986,9 +985,7 @@ namespace Emby.Server.Implementations.Session list.AddRange(subItems); } - items = list - .Where(i => i.LocationType != LocationType.Virtual) - .ToList(); + items = list; } if (command.PlayCommand == PlayCommand.PlayShuffle) @@ -1074,7 +1071,8 @@ namespace Emby.Server.Implementations.Session { ItemFields.SortName } - } + }, + IsVirtualItem = false }); return FilterToSingleMediaType(items) @@ -1097,7 +1095,8 @@ namespace Emby.Server.Implementations.Session { ItemFields.SortName } - } + }, + IsVirtualItem = false }); diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs index e961f9d51..35c29cd15 100644 --- a/MediaBrowser.Api/Session/SessionsService.cs +++ b/MediaBrowser.Api/Session/SessionsService.cs @@ -66,7 +66,7 @@ namespace MediaBrowser.Api.Session [Route("/Sessions/{Id}/Playing", "POST", Summary = "Instructs a session to play an item")] [Authenticated] - public class Play : IReturnVoid + public class Play : PlayRequest { /// /// Gets or sets the id. @@ -74,27 +74,6 @@ namespace MediaBrowser.Api.Session /// The id. [ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string Id { get; set; } - - /// - /// Artist, Genre, Studio, Person, or any kind of BaseItem - /// - /// The type of the item. - [ApiMember(Name = "ItemIds", Description = "The ids of the items to play, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)] - public string ItemIds { get; set; } - - /// - /// Gets or sets the start position ticks that the first item should be played at - /// - /// The start position ticks. - [ApiMember(Name = "StartPositionTicks", Description = "The starting position of the first item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public long? StartPositionTicks { get; set; } - - /// - /// Gets or sets the play command. - /// - /// The play command. - [ApiMember(Name = "PlayCommand", Description = "The type of play command to issue (PlayNow, PlayNext, PlayLast). Clients who have not yet implemented play next and play last may play now.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public PlayCommand PlayCommand { get; set; } } [Route("/Sessions/{Id}/Playing/{Command}", "POST", Summary = "Issues a playstate command to a client")] @@ -471,15 +450,7 @@ namespace MediaBrowser.Api.Session /// The request. public void Post(Play request) { - var command = new PlayRequest - { - ItemIds = request.ItemIds.Split(','), - - PlayCommand = request.PlayCommand, - StartPositionTicks = request.StartPositionTicks - }; - - var task = _sessionManager.SendPlayCommand(GetSession(_sessionContext).Result.Id, request.Id, command, CancellationToken.None); + var task = _sessionManager.SendPlayCommand(GetSession(_sessionContext).Result.Id, request.Id, request, CancellationToken.None); Task.WaitAll(task); } diff --git a/MediaBrowser.Model/Session/PlayRequest.cs b/MediaBrowser.Model/Session/PlayRequest.cs index 5db5e90cb..d50cb5953 100644 --- a/MediaBrowser.Model/Session/PlayRequest.cs +++ b/MediaBrowser.Model/Session/PlayRequest.cs @@ -1,4 +1,5 @@ - +using MediaBrowser.Model.Services; + namespace MediaBrowser.Model.Session { /// @@ -10,18 +11,21 @@ namespace MediaBrowser.Model.Session /// Gets or sets the item ids. /// /// The item ids. + [ApiMember(Name = "ItemIds", Description = "The ids of the items to play, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)] public string[] ItemIds { get; set; } /// /// Gets or sets the start position ticks that the first item should be played at /// /// The start position ticks. + [ApiMember(Name = "StartPositionTicks", Description = "The starting position of the first item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] public long? StartPositionTicks { get; set; } /// /// Gets or sets the play command. /// /// The play command. + [ApiMember(Name = "PlayCommand", Description = "The type of play command to issue (PlayNow, PlayNext, PlayLast). Clients who have not yet implemented play next and play last may play now.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] public PlayCommand PlayCommand { get; set; } /// @@ -29,5 +33,9 @@ namespace MediaBrowser.Model.Session /// /// The controlling user identifier. public string ControllingUserId { get; set; } + + public int? SubtitleStreamIndex { get; set; } + public int? AudioStreamIndex { get; set; } + public string MediaSourceId { get; set; } } } \ No newline at end of file diff --git a/SharedVersion.cs b/SharedVersion.cs index f0a2b930d..1505ef0b8 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.36.5")] +[assembly: AssemblyVersion("3.2.36.6")] From 711f58808447765b51367cb4a60581f8b8bb65ff Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 10 Nov 2017 16:22:38 -0500 Subject: [PATCH 08/21] 3.2.36.7 --- Emby.Server.Implementations/ApplicationHost.cs | 3 ++- .../Library/LibraryManager.cs | 6 ++++++ .../Net/SocketFactory.cs | 12 +++++++++++- .../Networking/NetworkManager.cs | 18 ++++++++++++------ .../Configuration/XbmcMetadataOptions.cs | 1 - SharedVersion.cs | 2 +- 6 files changed, 32 insertions(+), 10 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 2aeb4b971..745d83eb5 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -799,7 +799,7 @@ namespace Emby.Server.Implementations protected abstract IConnectManager CreateConnectManager(); protected abstract ISyncManager CreateSyncManager(); - + protected virtual IHttpClient CreateHttpClient() { return new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamFactory, GetDefaultUserAgent); @@ -953,6 +953,7 @@ namespace Emby.Server.Implementations var deviceRepo = new SqliteDeviceRepository(LogManager.GetLogger("DeviceManager"), ServerConfigurationManager, FileSystemManager, JsonSerializer); deviceRepo.Initialize(); DeviceManager = new DeviceManager(deviceRepo, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager); + RegisterSingleInstance(deviceRepo); RegisterSingleInstance(DeviceManager); var newsService = new Emby.Server.Implementations.News.NewsService(ApplicationPaths, JsonSerializer); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index bd8a09550..5eda986e1 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -709,6 +709,9 @@ namespace Emby.Server.Implementations.Library var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)); + // In case program data folder was moved + rootFolder.Path = rootFolderPath; + // Add in the plug-in folders foreach (var child in PluginFolderCreators) { @@ -771,6 +774,9 @@ namespace Emby.Server.Implementations.Library tmpItem = (UserRootFolder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath)); } + // In case program data folder was moved + tmpItem.Path = userRootPath; + _userRootFolder = tmpItem; } } diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs index f78fbdfd7..bdae1728f 100644 --- a/Emby.Server.Implementations/Net/SocketFactory.cs +++ b/Emby.Server.Implementations/Net/SocketFactory.cs @@ -196,8 +196,18 @@ namespace Emby.Server.Implementations.Net try { - //retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); + // seeing occasional exceptions thrown on qnap + // System.Net.Sockets.SocketException (0x80004005): Protocol not available retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + } + catch (SocketException) + { + + } + + try + { + //retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, multicastTimeToLive); var localIp = IPAddress.Any; diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index f8f7da78a..fbdb5c128 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/Emby.Server.Implementations/Networking/NetworkManager.cs @@ -59,7 +59,13 @@ namespace Emby.Server.Implementations.Networking list.AddRange(GetLocalIpAddressesFallback().Result); } - return list.Where(FilterIpAddress).DistinctBy(i => i.ToString()); + var listClone = list.ToList(); + + return list + .OrderBy(i => i.AddressFamily == AddressFamily.InterNetwork ? 0 : 1) + .ThenBy(i => listClone.IndexOf(i)) + .Where(FilterIpAddress) + .DistinctBy(i => i.ToString()); } private bool FilterIpAddress(IPAddress address) @@ -112,10 +118,10 @@ namespace Emby.Server.Implementations.Networking public bool IsInPrivateAddressSpaceAndLocalSubnet(string endpoint) { - var endpointFirstPart = endpoint.Split('.')[0]; - if (endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase)) { + var endpointFirstPart = endpoint.Split('.')[0]; + var subnets = GetSubnets(endpointFirstPart); foreach (var subnet_Match in subnets) @@ -217,7 +223,7 @@ namespace Emby.Server.Implementations.Networking } else if (address.AddressFamily == AddressFamily.InterNetworkV6) { - lengthMatch = 10; + lengthMatch = 9; if (IsInPrivateAddressSpace(endpoint)) { return true; @@ -317,7 +323,7 @@ namespace Emby.Server.Implementations.Networking return ipProperties.UnicastAddresses //.Where(i => i.IsDnsEligible) .Select(i => i.Address) - .Where(i => i.AddressFamily == AddressFamily.InterNetwork) + .Where(i => i.AddressFamily == AddressFamily.InterNetwork || i.AddressFamily == AddressFamily.InterNetworkV6) .ToList(); } catch (Exception ex) @@ -337,7 +343,7 @@ namespace Emby.Server.Implementations.Networking // Reverse them because the last one is usually the correct one // It's not fool-proof so ultimately the consumer will have to examine them and decide return host.AddressList - .Where(i => i.AddressFamily == AddressFamily.InterNetwork) + .Where(i => i.AddressFamily == AddressFamily.InterNetwork || i.AddressFamily == AddressFamily.InterNetworkV6) .Reverse(); } diff --git a/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs b/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs index 86eb40b97..de8a59a3d 100644 --- a/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs +++ b/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs @@ -18,7 +18,6 @@ namespace MediaBrowser.Model.Configuration SaveImagePathsInNfo = true; EnablePathSubstitution = true; - EnableExtraThumbsDuplication = true; } } } diff --git a/SharedVersion.cs b/SharedVersion.cs index 1505ef0b8..8ff6f604b 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.36.6")] +[assembly: AssemblyVersion("3.2.36.7")] From 351d29954a60084df3cca7dc6db2393298b366cd Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 12 Nov 2017 16:05:40 -0500 Subject: [PATCH 09/21] 3.2.36.8 --- .../Library/LibraryManager.cs | 12 ++++++++++-- MediaBrowser.Model/Entities/MediaStream.cs | 16 ++++++++-------- RSSDP/SsdpCommunicationsServer.cs | 6 ++++++ SharedVersion.cs | 2 +- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 5eda986e1..f71e2714a 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -710,7 +710,11 @@ namespace Emby.Server.Implementations.Library var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)); // In case program data folder was moved - rootFolder.Path = rootFolderPath; + if (!string.Equals(rootFolder.Path, rootFolderPath, StringComparison.Ordinal)) + { + _logger.Info("Resetting root folder path to {0}", rootFolderPath); + rootFolder.Path = rootFolderPath; + } // Add in the plug-in folders foreach (var child in PluginFolderCreators) @@ -775,7 +779,11 @@ namespace Emby.Server.Implementations.Library } // In case program data folder was moved - tmpItem.Path = userRootPath; + if (!string.Equals(tmpItem.Path, userRootPath, StringComparison.Ordinal)) + { + _logger.Info("Resetting user root folder path to {0}", userRootPath); + tmpItem.Path = userRootPath; + } _userRootFolder = tmpItem; } diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 4cdcde412..8a402e6f4 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -48,10 +48,10 @@ namespace MediaBrowser.Model.Entities { if (Type == MediaStreamType.Audio) { - if (!string.IsNullOrEmpty(Title)) - { - return AddLanguageIfNeeded(Title); - } + //if (!string.IsNullOrEmpty(Title)) + //{ + // return AddLanguageIfNeeded(Title); + //} List attributes = new List(); @@ -105,10 +105,10 @@ namespace MediaBrowser.Model.Entities if (Type == MediaStreamType.Subtitle) { - if (!string.IsNullOrEmpty(Title)) - { - return AddLanguageIfNeeded(Title); - } + //if (!string.IsNullOrEmpty(Title)) + //{ + // return AddLanguageIfNeeded(Title); + //} List attributes = new List(); diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index a4be24ebf..6b4f67b0b 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -366,6 +366,12 @@ namespace Rssdp.Infrastructure { foreach (var address in _networkManager.GetLocalIpAddresses()) { + if (address.AddressFamily == IpAddressFamily.InterNetworkV6) + { + // Not supported ? + continue; + } + try { sockets.Add(_SocketFactory.CreateSsdpUdpSocket(address, _LocalPort)); diff --git a/SharedVersion.cs b/SharedVersion.cs index 8ff6f604b..c89077695 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.36.7")] +[assembly: AssemblyVersion("3.2.36.8")] From 2c6cbb33eead0236cbf89fb4afab486aa8510647 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 14 Nov 2017 02:33:35 -0500 Subject: [PATCH 10/21] fixes #3013 - Open Movie Database (OMDB API) "Error: Invalid API key!" While getting movie poster. --- MediaBrowser.Providers/Omdb/OmdbImageProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs index 74bfa6e03..1ef420ddd 100644 --- a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs @@ -68,7 +68,7 @@ namespace MediaBrowser.Providers.Omdb list.Add(new RemoteImageInfo { ProviderName = Name, - Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=82e83907", imdbId) + Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=fe53f97e", imdbId) }); } } From 2f758676d0908f07dafc3ec5434bbaf4cf82529c Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 14 Nov 2017 02:41:21 -0500 Subject: [PATCH 11/21] support sharing m3u tuner streams --- .../Emby.Server.Implementations.csproj | 2 +- .../TunerHosts/HdHomerun/HdHomerunHost.cs | 2 +- .../HdHomerun/HdHomerunUdpStream.cs | 1 + .../LiveTv/TunerHosts/LiveStream.cs | 13 +++++- .../LiveTv/TunerHosts/M3UTunerHost.cs | 10 ++++- ...merunHttpStream.cs => SharedHttpStream.cs} | 40 +++++++++++++++---- MediaBrowser.Api/Library/LibraryService.cs | 15 ++++--- MediaBrowser.Common/Net/HttpRequestOptions.cs | 2 + MediaBrowser.Controller/Entities/Folder.cs | 10 +++++ SharedVersion.cs | 2 +- 10 files changed, 78 insertions(+), 19 deletions(-) rename Emby.Server.Implementations/LiveTv/TunerHosts/{HdHomerun/HdHomerunHttpStream.cs => SharedHttpStream.cs} (71%) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 6b2a005f9..7ccf54d20 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -404,11 +404,11 @@ - + diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 8cfe9de39..08eab9a35 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -618,7 +618,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } mediaSource.Path = httpUrl; - return new HdHomerunHttpStream(mediaSource, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _environment); + return new SharedHttpStream(mediaSource, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _environment); } return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number, profile), modelInfo.TunerCount, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager, _environment); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index 8b46b78be..a32930080 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -32,6 +32,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun OriginalStreamId = originalStreamId; _channelCommands = channelCommands; _numTuners = numTuners; + EnableStreamSharing = true; } public override async Task Open(CancellationToken openCancellationToken) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index cead1def0..bec92716b 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -29,8 +29,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts public List SharedStreamIds { get; private set; } protected readonly IEnvironmentInfo Environment; protected readonly IFileSystem FileSystem; + protected readonly IServerApplicationPaths AppPaths; - protected readonly string TempFilePath; + protected string TempFilePath; protected readonly ILogger Logger; protected readonly CancellationTokenSource LiveStreamCancellationTokenSource = new CancellationTokenSource(); @@ -44,7 +45,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts EnableStreamSharing = true; SharedStreamIds = new List(); UniqueId = Guid.NewGuid().ToString("N"); - TempFilePath = Path.Combine(appPaths.GetTranscodingTempPath(), UniqueId + ".ts"); + + AppPaths = appPaths; + + SetTempFilePath("ts"); + } + + protected void SetTempFilePath(string extension) + { + TempFilePath = Path.Combine(AppPaths.GetTranscodingTempPath(), UniqueId + "." + extension); } public virtual Task Open(CancellationToken openCancellationToken) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index 9fc6687d1..e41fb7fbe 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -79,8 +79,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { var sources = await GetChannelStreamMediaSources(info, channelId, cancellationToken).ConfigureAwait(false); - var liveStream = new LiveStream(sources.First(), _environment, FileSystem, Logger, Config.ApplicationPaths); - return liveStream; + var mediaSource = sources.First(); + + if (mediaSource.Protocol == MediaProtocol.Http && !mediaSource.RequiresLooping) + { + return new SharedHttpStream(mediaSource, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _environment); + } + + return new LiveStream(mediaSource, _environment, FileSystem, Logger, Config.ApplicationPaths); } public async Task Validate(TunerHostInfo info) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs similarity index 71% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs rename to Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index ddbbda737..a33b0945b 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -15,19 +15,20 @@ using MediaBrowser.Model.System; using System.Globalization; using MediaBrowser.Controller.IO; -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Emby.Server.Implementations.LiveTv.TunerHosts { - public class HdHomerunHttpStream : LiveStream, IDirectStreamProvider + public class SharedHttpStream : LiveStream, IDirectStreamProvider { private readonly IHttpClient _httpClient; private readonly IServerApplicationHost _appHost; - public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, IEnvironmentInfo environment) + public SharedHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, IEnvironmentInfo environment) : base(mediaSource, environment, fileSystem, logger, appPaths) { _httpClient = httpClient; _appHost = appHost; OriginalStreamId = originalStreamId; + EnableStreamSharing = true; } public override async Task Open(CancellationToken openCancellationToken) @@ -40,7 +41,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath)); - Logger.Info("Opening HDHR Live stream from {0}", url); + var typeName = GetType().Name; + Logger.Info("Opening " + typeName + " Live stream from {0}", url); var response = await _httpClient.SendAsync(new HttpRequestOptions { @@ -51,11 +53,35 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun // Increase a little bit TimeoutMs = 30000, - EnableHttpCompression = false + EnableHttpCompression = false, + + LogResponse = true, + LogResponseHeaders = true }, "GET").ConfigureAwait(false); - Logger.Info("Opened HDHR stream from {0}", url); + var extension = "ts"; + var requiresRemux = false; + + var contentType = response.ContentType ?? string.Empty; + if (contentType.IndexOf("mp4", StringComparison.OrdinalIgnoreCase) != -1 || + contentType.IndexOf("matroska", StringComparison.OrdinalIgnoreCase) != -1 || + contentType.IndexOf("dash", StringComparison.OrdinalIgnoreCase) != -1 || + contentType.IndexOf("mpegURL", StringComparison.OrdinalIgnoreCase) != -1) + { + requiresRemux = true; + } + + // Close the stream without any sharing features + if (requiresRemux) + { + using (response) + { + return; + } + } + + SetTempFilePath(extension); var taskCompletionSource = new TaskCompletionSource(); StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token); @@ -90,7 +116,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { using (var stream = response.Content) { - Logger.Info("Beginning HdHomerunHttpStream stream to file"); + Logger.Info("Beginning {0} stream to {1}", GetType().Name, TempFilePath); using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None)) { diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 6152ea20b..a036a00a6 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -335,7 +335,8 @@ namespace MediaBrowser.Api.Library Fields = request.Fields, Id = request.Id, Limit = request.Limit, - UserId = request.UserId + UserId = request.UserId, + ImageTypeLimit = request.ImageTypeLimit }); } if (item is MusicAlbum) @@ -350,7 +351,8 @@ namespace MediaBrowser.Api.Library Id = request.Id, Limit = request.Limit, UserId = request.UserId, - ExcludeArtistIds = request.ExcludeArtistIds + ExcludeArtistIds = request.ExcludeArtistIds, + ImageTypeLimit = request.ImageTypeLimit }); } if (item is MusicArtist) @@ -364,7 +366,8 @@ namespace MediaBrowser.Api.Library Fields = request.Fields, Id = request.Id, Limit = request.Limit, - UserId = request.UserId + UserId = request.UserId, + ImageTypeLimit = request.ImageTypeLimit }); } @@ -381,7 +384,8 @@ namespace MediaBrowser.Api.Library Fields = request.Fields, Id = request.Id, Limit = request.Limit, - UserId = request.UserId + UserId = request.UserId, + ImageTypeLimit = request.ImageTypeLimit }); } @@ -396,7 +400,8 @@ namespace MediaBrowser.Api.Library Fields = request.Fields, Id = request.Id, Limit = request.Limit, - UserId = request.UserId + UserId = request.UserId, + ImageTypeLimit = request.ImageTypeLimit }); } diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs index 0a279fa9c..8f0b155f3 100644 --- a/MediaBrowser.Common/Net/HttpRequestOptions.cs +++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs @@ -93,6 +93,8 @@ namespace MediaBrowser.Common.Net public bool LogRequest { get; set; } public bool LogRequestAsDebug { get; set; } public bool LogErrors { get; set; } + public bool LogResponse { get; set; } + public bool LogResponseHeaders { get; set; } public bool LogErrorResponseBody { get; set; } public bool EnableKeepAlive { get; set; } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index fb283067f..504d03a27 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1421,6 +1421,16 @@ namespace MediaBrowser.Controller.Entities // Sweep through recursively and update status foreach (var item in itemsResult) { + if (item.IsVirtualItem) + { + // The querying doesn't support virtual unaired + var episode = item as Episode; + if (episode != null && episode.IsUnaired) + { + continue; + } + } + item.MarkPlayed(user, datePlayed, resetPosition); } } diff --git a/SharedVersion.cs b/SharedVersion.cs index c89077695..812369b17 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.36.8")] +[assembly: AssemblyVersion("3.2.36.9")] From 9f46122d91f54a277d455fe6579df2f6a23f480d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 15 Nov 2017 16:33:04 -0500 Subject: [PATCH 12/21] 3.2.36.10 --- .../MediaEncoding/IMediaEncoder.cs | 3 - .../Images/LocalImageProvider.cs | 104 ++++++++++++++---- .../Savers/BaseXmlSaver.cs | 14 ++- MediaBrowser.Providers/Manager/ImageSaver.cs | 14 ++- .../Savers/BaseNfoSaver.cs | 14 ++- SharedVersion.cs | 2 +- 6 files changed, 125 insertions(+), 26 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 31cd96c9a..2712380c7 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -118,9 +118,6 @@ namespace MediaBrowser.Controller.MediaEncoding void UpdateEncoderPath(string path, string pathType); bool SupportsEncoder(string encoder); - void SetLogFilename(string name); - void ClearLogFilename(); - string[] GetPlayableStreamFileNames(string path, VideoType videoType); IEnumerable GetPrimaryPlaylistVobFiles(string path, IIsoMount isoMount, uint? titleNumber); } diff --git a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs index 56a2c271f..2901f51d5 100644 --- a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs @@ -9,6 +9,7 @@ using System.Globalization; using System.Linq; using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; +using MediaBrowser.Controller.Entities.Movies; namespace MediaBrowser.LocalMetadata.Images { @@ -131,35 +132,91 @@ namespace MediaBrowser.LocalMetadata.Images PopulatePrimaryImages(item, images, files, imagePrefix, isInMixedFolder); - AddImage(files, images, "logo", imagePrefix, isInMixedFolder, ImageType.Logo); - AddImage(files, images, "clearart", imagePrefix, isInMixedFolder, ImageType.Art); + var added = false; + var isEpisode = item is Episode; + var isSong = item.GetType() == typeof(Audio); + var isGame = item is Game; + var isPerson = item is Person; + + // Logo + if (!isEpisode && !isSong && !isPerson) + { + added = AddImage(files, images, "logo", imagePrefix, isInMixedFolder, ImageType.Logo); + if (!added) + { + added = AddImage(files, images, "clearlogo", imagePrefix, isInMixedFolder, ImageType.Logo); + } + } + + // Art + if (!isEpisode && !isSong && !isPerson) + { + AddImage(files, images, "clearart", imagePrefix, isInMixedFolder, ImageType.Art); + } // For music albums, prefer cdart before disc if (item is MusicAlbum) { - AddImage(files, images, "cdart", imagePrefix, isInMixedFolder, ImageType.Disc); - AddImage(files, images, "disc", imagePrefix, isInMixedFolder, ImageType.Disc); + added = AddImage(files, images, "cdart", imagePrefix, isInMixedFolder, ImageType.Disc); + + if (!added) + { + added = AddImage(files, images, "disc", imagePrefix, isInMixedFolder, ImageType.Disc); + } } - else + else if (isGame || item is Video || item is BoxSet) { - AddImage(files, images, "disc", imagePrefix, isInMixedFolder, ImageType.Disc); - AddImage(files, images, "cdart", imagePrefix, isInMixedFolder, ImageType.Disc); + added = AddImage(files, images, "disc", imagePrefix, isInMixedFolder, ImageType.Disc); + + if (!added) + { + added = AddImage(files, images, "cdart", imagePrefix, isInMixedFolder, ImageType.Disc); + } + + if (!added) + { + added = AddImage(files, images, "discart", imagePrefix, isInMixedFolder, ImageType.Disc); + } } - AddImage(files, images, "box", imagePrefix, isInMixedFolder, ImageType.Box); - AddImage(files, images, "back", imagePrefix, isInMixedFolder, ImageType.BoxRear); - AddImage(files, images, "boxrear", imagePrefix, isInMixedFolder, ImageType.BoxRear); - AddImage(files, images, "menu", imagePrefix, isInMixedFolder, ImageType.Menu); + if (isGame) + { + AddImage(files, images, "box", imagePrefix, isInMixedFolder, ImageType.Box); + AddImage(files, images, "menu", imagePrefix, isInMixedFolder, ImageType.Menu); + + added = AddImage(files, images, "back", imagePrefix, isInMixedFolder, ImageType.BoxRear); + + if (!added) + { + added = AddImage(files, images, "boxrear", imagePrefix, isInMixedFolder, ImageType.BoxRear); + } + } // Banner - AddImage(files, images, "banner", imagePrefix, isInMixedFolder, ImageType.Banner); + if (!isEpisode && !isSong && !isPerson) + { + AddImage(files, images, "banner", imagePrefix, isInMixedFolder, ImageType.Banner); + } // Thumb - AddImage(files, images, "landscape", imagePrefix, isInMixedFolder, ImageType.Thumb); - AddImage(files, images, "thumb", imagePrefix, isInMixedFolder, ImageType.Thumb); + if (!isEpisode && !isSong && !isPerson) + { + added = AddImage(files, images, "landscape", imagePrefix, isInMixedFolder, ImageType.Thumb); + if (!added) + { + added = AddImage(files, images, "thumb", imagePrefix, isInMixedFolder, ImageType.Thumb); + } + } - PopulateBackdrops(item, images, files, imagePrefix, isInMixedFolder, directoryService); - PopulateScreenshots(images, files, imagePrefix, isInMixedFolder); + if (!isEpisode && !isSong && !isPerson) + { + PopulateBackdrops(item, images, files, imagePrefix, isInMixedFolder, directoryService); + } + + if (item is IHasScreenshots) + { + PopulateScreenshots(images, files, imagePrefix, isInMixedFolder); + } } private static readonly string[] CommonImageFileNames = new[] @@ -232,19 +289,28 @@ namespace MediaBrowser.LocalMetadata.Images var fileNameWithoutExtension = item.FileNameWithoutExtension; if (!string.IsNullOrEmpty(fileNameWithoutExtension)) { - AddImage(files, images, fileNameWithoutExtension, ImageType.Primary); + if (AddImage(files, images, fileNameWithoutExtension, ImageType.Primary)) + { + return; + } } foreach (var name in imageFileNames) { - AddImage(files, images, imagePrefix + name, ImageType.Primary); + if (AddImage(files, images, imagePrefix + name, ImageType.Primary)) + { + return; + } } if (!isInMixedFolder) { foreach (var name in imageFileNames) { - AddImage(files, images, name, ImageType.Primary); + if (AddImage(files, images, name, ImageType.Primary)) + { + return; + } } } } diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs index 259f42391..a3800e5c1 100644 --- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs @@ -226,7 +226,19 @@ namespace MediaBrowser.LocalMetadata.Savers if (wasHidden || ConfigurationManager.Configuration.SaveMetadataHidden) { - FileSystem.SetHidden(path, true); + SetHidden(path, true); + } + } + + private void SetHidden(string path, bool hidden) + { + try + { + FileSystem.SetHidden(path, hidden); + } + catch (Exception ex) + { + Logger.Error("Error setting hidden attribute on {0} - {1}", path, ex.Message); } } diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index 7fdbdbcc7..4bf5e9208 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -256,7 +256,7 @@ namespace MediaBrowser.Providers.Manager if (_config.Configuration.SaveMetadataHidden) { - _fileSystem.SetHidden(path, true); + SetHidden(path, true); } } finally @@ -266,6 +266,18 @@ namespace MediaBrowser.Providers.Manager } } + private void SetHidden(string path, bool hidden) + { + try + { + _fileSystem.SetHidden(path, hidden); + } + catch (Exception ex) + { + _logger.Error("Error setting hidden attribute on {0} - {1}", path, ex.Message); + } + } + /// /// Gets the save paths. /// diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index 41c376ddf..d32081458 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -229,7 +229,19 @@ namespace MediaBrowser.XbmcMetadata.Savers if (wasHidden || ConfigurationManager.Configuration.SaveMetadataHidden) { - FileSystem.SetHidden(path, true); + SetHidden(path, true); + } + } + + private void SetHidden(string path, bool hidden) + { + try + { + FileSystem.SetHidden(path, hidden); + } + catch (Exception ex) + { + Logger.Error("Error setting hidden attribute on {0} - {1}", path, ex.Message); } } diff --git a/SharedVersion.cs b/SharedVersion.cs index 812369b17..1e024ec44 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.36.9")] +[assembly: AssemblyVersion("3.2.36.10")] From b5ab7776f16196610f2b7d462e59c8851e5ebde2 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 16 Nov 2017 16:25:18 -0500 Subject: [PATCH 13/21] 3.2.36.11 --- .../Library/Resolvers/Audio/AudioResolver.cs | 259 +++++++++++++++--- .../Library/Resolvers/Movies/MovieResolver.cs | 37 +-- .../Library/Resolvers/VideoResolver.cs | 30 -- .../Localization/Core/es-MX.json | 2 +- .../Manager/MetadataService.cs | 2 +- .../ImageEncoderHelper.cs | 4 +- SharedVersion.cs | 2 +- 7 files changed, 248 insertions(+), 88 deletions(-) diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index e2f2946db..84b4492cc 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -4,19 +4,26 @@ using MediaBrowser.Model.Entities; using System; using MediaBrowser.Controller.Entities; using System.IO; +using System.Linq; +using MediaBrowser.Controller.Providers; +using System.Collections.Generic; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Extensions; +using Emby.Naming.Video; +using Emby.Naming.AudioBook; namespace Emby.Server.Implementations.Library.Resolvers.Audio { /// /// Class AudioResolver /// - public class AudioResolver : ItemResolver + public class AudioResolver : ItemResolver, IMultiItemResolver { - private readonly ILibraryManager _libraryManager; + private readonly ILibraryManager LibraryManager; public AudioResolver(ILibraryManager libraryManager) { - _libraryManager = libraryManager; + LibraryManager = libraryManager; } /// @@ -28,6 +35,37 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio get { return ResolverPriority.Last; } } + public MultiItemResolverResult ResolveMultiple(Folder parent, + List files, + string collectionType, + IDirectoryService directoryService) + { + var result = ResolveMultipleInternal(parent, files, collectionType, directoryService); + + if (result != null) + { + foreach (var item in result.Items) + { + SetInitialItemValues((MediaBrowser.Controller.Entities.Audio.Audio)item, null); + } + } + + return result; + } + + private MultiItemResolverResult ResolveMultipleInternal(Folder parent, + List files, + string collectionType, + IDirectoryService directoryService) + { + if (string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase)) + { + return ResolveMultipleAudio(parent, files, directoryService, false, collectionType, true); + } + + return null; + } + /// /// Resolves the specified args. /// @@ -37,46 +75,193 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio { // Return audio if the path is a file and has a matching extension - if (!args.IsDirectory) + var libraryOptions = args.GetLibraryOptions(); + var collectionType = args.GetCollectionType(); + + var isBooksCollectionType = string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase); + + if (args.IsDirectory) { - var libraryOptions = args.GetLibraryOptions(); - - if (_libraryManager.IsAudioFile(args.Path, libraryOptions)) + if (!isBooksCollectionType) { - if (string.Equals(Path.GetExtension(args.Path), ".cue", StringComparison.OrdinalIgnoreCase)) - { - // if audio file exists of same name, return null - - return null; - } - - var collectionType = args.GetCollectionType(); - - var isMixed = string.IsNullOrWhiteSpace(collectionType); - - // For conflicting extensions, give priority to videos - if (isMixed && _libraryManager.IsVideoFile(args.Path, libraryOptions)) - { - return null; - } - - var isStandalone = args.Parent == null; - - if (isStandalone || - string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase) || - isMixed) - { - return new MediaBrowser.Controller.Entities.Audio.Audio(); - } - - if (string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase)) - { - return new AudioBook(); - } + return null; } + + var files = args.FileSystemChildren + .Where(i => !LibraryManager.IgnoreFile(i, args.Parent)) + .ToList(); + + if (isBooksCollectionType) + { + return FindAudio(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false); + } + + return null; + } + + if (LibraryManager.IsAudioFile(args.Path, libraryOptions)) + { + if (string.Equals(Path.GetExtension(args.Path), ".cue", StringComparison.OrdinalIgnoreCase)) + { + // if audio file exists of same name, return null + return null; + } + + var isMixedCollectionType = string.IsNullOrWhiteSpace(collectionType); + + // For conflicting extensions, give priority to videos + if (isMixedCollectionType && LibraryManager.IsVideoFile(args.Path, libraryOptions)) + { + return null; + } + + MediaBrowser.Controller.Entities.Audio.Audio item = null; + + var isMusicCollectionType = string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase); + + // Use regular audio type for mixed libraries, owned items and music + if (isMixedCollectionType || + args.Parent == null || + isMusicCollectionType) + { + item = new MediaBrowser.Controller.Entities.Audio.Audio(); + } + + else if (isBooksCollectionType) + { + item = new AudioBook(); + } + + if (item != null) + { + item.IsInMixedFolder = true; + } + + return item; } return null; } + + private T FindAudio(ItemResolveArgs args, string path, Folder parent, List fileSystemEntries, IDirectoryService directoryService, string collectionType, bool parseName) + where T : MediaBrowser.Controller.Entities.Audio.Audio, new() + { + var multiDiscFolders = new List(); + + var libraryOptions = args.GetLibraryOptions(); + var filesFromOtherItems = new List(); + + // TODO: Allow GetMultiDiscMovie in here + var supportsMultiVersion = false; + + var result = ResolveMultipleAudio(parent, fileSystemEntries, directoryService, supportsMultiVersion, collectionType, parseName) ?? + new MultiItemResolverResult(); + + if (result.Items.Count == 1) + { + var videoPath = result.Items[0].Path; + + // If we were supporting this we'd be checking filesFromOtherItems + var hasOtherItems = false; + + if (!hasOtherItems) + { + var item = (T)result.Items[0]; + item.IsInMixedFolder = false; + item.Name = Path.GetFileName(item.ContainingFolderPath); + return item; + } + } + + if (result.Items.Count == 0 && multiDiscFolders.Count > 0) + { + //return GetMultiDiscAudio(multiDiscFolders, directoryService); + } + + return null; + } + + private MultiItemResolverResult ResolveMultipleAudio(Folder parent, IEnumerable fileSystemEntries, IDirectoryService directoryService, bool suppportMultiEditions, string collectionType, bool parseName) + where T : MediaBrowser.Controller.Entities.Audio.Audio, new() + { + var files = new List(); + var items = new List(); + var leftOver = new List(); + + // Loop through each child file/folder and see if we find a video + foreach (var child in fileSystemEntries) + { + if (child.IsDirectory) + { + leftOver.Add(child); + } + else if (IsIgnored(child.Name)) + { + + } + else + { + files.Add(child); + } + } + + var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions(); + + var resolver = new AudioBookListResolver(namingOptions); + var resolverResult = resolver.Resolve(files).ToList(); + + var result = new MultiItemResolverResult + { + ExtraFiles = leftOver, + Items = items + }; + + var isInMixedFolder = resolverResult.Count > 1 || (parent != null && parent.IsTopParent); + + foreach (var resolvedItem in resolverResult) + { + var firstMedia = resolvedItem.Files.First(); + + var libraryItem = new T + { + Path = firstMedia.Path, + IsInMixedFolder = isInMixedFolder, + //ProductionYear = resolvedItem.Year, + Name = parseName ? + resolvedItem.Name : + Path.GetFileNameWithoutExtension(firstMedia.Path), + //AdditionalParts = resolvedItem.Files.Skip(1).Select(i => i.Path).ToArray(), + //LocalAlternateVersions = resolvedItem.AlternateVersions.Select(i => i.Path).ToArray() + }; + + result.Items.Add(libraryItem); + } + + result.ExtraFiles.AddRange(files.Where(i => !ContainsFile(resolverResult, i))); + + return result; + } + + private bool ContainsFile(List result, FileSystemMetadata file) + { + return result.Any(i => ContainsFile(i, file)); + } + + private bool ContainsFile(AudioBookInfo result, FileSystemMetadata file) + { + return result.Files.Any(i => ContainsFile(i, file)) || + result.AlternateVersions.Any(i => ContainsFile(i, file)) || + result.Extras.Any(i => ContainsFile(i, file)); + } + + private bool ContainsFile(AudioBookFileInfo result, FileSystemMetadata file) + { + return string.Equals(result.Path, file.FullName, StringComparison.OrdinalIgnoreCase); + } + + private bool IsIgnored(string filename) + { + return false; + } } } diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index d69a2b240..667616414 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -202,14 +202,14 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies { var collectionType = args.GetCollectionType(); - if (IsInvalid(args.Parent, collectionType)) - { - return null; - } - // Find movies with their own folders if (args.IsDirectory) { + if (IsInvalid(args.Parent, collectionType)) + { + return null; + } + var files = args.FileSystemChildren .Where(i => !LibraryManager.IgnoreFile(i, args.Parent)) .ToList(); @@ -251,8 +251,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return null; } - // Owned items will be caught by the plain video resolver + // Handle owned items if (args.Parent == null) + { + return base.Resolve(args); + } + + if (IsInvalid(args.Parent, collectionType)) { return null; } @@ -528,6 +533,15 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return returnVideo; } + private string[] ValidCollectionTypes = new[] + { + CollectionType.Movies, + CollectionType.HomeVideos, + CollectionType.MusicVideos, + CollectionType.Movies, + CollectionType.Photos + }; + private bool IsInvalid(Folder parent, string collectionType) { if (parent != null) @@ -538,21 +552,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies } } - var validCollectionTypes = new[] - { - CollectionType.Movies, - CollectionType.HomeVideos, - CollectionType.MusicVideos, - CollectionType.Movies, - CollectionType.Photos - }; - if (string.IsNullOrWhiteSpace(collectionType)) { return false; } - return !validCollectionTypes.Contains(collectionType, StringComparer.OrdinalIgnoreCase); + return !ValidCollectionTypes.Contains(collectionType, StringComparer.OrdinalIgnoreCase); } private IImageProcessor _imageProcessor; diff --git a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs index 5c7a528f5..030ff88f7 100644 --- a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs @@ -5,36 +5,6 @@ using MediaBrowser.Model.IO; namespace Emby.Server.Implementations.Library.Resolvers { - /// - /// Resolves a Path into a Video - /// - public class VideoResolver : BaseVideoResolver