mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-09-06 03:26:07 +02:00
commit
06b0cfb86f
55 changed files with 2462 additions and 407 deletions
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) :
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 bytesRead = await CopyToAsyncInternal(fs, outputStream, BufferSize, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var fsPosition = fs.Position;
|
||||
|
||||
var bytesRead = fsPosition - position;
|
||||
|
||||
//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)
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
int bytesRead;
|
||||
int totalBytesRead = 0;
|
||||
|
||||
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
|
||||
{
|
||||
//if (_job != null)
|
||||
//{
|
||||
// var didPause = false;
|
||||
// var totalPauseTime = 0;
|
||||
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// 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);
|
||||
|
||||
_bytesWritten += count;
|
||||
_bytesWritten += bytesRead;
|
||||
totalBytesRead += bytesRead;
|
||||
|
||||
if (_job != null)
|
||||
{
|
||||
_job.BytesDownloaded = Math.Max(_job.BytesDownloaded ?? _bytesWritten, _bytesWritten);
|
||||
}
|
||||
}
|
||||
|
||||
return totalBytesRead;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,12 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||
}
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public override bool EnableForceSaveOnDateModifiedChange
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public Audio()
|
||||
{
|
||||
Artists = new List<string>();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -34,6 +34,12 @@ namespace MediaBrowser.Controller.Entities
|
|||
return SeriesName;
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public override bool EnableForceSaveOnDateModifiedChange
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public Guid? FindSeriesId()
|
||||
{
|
||||
return SeriesId;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,12 @@ namespace MediaBrowser.Controller.Entities
|
|||
}
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public override bool EnableForceSaveOnDateModifiedChange
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override bool CanDownload()
|
||||
{
|
||||
return true;
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
15
MediaBrowser.Model/Dlna/ITranscoderSupport.cs
Normal file
15
MediaBrowser.Model/Dlna/ITranscoderSupport.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -75,8 +75,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
{
|
||||
}
|
||||
|
||||
// 256k
|
||||
private const int BufferSize = 262144;
|
||||
private const int BufferSize = 81920;
|
||||
|
||||
/// <summary>
|
||||
/// Writes to.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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" />
|
||||
|
|
1348
MediaBrowser.Tests/ConsistencyTests/Resources/SampleTransformed.htm
Normal file
1348
MediaBrowser.Tests/ConsistencyTests/Resources/SampleTransformed.htm
Normal file
File diff suppressed because it is too large
Load diff
145
MediaBrowser.Tests/ConsistencyTests/Resources/StringCheck.xslt
Normal file
145
MediaBrowser.Tests/ConsistencyTests/Resources/StringCheck.xslt
Normal file
|
@ -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>
|
|
@ -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>
|
260
MediaBrowser.Tests/ConsistencyTests/StringUsageReporter.cs
Normal file
260
MediaBrowser.Tests/ConsistencyTests/StringUsageReporter.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
@ -1422,11 +1410,6 @@
|
|||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</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>
|
||||
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue