diff options
| author | Luke <luke.pulverenti@gmail.com> | 2016-07-27 15:32:07 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2016-07-27 15:32:07 -0400 |
| commit | 06b0cfb86fec5f1de83080f4fece2513dfa9cf6c (patch) | |
| tree | b94e74277d1b64d804acb12c6e4c482452381353 | |
| parent | b785e919f375d1b876d2e82e0e377db9b6cfbc71 (diff) | |
| parent | 897e0566294e854395f61040a8b922c1d0166930 (diff) | |
Merge pull request #1991 from MediaBrowser/beta
Beta
55 files changed, 2462 insertions, 407 deletions
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index f60a106da..ce3691095 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1669,7 +1669,7 @@ namespace MediaBrowser.Api.Playback RequestedUrl = url, UserAgent = Request.UserAgent }; - + //if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 || // (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 || // (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1) @@ -1770,6 +1770,19 @@ namespace MediaBrowser.Api.Playback { state.OutputVideoCodec = "copy"; } + else + { + // If the user doesn't have access to transcoding, then force stream copy, regardless of whether it will be compatible or not + var auth = AuthorizationContext.GetAuthorizationInfo(Request); + if (!string.IsNullOrWhiteSpace(auth.UserId)) + { + var user = UserManager.GetUserById(auth.UserId); + if (!user.Policy.EnableVideoPlaybackTranscoding) + { + state.OutputVideoCodec = "copy"; + } + } + } if (state.AudioStream != null && CanStreamCopyAudio(state, state.SupportedAudioCodecs)) { diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index f4ecf3693..7de309699 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -257,8 +257,7 @@ namespace MediaBrowser.Api.Playback.Hls return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false); } - // 256k - private const int BufferSize = 262144; + private const int BufferSize = 81920; private long GetStartPositionTicks(StreamState state, int requestedIndex) { @@ -942,17 +941,5 @@ namespace MediaBrowser.Api.Playback.Hls { return isOutputVideo ? ".ts" : ".ts"; } - - protected override bool CanStreamCopyVideo(StreamState state) - { - var isLiveStream = IsLiveStream(state); - - //if (!isLiveStream && Request.QueryString["AllowCustomSegmenting"] != "true") - //{ - // return false; - //} - - return base.CanStreamCopyVideo(state); - } } }
\ No newline at end of file diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 2d9cc40c0..0b989784c 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.MediaEncoding; namespace MediaBrowser.Api.Playback @@ -68,8 +69,9 @@ namespace MediaBrowser.Api.Playback private readonly IServerConfigurationManager _config; private readonly INetworkManager _networkManager; private readonly IMediaEncoder _mediaEncoder; + private readonly IUserManager _userManager; - public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, IServerConfigurationManager config, INetworkManager networkManager, IMediaEncoder mediaEncoder) + public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, IServerConfigurationManager config, INetworkManager networkManager, IMediaEncoder mediaEncoder, IUserManager userManager) { _mediaSourceManager = mediaSourceManager; _deviceManager = deviceManager; @@ -77,6 +79,7 @@ namespace MediaBrowser.Api.Playback _config = config; _networkManager = networkManager; _mediaEncoder = mediaEncoder; + _userManager = userManager; } public object Get(GetBitrateTestBytes request) @@ -119,7 +122,7 @@ namespace MediaBrowser.Api.Playback SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate, request.StartTimeTicks ?? 0, result.MediaSource.Id, request.AudioStreamIndex, - request.SubtitleStreamIndex, request.PlaySessionId); + request.SubtitleStreamIndex, request.PlaySessionId, request.UserId); } else { @@ -159,7 +162,7 @@ namespace MediaBrowser.Api.Playback { var mediaSourceId = request.MediaSourceId; - SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex); + SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex, request.UserId); } return ToOptimizedResult(info); @@ -221,13 +224,14 @@ namespace MediaBrowser.Api.Playback long startTimeTicks, string mediaSourceId, int? audioStreamIndex, - int? subtitleStreamIndex) + int? subtitleStreamIndex, + string userId) { var item = _libraryManager.GetItemById(itemId); foreach (var mediaSource in result.MediaSources) { - SetDeviceSpecificData(item, mediaSource, profile, auth, maxBitrate, startTimeTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, result.PlaySessionId); + SetDeviceSpecificData(item, mediaSource, profile, auth, maxBitrate, startTimeTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, result.PlaySessionId, userId); } SortMediaSources(result, maxBitrate); @@ -242,7 +246,8 @@ namespace MediaBrowser.Api.Playback string mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex, - string playSessionId) + string playSessionId, + string userId) { var streamBuilder = new StreamBuilder(_mediaEncoder, Logger); @@ -262,6 +267,8 @@ namespace MediaBrowser.Api.Playback options.SubtitleStreamIndex = subtitleStreamIndex; } + var user = _userManager.GetUserById(userId); + if (mediaSource.SupportsDirectPlay) { var supportsDirectStream = mediaSource.SupportsDirectStream; @@ -270,6 +277,14 @@ namespace MediaBrowser.Api.Playback mediaSource.SupportsDirectStream = true; options.MaxBitrate = maxBitrate; + if (item is Audio) + { + if (!user.Policy.EnableAudioPlaybackTranscoding) + { + options.ForceDirectPlay = true; + } + } + // The MediaSource supports direct stream, now test to see if the client supports it var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? streamBuilder.BuildAudioItem(options) : @@ -293,6 +308,14 @@ namespace MediaBrowser.Api.Playback { options.MaxBitrate = GetMaxBitrate(maxBitrate); + if (item is Audio) + { + if (!user.Policy.EnableAudioPlaybackTranscoding) + { + options.ForceDirectStream = true; + } + } + // The MediaSource supports direct stream, now test to see if the client supports it var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? streamBuilder.BuildAudioItem(options) : diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index d8b7ce2ef..d75b8947a 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -347,7 +347,7 @@ namespace MediaBrowser.Api.Playback.Progressive outputHeaders[item.Key] = item.Value; } - Func<Stream,Task> streamWriter = stream => new ProgressiveFileCopier(FileSystem, job, Logger).StreamFile(outputPath, stream); + Func<Stream,Task> streamWriter = stream => new ProgressiveFileCopier(FileSystem, job, Logger).StreamFile(outputPath, stream, CancellationToken.None); return ResultFactory.GetAsyncStreamWriter(streamWriter, outputHeaders); } diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs index bf543579f..860e57b32 100644 --- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs +++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs @@ -3,88 +3,12 @@ using ServiceStack.Web; using System; using System.Collections.Generic; using System.IO; +using System.Threading; using System.Threading.Tasks; using CommonIO; namespace MediaBrowser.Api.Playback.Progressive { - public class ProgressiveStreamWriter : IStreamWriter, IHasOptions - { - private string Path { get; set; } - private ILogger Logger { get; set; } - private readonly IFileSystem _fileSystem; - private readonly TranscodingJob _job; - - /// <summary> - /// The _options - /// </summary> - private readonly IDictionary<string, string> _options = new Dictionary<string, string>(); - /// <summary> - /// Gets the options. - /// </summary> - /// <value>The options.</value> - public IDictionary<string, string> Options - { - get { return _options; } - } - - /// <summary> - /// Initializes a new instance of the <see cref="ProgressiveStreamWriter" /> class. - /// </summary> - /// <param name="path">The path.</param> - /// <param name="logger">The logger.</param> - /// <param name="fileSystem">The file system.</param> - public ProgressiveStreamWriter(string path, ILogger logger, IFileSystem fileSystem, TranscodingJob job) - { - Path = path; - Logger = logger; - _fileSystem = fileSystem; - _job = job; - } - - /// <summary> - /// Writes to. - /// </summary> - /// <param name="responseStream">The response stream.</param> - public void WriteTo(Stream responseStream) - { - var task = WriteToAsync(responseStream); - Task.WaitAll(task); - } - - /// <summary> - /// Writes to. - /// </summary> - /// <param name="responseStream">The response stream.</param> - public async Task WriteToAsync(Stream responseStream) - { - try - { - await new ProgressiveFileCopier(_fileSystem, _job, Logger).StreamFile(Path, responseStream).ConfigureAwait(false); - } - catch (IOException) - { - // These error are always the same so don't dump the whole stack trace - Logger.Error("Error streaming media. The client has most likely disconnected or transcoding has failed."); - - throw; - } - catch (Exception ex) - { - Logger.ErrorException("Error streaming media. The client has most likely disconnected or transcoding has failed.", ex); - - throw; - } - finally - { - if (_job != null) - { - ApiEntryPoint.Instance.OnTranscodeEndRequest(_job); - } - } - } - } - public class ProgressiveFileCopier { private readonly IFileSystem _fileSystem; @@ -92,7 +16,7 @@ namespace MediaBrowser.Api.Playback.Progressive private readonly ILogger _logger; // 256k - private const int BufferSize = 262144; + private const int BufferSize = 81920; private long _bytesWritten = 0; @@ -103,22 +27,18 @@ namespace MediaBrowser.Api.Playback.Progressive _logger = logger; } - public async Task StreamFile(string path, Stream outputStream) + public async Task StreamFile(string path, Stream outputStream, CancellationToken cancellationToken) { var eofCount = 0; - long position = 0; using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) { while (eofCount < 15) { - await CopyToInternal(fs, outputStream, BufferSize).ConfigureAwait(false); - - var fsPosition = fs.Position; - - var bytesRead = fsPosition - position; + var bytesRead = await CopyToAsyncInternal(fs, outputStream, BufferSize, cancellationToken).ConfigureAwait(false); - //Logger.Debug("Streamed {0} bytes from file {1}", bytesRead, path); + //var position = fs.Position; + //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); if (bytesRead == 0) { @@ -126,57 +46,36 @@ namespace MediaBrowser.Api.Playback.Progressive { eofCount++; } - await Task.Delay(100).ConfigureAwait(false); + await Task.Delay(100, cancellationToken).ConfigureAwait(false); } else { eofCount = 0; } - - position = fsPosition; } } } - private async Task CopyToInternal(Stream source, Stream destination, int bufferSize) + private async Task<int> CopyToAsyncInternal(Stream source, Stream destination, Int32 bufferSize, CancellationToken cancellationToken) { - var array = new byte[bufferSize]; - int count; - while ((count = await source.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0) - { - //if (_job != null) - //{ - // var didPause = false; - // var totalPauseTime = 0; + byte[] buffer = new byte[bufferSize]; + int bytesRead; + int totalBytesRead = 0; - // if (_job.IsUserPaused) - // { - // _logger.Debug("Pausing writing to network stream while user has paused playback."); - - // while (_job.IsUserPaused && totalPauseTime < 30000) - // { - // didPause = true; - // var pauseTime = 500; - // totalPauseTime += pauseTime; - // await Task.Delay(pauseTime).ConfigureAwait(false); - // } - // } - - // if (didPause) - // { - // _logger.Debug("Resuming writing to network stream due to user unpausing playback."); - // } - //} - - await destination.WriteAsync(array, 0, count).ConfigureAwait(false); + while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) + { + await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); - _bytesWritten += count; + _bytesWritten += bytesRead; + totalBytesRead += bytesRead; if (_job != null) { _job.BytesDownloaded = Math.Max(_job.BytesDownloaded ?? _bytesWritten, _bytesWritten); } } + + return totalBytesRead; } } } diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj index 108eddcf9..ced2dd5a3 100644 --- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj +++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj @@ -55,7 +55,7 @@ <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath> </Reference> <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL"> - <HintPath>..\packages\NLog.4.3.5\lib\net45\NLog.dll</HintPath> + <HintPath>..\packages\NLog.4.3.6\lib\net45\NLog.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="Patterns.Logging"> diff --git a/MediaBrowser.Common.Implementations/packages.config b/MediaBrowser.Common.Implementations/packages.config index 882acc9ff..594b4c7c5 100644 --- a/MediaBrowser.Common.Implementations/packages.config +++ b/MediaBrowser.Common.Implementations/packages.config @@ -2,7 +2,7 @@ <packages> <package id="CommonIO" version="1.0.0.9" targetFramework="net45" /> <package id="morelinq" version="1.4.0" targetFramework="net45" /> - <package id="NLog" version="4.3.5" targetFramework="net45" /> + <package id="NLog" version="4.3.6" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> <package id="SimpleInjector" version="3.2.0" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.Common/IO/StreamDefaults.cs b/MediaBrowser.Common/IO/StreamDefaults.cs index 450d293d4..8b16d89b3 100644 --- a/MediaBrowser.Common/IO/StreamDefaults.cs +++ b/MediaBrowser.Common/IO/StreamDefaults.cs @@ -9,11 +9,11 @@ namespace MediaBrowser.Common.IO /// <summary> /// The default copy to buffer size /// </summary> - public const int DefaultCopyToBufferSize = 262144; + public const int DefaultCopyToBufferSize = 81920; /// <summary> /// The default file stream buffer size /// </summary> - public const int DefaultFileStreamBufferSize = 262144; + public const int DefaultFileStreamBufferSize = 81920; } } diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index b3df34c4d..1897511af 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -46,6 +46,12 @@ namespace MediaBrowser.Controller.Entities.Audio } } + [IgnoreDataMember] + public override bool EnableForceSaveOnDateModifiedChange + { + get { return true; } + } + public Audio() { Artists = new List<string>(); diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 0860cb61c..c7a6b75ff 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -455,7 +455,10 @@ namespace MediaBrowser.Controller.Entities public DateTime DateLastRefreshed { get; set; } [IgnoreDataMember] - public DateTime? DateModifiedDuringLastRefresh { get; set; } + public virtual bool EnableForceSaveOnDateModifiedChange + { + get { return false; } + } /// <summary> /// The logger diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs index 2ca56bc70..59ab95437 100644 --- a/MediaBrowser.Controller/Entities/Book.cs +++ b/MediaBrowser.Controller/Entities/Book.cs @@ -34,6 +34,12 @@ namespace MediaBrowser.Controller.Entities return SeriesName; } + [IgnoreDataMember] + public override bool EnableForceSaveOnDateModifiedChange + { + get { return true; } + } + public Guid? FindSeriesId() { return SeriesId; diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 89b9479e7..0397e9a88 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -373,13 +373,6 @@ namespace MediaBrowser.Controller.Entities if (currentChildren.TryGetValue(child.Id, out currentChild) && IsValidFromResolver(currentChild, child)) { - var currentChildLocationType = currentChild.LocationType; - if (currentChildLocationType != LocationType.Remote && - currentChildLocationType != LocationType.Virtual) - { - currentChild.DateModified = child.DateModified; - } - await UpdateIsOffline(currentChild, false).ConfigureAwait(false); validChildren.Add(currentChild); diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs index 317c71529..54386a179 100644 --- a/MediaBrowser.Controller/Entities/Game.cs +++ b/MediaBrowser.Controller/Entities/Game.cs @@ -4,6 +4,7 @@ using MediaBrowser.Model.Entities; using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; namespace MediaBrowser.Controller.Entities { @@ -32,6 +33,12 @@ namespace MediaBrowser.Controller.Entities locationType != LocationType.Virtual; } + [IgnoreDataMember] + public override bool EnableForceSaveOnDateModifiedChange + { + get { return true; } + } + /// <summary> /// Gets or sets the remote trailers. /// </summary> @@ -42,6 +49,7 @@ namespace MediaBrowser.Controller.Entities /// Gets the type of the media. /// </summary> /// <value>The type of the media.</value> + [IgnoreDataMember] public override string MediaType { get { return Model.Entities.MediaType.Game; } diff --git a/MediaBrowser.Controller/Entities/IHasImages.cs b/MediaBrowser.Controller/Entities/IHasImages.cs index 897250caa..226748098 100644 --- a/MediaBrowser.Controller/Entities/IHasImages.cs +++ b/MediaBrowser.Controller/Entities/IHasImages.cs @@ -207,8 +207,6 @@ namespace MediaBrowser.Controller.Entities /// <param name="image">The image.</param> /// <param name="index">The index.</param> void SetImage(ItemImageInfo image, int index); - - DateTime? DateModifiedDuringLastRefresh { get; set; } } public static class HasImagesExtensions diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs index c7940c8a9..378c4a390 100644 --- a/MediaBrowser.Controller/Entities/IHasMetadata.cs +++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.Controller.Entities /// Gets the date modified. /// </summary> /// <value>The date modified.</value> - DateTime DateModified { get; } + DateTime DateModified { get; set; } /// <summary> /// Gets or sets the date last saved. @@ -51,5 +51,7 @@ namespace MediaBrowser.Controller.Entities bool SupportsPeople { get; } bool RequiresRefresh(); + + bool EnableForceSaveOnDateModifiedChange { get; } } } diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs index de756563d..804ea04a5 100644 --- a/MediaBrowser.Controller/Entities/Photo.cs +++ b/MediaBrowser.Controller/Entities/Photo.cs @@ -51,6 +51,12 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public override bool EnableForceSaveOnDateModifiedChange + { + get { return true; } + } + public override bool CanDownload() { return true; diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 73c893dd6..eba1e466a 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -58,6 +58,12 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public override bool EnableForceSaveOnDateModifiedChange + { + get { return true; } + } + public int? TotalBitrate { get; set; } public ExtraType? ExtraType { get; set; } diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs index c057c9707..e7e3323c2 100644 --- a/MediaBrowser.Controller/Providers/DirectoryService.cs +++ b/MediaBrowser.Controller/Providers/DirectoryService.cs @@ -102,6 +102,12 @@ namespace MediaBrowser.Controller.Providers { var directory = Path.GetDirectoryName(path); + if (string.IsNullOrWhiteSpace(directory)) + { + _logger.Debug("Parent path is null for {0}", path); + return null; + } + var dict = GetFileSystemDictionary(directory, false); FileSystemMetadata entry; diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index 862d95f7e..7a9589c98 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -346,8 +346,8 @@ <Compile Include="..\MediaBrowser.Model\Dlna\HttpHeaderInfo.cs"> <Link>Dlna\HttpHeaderInfo.cs</Link> </Compile> - <Compile Include="..\MediaBrowser.Model\Dlna\ILocalPlayer.cs"> - <Link>Dlna\ILocalPlayer.cs</Link> + <Compile Include="..\MediaBrowser.Model\Dlna\ITranscoderSupport.cs"> + <Link>Dlna\ITranscoderSupport.cs</Link> </Compile> <Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfile.cs"> <Link>Dlna\MediaFormatProfile.cs</Link> @@ -355,9 +355,6 @@ <Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfileResolver.cs"> <Link>Dlna\MediaFormatProfileResolver.cs</Link> </Compile> - <Compile Include="..\MediaBrowser.Model\Dlna\NullLocalPlayer.cs"> - <Link>Dlna\NullLocalPlayer.cs</Link> - </Compile> <Compile Include="..\MediaBrowser.Model\Dlna\PlaybackErrorCode.cs"> <Link>Dlna\PlaybackErrorCode.cs</Link> </Compile> diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 953e5b7be..420b536ae 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -318,8 +318,8 @@ <Compile Include="..\MediaBrowser.Model\Dlna\HttpHeaderInfo.cs"> <Link>Dlna\HttpHeaderInfo.cs</Link> </Compile> - <Compile Include="..\MediaBrowser.Model\Dlna\ILocalPlayer.cs"> - <Link>Dlna\ILocalPlayer.cs</Link> + <Compile Include="..\MediaBrowser.Model\Dlna\ITranscoderSupport.cs"> + <Link>Dlna\ITranscoderSupport.cs</Link> </Compile> <Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfile.cs"> <Link>Dlna\MediaFormatProfile.cs</Link> @@ -327,9 +327,6 @@ <Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfileResolver.cs"> <Link>Dlna\MediaFormatProfileResolver.cs</Link> </Compile> - <Compile Include="..\MediaBrowser.Model\Dlna\NullLocalPlayer.cs"> - <Link>Dlna\NullLocalPlayer.cs</Link> - </Compile> <Compile Include="..\MediaBrowser.Model\Dlna\PlaybackErrorCode.cs"> <Link>Dlna\PlaybackErrorCode.cs</Link> </Compile> diff --git a/MediaBrowser.Model/Dlna/AudioOptions.cs b/MediaBrowser.Model/Dlna/AudioOptions.cs index 162b88c98..c208e8ab0 100644 --- a/MediaBrowser.Model/Dlna/AudioOptions.cs +++ b/MediaBrowser.Model/Dlna/AudioOptions.cs @@ -18,6 +18,8 @@ namespace MediaBrowser.Model.Dlna public bool EnableDirectPlay { get; set; } public bool EnableDirectStream { get; set; } + public bool ForceDirectPlay { get; set; } + public bool ForceDirectStream { get; set; } public string ItemId { get; set; } public List<MediaSourceInfo> MediaSources { get; set; } diff --git a/MediaBrowser.Model/Dlna/ILocalPlayer.cs b/MediaBrowser.Model/Dlna/ILocalPlayer.cs deleted file mode 100644 index 9de360023..000000000 --- a/MediaBrowser.Model/Dlna/ILocalPlayer.cs +++ /dev/null @@ -1,39 +0,0 @@ - -namespace MediaBrowser.Model.Dlna -{ - public interface ILocalPlayer - { - /// <summary> - /// Determines whether this instance [can access file] the specified path. - /// </summary> - /// <param name="path">The path.</param> - /// <returns><c>true</c> if this instance [can access file] the specified path; otherwise, <c>false</c>.</returns> - bool CanAccessFile(string path); - /// <summary> - /// Determines whether this instance [can access directory] the specified path. - /// </summary> - /// <param name="path">The path.</param> - /// <returns><c>true</c> if this instance [can access directory] the specified path; otherwise, <c>false</c>.</returns> - bool CanAccessDirectory(string path); - /// <summary> - /// Determines whether this instance [can access URL] the specified URL. - /// </summary> - /// <param name="url">The URL.</param> - /// <param name="requiresCustomRequestHeaders">if set to <c>true</c> [requires custom request headers].</param> - /// <returns><c>true</c> if this instance [can access URL] the specified URL; otherwise, <c>false</c>.</returns> - bool CanAccessUrl(string url, bool requiresCustomRequestHeaders); - } - - public interface ITranscoderSupport - { - bool CanEncodeToAudioCodec(string codec); - } - - public class FullTranscoderSupport : ITranscoderSupport - { - public bool CanEncodeToAudioCodec(string codec) - { - return true; - } - } -} diff --git a/MediaBrowser.Model/Dlna/ITranscoderSupport.cs b/MediaBrowser.Model/Dlna/ITranscoderSupport.cs new file mode 100644 index 000000000..0dac23403 --- /dev/null +++ b/MediaBrowser.Model/Dlna/ITranscoderSupport.cs @@ -0,0 +1,15 @@ +namespace MediaBrowser.Model.Dlna +{ + public interface ITranscoderSupport + { + bool CanEncodeToAudioCodec(string codec); + } + + public class FullTranscoderSupport : ITranscoderSupport + { + public bool CanEncodeToAudioCodec(string codec) + { + return true; + } + } +} diff --git a/MediaBrowser.Model/Dlna/NullLocalPlayer.cs b/MediaBrowser.Model/Dlna/NullLocalPlayer.cs deleted file mode 100644 index c34b63887..000000000 --- a/MediaBrowser.Model/Dlna/NullLocalPlayer.cs +++ /dev/null @@ -1,21 +0,0 @@ - -namespace MediaBrowser.Model.Dlna -{ - public class NullLocalPlayer : ILocalPlayer - { - public bool CanAccessFile(string path) - { - return false; - } - - public bool CanAccessDirectory(string path) - { - return false; - } - - public bool CanAccessUrl(string url, bool requiresCustomRequestHeaders) - { - return false; - } - } -} diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 2863eba2e..c05ca4187 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -11,29 +11,17 @@ namespace MediaBrowser.Model.Dlna { public class StreamBuilder { - private readonly ILocalPlayer _localPlayer; private readonly ILogger _logger; private readonly ITranscoderSupport _transcoderSupport; - public StreamBuilder(ILocalPlayer localPlayer, ITranscoderSupport transcoderSupport, ILogger logger) + public StreamBuilder(ITranscoderSupport transcoderSupport, ILogger logger) { _transcoderSupport = transcoderSupport; - _localPlayer = localPlayer; _logger = logger; } - public StreamBuilder(ITranscoderSupport transcoderSupport, ILogger logger) - : this(new NullLocalPlayer(), transcoderSupport, logger) - { - } - - public StreamBuilder(ILocalPlayer localPlayer, ILogger logger) - : this(localPlayer, new FullTranscoderSupport(), logger) - { - } - public StreamBuilder(ILogger logger) - : this(new NullLocalPlayer(), new FullTranscoderSupport(), logger) + : this(new FullTranscoderSupport(), logger) { } @@ -127,6 +115,20 @@ namespace MediaBrowser.Model.Dlna DeviceProfile = options.Profile }; + if (options.ForceDirectPlay) + { + playlistItem.PlayMethod = PlayMethod.DirectPlay; + playlistItem.Container = item.Container; + return playlistItem; + } + + if (options.ForceDirectStream) + { + playlistItem.PlayMethod = PlayMethod.DirectStream; + playlistItem.Container = item.Container; + return playlistItem; + } + MediaStream audioStream = item.GetDefaultAudioStream(null); List<PlayMethod> directPlayMethods = GetAudioDirectPlayMethods(item, audioStream, options); @@ -182,19 +184,7 @@ namespace MediaBrowser.Model.Dlna if (all) { - if (item.Protocol == MediaProtocol.File && - directPlayMethods.Contains(PlayMethod.DirectPlay) && - _localPlayer.CanAccessFile(item.Path)) - { - playlistItem.PlayMethod = PlayMethod.DirectPlay; - } - else if (item.Protocol == MediaProtocol.Http && - directPlayMethods.Contains(PlayMethod.DirectPlay) && - _localPlayer.CanAccessUrl(item.Path, item.RequiredHttpHeaders.Count > 0)) - { - playlistItem.PlayMethod = PlayMethod.DirectPlay; - } - else if (directPlayMethods.Contains(PlayMethod.DirectStream)) + if (directPlayMethods.Contains(PlayMethod.DirectStream)) { playlistItem.PlayMethod = PlayMethod.DirectStream; } @@ -413,8 +403,8 @@ namespace MediaBrowser.Model.Dlna MediaStream videoStream = item.VideoStream; // TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough - bool isEligibleForDirectPlay = options.EnableDirectPlay && IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options), subtitleStream, options, PlayMethod.DirectPlay); - bool isEligibleForDirectStream = options.EnableDirectStream && IsEligibleForDirectPlay(item, options.GetMaxBitrate(), subtitleStream, options, PlayMethod.DirectStream); + bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options), subtitleStream, options, PlayMethod.DirectPlay)); + bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || IsEligibleForDirectPlay(item, options.GetMaxBitrate(), subtitleStream, options, PlayMethod.DirectStream)); _logger.Info("Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}", options.Profile.Name ?? "Unknown Profile", @@ -425,7 +415,7 @@ namespace MediaBrowser.Model.Dlna if (isEligibleForDirectPlay || isEligibleForDirectStream) { // See if it can be direct played - PlayMethod? directPlay = GetVideoDirectPlayProfile(options.Profile, item, videoStream, audioStream, isEligibleForDirectPlay, isEligibleForDirectStream); + PlayMethod? directPlay = GetVideoDirectPlayProfile(options, item, videoStream, audioStream, isEligibleForDirectPlay, isEligibleForDirectStream); if (directPlay != null) { @@ -645,13 +635,24 @@ namespace MediaBrowser.Model.Dlna return Math.Min(defaultBitrate, encoderAudioBitrateLimit); } - private PlayMethod? GetVideoDirectPlayProfile(DeviceProfile profile, + private PlayMethod? GetVideoDirectPlayProfile(VideoOptions options, MediaSourceInfo mediaSource, MediaStream videoStream, MediaStream audioStream, bool isEligibleForDirectPlay, bool isEligibleForDirectStream) { + DeviceProfile profile = options.Profile; + + if (options.ForceDirectPlay) + { + return PlayMethod.DirectPlay; + } + if (options.ForceDirectStream) + { + return PlayMethod.DirectStream; + } + if (videoStream == null) { _logger.Info("Profile: {0}, Cannot direct stream with no known video stream. Path: {1}", @@ -829,25 +830,6 @@ namespace MediaBrowser.Model.Dlna } } - if (isEligibleForDirectPlay && mediaSource.SupportsDirectPlay) - { - if (mediaSource.Protocol == MediaProtocol.Http) - { - if (_localPlayer.CanAccessUrl(mediaSource.Path, mediaSource.RequiredHttpHeaders.Count > 0)) - { - return PlayMethod.DirectPlay; - } - } - - else if (mediaSource.Protocol == MediaProtocol.File) - { - if (_localPlayer.CanAccessFile(mediaSource.Path)) - { - return PlayMethod.DirectPlay; - } - } - } - if (isEligibleForDirectStream && mediaSource.SupportsDirectStream) { return PlayMethod.DirectStream; diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 931b2ebcb..e3c1e52a5 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -118,9 +118,8 @@ <Compile Include="Devices\DeviceInfo.cs" /> <Compile Include="Devices\DevicesOptions.cs" /> <Compile Include="Dlna\EncodingContext.cs" /> - <Compile Include="Dlna\ILocalPlayer.cs" /> + <Compile Include="Dlna\ITranscoderSupport.cs" /> <Compile Include="Dlna\StreamInfoSorter.cs" /> - <Compile Include="Dlna\NullLocalPlayer.cs" /> <Compile Include="Dlna\PlaybackErrorCode.cs" /> <Compile Include="Dlna\PlaybackException.cs" /> <Compile Include="Dlna\ResolutionConfiguration.cs" /> diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 678d495cb..9776c4e2f 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -143,6 +143,22 @@ namespace MediaBrowser.Providers.Manager var beforeSaveResult = await BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh, updateType).ConfigureAwait(false); updateType = updateType | beforeSaveResult; + if (item.LocationType == LocationType.FileSystem) + { + var file = refreshOptions.DirectoryService.GetFile(item.Path); + if (file != null) + { + var fileLastWriteTime = file.LastWriteTimeUtc; + if (item.EnableForceSaveOnDateModifiedChange && fileLastWriteTime != item.DateModified) + { + Logger.Debug("Date modified for {0}. Old date {1} new date {2} Id {3}", item.Path, item.DateModified, fileLastWriteTime, item.Id); + requiresRefresh = true; + } + + item.DateModified = fileLastWriteTime; + } + } + // Save if changes were made, or it's never been saved before if (refreshOptions.ForceSave || updateType > ItemUpdateType.None || isFirstRefresh || refreshOptions.ReplaceAllMetadata || requiresRefresh) { @@ -155,12 +171,10 @@ namespace MediaBrowser.Providers.Manager if (hasRefreshedMetadata && hasRefreshedImages) { item.DateLastRefreshed = DateTime.UtcNow; - item.DateModifiedDuringLastRefresh = item.DateModified; } else { item.DateLastRefreshed = default(DateTime); - item.DateModifiedDuringLastRefresh = null; } // Save to database diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index bec9280b3..6c7918988 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -167,9 +167,10 @@ namespace MediaBrowser.Providers.MediaInfo public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) { - if (item.DateModifiedDuringLastRefresh.HasValue) + var file = directoryService.GetFile(item.Path); + if (file != null && file.LastWriteTimeUtc != item.DateModified) { - return item.DateModifiedDuringLastRefresh.Value != item.DateModified; + return true; } return false; diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index b89b0b925..11280cff2 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -171,12 +171,10 @@ namespace MediaBrowser.Providers.MediaInfo public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) { - if (item.DateModifiedDuringLastRefresh.HasValue) + var file = directoryService.GetFile(item.Path); + if (file != null && file.LastWriteTimeUtc != item.DateModified) { - if (item.DateModifiedDuringLastRefresh.Value != item.DateModified) - { - return true; - } + return true; } if (item.SupportsLocalMetadata) diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs index 9a1f072cd..5fc363062 100644 --- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs @@ -195,12 +195,10 @@ namespace MediaBrowser.Providers.MediaInfo public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) { - if (item.DateModifiedDuringLastRefresh.HasValue) + var file = directoryService.GetFile(item.Path); + if (file != null && file.LastWriteTimeUtc != item.DateModified) { - if (item.DateModifiedDuringLastRefresh.Value != item.DateModified) - { - return true; - } + return true; } return false; diff --git a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs index a6b6de7e5..1bf4ed6c0 100644 --- a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs +++ b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs @@ -199,7 +199,6 @@ namespace MediaBrowser.Providers.Movies var ourRelease = releases.FirstOrDefault(c => c.iso_3166_1.Equals(preferredCountryCode, StringComparison.OrdinalIgnoreCase)); var usRelease = releases.FirstOrDefault(c => c.iso_3166_1.Equals("US", StringComparison.OrdinalIgnoreCase)); - var minimunRelease = releases.OrderBy(c => c.release_date).FirstOrDefault(); if (ourRelease != null) { @@ -210,10 +209,6 @@ namespace MediaBrowser.Providers.Movies { movie.OfficialRating = usRelease.certification; } - else if (minimunRelease != null) - { - movie.OfficialRating = minimunRelease.iso_3166_1 + "-" + minimunRelease.certification; - } } if (!string.IsNullOrWhiteSpace(movieData.release_date)) diff --git a/MediaBrowser.Providers/Photos/PhotoProvider.cs b/MediaBrowser.Providers/Photos/PhotoProvider.cs index 882363b2f..619b72636 100644 --- a/MediaBrowser.Providers/Photos/PhotoProvider.cs +++ b/MediaBrowser.Providers/Photos/PhotoProvider.cs @@ -154,9 +154,10 @@ namespace MediaBrowser.Providers.Photos public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) { - if (item.DateModifiedDuringLastRefresh.HasValue) + var file = directoryService.GetFile(item.Path); + if (file != null && file.LastWriteTimeUtc != item.DateModified) { - return item.DateModifiedDuringLastRefresh.Value != item.DateModified; + return true; } return false; diff --git a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs index a15de4866..b51b11380 100644 --- a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs +++ b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Configuration; +using System; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -6,12 +7,42 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; +using System.Threading.Tasks; using CommonIO; namespace MediaBrowser.Providers.TV { public class EpisodeMetadataService : MetadataService<Episode, EpisodeInfo> { + protected override async Task<ItemUpdateType> BeforeSave(Episode item, bool isFullRefresh, ItemUpdateType currentUpdateType) + { + var updateType = await base.BeforeSave(item, isFullRefresh, currentUpdateType).ConfigureAwait(false); + + if (updateType <= ItemUpdateType.None) + { + if (!string.Equals(item.SeriesName, item.FindSeriesName(), StringComparison.Ordinal)) + { + updateType |= ItemUpdateType.MetadataImport; + } + } + if (updateType <= ItemUpdateType.None) + { + if (!string.Equals(item.SeriesSortName, item.FindSeriesSortName(), StringComparison.Ordinal)) + { + updateType |= ItemUpdateType.MetadataImport; + } + } + if (updateType <= ItemUpdateType.None) + { + if (!string.Equals(item.SeasonName, item.FindSeasonName(), StringComparison.Ordinal)) + { + updateType |= ItemUpdateType.MetadataImport; + } + } + + return updateType; + } + protected override void MergeData(MetadataResult<Episode> source, MetadataResult<Episode> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); diff --git a/MediaBrowser.Providers/TV/SeasonMetadataService.cs b/MediaBrowser.Providers/TV/SeasonMetadataService.cs index e4894915d..f3e6f8e9c 100644 --- a/MediaBrowser.Providers/TV/SeasonMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeasonMetadataService.cs @@ -35,6 +35,21 @@ namespace MediaBrowser.Providers.TV updateType |= SaveIsVirtualItem(item, episodes); } + if (updateType <= ItemUpdateType.None) + { + if (!string.Equals(item.SeriesName, item.FindSeriesName(), StringComparison.Ordinal)) + { + updateType |= ItemUpdateType.MetadataImport; + } + } + if (updateType <= ItemUpdateType.None) + { + if (!string.Equals(item.SeriesSortName, item.FindSeriesSortName(), StringComparison.Ordinal)) + { + updateType |= ItemUpdateType.MetadataImport; + } + } + return updateType; } diff --git a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs b/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs index 7ac92408b..71cd20743 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs @@ -28,8 +28,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer public Action OnComplete { get; set; } private readonly ILogger _logger; - // 256k - private const int BufferSize = 262144; + private const int BufferSize = 81920; /// <summary> /// The _options diff --git a/MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs b/MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs index f5906f6b7..ae408f8d6 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs @@ -75,8 +75,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer { } - // 256k - private const int BufferSize = 262144; + private const int BufferSize = 81920; /// <summary> /// Writes to. diff --git a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs index 60e7e2df3..9f949db92 100644 --- a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs +++ b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs @@ -44,7 +44,6 @@ namespace MediaBrowser.Server.Implementations.Library // Make sure DateCreated and DateModified have values var fileInfo = directoryService.GetFile(item.Path); - item.DateModified = fileSystem.GetLastWriteTimeUtc(fileInfo); SetDateCreated(item, fileSystem, fileInfo); EnsureName(item, fileInfo); @@ -80,7 +79,7 @@ namespace MediaBrowser.Server.Implementations.Library item.GetParents().Any(i => i.IsLocked); // Make sure DateCreated and DateModified have values - EnsureDates(fileSystem, item, args, true); + EnsureDates(fileSystem, item, args); } /// <summary> @@ -125,8 +124,7 @@ namespace MediaBrowser.Server.Implementations.Library /// <param name="fileSystem">The file system.</param> /// <param name="item">The item.</param> /// <param name="args">The args.</param> - /// <param name="includeCreationTime">if set to <c>true</c> [include creation time].</param> - private static void EnsureDates(IFileSystem fileSystem, BaseItem item, ItemResolveArgs args, bool includeCreationTime) + private static void EnsureDates(IFileSystem fileSystem, BaseItem item, ItemResolveArgs args) { if (fileSystem == null) { @@ -148,12 +146,7 @@ namespace MediaBrowser.Server.Implementations.Library if (childData != null) { - if (includeCreationTime) - { - SetDateCreated(item, fileSystem, childData); - } - - item.DateModified = fileSystem.GetLastWriteTimeUtc(childData); + SetDateCreated(item, fileSystem, childData); } else { @@ -161,21 +154,13 @@ namespace MediaBrowser.Server.Implementations.Library if (fileData.Exists) { - if (includeCreationTime) - { - SetDateCreated(item, fileSystem, fileData); - } - item.DateModified = fileSystem.GetLastWriteTimeUtc(fileData); + SetDateCreated(item, fileSystem, fileData); } } } else { - if (includeCreationTime) - { - SetDateCreated(item, fileSystem, args.FileInfo); - } - item.DateModified = fileSystem.GetLastWriteTimeUtc(args.FileInfo); + SetDateCreated(item, fileSystem, args.FileInfo); } } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 0f91d5285..2c4c74b35 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -56,8 +56,8 @@ <Reference Include="Interfaces.IO"> <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath> </Reference> - <Reference Include="MediaBrowser.Naming, Version=1.0.6046.32295, Culture=neutral, processorArchitecture=MSIL"> - <HintPath>..\packages\MediaBrowser.Naming.1.0.0.53\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath> + <Reference Include="MediaBrowser.Naming, Version=1.0.6046.41603, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\MediaBrowser.Naming.1.0.0.54\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="MoreLinq"> diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 7f556cfb0..63dd29e0d 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -257,7 +257,6 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection.AddColumn(Logger, "TypedBaseItems", "TrailerTypes", "Text"); _connection.AddColumn(Logger, "TypedBaseItems", "CriticRating", "Float"); _connection.AddColumn(Logger, "TypedBaseItems", "CriticRatingSummary", "Text"); - _connection.AddColumn(Logger, "TypedBaseItems", "DateModifiedDuringLastRefresh", "DATETIME"); _connection.AddColumn(Logger, "TypedBaseItems", "InheritedTags", "Text"); _connection.AddColumn(Logger, "TypedBaseItems", "CleanName", "Text"); _connection.AddColumn(Logger, "TypedBaseItems", "PresentationUniqueKey", "Text"); @@ -402,7 +401,6 @@ namespace MediaBrowser.Server.Implementations.Persistence "Tags", "SourceType", "TrailerTypes", - "DateModifiedDuringLastRefresh", "OriginalTitle", "PrimaryVersionId", "DateLastMediaAdded", @@ -523,7 +521,6 @@ namespace MediaBrowser.Server.Implementations.Persistence "TrailerTypes", "CriticRating", "CriticRatingSummary", - "DateModifiedDuringLastRefresh", "InheritedTags", "CleanName", "PresentationUniqueKey", @@ -902,15 +899,6 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveItemCommand.GetParameter(index++).Value = item.CriticRating; _saveItemCommand.GetParameter(index++).Value = item.CriticRatingSummary; - if (!item.DateModifiedDuringLastRefresh.HasValue || item.DateModifiedDuringLastRefresh.Value == default(DateTime)) - { - _saveItemCommand.GetParameter(index++).Value = null; - } - else - { - _saveItemCommand.GetParameter(index++).Value = item.DateModifiedDuringLastRefresh.Value; - } - var inheritedTags = item.GetInheritedTags(); if (inheritedTags.Count > 0) { @@ -1370,88 +1358,101 @@ namespace MediaBrowser.Server.Implementations.Persistence } } - if (!reader.IsDBNull(51)) - { - item.DateModifiedDuringLastRefresh = reader.GetDateTime(51).ToUniversalTime(); - } + var index = 51; - if (!reader.IsDBNull(52)) + if (!reader.IsDBNull(index)) { - item.OriginalTitle = reader.GetString(52); + item.OriginalTitle = reader.GetString(index); } + index++; var video = item as Video; if (video != null) { - if (!reader.IsDBNull(53)) + if (!reader.IsDBNull(index)) { - video.PrimaryVersionId = reader.GetString(53); + video.PrimaryVersionId = reader.GetString(index); } } + index++; var folder = item as Folder; - if (folder != null && !reader.IsDBNull(54)) + if (folder != null && !reader.IsDBNull(index)) { - folder.DateLastMediaAdded = reader.GetDateTime(54).ToUniversalTime(); + folder.DateLastMediaAdded = reader.GetDateTime(index).ToUniversalTime(); } + index++; - if (!reader.IsDBNull(55)) + if (!reader.IsDBNull(index)) { - item.Album = reader.GetString(55); + item.Album = reader.GetString(index); } + index++; - if (!reader.IsDBNull(56)) + if (!reader.IsDBNull(index)) { - item.CriticRating = reader.GetFloat(56); + item.CriticRating = reader.GetFloat(index); } + index++; - if (!reader.IsDBNull(57)) + if (!reader.IsDBNull(index)) { - item.CriticRatingSummary = reader.GetString(57); + item.CriticRatingSummary = reader.GetString(index); } + index++; - if (!reader.IsDBNull(58)) + if (!reader.IsDBNull(index)) { - item.IsVirtualItem = reader.GetBoolean(58); + item.IsVirtualItem = reader.GetBoolean(index); } + index++; var hasSeries = item as IHasSeries; if (hasSeries != null) { - if (!reader.IsDBNull(59)) + if (!reader.IsDBNull(index)) { - hasSeries.SeriesName = reader.GetString(59); + hasSeries.SeriesName = reader.GetString(index); } } + index++; var episode = item as Episode; if (episode != null) { - if (!reader.IsDBNull(60)) + if (!reader.IsDBNull(index)) { - episode.SeasonName = reader.GetString(60); + episode.SeasonName = reader.GetString(index); } - if (!reader.IsDBNull(61)) + index++; + if (!reader.IsDBNull(index)) { - episode.SeasonId = reader.GetGuid(61); + episode.SeasonId = reader.GetGuid(index); } } + else + { + index++; + } + index++; if (hasSeries != null) { - if (!reader.IsDBNull(62)) + if (!reader.IsDBNull(index)) { - hasSeries.SeriesId = reader.GetGuid(62); + hasSeries.SeriesId = reader.GetGuid(index); } } + index++; if (hasSeries != null) { - if (!reader.IsDBNull(63)) + if (!reader.IsDBNull(index)) { - hasSeries.SeriesSortName = reader.GetString(63); + hasSeries.SeriesSortName = reader.GetString(index); } } + index++; return item; } diff --git a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs b/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs index c2a4339f0..ddc1de9cd 100644 --- a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs +++ b/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs @@ -119,12 +119,29 @@ namespace MediaBrowser.Server.Implementations.TV // Avoid implicitly captured closure var currentUser = user; - return series + var allNextUp = series .Select(i => GetNextUp(i, currentUser)) + .Where(i => i.Item1 != null) // Include if an episode was found, and either the series is not unwatched or the specific series was requested - .Where(i => i.Item1 != null && (!i.Item3 || !string.IsNullOrWhiteSpace(request.SeriesId))) .OrderByDescending(i => i.Item2) .ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue) + .ToList(); + + // If viewing all next up for all series, remove first episodes + if (string.IsNullOrWhiteSpace(request.SeriesId)) + { + var withoutFirstEpisode = allNextUp + .Where(i => !i.Item3) + .ToList(); + + // But if that returns empty, keep those first episodes (avoid completely empty view) + if (withoutFirstEpisode.Count > 0) + { + allNextUp = withoutFirstEpisode; + } + } + + return allNextUp .Select(i => i.Item1) .Take(request.Limit ?? int.MaxValue); } diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index 9ae0a126a..03f7160ae 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -4,7 +4,7 @@ <package id="Emby.XmlTv" version="1.0.0.55" targetFramework="net45" />
<package id="ini-parser" version="2.3.0" targetFramework="net45" />
<package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
- <package id="MediaBrowser.Naming" version="1.0.0.53" targetFramework="net45" />
+ <package id="MediaBrowser.Naming" version="1.0.0.54" targetFramework="net45" />
<package id="Mono.Nat" version="1.2.24.0" targetFramework="net45" />
<package id="morelinq" version="1.4.0" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
diff --git a/MediaBrowser.Tests/ConsistencyTests/Resources/SampleTransformed.htm b/MediaBrowser.Tests/ConsistencyTests/Resources/SampleTransformed.htm new file mode 100644 index 000000000..9bff396d1 --- /dev/null +++ b/MediaBrowser.Tests/ConsistencyTests/Resources/SampleTransformed.htm @@ -0,0 +1,1348 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <title>String Usage Report</title> + <style> +body { + background: #F3F3F4; + color: #1E1E1F; + font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; + padding: 0; + margin: 0; +} +h1 { + padding: 10px 0px 10px 10px; + font-size: 21pt; + background-color: #E2E2E2; + border-bottom: 1px #C1C1C2 solid; + color: #201F20; + margin: 0; + font-weight: normal; +} +h2 { + font-size: 18pt; + font-weight: normal; + padding: 15px 0 5px 0; + margin: 0; +} +h3 { + font-weight: normal; + font-size: 15pt; + margin: 0; + padding: 15px 0 5px 0; + background-color: transparent; +} +/* Color all hyperlinks one color */ +a { + color: #1382CE; +} +/* Table styles */ +table { + border-spacing: 0 0; + border-collapse: collapse; + font-size: 10pt; +} +table th { + background: #E7E7E8; + text-align: left; + text-decoration: none; + font-weight: normal; + padding: 3px 6px 3px 6px; + border: 1px solid #CBCBCB; +} +table td { + vertical-align: top; + padding: 3px 6px 5px 5px; + margin: 0px; + border: 1px solid #CBCBCB; + background: #F7F7F8; +} +/* Local link is a style for hyperlinks that link to file:/// content, there are lots so color them as 'normal' text until the user mouse overs */ +.localLink { + color: #1E1E1F; + background: #EEEEED; + text-decoration: none; +} +.localLink:hover { + color: #1382CE; + background: #FFFF99; + text-decoration: none; +} +.baseCell { + width: 100%; + color: #427A9F; +} +.stringCell { + display: table; +} +.tokenCell { + white-space: nowrap; +} +.occurrence { + padding-left: 40px; +} +.block { + display: table-cell; +} +/* Padding around the content after the h1 */ +#content { + padding: 0px 12px 12px 12px; +} +#messages table { + width: 97%; +} + </style> + </head> + <body> + <h1>String Usage Report</h1> + <div id="content"> + <h2>Strings</h2> + <div id="messages"> + <table> + <tbody> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelExit</strong>: "</div> + <div class="block">Exit"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelVisitCommunity</strong>: "</div> + <div class="block">Visit Community"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelGithub</strong>: "</div> + <div class="block">Github"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelSwagger</strong>: "</div> + <div class="block">Swagger"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelStandard</strong>: "</div> + <div class="block">Standard"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelApiDocumentation</strong>: "</div> + <div class="block">Api Documentation"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelDeveloperResources</strong>: "</div> + <div class="block">Developer Resources"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelBrowseLibrary</strong>: "</div> + <div class="block">Browse Library"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelConfigureServer</strong>: "</div> + <div class="block">Configure Emby"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelOpenLibraryViewer</strong>: "</div> + <div class="block">Open Library Viewer"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelRestartServer</strong>: "</div> + <div class="block">Restart Server"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelShowLogWindow</strong>: "</div> + <div class="block">Show Log Window"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelPrevious</strong>: "</div> + <div class="block">Previous"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html">\wizardagreement.html:21</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardcomponents.html">\wizardcomponents.html:54</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardfinish.html">\wizardfinish.html:40</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardlibrary.html">\wizardlibrary.html:19</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardlivetvguide.html">\wizardlivetvguide.html:30</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardlivetvtuner.html">\wizardlivetvtuner.html:31</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardservice.html">\wizardservice.html:17</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardsettings.html">\wizardsettings.html:32</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizarduser.html">\wizarduser.html:27</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelFinish</strong>: "</div> + <div class="block">Finish"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardfinish.html">\wizardfinish.html:41</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelNext</strong>: "</div> + <div class="block">Next"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html">\wizardagreement.html:22</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardcomponents.html">\wizardcomponents.html:55</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardlibrary.html">\wizardlibrary.html:20</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardlivetvguide.html">\wizardlivetvguide.html:31</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardlivetvtuner.html">\wizardlivetvtuner.html:32</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardservice.html">\wizardservice.html:18</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardsettings.html">\wizardsettings.html:33</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardstart.html">\wizardstart.html:25</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizarduser.html">\wizarduser.html:28</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelYoureDone</strong>: "</div> + <div class="block">You're Done!"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardfinish.html">\wizardfinish.html:7</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>WelcomeToProject</strong>: "</div> + <div class="block">Welcome to Emby!"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardstart.html">\wizardstart.html:10</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>ThisWizardWillGuideYou</strong>: "</div> + <div class="block">This wizard will help guide you through the setup process. To begin, please select your preferred language."</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardstart.html">\wizardstart.html:16</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>TellUsAboutYourself</strong>: "</div> + <div class="block">Tell us about yourself"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizarduser.html">\wizarduser.html:8</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>ButtonQuickStartGuide</strong>: "</div> + <div class="block">Quick start guide"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardstart.html">\wizardstart.html:12</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelYourFirstName</strong>: "</div> + <div class="block">Your first name:"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizarduser.html">\wizarduser.html:14</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>MoreUsersCanBeAddedLater</strong>: "</div> + <div class="block">More users can be added later within the Dashboard."</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizarduser.html">\wizarduser.html:15</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>UserProfilesIntro</strong>: "</div> + <div class="block">Emby includes built-in support for user profiles, enabling each user to have their own display settings, playstate and parental controls."</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizarduser.html">\wizarduser.html:11</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelWindowsService</strong>: "</div> + <div class="block">Windows Service"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardservice.html">\wizardservice.html:7</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>AWindowsServiceHasBeenInstalled</strong>: "</div> + <div class="block">A Windows Service has been installed."</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardservice.html">\wizardservice.html:10</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>WindowsServiceIntro1</strong>: "</div> + <div class="block">Emby Server normally runs as a desktop application with a tray icon, but if you prefer to run it as a background service, it can be started from the windows services control panel instead."</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardservice.html">\wizardservice.html:12</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>WindowsServiceIntro2</strong>: "</div> + <div class="block">If using the windows service, please note that it cannot be run at the same time as the tray icon, so you'll need to exit the tray in order to run the service. The service will also need to be configured with administrative privileges via the control panel. When running as a service, you will need to ensure that the service account has access to your media folders."</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardservice.html">\wizardservice.html:14</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>WizardCompleted</strong>: "</div> + <div class="block">That's all we need for now. Emby has begun collecting information about your media library. Check out some of our apps, and then click <b>Finish</b> to view the <b>Server Dashboard</b>."</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardfinish.html">\wizardfinish.html:10</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelConfigureSettings</strong>: "</div> + <div class="block">Configure settings"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardsettings.html">\wizardsettings.html:8</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelEnableVideoImageExtraction</strong>: "</div> + <div class="block">Enable video image extraction"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>VideoImageExtractionHelp</strong>: "</div> + <div class="block">For videos that don't already have images, and that we're unable to find internet images for. This will add some additional time to the initial library scan but will result in a more pleasing presentation."</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelEnableChapterImageExtractionForMovies</strong>: "</div> + <div class="block">Extract chapter image extraction for Movies"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelChapterImageExtractionForMoviesHelp</strong>: "</div> + <div class="block">Extracting chapter images will allow clients to display graphical scene selection menus. The process can be slow, cpu-intensive and may require several gigabytes of space. It runs as a nightly scheduled task, although this is configurable in the scheduled tasks area. It is not recommended to run this task during peak usage hours."</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelEnableAutomaticPortMapping</strong>: "</div> + <div class="block">Enable automatic port mapping"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelEnableAutomaticPortMappingHelp</strong>: "</div> + <div class="block">UPnP allows automated router configuration for easy remote access. This may not work with some router models."</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>HeaderTermsOfService</strong>: "</div> + <div class="block">Emby Terms of Service"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html">\wizardagreement.html:9</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>MessagePleaseAcceptTermsOfService</strong>: "</div> + <div class="block">Please accept the terms of service and privacy policy before continuing."</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html">\wizardagreement.html:12</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>OptionIAcceptTermsOfService</strong>: "</div> + <div class="block">I accept the terms of service"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html">\wizardagreement.html:17</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>ButtonPrivacyPolicy</strong>: "</div> + <div class="block">Privacy policy"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html">\wizardagreement.html:14</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>ButtonTermsOfService</strong>: "</div> + <div class="block">Terms of Service"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html">\wizardagreement.html:15</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>HeaderDeveloperOptions</strong>: "</div> + <div class="block">Developer Options"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dashboardgeneral.html">\dashboardgeneral.html:108</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>OptionEnableWebClientResponseCache</strong>: "</div> + <div class="block">Enable web response caching"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dashboardgeneral.html">\dashboardgeneral.html:112</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>OptionDisableForDevelopmentHelp</strong>: "</div> + <div class="block">Configure these as needed for web development purposes."</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dashboardgeneral.html">\dashboardgeneral.html:119</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>OptionEnableWebClientResourceMinification</strong>: "</div> + <div class="block">Enable web resource minification"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dashboardgeneral.html">\dashboardgeneral.html:116</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelDashboardSourcePath</strong>: "</div> + <div class="block">Web client source path:"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dashboardgeneral.html">\dashboardgeneral.html:124</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelDashboardSourcePathHelp</strong>: "</div> + <div class="block">If running the server from source, specify the path to the dashboard-ui folder. All web client files will be served from this location."</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dashboardgeneral.html">\dashboardgeneral.html:126</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>ButtonConvertMedia</strong>: "</div> + <div class="block">Convert media"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\syncactivity.html">\syncactivity.html:22</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>ButtonOrganize</strong>: "</div> + <div class="block">Organize"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\autoorganizelog.html">\autoorganizelog.html:8</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\autoorganizelog.js">\scripts\autoorganizelog.js:293</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\autoorganizelog.js">\scripts\autoorganizelog.js:294</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\autoorganizelog.js">\scripts\autoorganizelog.js:296</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LinkedToEmbyConnect</strong>: "</div> + <div class="block">Linked to Emby Connect"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>HeaderSupporterBenefits</strong>: "</div> + <div class="block">Emby Premiere Benefits"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>HeaderAddUser</strong>: "</div> + <div class="block">Add User"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelAddConnectSupporterHelp</strong>: "</div> + <div class="block">To add a user who isn't listed, you'll need to first link their account to Emby Connect from their user profile page."</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>LabelPinCode</strong>: "</div> + <div class="block">Pin code:"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>OptionHideWatchedContentFromLatestMedia</strong>: "</div> + <div class="block">Hide watched content from latest media"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\mypreferenceshome.html">\mypreferenceshome.html:114</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>HeaderSync</strong>: "</div> + <div class="block">Sync"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\mysyncsettings.html">\mysyncsettings.html:7</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\registrationservices.js">\scripts\registrationservices.js:175</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\useredit.html">\useredit.html:82</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>ButtonOk</strong>: "</div> + <div class="block">Ok"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\components\directorybrowser\directorybrowser.js">\components\directorybrowser\directorybrowser.js:147</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\components\fileorganizer\fileorganizer.template.html">\components\fileorganizer\fileorganizer.template.html:45</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\components\medialibrarycreator\medialibrarycreator.template.html">\components\medialibrarycreator\medialibrarycreator.template.html:30</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\components\metadataeditor\personeditor.template.html">\components\metadataeditor\personeditor.template.html:33</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:372</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:453</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:504</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:542</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:590</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:630</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:661</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:706</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\nowplaying.html">\nowplaying.html:113</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\ratingdialog.js">\scripts\ratingdialog.js:42</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>ButtonCancel</strong>: "</div> + <div class="block">Cancel"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\components\tvproviders\schedulesdirect.template.html">\components\tvproviders\schedulesdirect.template.html:68</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\components\tvproviders\xmltv.template.html">\components\tvproviders\xmltv.template.html:48</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\connectlogin.html">\connectlogin.html:74</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\connectlogin.html">\connectlogin.html:108</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:325</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:375</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:456</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:507</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:545</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:593</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:633</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:664</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:709</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\forgotpassword.html">\forgotpassword.html:23</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\forgotpasswordpin.html">\forgotpasswordpin.html:22</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\livetvseriestimer.html">\livetvseriestimer.html:62</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\livetvtunerprovider-hdhomerun.html">\livetvtunerprovider-hdhomerun.html:35</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\livetvtunerprovider-m3u.html">\livetvtunerprovider-m3u.html:19</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\livetvtunerprovider-satip.html">\livetvtunerprovider-satip.html:65</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\login.html">\login.html:27</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\notificationsetting.html">\notificationsetting.html:64</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scheduledtask.html">\scheduledtask.html:85</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarylist.js">\scripts\librarylist.js:349</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\mediacontroller.js">\scripts\mediacontroller.js:167</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\mediacontroller.js">\scripts\mediacontroller.js:436</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\ratingdialog.js">\scripts\ratingdialog.js:43</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\site.js">\scripts\site.js:1025</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\userprofilespage.js">\scripts\userprofilespage.js:198</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\syncsettings.html">\syncsettings.html:43</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\useredit.html">\useredit.html:111</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\userlibraryaccess.html">\userlibraryaccess.html:57</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\usernew.html">\usernew.html:45</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\userparentalcontrol.html">\userparentalcontrol.html:101</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>ButtonExit</strong>: "</div> + <div class="block">Exit"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>ButtonNew</strong>: "</div> + <div class="block">New"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\components\fileorganizer\fileorganizer.template.html">\components\fileorganizer\fileorganizer.template.html:18</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:107</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:278</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:290</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:296</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:302</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:308</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html">\dlnaprofile.html:314</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofiles.html">\dlnaprofiles.html:14</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\serversecurity.html">\serversecurity.html:8</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>HeaderTaskTriggers</strong>: "</div> + <div class="block">Task Triggers"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scheduledtask.html">\scheduledtask.html:11</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>HeaderTV</strong>: "</div> + <div class="block">TV"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\librarysettings.html">\librarysettings.html:113</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>HeaderAudio</strong>: "</div> + <div class="block">Audio"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\librarysettings.html">\librarysettings.html:39</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>HeaderVideo</strong>: "</div> + <div class="block">Video"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\librarysettings.html">\librarysettings.html:50</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>HeaderPaths</strong>: "</div> + <div class="block">Paths"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dashboard.html">\dashboard.html:92</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>CategorySync</strong>: "</div> + <div class="block">Sync"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>TabPlaylist</strong>: "</div> + <div class="block">Playlist"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\nowplaying.html">\nowplaying.html:20</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>HeaderEasyPinCode</strong>: "</div> + <div class="block">Easy Pin Code"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\myprofile.html">\myprofile.html:69</a> + </td> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\userpassword.html">\userpassword.html:42</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>HeaderGrownupsOnly</strong>: "</div> + <div class="block">Grown-ups Only!"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>DividerOr</strong>: "</div> + <div class="block">-- or --"</div> + </div> + </th> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>HeaderInstalledServices</strong>: "</div> + <div class="block">Installed Services"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\appservices.html">\appservices.html:6</a> + </td> + </tr> + <tr> + <th class="baseCell"> + <div class="stringCell"> + <div class="block tokenCell"> + <strong>HeaderAvailableServices</strong>: "</div> + <div class="block">Available Services"</div> + </div> + </th> + </tr> + <tr> + <td class="baseCell occurrence"> + <a href="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\appservices.html">\appservices.html:11</a> + </td> + </tr> + </tbody> + </table> + </div> + </div> + </body> +</html>
\ No newline at end of file diff --git a/MediaBrowser.Tests/ConsistencyTests/Resources/StringCheck.xslt b/MediaBrowser.Tests/ConsistencyTests/Resources/StringCheck.xslt new file mode 100644 index 000000000..39586022b --- /dev/null +++ b/MediaBrowser.Tests/ConsistencyTests/Resources/StringCheck.xslt @@ -0,0 +1,145 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- DWXMLSource="StringCheckSample.xml" --> +<!DOCTYPE xsl:stylesheet [ + <!ENTITY nbsp " "> + <!ENTITY copy "©"> + <!ENTITY reg "®"> + <!ENTITY trade "™"> + <!ENTITY mdash "—"> + <!ENTITY ldquo "“"> + <!ENTITY rdquo "”"> + <!ENTITY pound "£"> + <!ENTITY yen "¥"> + <!ENTITY euro "€"> +]> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:output method="html" encoding="utf-8" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/> + <xsl:template match="/"> + <html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + <title> + <xsl:value-of select="StringUsages/@ReportTitle"/> + </title> + <style> +body { + background: #F3F3F4; + color: #1E1E1F; + font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; + padding: 0; + margin: 0; +} +h1 { + padding: 10px 0px 10px 10px; + font-size: 21pt; + background-color: #E2E2E2; + border-bottom: 1px #C1C1C2 solid; + color: #201F20; + margin: 0; + font-weight: normal; +} +h2 { + font-size: 18pt; + font-weight: normal; + padding: 15px 0 5px 0; + margin: 0; +} +h3 { + font-weight: normal; + font-size: 15pt; + margin: 0; + padding: 15px 0 5px 0; + background-color: transparent; +} +/* Color all hyperlinks one color */ +a { + color: #1382CE; +} +/* Table styles */ +table { + border-spacing: 0 0; + border-collapse: collapse; + font-size: 10pt; +} +table th { + background: #E7E7E8; + text-align: left; + text-decoration: none; + font-weight: normal; + padding: 3px 6px 3px 6px; + border: 1px solid #CBCBCB; +} +table td { + vertical-align: top; + padding: 3px 6px 5px 5px; + margin: 0px; + border: 1px solid #CBCBCB; + background: #F7F7F8; +} +/* Local link is a style for hyperlinks that link to file:/// content, there are lots so color them as 'normal' text until the user mouse overs */ +.localLink { + color: #1E1E1F; + background: #EEEEED; + text-decoration: none; +} +.localLink:hover { + color: #1382CE; + background: #FFFF99; + text-decoration: none; +} +.baseCell { + width: 100%; + color: #427A9F; +} +.stringCell { + display: table; +} +.tokenCell { + white-space: nowrap; +} +.occurrence { + padding-left: 40px; +} +.block { + display: table-cell; +} +/* Padding around the content after the h1 */ +#content { + padding: 0px 12px 12px 12px; +} +#messages table { + width: 97%; +} + </style> + </head> + <body> + <h1> + <xsl:value-of select="StringUsages/@ReportTitle"/> + </h1> + <div id="content"> + <h2>Strings</h2> + <div id="messages"> + <table> + <tbody> + <xsl:for-each select="StringUsages/Dictionary"> + <tr> + <th class="baseCell"> <div class="stringCell"> + <div class="block tokenCell"><strong><xsl:value-of select="@Token"/></strong>: "</div> + <div class="block"><xsl:value-of select="@Text"/>"</div> + </div></th> + </tr> + <xsl:for-each select="Occurence"> + <xsl:variable name="hyperlink"><xsl:value-of select="@FullPath" /></xsl:variable> + <tr> + <td class="baseCell occurrence"><a href="{@FullPath}"><xsl:value-of select="@FileName"/>:<xsl:value-of select="@LineNumber"/></a></td> + </tr> + </xsl:for-each> + </xsl:for-each> + </tbody> + </table> + </div> + </div> + </body> + </html> + </xsl:template> +</xsl:stylesheet>
\ No newline at end of file diff --git a/MediaBrowser.Tests/ConsistencyTests/Resources/StringCheckSample.xml b/MediaBrowser.Tests/ConsistencyTests/Resources/StringCheckSample.xml new file mode 100644 index 000000000..118ed55ae --- /dev/null +++ b/MediaBrowser.Tests/ConsistencyTests/Resources/StringCheckSample.xml @@ -0,0 +1,239 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<?xml-stylesheet type="text/xsl" href="StringCheck.xslt"?> +<StringUsages Mode="All"> + <Dictionary Token="LabelExit" Text="Exit" /> + <Dictionary Token="LabelVisitCommunity" Text="Visit Community" /> + <Dictionary Token="LabelGithub" Text="Github" /> + <Dictionary Token="LabelSwagger" Text="Swagger" /> + <Dictionary Token="LabelStandard" Text="Standard" /> + <Dictionary Token="LabelApiDocumentation" Text="Api Documentation" /> + <Dictionary Token="LabelDeveloperResources" Text="Developer Resources" /> + <Dictionary Token="LabelBrowseLibrary" Text="Browse Library" /> + <Dictionary Token="LabelConfigureServer" Text="Configure Emby" /> + <Dictionary Token="LabelOpenLibraryViewer" Text="Open Library Viewer" /> + <Dictionary Token="LabelRestartServer" Text="Restart Server" /> + <Dictionary Token="LabelShowLogWindow" Text="Show Log Window" /> + <Dictionary Token="LabelPrevious" Text="Previous"> + <Occurence FileName="\wizardagreement.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html" LineNumber="21" /> + <Occurence FileName="\wizardcomponents.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardcomponents.html" LineNumber="54" /> + <Occurence FileName="\wizardfinish.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardfinish.html" LineNumber="40" /> + <Occurence FileName="\wizardlibrary.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardlibrary.html" LineNumber="19" /> + <Occurence FileName="\wizardlivetvguide.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardlivetvguide.html" LineNumber="30" /> + <Occurence FileName="\wizardlivetvtuner.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardlivetvtuner.html" LineNumber="31" /> + <Occurence FileName="\wizardservice.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardservice.html" LineNumber="17" /> + <Occurence FileName="\wizardsettings.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardsettings.html" LineNumber="32" /> + <Occurence FileName="\wizarduser.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizarduser.html" LineNumber="27" /> + </Dictionary> + <Dictionary Token="LabelFinish" Text="Finish"> + <Occurence FileName="\wizardfinish.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardfinish.html" LineNumber="41" /> + </Dictionary> + <Dictionary Token="LabelNext" Text="Next"> + <Occurence FileName="\wizardagreement.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html" LineNumber="22" /> + <Occurence FileName="\wizardcomponents.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardcomponents.html" LineNumber="55" /> + <Occurence FileName="\wizardlibrary.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardlibrary.html" LineNumber="20" /> + <Occurence FileName="\wizardlivetvguide.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardlivetvguide.html" LineNumber="31" /> + <Occurence FileName="\wizardlivetvtuner.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardlivetvtuner.html" LineNumber="32" /> + <Occurence FileName="\wizardservice.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardservice.html" LineNumber="18" /> + <Occurence FileName="\wizardsettings.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardsettings.html" LineNumber="33" /> + <Occurence FileName="\wizardstart.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardstart.html" LineNumber="25" /> + <Occurence FileName="\wizarduser.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizarduser.html" LineNumber="28" /> + </Dictionary> + <Dictionary Token="LabelYoureDone" Text="You're Done!"> + <Occurence FileName="\wizardfinish.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardfinish.html" LineNumber="7" /> + </Dictionary> + <Dictionary Token="WelcomeToProject" Text="Welcome to Emby!"> + <Occurence FileName="\wizardstart.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardstart.html" LineNumber="10" /> + </Dictionary> + <Dictionary Token="ThisWizardWillGuideYou" Text="This wizard will help guide you through the setup process. To begin, please select your preferred language."> + <Occurence FileName="\wizardstart.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardstart.html" LineNumber="16" /> + </Dictionary> + <Dictionary Token="TellUsAboutYourself" Text="Tell us about yourself"> + <Occurence FileName="\wizarduser.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizarduser.html" LineNumber="8" /> + </Dictionary> + <Dictionary Token="ButtonQuickStartGuide" Text="Quick start guide"> + <Occurence FileName="\wizardstart.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardstart.html" LineNumber="12" /> + </Dictionary> + <Dictionary Token="LabelYourFirstName" Text="Your first name:"> + <Occurence FileName="\wizarduser.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizarduser.html" LineNumber="14" /> + </Dictionary> + <Dictionary Token="MoreUsersCanBeAddedLater" Text="More users can be added later within the Dashboard."> + <Occurence FileName="\wizarduser.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizarduser.html" LineNumber="15" /> + </Dictionary> + <Dictionary Token="UserProfilesIntro" Text="Emby includes built-in support for user profiles, enabling each user to have their own display settings, playstate and parental controls."> + <Occurence FileName="\wizarduser.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizarduser.html" LineNumber="11" /> + </Dictionary> + <Dictionary Token="LabelWindowsService" Text="Windows Service"> + <Occurence FileName="\wizardservice.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardservice.html" LineNumber="7" /> + </Dictionary> + <Dictionary Token="AWindowsServiceHasBeenInstalled" Text="A Windows Service has been installed."> + <Occurence FileName="\wizardservice.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardservice.html" LineNumber="10" /> + </Dictionary> + <Dictionary Token="WindowsServiceIntro1" Text="Emby Server normally runs as a desktop application with a tray icon, but if you prefer to run it as a background service, it can be started from the windows services control panel instead."> + <Occurence FileName="\wizardservice.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardservice.html" LineNumber="12" /> + </Dictionary> + <Dictionary Token="WindowsServiceIntro2" Text="If using the windows service, please note that it cannot be run at the same time as the tray icon, so you'll need to exit the tray in order to run the service. The service will also need to be configured with administrative privileges via the control panel. When running as a service, you will need to ensure that the service account has access to your media folders."> + <Occurence FileName="\wizardservice.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardservice.html" LineNumber="14" /> + </Dictionary> + <Dictionary Token="WizardCompleted" Text="That's all we need for now. Emby has begun collecting information about your media library. Check out some of our apps, and then click <b>Finish</b> to view the <b>Server Dashboard</b>."> + <Occurence FileName="\wizardfinish.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardfinish.html" LineNumber="10" /> + </Dictionary> + <Dictionary Token="LabelConfigureSettings" Text="Configure settings"> + <Occurence FileName="\wizardsettings.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardsettings.html" LineNumber="8" /> + </Dictionary> + <Dictionary Token="LabelEnableVideoImageExtraction" Text="Enable video image extraction" /> + <Dictionary Token="VideoImageExtractionHelp" Text="For videos that don't already have images, and that we're unable to find internet images for. This will add some additional time to the initial library scan but will result in a more pleasing presentation." /> + <Dictionary Token="LabelEnableChapterImageExtractionForMovies" Text="Extract chapter image extraction for Movies" /> + <Dictionary Token="LabelChapterImageExtractionForMoviesHelp" Text="Extracting chapter images will allow clients to display graphical scene selection menus. The process can be slow, cpu-intensive and may require several gigabytes of space. It runs as a nightly scheduled task, although this is configurable in the scheduled tasks area. It is not recommended to run this task during peak usage hours." /> + <Dictionary Token="LabelEnableAutomaticPortMapping" Text="Enable automatic port mapping" /> + <Dictionary Token="LabelEnableAutomaticPortMappingHelp" Text="UPnP allows automated router configuration for easy remote access. This may not work with some router models." /> + <Dictionary Token="HeaderTermsOfService" Text="Emby Terms of Service"> + <Occurence FileName="\wizardagreement.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html" LineNumber="9" /> + </Dictionary> + <Dictionary Token="MessagePleaseAcceptTermsOfService" Text="Please accept the terms of service and privacy policy before continuing."> + <Occurence FileName="\wizardagreement.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html" LineNumber="12" /> + </Dictionary> + <Dictionary Token="OptionIAcceptTermsOfService" Text="I accept the terms of service"> + <Occurence FileName="\wizardagreement.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html" LineNumber="17" /> + </Dictionary> + <Dictionary Token="ButtonPrivacyPolicy" Text="Privacy policy"> + <Occurence FileName="\wizardagreement.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html" LineNumber="14" /> + </Dictionary> + <Dictionary Token="ButtonTermsOfService" Text="Terms of Service"> + <Occurence FileName="\wizardagreement.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html" LineNumber="15" /> + </Dictionary> + <Dictionary Token="HeaderDeveloperOptions" Text="Developer Options"> + <Occurence FileName="\dashboardgeneral.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dashboardgeneral.html" LineNumber="108" /> + </Dictionary> + <Dictionary Token="OptionEnableWebClientResponseCache" Text="Enable web response caching"> + <Occurence FileName="\dashboardgeneral.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dashboardgeneral.html" LineNumber="112" /> + </Dictionary> + <Dictionary Token="OptionDisableForDevelopmentHelp" Text="Configure these as needed for web development purposes."> + <Occurence FileName="\dashboardgeneral.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dashboardgeneral.html" LineNumber="119" /> + </Dictionary> + <Dictionary Token="OptionEnableWebClientResourceMinification" Text="Enable web resource minification"> + <Occurence FileName="\dashboardgeneral.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dashboardgeneral.html" LineNumber="116" /> + </Dictionary> + <Dictionary Token="LabelDashboardSourcePath" Text="Web client source path:"> + <Occurence FileName="\dashboardgeneral.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dashboardgeneral.html" LineNumber="124" /> + </Dictionary> + <Dictionary Token="LabelDashboardSourcePathHelp" Text="If running the server from source, specify the path to the dashboard-ui folder. All web client files will be served from this location."> + <Occurence FileName="\dashboardgeneral.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dashboardgeneral.html" LineNumber="126" /> + </Dictionary> + <Dictionary Token="ButtonConvertMedia" Text="Convert media"> + <Occurence FileName="\syncactivity.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\syncactivity.html" LineNumber="22" /> + </Dictionary> + <Dictionary Token="ButtonOrganize" Text="Organize"> + <Occurence FileName="\autoorganizelog.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\autoorganizelog.html" LineNumber="8" /> + <Occurence FileName="\scripts\autoorganizelog.js" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\autoorganizelog.js" LineNumber="293" /> + <Occurence FileName="\scripts\autoorganizelog.js" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\autoorganizelog.js" LineNumber="294" /> + <Occurence FileName="\scripts\autoorganizelog.js" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\autoorganizelog.js" LineNumber="296" /> + </Dictionary> + <Dictionary Token="LinkedToEmbyConnect" Text="Linked to Emby Connect" /> + <Dictionary Token="HeaderSupporterBenefits" Text="Emby Premiere Benefits" /> + <Dictionary Token="HeaderAddUser" Text="Add User" /> + <Dictionary Token="LabelAddConnectSupporterHelp" Text="To add a user who isn't listed, you'll need to first link their account to Emby Connect from their user profile page." /> + <Dictionary Token="LabelPinCode" Text="Pin code:" /> + <Dictionary Token="OptionHideWatchedContentFromLatestMedia" Text="Hide watched content from latest media"> + <Occurence FileName="\mypreferenceshome.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\mypreferenceshome.html" LineNumber="114" /> + </Dictionary> + <Dictionary Token="HeaderSync" Text="Sync"> + <Occurence FileName="\mysyncsettings.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\mysyncsettings.html" LineNumber="7" /> + <Occurence FileName="\scripts\registrationservices.js" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\registrationservices.js" LineNumber="175" /> + <Occurence FileName="\useredit.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\useredit.html" LineNumber="82" /> + </Dictionary> + <Dictionary Token="ButtonOk" Text="Ok"> + <Occurence FileName="\components\directorybrowser\directorybrowser.js" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\components\directorybrowser\directorybrowser.js" LineNumber="147" /> + <Occurence FileName="\components\fileorganizer\fileorganizer.template.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\components\fileorganizer\fileorganizer.template.html" LineNumber="45" /> + <Occurence FileName="\components\medialibrarycreator\medialibrarycreator.template.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\components\medialibrarycreator\medialibrarycreator.template.html" LineNumber="30" /> + <Occurence FileName="\components\metadataeditor\personeditor.template.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\components\metadataeditor\personeditor.template.html" LineNumber="33" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="372" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="453" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="504" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="542" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="590" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="630" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="661" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="706" /> + <Occurence FileName="\nowplaying.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\nowplaying.html" LineNumber="113" /> + <Occurence FileName="\scripts\ratingdialog.js" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\ratingdialog.js" LineNumber="42" /> + </Dictionary> + <Dictionary Token="ButtonCancel" Text="Cancel"> + <Occurence FileName="\components\tvproviders\schedulesdirect.template.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\components\tvproviders\schedulesdirect.template.html" LineNumber="68" /> + <Occurence FileName="\components\tvproviders\xmltv.template.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\components\tvproviders\xmltv.template.html" LineNumber="48" /> + <Occurence FileName="\connectlogin.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\connectlogin.html" LineNumber="74" /> + <Occurence FileName="\connectlogin.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\connectlogin.html" LineNumber="108" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="325" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="375" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="456" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="507" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="545" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="593" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="633" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="664" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="709" /> + <Occurence FileName="\forgotpassword.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\forgotpassword.html" LineNumber="23" /> + <Occurence FileName="\forgotpasswordpin.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\forgotpasswordpin.html" LineNumber="22" /> + <Occurence FileName="\livetvseriestimer.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\livetvseriestimer.html" LineNumber="62" /> + <Occurence FileName="\livetvtunerprovider-hdhomerun.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\livetvtunerprovider-hdhomerun.html" LineNumber="35" /> + <Occurence FileName="\livetvtunerprovider-m3u.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\livetvtunerprovider-m3u.html" LineNumber="19" /> + <Occurence FileName="\livetvtunerprovider-satip.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\livetvtunerprovider-satip.html" LineNumber="65" /> + <Occurence FileName="\login.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\login.html" LineNumber="27" /> + <Occurence FileName="\notificationsetting.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\notificationsetting.html" LineNumber="64" /> + <Occurence FileName="\scheduledtask.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scheduledtask.html" LineNumber="85" /> + <Occurence FileName="\scripts\librarylist.js" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarylist.js" LineNumber="349" /> + <Occurence FileName="\scripts\mediacontroller.js" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\mediacontroller.js" LineNumber="167" /> + <Occurence FileName="\scripts\mediacontroller.js" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\mediacontroller.js" LineNumber="436" /> + <Occurence FileName="\scripts\ratingdialog.js" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\ratingdialog.js" LineNumber="43" /> + <Occurence FileName="\scripts\site.js" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\site.js" LineNumber="1025" /> + <Occurence FileName="\scripts\userprofilespage.js" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scripts\userprofilespage.js" LineNumber="198" /> + <Occurence FileName="\syncsettings.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\syncsettings.html" LineNumber="43" /> + <Occurence FileName="\useredit.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\useredit.html" LineNumber="111" /> + <Occurence FileName="\userlibraryaccess.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\userlibraryaccess.html" LineNumber="57" /> + <Occurence FileName="\usernew.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\usernew.html" LineNumber="45" /> + <Occurence FileName="\userparentalcontrol.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\userparentalcontrol.html" LineNumber="101" /> + </Dictionary> + <Dictionary Token="ButtonExit" Text="Exit" /> + <Dictionary Token="ButtonNew" Text="New"> + <Occurence FileName="\components\fileorganizer\fileorganizer.template.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\components\fileorganizer\fileorganizer.template.html" LineNumber="18" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="107" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="278" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="290" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="296" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="302" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="308" /> + <Occurence FileName="\dlnaprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofile.html" LineNumber="314" /> + <Occurence FileName="\dlnaprofiles.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dlnaprofiles.html" LineNumber="14" /> + <Occurence FileName="\serversecurity.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\serversecurity.html" LineNumber="8" /> + </Dictionary> + <Dictionary Token="HeaderTaskTriggers" Text="Task Triggers"> + <Occurence FileName="\scheduledtask.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\scheduledtask.html" LineNumber="11" /> + </Dictionary> + <Dictionary Token="HeaderTV" Text="TV"> + <Occurence FileName="\librarysettings.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\librarysettings.html" LineNumber="113" /> + </Dictionary> + <Dictionary Token="HeaderAudio" Text="Audio"> + <Occurence FileName="\librarysettings.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\librarysettings.html" LineNumber="39" /> + </Dictionary> + <Dictionary Token="HeaderVideo" Text="Video"> + <Occurence FileName="\librarysettings.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\librarysettings.html" LineNumber="50" /> + </Dictionary> + <Dictionary Token="HeaderPaths" Text="Paths"> + <Occurence FileName="\dashboard.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\dashboard.html" LineNumber="92" /> + </Dictionary> + <Dictionary Token="CategorySync" Text="Sync" /> + <Dictionary Token="TabPlaylist" Text="Playlist"> + <Occurence FileName="\nowplaying.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\nowplaying.html" LineNumber="20" /> + </Dictionary> + <Dictionary Token="HeaderEasyPinCode" Text="Easy Pin Code"> + <Occurence FileName="\myprofile.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\myprofile.html" LineNumber="69" /> + <Occurence FileName="\userpassword.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\userpassword.html" LineNumber="42" /> + </Dictionary> + <Dictionary Token="HeaderGrownupsOnly" Text="Grown-ups Only!" /> + <Dictionary Token="DividerOr" Text="-- or --" /> + <Dictionary Token="HeaderInstalledServices" Text="Installed Services"> + <Occurence FileName="\appservices.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\appservices.html" LineNumber="6" /> + </Dictionary> + <Dictionary Token="HeaderAvailableServices" Text="Available Services"> + <Occurence FileName="\appservices.html" FullPath="F:\Projects\Softworkz_Emby\Emby\MediaBrowser.WebDashboard\dashboard-ui\appservices.html" LineNumber="11" /> + </Dictionary> +</StringUsages>
\ No newline at end of file diff --git a/MediaBrowser.Tests/ConsistencyTests/StringUsageReporter.cs b/MediaBrowser.Tests/ConsistencyTests/StringUsageReporter.cs new file mode 100644 index 000000000..d036a6c6d --- /dev/null +++ b/MediaBrowser.Tests/ConsistencyTests/StringUsageReporter.cs @@ -0,0 +1,260 @@ +using MediaBrowser.Tests.ConsistencyTests.TextIndexing; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace MediaBrowser.Tests.ConsistencyTests +{ + /// <summary> + /// This class contains tests for reporting the usage of localization string tokens + /// in the dashboard-ui or similar. + /// </summary> + /// <remarks> + /// <para>Run one of the two tests using Visual Studio's "Test Explorer":</para> + /// <para> + /// <list type="bullet"> + /// <item><see cref="ReportStringUsage"/></item> + /// <item><see cref="ReportUnusedStrings"/></item> + /// </list> + /// </para> + /// <para> + /// On successful run, the bottom section of the test explorer will contain a link "Output". + /// This link will open the test results, displaying the trace and two attachment links. + /// One link will open the output folder, the other link will open the output xml file. + /// </para> + /// <para> + /// The output xml file contains a stylesheet link to render the results as html. + /// How that works depends on the default application configured for XML files: + /// </para> + /// <para><list type="bullet"> + /// <item><term>Visual Studio</term> + /// <description>Will open in XML source view. To view the html result, click menu + /// 'XML' => 'Start XSLT without debugging'</description></item> + /// <item><term>Internet Explorer</term> + /// <description>XSL transform will be applied automatically.</description></item> + /// <item><term>Firefox</term> + /// <description>XSL transform will be applied automatically.</description></item> + /// <item><term>Chrome</term> + /// <description>Does not work. Chrome is unable/unwilling to apply xslt transforms from local files.</description></item> + /// </list></para> + /// </remarks> + [TestClass] + public class StringUsageReporter + { + /// <summary> + /// Root path of the web application + /// </summary> + /// <remarks> + /// Can be an absolute path or a path relative to the binaries folder (bin\Debug). + /// </remarks> + public const string WebFolder = @"..\..\..\MediaBrowser.WebDashboard\dashboard-ui"; + + /// <summary> + /// Path to the strings file, relative to <see cref="WebFolder"/>. + /// </summary> + public const string StringsFile = @"strings\en-US.json"; + + /// <summary> + /// Path to the output folder + /// </summary> + /// <remarks> + /// Can be an absolute path or a path relative to the binaries folder (bin\Debug). + /// Important: When changing the output path, make sure that "StringCheck.xslt" is present + /// to make the XML transform work. + /// </remarks> + public const string OutputPath = @"."; + + /// <summary> + /// List of file extension to search. + /// </summary> + public static string[] TargetExtensions = new[] { "js", "html" }; + + /// <summary> + /// List of paths to exclude from search. + /// </summary> + public static string[] ExcludePaths = new[] { @"\bower_components\", @"\thirdparty\" }; + + private TestContext testContextInstance; + + /// <summary> + ///Gets or sets the test context which provides + ///information about and functionality for the current test run. + ///</summary> + public TestContext TestContext + { + get + { + return testContextInstance; + } + set + { + testContextInstance = value; + } + } + + [TestMethod] + public void ReportStringUsage() + { + this.CheckDashboardStrings(false); + } + + [TestMethod] + public void ReportUnusedStrings() + { + this.CheckDashboardStrings(true); + } + + private void CheckDashboardStrings(Boolean unusedOnly) + { + // Init Folders + var currentDir = System.IO.Directory.GetCurrentDirectory(); + Trace("CurrentDir: {0}", currentDir); + + var rootFolderInfo = ResolveFolder(currentDir, WebFolder); + Trace("Web Root: {0}", rootFolderInfo.FullName); + + var outputFolderInfo = ResolveFolder(currentDir, OutputPath); + Trace("Output Path: {0}", outputFolderInfo.FullName); + + // Load Strings + var stringsFileName = Path.Combine(rootFolderInfo.FullName, StringsFile); + + if (!File.Exists(stringsFileName)) + { + throw new Exception(string.Format("Strings file not found: {0}", stringsFileName)); + } + + int lineNumbers; + var stringsDic = this.CreateStringsDictionary(new FileInfo(stringsFileName), out lineNumbers); + + Trace("Loaded {0} strings from strings file containing {1} lines", stringsDic.Count, lineNumbers); + + var allFiles = rootFolderInfo.GetFiles("*", SearchOption.AllDirectories); + + var filteredFiles1 = allFiles.Where(f => TargetExtensions.Any(e => f.Name.EndsWith(e))); + var filteredFiles2 = filteredFiles1.Where(f => !ExcludePaths.Any(p => f.FullName.Contains(p))); + + var selectedFiles = filteredFiles2.OrderBy(f => f.FullName).ToList(); + + var wordIndex = IndexBuilder.BuildIndexFromFiles(selectedFiles, rootFolderInfo.FullName); + + Trace("Created word index from {0} files containing {1} individual words", selectedFiles.Count, wordIndex.Keys.Count); + + var outputFileName = Path.Combine(outputFolderInfo.FullName, string.Format("StringCheck_{0:yyyyMMddHHmmss}.xml", DateTime.Now)); + var settings = new XmlWriterSettings + { + Indent = true, + Encoding = Encoding.UTF8, + WriteEndDocumentOnClose = true + }; + + Trace("Output file: {0}", outputFileName); + + using (XmlWriter writer = XmlWriter.Create(outputFileName, settings)) + { + writer.WriteStartDocument(true); + + // Write the Processing Instruction node. + string xslText = "type=\"text/xsl\" href=\"StringCheck.xslt\""; + writer.WriteProcessingInstruction("xml-stylesheet", xslText); + + writer.WriteStartElement("StringUsages"); + writer.WriteAttributeString("ReportTitle", unusedOnly ? "Unused Strings Report" : "String Usage Report"); + writer.WriteAttributeString("Mode", unusedOnly ? "UnusedOnly" : "All"); + + foreach (var kvp in stringsDic) + { + var occurences = wordIndex.Find(kvp.Key); + + if (occurences == null || !unusedOnly) + { + ////Trace("{0}: {1}", kvp.Key, kvp.Value); + writer.WriteStartElement("Dictionary"); + writer.WriteAttributeString("Token", kvp.Key); + writer.WriteAttributeString("Text", kvp.Value); + + if (occurences != null && !unusedOnly) + { + foreach (var occurence in occurences) + { + writer.WriteStartElement("Occurence"); + writer.WriteAttributeString("FileName", occurence.FileName); + writer.WriteAttributeString("FullPath", occurence.FullPath); + writer.WriteAttributeString("LineNumber", occurence.LineNumber.ToString()); + writer.WriteEndElement(); + ////Trace(" {0}:{1}", occurence.FileName, occurence.LineNumber); + } + } + + writer.WriteEndElement(); + } + } + } + + TestContext.AddResultFile(outputFileName); + TestContext.AddResultFile(outputFolderInfo.FullName); + } + + private SortedDictionary<string, string> CreateStringsDictionary(FileInfo file, out int lineNumbers) + { + var dic = new SortedDictionary<string, string>(); + lineNumbers = 0; + + using (var reader = file.OpenText()) + { + while (!reader.EndOfStream) + { + lineNumbers++; + var words = reader + .ReadLine() + .Split(new[] { "\":" }, StringSplitOptions.RemoveEmptyEntries); + + + if (words.Length == 2) + { + var token = words[0].Replace("\"", string.Empty).Trim(); + var text = words[1].Replace("\",", string.Empty).Replace("\"", string.Empty).Trim(); + + if (dic.Keys.Contains(token)) + { + throw new Exception(string.Format("Double string entry found: {0}", token)); + } + + dic.Add(token, text); + } + } + } + + return dic; + } + + private DirectoryInfo ResolveFolder(string currentDir, string folderPath) + { + if (folderPath.IndexOf(@"\:") != 1) + { + folderPath = Path.Combine(currentDir, folderPath); + } + + var folderInfo = new DirectoryInfo(folderPath); + + if (!folderInfo.Exists) + { + throw new Exception(string.Format("Folder not found: {0}", folderInfo.FullName)); + } + + return folderInfo; + } + + + private void Trace(string message, params object[] parameters) + { + var formatted = string.Format(message, parameters); + System.Diagnostics.Trace.WriteLine(formatted); + } + } +} diff --git a/MediaBrowser.Tests/ConsistencyTests/TextIndexing/IndexBuilder.cs b/MediaBrowser.Tests/ConsistencyTests/TextIndexing/IndexBuilder.cs new file mode 100644 index 000000000..07c0df86c --- /dev/null +++ b/MediaBrowser.Tests/ConsistencyTests/TextIndexing/IndexBuilder.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.Tests.ConsistencyTests.TextIndexing +{ + public class IndexBuilder + { + public const int MinumumWordLength = 4; + + public static char[] WordChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray(); + + public static WordIndex BuildIndexFromFiles(IEnumerable<FileInfo> wordFiles, string rootFolderPath) + { + var index = new WordIndex(); + + var wordSeparators = Enumerable.Range(32, 127).Select(e => Convert.ToChar(e)).Where(c => !WordChars.Contains(c)).ToArray(); + wordSeparators = wordSeparators.Concat(new[] { '\t' }).ToArray(); // add tab + + foreach (var file in wordFiles) + { + var lineNumber = 1; + var displayFileName = file.FullName.Replace(rootFolderPath, string.Empty); + using (var reader = file.OpenText()) + { + while (!reader.EndOfStream) + { + var words = reader + .ReadLine() + .Split(wordSeparators, StringSplitOptions.RemoveEmptyEntries); + ////.Select(f => f.Trim()); + + var wordIndex = 1; + foreach (var word in words) + { + if (word.Length >= MinumumWordLength) + { + index.AddWordOccurrence(word, displayFileName, file.FullName, lineNumber, wordIndex++); + } + } + + lineNumber++; + } + } + } + + return index; + } + + } +} diff --git a/MediaBrowser.Tests/ConsistencyTests/TextIndexing/WordIndex.cs b/MediaBrowser.Tests/ConsistencyTests/TextIndexing/WordIndex.cs new file mode 100644 index 000000000..4ced81237 --- /dev/null +++ b/MediaBrowser.Tests/ConsistencyTests/TextIndexing/WordIndex.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.Tests.ConsistencyTests.TextIndexing +{ + public class WordIndex : Dictionary<string, WordOccurrences> + { + public WordIndex() : base(StringComparer.InvariantCultureIgnoreCase) + { + } + + public void AddWordOccurrence(string word, string fileName, string fullPath, int lineNumber, int wordIndex) + { + WordOccurrences current; + if (!this.TryGetValue(word, out current)) + { + current = new WordOccurrences(); + this[word] = current; + } + + current.AddOccurrence(fileName, fullPath, lineNumber, wordIndex); + } + + public WordOccurrences Find(string word) + { + WordOccurrences found; + if (this.TryGetValue(word, out found)) + { + return found; + } + + return null; + } + + } +} diff --git a/MediaBrowser.Tests/ConsistencyTests/TextIndexing/WordOccurrence.cs b/MediaBrowser.Tests/ConsistencyTests/TextIndexing/WordOccurrence.cs new file mode 100644 index 000000000..40631f582 --- /dev/null +++ b/MediaBrowser.Tests/ConsistencyTests/TextIndexing/WordOccurrence.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.Tests.ConsistencyTests.TextIndexing +{ + public struct WordOccurrence + { + public readonly string FileName; // file containing the word. + public readonly string FullPath; // file containing the word. + public readonly int LineNumber; // line within the file. + public readonly int WordIndex; // index within the line. + + public WordOccurrence(string fileName, string fullPath, int lineNumber, int wordIndex) + { + FileName = fileName; + FullPath = fullPath; + LineNumber = lineNumber; + WordIndex = wordIndex; + } + } +} diff --git a/MediaBrowser.Tests/ConsistencyTests/TextIndexing/WordOccurrences.cs b/MediaBrowser.Tests/ConsistencyTests/TextIndexing/WordOccurrences.cs new file mode 100644 index 000000000..3ba3b5916 --- /dev/null +++ b/MediaBrowser.Tests/ConsistencyTests/TextIndexing/WordOccurrences.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.Tests.ConsistencyTests.TextIndexing +{ + public class WordOccurrences : List<WordOccurrence> + { + public void AddOccurrence(string fileName, string fullPath, int lineNumber, int wordIndex) + { + this.Add(new WordOccurrence(fileName, fullPath, lineNumber, wordIndex)); + } + + } +} diff --git a/MediaBrowser.Tests/MediaBrowser.Tests.csproj b/MediaBrowser.Tests/MediaBrowser.Tests.csproj index 0cfe8182c..76a186109 100644 --- a/MediaBrowser.Tests/MediaBrowser.Tests.csproj +++ b/MediaBrowser.Tests/MediaBrowser.Tests.csproj @@ -25,6 +25,7 @@ <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> + <DocumentationFile>bin\Debug\MediaBrowser.Tests.XML</DocumentationFile> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>none</DebugType> @@ -36,6 +37,7 @@ </PropertyGroup> <ItemGroup> <Reference Include="System" /> + <Reference Include="System.XML" /> </ItemGroup> <Choose> <When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'"> @@ -50,6 +52,11 @@ </Otherwise> </Choose> <ItemGroup> + <Compile Include="ConsistencyTests\StringUsageReporter.cs" /> + <Compile Include="ConsistencyTests\TextIndexing\IndexBuilder.cs" /> + <Compile Include="ConsistencyTests\TextIndexing\WordIndex.cs" /> + <Compile Include="ConsistencyTests\TextIndexing\WordOccurrence.cs" /> + <Compile Include="ConsistencyTests\TextIndexing\WordOccurrences.cs" /> <Compile Include="MediaEncoding\Subtitles\AssParserTests.cs" /> <Compile Include="MediaEncoding\Subtitles\SrtParserTests.cs" /> <Compile Include="MediaEncoding\Subtitles\VttWriterTest.cs" /> @@ -98,6 +105,14 @@ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup> + <ItemGroup> + <ContentWithTargetPath Include="ConsistencyTests\Resources\StringCheck.xslt"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <TargetPath>StringCheck.xslt</TargetPath> + </ContentWithTargetPath> + <None Include="ConsistencyTests\Resources\SampleTransformed.htm" /> + <None Include="ConsistencyTests\Resources\StringCheckSample.xml" /> + </ItemGroup> <Choose> <When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'"> <ItemGroup> diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 4c353c413..b7b1f1dfd 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -440,15 +440,7 @@ namespace MediaBrowser.WebDashboard.Api files.Insert(0, "cordova.js"); } - var tags = files.Select(s => - { - if (s.IndexOf("require", StringComparison.OrdinalIgnoreCase) == -1 && s.IndexOf("alameda", StringComparison.OrdinalIgnoreCase) == -1) - { - return string.Format("<script src=\"{0}\" async></script>", s); - } - return string.Format("<script src=\"{0}\"></script>", s); - - }).ToArray(); + var tags = files.Select(s => string.Format("<script src=\"{0}\" defer></script>", s)).ToArray(); builder.Append(string.Join(string.Empty, tags)); diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 44be96aa8..5c520afcf 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -113,6 +113,9 @@ <Content Include="dashboard-ui\components\chromecasthelpers.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\components\directorybrowser\directorybrowser.css">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\components\favoriteitems.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -140,12 +143,6 @@ <Content Include="dashboard-ui\components\guestinviter\guestinviter.template.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\components\metadataeditor\metadataeditor.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\components\metadataeditor\personeditor.template.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\components\navdrawer\navdrawer.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -176,9 +173,6 @@ <Content Include="dashboard-ui\css\images\logo.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\devices\windowsphone\wp.css">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\home.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -230,12 +224,6 @@ <Content Include="dashboard-ui\components\medialibraryeditor\medialibraryeditor.template.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\components\metadataeditor\personeditor.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\components\metadataeditor\metadataeditor.template.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\css\images\ani_equalizer_black.gif">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1423,11 +1411,6 @@ </Content>
</ItemGroup>
<ItemGroup>
- <Content Include="dashboard-ui\scripts\extensions.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- </ItemGroup>
- <ItemGroup>
<Content Include="dashboard-ui\css\images\supporter\supporterbadge.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1512,9 +1495,6 @@ </Content>
</ItemGroup>
<ItemGroup>
- <None Include="dashboard-ui\css\fonts\Montserrat.woff">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
<Content Include="dashboard-ui\strings\ar.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index dc92b318e..e96a77e64 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common.Internal</id> - <version>3.0.652</version> + <version>3.0.654</version> <title>MediaBrowser.Common.Internal</title> <authors>Luke</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,8 +12,8 @@ <description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description> <copyright>Copyright © Emby 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.652" /> - <dependency id="NLog" version="4.3.5" /> + <dependency id="MediaBrowser.Common" version="3.0.654" /> + <dependency id="NLog" version="4.3.6" /> <dependency id="SimpleInjector" version="3.2.0" /> </dependencies> </metadata> diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index a8b78b4e3..ada11fd45 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common</id> - <version>3.0.652</version> + <version>3.0.654</version> <title>MediaBrowser.Common</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 105d2c478..f4a79f454 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <metadata> <id>MediaBrowser.Server.Core</id> - <version>3.0.652</version> + <version>3.0.654</version> <title>Media Browser.Server.Core</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains core components required to build plugins for Emby Server.</description> <copyright>Copyright © Emby 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.652" /> + <dependency id="MediaBrowser.Common" version="3.0.654" /> <dependency id="Interfaces.IO" version="1.0.0.5" /> </dependencies> </metadata> |
