added new cabac value

pull/702/head
Luke Pulverenti 10 years ago
parent 35f40993b2
commit 5a5b48feff

@ -122,53 +122,12 @@ namespace MediaBrowser.Api
return ToOptimizedResult(result); return ToOptimizedResult(result);
} }
const string XbmcMetadata = "Xbmc Nfo";
const string MediaBrowserMetadata = "Media Browser Xml";
public void Post(AutoSetMetadataOptions request) public void Post(AutoSetMetadataOptions request)
{ {
var service = AutoDetectMetadataService(); _configurationManager.DisableMetadataService("Media Browser Xml");
Logger.Info("Setting preferred metadata format to " + service);
var serviceToDisable = string.Equals(service, XbmcMetadata) ?
MediaBrowserMetadata :
XbmcMetadata;
_configurationManager.DisableMetadataService(serviceToDisable);
_configurationManager.SaveConfiguration(); _configurationManager.SaveConfiguration();
} }
private string AutoDetectMetadataService()
{
try
{
var paths = _libraryManager.GetDefaultVirtualFolders()
.SelectMany(i => i.Locations)
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(i => new DirectoryInfo(i))
.ToList();
if (paths.SelectMany(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories))
.Any())
{
return XbmcMetadata;
}
if (paths.SelectMany(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories))
.Any(i => string.Equals(i.Name, "series.xml", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "movie.xml", StringComparison.OrdinalIgnoreCase)))
{
return MediaBrowserMetadata;
}
}
catch (Exception)
{
}
return XbmcMetadata;
}
/// <summary> /// <summary>
/// Posts the specified configuraiton. /// Posts the specified configuraiton.
/// </summary> /// </summary>

@ -1978,6 +1978,7 @@ namespace MediaBrowser.Api.Playback
state.TargetPacketLength, state.TargetPacketLength,
state.TargetTimestamp, state.TargetTimestamp,
state.IsTargetAnamorphic, state.IsTargetAnamorphic,
state.IsTargetCabac,
state.TargetRefFrames); state.TargetRefFrames);
if (mediaProfile != null) if (mediaProfile != null)
@ -2067,6 +2068,7 @@ namespace MediaBrowser.Api.Playback
state.TargetPacketLength, state.TargetPacketLength,
state.TranscodeSeekInfo, state.TranscodeSeekInfo,
state.IsTargetAnamorphic, state.IsTargetAnamorphic,
state.IsTargetCabac,
state.TargetRefFrames state.TargetRefFrames
).FirstOrDefault() ?? string.Empty; ).FirstOrDefault() ?? string.Empty;

@ -501,7 +501,7 @@ namespace MediaBrowser.Api.Playback.Hls
private void AppendPlaylist(StringBuilder builder, string url, int bitrate, string subtitleGroup) private void AppendPlaylist(StringBuilder builder, string url, int bitrate, string subtitleGroup)
{ {
var header = "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + bitrate.ToString(UsCulture); var header = "#EXT-X-STREAM-INF:BANDWIDTH=" + bitrate.ToString(UsCulture);
if (!string.IsNullOrWhiteSpace(subtitleGroup)) if (!string.IsNullOrWhiteSpace(subtitleGroup))
{ {

@ -1,5 +1,4 @@
using System.Threading; using MediaBrowser.Common.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using ServiceStack.Web; using ServiceStack.Web;
using System; using System;
@ -49,9 +48,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// <param name="responseStream">The response stream.</param> /// <param name="responseStream">The response stream.</param>
public void WriteTo(Stream responseStream) public void WriteTo(Stream responseStream)
{ {
var task = WriteToAsync(responseStream); WriteToInternal(responseStream);
Task.WaitAll(task);
} }
/// <summary> /// <summary>
@ -59,12 +56,12 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary> /// </summary>
/// <param name="responseStream">The response stream.</param> /// <param name="responseStream">The response stream.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
public async Task WriteToAsync(Stream responseStream) private void WriteToInternal(Stream responseStream)
{ {
try try
{ {
await new ProgressiveFileCopier(_fileSystem, _job) new ProgressiveFileCopier(_fileSystem, _job)
.StreamFile(Path, responseStream).ConfigureAwait(false); .StreamFile(Path, responseStream);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -95,16 +92,16 @@ namespace MediaBrowser.Api.Playback.Progressive
_job = job; _job = job;
} }
public async Task StreamFile(string path, Stream outputStream) public void StreamFile(string path, Stream outputStream)
{ {
var eofCount = 0; var eofCount = 0;
long position = 0; long position = 0;
using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, false))
{ {
while (eofCount < 15) while (eofCount < 15)
{ {
await CopyToAsyncInternal(fs, outputStream, 81920, CancellationToken.None).ConfigureAwait(false); CopyToInternal(fs, outputStream, 81920);
var fsPosition = fs.Position; var fsPosition = fs.Position;
@ -118,7 +115,8 @@ namespace MediaBrowser.Api.Playback.Progressive
{ {
eofCount++; eofCount++;
} }
await Task.Delay(100).ConfigureAwait(false); var task = Task.Delay(100);
Task.WaitAll(task);
} }
else else
{ {
@ -130,13 +128,13 @@ namespace MediaBrowser.Api.Playback.Progressive
} }
} }
private async Task CopyToAsyncInternal(Stream source, Stream destination, int bufferSize, CancellationToken cancellationToken) private void CopyToInternal(Stream source, Stream destination, int bufferSize)
{ {
byte[] array = new byte[bufferSize]; byte[] array = new byte[bufferSize];
int count; int count;
while ((count = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0) while ((count = source.Read(array, 0, array.Length)) != 0)
{ {
await destination.WriteAsync(array, 0, count, cancellationToken).ConfigureAwait(false); destination.Write(array, 0, count);
_bytesWritten += count; _bytesWritten += count;

@ -2,7 +2,6 @@
using ServiceStack.Web; using ServiceStack.Web;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks;
namespace MediaBrowser.Api.Playback namespace MediaBrowser.Api.Playback
{ {
@ -41,22 +40,7 @@ namespace MediaBrowser.Api.Playback
/// <param name="responseStream">The response stream.</param> /// <param name="responseStream">The response stream.</param>
public void WriteTo(Stream responseStream) public void WriteTo(Stream responseStream)
{ {
var task = WriteToAsync(responseStream); _response.Content.CopyTo(responseStream, 819200);
Task.WaitAll(task);
}
/// <summary>
/// Writes to async.
/// </summary>
/// <param name="responseStream">The response stream.</param>
/// <returns>Task.</returns>
public async Task WriteToAsync(Stream responseStream)
{
using (_response)
{
await _response.Content.CopyToAsync(responseStream, 819200).ConfigureAwait(false);
}
} }
} }
} }

@ -418,5 +418,18 @@ namespace MediaBrowser.Api.Playback
return false; return false;
} }
} }
public bool? IsTargetCabac
{
get
{
if (Request.Static)
{
return VideoStream == null ? null : VideoStream.IsCabac;
}
return true;
}
}
} }
} }

@ -434,21 +434,9 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
throw exception; throw exception;
} }
catch (HttpRequestException ex)
{
_logger.ErrorException("Error getting response from " + options.Url, ex);
throw new HttpException(ex.Message, ex);
}
catch (WebException ex)
{
throw GetException(ex, options);
}
catch (Exception ex) catch (Exception ex)
{ {
_logger.ErrorException("Error getting response from " + options.Url, ex); throw GetException(ex, options);
throw;
} }
finally finally
{ {
@ -636,21 +624,10 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
return GetResponseInfo(httpResponse, tempFile, contentLength); return GetResponseInfo(httpResponse, tempFile, contentLength);
} }
} }
catch (OperationCanceledException ex)
{
throw GetTempFileException(ex, options, tempFile);
}
catch (HttpRequestException ex)
{
throw GetTempFileException(ex, options, tempFile);
}
catch (WebException ex)
{
throw GetTempFileException(ex, options, tempFile);
}
catch (Exception ex) catch (Exception ex)
{ {
throw GetTempFileException(ex, options, tempFile); DeleteTempFile(tempFile);
throw GetException(ex, options);
} }
finally finally
{ {
@ -675,44 +652,25 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
protected static readonly CultureInfo UsCulture = new CultureInfo("en-US"); protected static readonly CultureInfo UsCulture = new CultureInfo("en-US");
/// <summary> private Exception GetException(Exception ex, HttpRequestOptions options)
/// Handles the temp file exception.
/// </summary>
/// <param name="ex">The ex.</param>
/// <param name="options">The options.</param>
/// <param name="tempFile">The temp file.</param>
/// <returns>Task.</returns>
/// <exception cref="HttpException"></exception>
private Exception GetTempFileException(Exception ex, HttpRequestOptions options, string tempFile)
{ {
var operationCanceledException = ex as OperationCanceledException; var webException = ex as WebException
?? ex.InnerException as WebException;
if (operationCanceledException != null) if (webException != null)
{ {
// Cleanup return GetException(webException, options);
DeleteTempFile(tempFile);
return GetCancellationException(options.Url, options.CancellationToken, operationCanceledException);
} }
_logger.ErrorException("Error getting response from " + options.Url, ex); var operationCanceledException = ex as OperationCanceledException
?? ex.InnerException as OperationCanceledException;
// Cleanup
DeleteTempFile(tempFile);
var httpRequestException = ex as HttpRequestException;
if (httpRequestException != null) if (operationCanceledException != null)
{ {
return new HttpException(ex.Message, ex); return GetCancellationException(options.Url, options.CancellationToken, operationCanceledException);
} }
var webException = ex as WebException; _logger.ErrorException("Error getting response from " + options.Url, ex);
if (webException != null)
{
throw GetException(webException, options);
}
return ex; return ex;
} }

@ -4,5 +4,7 @@ namespace MediaBrowser.Controller.Connect
public class UserLinkResult public class UserLinkResult
{ {
public bool IsPending { get; set; } public bool IsPending { get; set; }
public bool IsNewUserInvitation { get; set; }
public string GuestDisplayName { get; set; }
} }
} }

@ -74,7 +74,8 @@ namespace MediaBrowser.Controller.Entities
{ {
FileInfo = new DirectoryInfo(path), FileInfo = new DirectoryInfo(path),
Path = path, Path = path,
Parent = Parent Parent = Parent,
CollectionType = CollectionType
}; };
// Gather child folder and files // Gather child folder and files

@ -736,7 +736,9 @@ namespace MediaBrowser.Controller.Entities
/// <returns>IEnumerable{BaseItem}.</returns> /// <returns>IEnumerable{BaseItem}.</returns>
protected virtual IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService) protected virtual IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
{ {
return LibraryManager.ResolvePaths<BaseItem>(GetFileSystemChildren(directoryService), directoryService, this); var collectionType = LibraryManager.FindCollectionType(this);
return LibraryManager.ResolvePaths<BaseItem>(GetFileSystemChildren(directoryService), directoryService, this, collectionType);
} }
/// <summary> /// <summary>
@ -745,7 +747,16 @@ namespace MediaBrowser.Controller.Entities
/// <returns>IEnumerable{BaseItem}.</returns> /// <returns>IEnumerable{BaseItem}.</returns>
protected IEnumerable<BaseItem> GetCachedChildren() protected IEnumerable<BaseItem> GetCachedChildren()
{ {
return ItemRepository.GetChildren(Id).Select(RetrieveChild).Where(i => i != null); var childrenItems = ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null);
//var children = ItemRepository.GetChildren(Id).Select(RetrieveChild).Where(i => i != null).ToList();
//if (children.Count != childrenItems.Count)
//{
// var b = this;
//}
return childrenItems;
} }
/// <summary> /// <summary>
@ -770,6 +781,29 @@ namespace MediaBrowser.Controller.Entities
return item; return item;
} }
private BaseItem RetrieveChild(BaseItem child)
{
var item = LibraryManager.GetMemoryItemById(child.Id);
if (item != null)
{
if (item is IByReferenceItem)
{
return LibraryManager.GetOrAddByReferenceItem(item);
}
item.Parent = this;
}
else
{
child.Parent = this;
LibraryManager.RegisterItem(child);
item = child;
}
return item;
}
public virtual Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) public virtual Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query)
{ {
var user = query.User; var user = query.User;

@ -17,30 +17,14 @@ namespace MediaBrowser.Controller.Library
/// </summary> /// </summary>
public interface ILibraryManager public interface ILibraryManager
{ {
/// <summary>
/// Resolves the item.
/// </summary>
/// <param name="args">The args.</param>
/// <returns>BaseItem.</returns>
BaseItem ResolveItem(ItemResolveArgs args);
/// <summary>
/// Resolves a path into a BaseItem
/// </summary>
/// <param name="fileInfo">The file info.</param>
/// <param name="directoryService">The directory service.</param>
/// <param name="parent">The parent.</param>
/// <returns>BaseItem.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
BaseItem ResolvePath(FileSystemInfo fileInfo, IDirectoryService directoryService, Folder parent = null);
/// <summary> /// <summary>
/// Resolves the path. /// Resolves the path.
/// </summary> /// </summary>
/// <param name="fileInfo">The file information.</param> /// <param name="fileInfo">The file information.</param>
/// <param name="parent">The parent.</param> /// <param name="parent">The parent.</param>
/// <param name="collectionType">Type of the collection.</param>
/// <returns>BaseItem.</returns> /// <returns>BaseItem.</returns>
BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null); BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null, string collectionType = null);
/// <summary> /// <summary>
/// Resolves a set of files into a list of BaseItem /// Resolves a set of files into a list of BaseItem
@ -49,8 +33,9 @@ namespace MediaBrowser.Controller.Library
/// <param name="files">The files.</param> /// <param name="files">The files.</param>
/// <param name="directoryService">The directory service.</param> /// <param name="directoryService">The directory service.</param>
/// <param name="parent">The parent.</param> /// <param name="parent">The parent.</param>
/// <param name="collectionType">Type of the collection.</param>
/// <returns>List{``0}.</returns> /// <returns>List{``0}.</returns>
List<T> ResolvePaths<T>(IEnumerable<FileSystemInfo> files, IDirectoryService directoryService, Folder parent) List<T> ResolvePaths<T>(IEnumerable<FileSystemInfo> files, IDirectoryService directoryService, Folder parent, string collectionType = null)
where T : BaseItem; where T : BaseItem;
/// <summary> /// <summary>
@ -151,6 +136,13 @@ namespace MediaBrowser.Controller.Library
/// <returns>BaseItem.</returns> /// <returns>BaseItem.</returns>
BaseItem GetItemById(Guid id); BaseItem GetItemById(Guid id);
/// <summary>
/// Gets the memory item by identifier.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>BaseItem.</returns>
BaseItem GetMemoryItemById(Guid id);
/// <summary> /// <summary>
/// Gets the intros. /// Gets the intros.
/// </summary> /// </summary>

@ -230,29 +230,18 @@ namespace MediaBrowser.Controller.Library
} }
/// <summary> /// <summary>
/// Gets the name of the meta file by. /// Determines whether [contains meta file by name] [the specified name].
/// </summary> /// </summary>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <returns>FileSystemInfo.</returns> /// <returns><c>true</c> if [contains meta file by name] [the specified name]; otherwise, <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException"></exception> public bool ContainsMetaFileByName(string name)
public FileSystemInfo GetMetaFileByName(string name)
{ {
if (string.IsNullOrEmpty(name)) if (string.IsNullOrEmpty(name))
{ {
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
return GetFileSystemEntryByName(name); return GetFileSystemEntryByName(name) != null;
}
/// <summary>
/// Determines whether [contains meta file by name] [the specified name].
/// </summary>
/// <param name="name">The name.</param>
/// <returns><c>true</c> if [contains meta file by name] [the specified name]; otherwise, <c>false</c>.</returns>
public bool ContainsMetaFileByName(string name)
{
return GetMetaFileByName(name) != null;
} }
/// <summary> /// <summary>
@ -265,20 +254,13 @@ namespace MediaBrowser.Controller.Library
return GetFileSystemEntryByName(name) != null; return GetFileSystemEntryByName(name) != null;
} }
private bool _collectionTypeDiscovered;
private string _collectionType;
public string GetCollectionType() public string GetCollectionType()
{ {
if (!_collectionTypeDiscovered) return CollectionType;
{
_collectionType = Parent == null ? null : _libraryManager.FindCollectionType(Parent);
_collectionTypeDiscovered = true;
}
return _collectionType;
} }
public string CollectionType { get; set; }
#region Equality Overrides #region Equality Overrides
/// <summary> /// <summary>

@ -284,7 +284,7 @@ namespace MediaBrowser.Controller.Library
{ {
if (IsSeasonFolder(child.FullName, directoryService, fileSystem)) if (IsSeasonFolder(child.FullName, directoryService, fileSystem))
{ {
logger.Debug("{0} is a series because of season folder {1}.", path, child.FullName); //logger.Debug("{0} is a series because of season folder {1}.", path, child.FullName);
return true; return true;
} }

@ -33,7 +33,7 @@ namespace MediaBrowser.Controller.Persistence
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task DeleteItem(Guid id, CancellationToken cancellationToken); Task DeleteItem(Guid id, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the critic reviews. /// Gets the critic reviews.
/// </summary> /// </summary>
@ -41,6 +41,13 @@ namespace MediaBrowser.Controller.Persistence
/// <returns>Task{IEnumerable{ItemReview}}.</returns> /// <returns>Task{IEnumerable{ItemReview}}.</returns>
IEnumerable<ItemReview> GetCriticReviews(Guid itemId); IEnumerable<ItemReview> GetCriticReviews(Guid itemId);
/// <summary>
/// Gets the children items.
/// </summary>
/// <param name="parentId">The parent identifier.</param>
/// <returns>IEnumerable&lt;BaseItem&gt;.</returns>
IEnumerable<BaseItem> GetChildrenItems(Guid parentId);
/// <summary> /// <summary>
/// Saves the critic reviews. /// Saves the critic reviews.
/// </summary> /// </summary>
@ -101,7 +108,7 @@ namespace MediaBrowser.Controller.Persistence
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <returns>IEnumerable{Guid}.</returns> /// <returns>IEnumerable{Guid}.</returns>
IEnumerable<BaseItem> GetItemsOfType(Type type); IEnumerable<BaseItem> GetItemsOfType(Type type);
/// <summary> /// <summary>
/// Saves the children. /// Saves the children.
/// </summary> /// </summary>

@ -246,7 +246,7 @@ namespace MediaBrowser.Controller.Resolvers
if (config.UseFileCreationTimeForDateAdded) if (config.UseFileCreationTimeForDateAdded)
{ {
item.DateModified = fileSystem.GetCreationTimeUtc(info); item.DateCreated = fileSystem.GetCreationTimeUtc(info);
} }
else else
{ {

@ -80,6 +80,12 @@ namespace MediaBrowser.Controller.Session
/// <value>The last activity date.</value> /// <value>The last activity date.</value>
public DateTime LastActivityDate { get; set; } public DateTime LastActivityDate { get; set; }
/// <summary>
/// Gets or sets the last playback check in.
/// </summary>
/// <value>The last playback check in.</value>
public DateTime LastPlaybackCheckIn { get; set; }
/// <summary> /// <summary>
/// Gets or sets the name of the device. /// Gets or sets the name of the device.
/// </summary> /// </summary>

@ -149,6 +149,7 @@ namespace MediaBrowser.Dlna.Didl
streamInfo.TargetPacketLength, streamInfo.TargetPacketLength,
streamInfo.TranscodeSeekInfo, streamInfo.TranscodeSeekInfo,
streamInfo.IsTargetAnamorphic, streamInfo.IsTargetAnamorphic,
streamInfo.IsTargetCabac,
streamInfo.TargetRefFrames); streamInfo.TargetRefFrames);
foreach (var contentFeature in contentFeatureList) foreach (var contentFeature in contentFeatureList)
@ -270,6 +271,7 @@ namespace MediaBrowser.Dlna.Didl
streamInfo.TargetPacketLength, streamInfo.TargetPacketLength,
streamInfo.TargetTimestamp, streamInfo.TargetTimestamp,
streamInfo.IsTargetAnamorphic, streamInfo.IsTargetAnamorphic,
streamInfo.IsTargetCabac,
streamInfo.TargetRefFrames); streamInfo.TargetRefFrames);
var filename = url.Substring(0, url.IndexOf('?')); var filename = url.Substring(0, url.IndexOf('?'));

@ -452,19 +452,9 @@ namespace MediaBrowser.Dlna.PlayTo
private void AddItemFromId(Guid id, List<BaseItem> list) private void AddItemFromId(Guid id, List<BaseItem> list)
{ {
var item = _libraryManager.GetItemById(id); var item = _libraryManager.GetItemById(id);
if (item.IsFolder) if (item.MediaType == MediaType.Audio || item.MediaType == MediaType.Video)
{ {
foreach (var childId in _itemRepository.GetChildren(item.Id)) list.Add(item);
{
AddItemFromId(childId, list);
}
}
else
{
if (item.MediaType == MediaType.Audio || item.MediaType == MediaType.Video)
{
list.Add(item);
}
} }
} }
@ -537,6 +527,7 @@ namespace MediaBrowser.Dlna.PlayTo
streamInfo.TargetPacketLength, streamInfo.TargetPacketLength,
streamInfo.TranscodeSeekInfo, streamInfo.TranscodeSeekInfo,
streamInfo.IsTargetAnamorphic, streamInfo.IsTargetAnamorphic,
streamInfo.IsTargetCabac,
streamInfo.TargetRefFrames); streamInfo.TargetRefFrames);
return list.FirstOrDefault(); return list.FirstOrDefault();

@ -51,6 +51,7 @@
<ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="false" /> <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="false" />
<ProfileCondition condition="LessThanEqual" property="VideoBitDepth" value="8" isRequired="false" /> <ProfileCondition condition="LessThanEqual" property="VideoBitDepth" value="8" isRequired="false" />
<ProfileCondition condition="NotEquals" property="IsAnamorphic" value="true" isRequired="false" /> <ProfileCondition condition="NotEquals" property="IsAnamorphic" value="true" isRequired="false" />
<ProfileCondition condition="Equals" property="IsCabac" value="true" isRequired="false" />
</Conditions> </Conditions>
</CodecProfile> </CodecProfile>
<CodecProfile type="Video"> <CodecProfile type="Video">

File diff suppressed because one or more lines are too long

@ -24,6 +24,12 @@ namespace MediaBrowser.MediaInfo
result.IsInterlaced = text.IndexOf("interlac", StringComparison.OrdinalIgnoreCase) != -1; result.IsInterlaced = text.IndexOf("interlac", StringComparison.OrdinalIgnoreCase) != -1;
} }
text = GetValue(lib, videoStreamIndex, new[] { "Format_Settings_CABAC", "Format_Settings_CABAC/String" });
if (!string.IsNullOrWhiteSpace(text))
{
result.IsCabac = string.Equals(text, "yes", StringComparison.OrdinalIgnoreCase);
}
int bitDepth; int bitDepth;
text = GetValue(lib, videoStreamIndex, new[] { "BitDepth", "BitDepth/String" }); text = GetValue(lib, videoStreamIndex, new[] { "BitDepth", "BitDepth/String" });
@ -51,6 +57,7 @@ namespace MediaBrowser.MediaInfo
public class MediaInfoResult public class MediaInfoResult
{ {
public bool? IsCabac { get; set; }
public bool? IsInterlaced { get; set; } public bool? IsInterlaced { get; set; }
public int? BitDepth { get; set; } public int? BitDepth { get; set; }
public int? RefFrames { get; set; } public int? RefFrames { get; set; }

@ -1088,9 +1088,6 @@
<Compile Include="..\MediaBrowser.Model\Users\AuthenticationResult.cs"> <Compile Include="..\MediaBrowser.Model\Users\AuthenticationResult.cs">
<Link>Users\AuthenticationResult.cs</Link> <Link>Users\AuthenticationResult.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\Weather\WeatherUnits.cs">
<Link>Weather\WeatherUnits.cs</Link>
</Compile>
<Compile Include="..\SharedVersion.cs"> <Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link> <Link>Properties\SharedVersion.cs</Link>
</Compile> </Compile>

@ -1047,9 +1047,6 @@
<Compile Include="..\MediaBrowser.Model\Users\AuthenticationResult.cs"> <Compile Include="..\MediaBrowser.Model\Users\AuthenticationResult.cs">
<Link>Users\AuthenticationResult.cs</Link> <Link>Users\AuthenticationResult.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\Weather\WeatherUnits.cs">
<Link>Weather\WeatherUnits.cs</Link>
</Compile>
<Compile Include="..\SharedVersion.cs"> <Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link> <Link>Properties\SharedVersion.cs</Link>
</Compile> </Compile>

@ -19,6 +19,7 @@ namespace MediaBrowser.Model.Dlna
int? packetLength, int? packetLength,
TransportStreamTimestamp? timestamp, TransportStreamTimestamp? timestamp,
bool? isAnamorphic, bool? isAnamorphic,
bool? isCabac,
int? refFrames) int? refFrames)
{ {
switch (condition.Property) switch (condition.Property)
@ -31,6 +32,8 @@ namespace MediaBrowser.Model.Dlna
return true; return true;
case ProfileConditionValue.IsAnamorphic: case ProfileConditionValue.IsAnamorphic:
return IsConditionSatisfied(condition, isAnamorphic); return IsConditionSatisfied(condition, isAnamorphic);
case ProfileConditionValue.IsCabac:
return IsConditionSatisfied(condition, isCabac);
case ProfileConditionValue.VideoFramerate: case ProfileConditionValue.VideoFramerate:
return IsConditionSatisfied(condition, videoFramerate); return IsConditionSatisfied(condition, videoFramerate);
case ProfileConditionValue.VideoLevel: case ProfileConditionValue.VideoLevel:

@ -116,6 +116,7 @@ namespace MediaBrowser.Model.Dlna
int? packetLength, int? packetLength,
TranscodeSeekInfo transcodeSeekInfo, TranscodeSeekInfo transcodeSeekInfo,
bool? isAnamorphic, bool? isAnamorphic,
bool? isCabac,
int? refFrames) int? refFrames)
{ {
// first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none // first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none
@ -156,6 +157,7 @@ namespace MediaBrowser.Model.Dlna
packetLength, packetLength,
timestamp, timestamp,
isAnamorphic, isAnamorphic,
isCabac,
refFrames); refFrames);
List<string> orgPnValues = new List<string>(); List<string> orgPnValues = new List<string>();

@ -281,6 +281,7 @@ namespace MediaBrowser.Model.Dlna
int? packetLength, int? packetLength,
TransportStreamTimestamp timestamp, TransportStreamTimestamp timestamp,
bool? isAnamorphic, bool? isAnamorphic,
bool? isCabac,
int? refFrames) int? refFrames)
{ {
container = StringHelper.TrimStart((container ?? string.Empty), '.'); container = StringHelper.TrimStart((container ?? string.Empty), '.');
@ -315,7 +316,7 @@ namespace MediaBrowser.Model.Dlna
var anyOff = false; var anyOff = false;
foreach (ProfileCondition c in i.Conditions) foreach (ProfileCondition c in i.Conditions)
{ {
if (!conditionProcessor.IsVideoConditionSatisfied(c, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames)) if (!conditionProcessor.IsVideoConditionSatisfied(c, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames))
{ {
anyOff = true; anyOff = true;
break; break;

@ -16,6 +16,7 @@
VideoProfile = 11, VideoProfile = 11,
VideoTimestamp = 12, VideoTimestamp = 12,
IsAnamorphic = 13, IsAnamorphic = 13,
RefFrames = 14 RefFrames = 14,
IsCabac = 15
} }
} }

@ -114,7 +114,8 @@ namespace MediaBrowser.Model.Dlna.Profiles
new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.Width, "1920"), new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.Width, "1920"),
new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.Height, "1080"), new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.Height, "1080"),
new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.VideoBitDepth, "8"), new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.VideoBitDepth, "8"),
new ProfileCondition(ProfileConditionType.NotEquals, ProfileConditionValue.IsAnamorphic, "true") new ProfileCondition(ProfileConditionType.NotEquals, ProfileConditionValue.IsAnamorphic, "true"),
new ProfileCondition(ProfileConditionType.Equals, ProfileConditionValue.IsCabac, "true")
} }
}, },

@ -419,6 +419,7 @@ namespace MediaBrowser.Model.Dlna
string videoProfile = videoStream == null ? null : videoStream.Profile; string videoProfile = videoStream == null ? null : videoStream.Profile;
float? videoFramerate = videoStream == null ? null : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate; float? videoFramerate = videoStream == null ? null : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate;
bool? isAnamorphic = videoStream == null ? null : videoStream.IsAnamorphic; bool? isAnamorphic = videoStream == null ? null : videoStream.IsAnamorphic;
bool? isCabac = videoStream == null ? null : videoStream.IsCabac;
int? audioBitrate = audioStream == null ? null : audioStream.BitRate; int? audioBitrate = audioStream == null ? null : audioStream.BitRate;
int? audioChannels = audioStream == null ? null : audioStream.Channels; int? audioChannels = audioStream == null ? null : audioStream.Channels;
@ -431,7 +432,7 @@ namespace MediaBrowser.Model.Dlna
// Check container conditions // Check container conditions
foreach (ProfileCondition i in conditions) foreach (ProfileCondition i in conditions)
{ {
if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames)) if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames))
{ {
return null; return null;
} }
@ -458,7 +459,7 @@ namespace MediaBrowser.Model.Dlna
foreach (ProfileCondition i in conditions) foreach (ProfileCondition i in conditions)
{ {
if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames)) if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames))
{ {
return null; return null;
} }
@ -647,6 +648,7 @@ namespace MediaBrowser.Model.Dlna
} }
case ProfileConditionValue.AudioProfile: case ProfileConditionValue.AudioProfile:
case ProfileConditionValue.IsAnamorphic: case ProfileConditionValue.IsAnamorphic:
case ProfileConditionValue.IsCabac:
case ProfileConditionValue.Has64BitOffsets: case ProfileConditionValue.Has64BitOffsets:
case ProfileConditionValue.PacketLength: case ProfileConditionValue.PacketLength:
case ProfileConditionValue.VideoTimestamp: case ProfileConditionValue.VideoTimestamp:

@ -456,6 +456,19 @@ namespace MediaBrowser.Model.Dlna
} }
} }
public bool? IsTargetCabac
{
get
{
if (IsDirectStream)
{
return TargetVideoStream == null ? null : TargetVideoStream.IsCabac;
}
return true;
}
}
public int? TargetWidth public int? TargetWidth
{ {
get get

@ -175,5 +175,11 @@ namespace MediaBrowser.Model.Entities
/// </summary> /// </summary>
/// <value><c>true</c> if this instance is anamorphic; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance is anamorphic; otherwise, <c>false</c>.</value>
public bool? IsAnamorphic { get; set; } public bool? IsAnamorphic { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is cabac.
/// </summary>
/// <value><c>null</c> if [is cabac] contains no value, <c>true</c> if [is cabac]; otherwise, <c>false</c>.</value>
public bool? IsCabac { get; set; }
} }
} }

@ -402,7 +402,6 @@
<Compile Include="Updates\PackageInfo.cs" /> <Compile Include="Updates\PackageInfo.cs" />
<Compile Include="Updates\PackageVersionInfo.cs" /> <Compile Include="Updates\PackageVersionInfo.cs" />
<Compile Include="Users\AuthenticationResult.cs" /> <Compile Include="Users\AuthenticationResult.cs" />
<Compile Include="Weather\WeatherUnits.cs" />
<None Include="Fody.targets" /> <None Include="Fody.targets" />
<None Include="FodyWeavers.xml" /> <None Include="FodyWeavers.xml" />
<None Include="MediaBrowser.Model.snk" /> <None Include="MediaBrowser.Model.snk" />

@ -5,26 +5,7 @@ namespace MediaBrowser.Model.Querying
/// <summary> /// <summary>
/// Represents the result of a query for items /// Represents the result of a query for items
/// </summary> /// </summary>
public class ItemsResult public class ItemsResult : QueryResult<BaseItemDto>
{ {
/// <summary>
/// The set of items returned based on sorting, paging, etc
/// </summary>
/// <value>The items.</value>
public BaseItemDto[] Items { get; set; }
/// <summary>
/// The total number of records available
/// </summary>
/// <value>The total record count.</value>
public int TotalRecordCount { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ItemsResult"/> class.
/// </summary>
public ItemsResult()
{
Items = new BaseItemDto[] { };
}
} }
} }

@ -1,17 +0,0 @@
namespace MediaBrowser.Model.Weather
{
/// <summary>
/// Enum WeatherUnits
/// </summary>
public enum WeatherUnits
{
/// <summary>
/// The fahrenheit
/// </summary>
Fahrenheit,
/// <summary>
/// The celsius
/// </summary>
Celsius
}
}

@ -286,6 +286,7 @@ namespace MediaBrowser.Providers.MediaInfo
{ {
var result = new MediaInfoLib().GetVideoInfo(video.Path); var result = new MediaInfoLib().GetVideoInfo(video.Path);
videoStream.IsCabac = result.IsCabac ?? videoStream.IsCabac;
videoStream.IsInterlaced = result.IsInterlaced ?? videoStream.IsInterlaced; videoStream.IsInterlaced = result.IsInterlaced ?? videoStream.IsInterlaced;
videoStream.BitDepth = result.BitDepth ?? videoStream.BitDepth; videoStream.BitDepth = result.BitDepth ?? videoStream.BitDepth;
videoStream.RefFrames = result.RefFrames; videoStream.RefFrames = result.RefFrames;

@ -18,7 +18,7 @@ namespace MediaBrowser.Providers.Movies
{ {
class FanartMovieUpdatesPostScanTask : ILibraryPostScanTask class FanartMovieUpdatesPostScanTask : ILibraryPostScanTask
{ {
private const string UpdatesUrl = "http://api.fanart.tv/webservice/newmovies/{0}/{1}/"; private const string UpdatesUrl = "http://webservice.fanart.tv/v3/movies/latest?api_key={0}&date={1}";
/// <summary> /// <summary>
/// The _HTTP client /// The _HTTP client
@ -118,11 +118,26 @@ namespace MediaBrowser.Providers.Movies
return new List<string>(); return new List<string>();
} }
var updates = _jsonSerializer.DeserializeFromString<List<FanartUpdatesPostScanTask.FanArtUpdate>>(json); var updates = _jsonSerializer.DeserializeFromString<List<RootObject>>(json);
var existingDictionary = existingIds.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); var existingDictionary = existingIds.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
return updates.Select(i => i.id).Where(existingDictionary.ContainsKey); return updates.SelectMany(i =>
{
var list = new List<string>();
if (!string.IsNullOrWhiteSpace(i.imdb_id))
{
list.Add(i.imdb_id);
}
if (!string.IsNullOrWhiteSpace(i.tmdb_id))
{
list.Add(i.tmdb_id);
}
return list;
}).Where(existingDictionary.ContainsKey);
} }
} }
} }
@ -136,7 +151,7 @@ namespace MediaBrowser.Providers.Movies
{ {
_logger.Info("Updating movie " + id); _logger.Info("Updating movie " + id);
await FanartMovieImageProvider.Current.DownloadMovieXml(id, cancellationToken).ConfigureAwait(false); await FanartMovieImageProvider.Current.DownloadMovieJson(id, cancellationToken).ConfigureAwait(false);
numComplete++; numComplete++;
double percent = numComplete; double percent = numComplete;
@ -157,9 +172,10 @@ namespace MediaBrowser.Providers.Movies
return (dateTime - new DateTime(1970, 1, 1).ToUniversalTime()).TotalSeconds; return (dateTime - new DateTime(1970, 1, 1).ToUniversalTime()).TotalSeconds;
} }
public class FanArtUpdate public class RootObject
{ {
public string id { get; set; } public string tmdb_id { get; set; }
public string imdb_id { get; set; }
public string name { get; set; } public string name { get; set; }
public string new_images { get; set; } public string new_images { get; set; }
public string total_images { get; set; } public string total_images { get; set; }

@ -1,25 +1,22 @@
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO; using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Providers.Music; using MediaBrowser.Providers.Music;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml;
namespace MediaBrowser.Providers.Movies namespace MediaBrowser.Providers.Movies
{ {
@ -29,16 +26,19 @@ namespace MediaBrowser.Providers.Movies
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _json;
private const string FanArtBaseUrl = "http://api.fanart.tv/webservice/movie/{0}/{1}/xml/all/1/1"; private const string FanArtBaseUrl = "http://webservice.fanart.tv/v3/movies/{1}?api_key={0}";
// &client_key=52c813aa7b8c8b3bb87f4797532a2f8c
internal static FanartMovieImageProvider Current; internal static FanartMovieImageProvider Current;
public FanartMovieImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) public FanartMovieImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IJsonSerializer json)
{ {
_config = config; _config = config;
_httpClient = httpClient; _httpClient = httpClient;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_json = json;
Current = this; Current = this;
} }
@ -88,13 +88,13 @@ namespace MediaBrowser.Providers.Movies
if (!string.IsNullOrEmpty(movieId)) if (!string.IsNullOrEmpty(movieId))
{ {
await EnsureMovieXml(movieId, cancellationToken).ConfigureAwait(false); await EnsureMovieJson(movieId, cancellationToken).ConfigureAwait(false);
var xmlPath = GetFanartXmlPath(movieId); var path = GetFanartJsonPath(movieId);
try try
{ {
AddImages(list, xmlPath, cancellationToken); AddImages(list, path, cancellationToken);
} }
catch (FileNotFoundException) catch (FileNotFoundException)
{ {
@ -130,198 +130,63 @@ namespace MediaBrowser.Providers.Movies
.ThenByDescending(i => i.CommunityRating ?? 0); .ThenByDescending(i => i.CommunityRating ?? 0);
} }
private void AddImages(List<RemoteImageInfo> list, string xmlPath, CancellationToken cancellationToken) private void AddImages(List<RemoteImageInfo> list, string path, CancellationToken cancellationToken)
{ {
using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8)) var root = _json.DeserializeFromFile<RootObject>(path);
{
// Use XmlReader for best performance
using (var reader = XmlReader.Create(streamReader, new XmlReaderSettings
{
CheckCharacters = false,
IgnoreProcessingInstructions = true,
IgnoreComments = true,
ValidationType = ValidationType.None
}))
{
reader.MoveToContent();
// Loop through each element
while (reader.Read())
{
cancellationToken.ThrowIfCancellationRequested();
if (reader.NodeType == XmlNodeType.Element) AddImages(list, root, cancellationToken);
{
switch (reader.Name)
{
case "movie":
{
using (var subReader = reader.ReadSubtree())
{
AddImages(list, subReader, cancellationToken);
}
break;
}
default:
reader.Skip();
break;
}
}
}
}
}
} }
private void AddImages(List<RemoteImageInfo> list, XmlReader reader, CancellationToken cancellationToken) private void AddImages(List<RemoteImageInfo> list, RootObject obj, CancellationToken cancellationToken)
{ {
reader.MoveToContent(); PopulateImages(list, obj.hdmovieclearart, ImageType.Art, 1000, 562);
PopulateImages(list, obj.hdmovielogo, ImageType.Logo, 800, 310);
while (reader.Read()) PopulateImages(list, obj.moviedisc, ImageType.Disc, 1000, 1000);
{ PopulateImages(list, obj.movieposter, ImageType.Primary, 1000, 1426);
if (reader.NodeType == XmlNodeType.Element) PopulateImages(list, obj.movielogo, ImageType.Logo, 400, 155);
{ PopulateImages(list, obj.movieart, ImageType.Art, 500, 281);
switch (reader.Name) PopulateImages(list, obj.moviethumb, ImageType.Thumb, 1000, 562);
{ PopulateImages(list, obj.moviebanner, ImageType.Banner, 1000, 185);
case "hdmoviecleararts": PopulateImages(list, obj.moviebackground, ImageType.Backdrop, 1920, 1080);
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Art, 1000, 562);
}
break;
}
case "hdmovielogos":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Logo, 800, 310);
}
break;
}
case "moviediscs":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Disc, 1000, 1000);
}
break;
}
case "movieposters":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Primary, 1000, 1426);
}
break;
}
case "movielogos":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Logo, 400, 155);
}
break;
}
case "moviearts":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Art, 500, 281);
}
break;
}
case "moviethumbs":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Thumb, 1000, 562);
}
break;
}
case "moviebanners":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Banner, 1000, 185);
}
break;
}
case "moviebackgrounds":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Backdrop, 1920, 1080);
}
break;
}
default:
{
using (reader.ReadSubtree())
{
}
break;
}
}
}
}
} }
private void PopulateImageCategory(List<RemoteImageInfo> list, XmlReader reader, CancellationToken cancellationToken, ImageType type, int width, int height) private void PopulateImages(List<RemoteImageInfo> list, List<Image> images, ImageType type, int width, int height)
{ {
reader.MoveToContent(); if (images == null)
{
return;
}
while (reader.Read()) list.AddRange(images.Select(i =>
{ {
cancellationToken.ThrowIfCancellationRequested(); var url = i.url;
if (reader.NodeType == XmlNodeType.Element) if (!string.IsNullOrEmpty(url))
{ {
switch (reader.Name) var likesString = i.likes;
int likes;
var info = new RemoteImageInfo
{
RatingType = RatingType.Likes,
Type = type,
Width = width,
Height = height,
ProviderName = Name,
Url = url,
Language = i.lang
};
if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Any, _usCulture, out likes))
{ {
case "hdmovielogo": info.CommunityRating = likes;
case "moviedisc":
case "hdmovieclearart":
case "movieposter":
case "movielogo":
case "movieart":
case "moviethumb":
case "moviebanner":
case "moviebackground":
{
var url = reader.GetAttribute("url");
if (!string.IsNullOrEmpty(url))
{
var likesString = reader.GetAttribute("likes");
int likes;
var info = new RemoteImageInfo
{
RatingType = RatingType.Likes,
Type = type,
Width = width,
Height = height,
ProviderName = Name,
Url = url,
Language = reader.GetAttribute("lang")
};
if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Any, _usCulture, out likes))
{
info.CommunityRating = likes;
}
list.Add(info);
}
break;
}
default:
reader.Skip();
break;
} }
return info;
} }
}
return null;
}).Where(i => i != null));
} }
public int Order public int Order
@ -347,13 +212,17 @@ namespace MediaBrowser.Providers.Movies
} }
var id = item.GetProviderId(MetadataProviders.Tmdb); var id = item.GetProviderId(MetadataProviders.Tmdb);
if (string.IsNullOrEmpty(id))
{
id = item.GetProviderId(MetadataProviders.Imdb);
}
if (!string.IsNullOrEmpty(id)) if (!string.IsNullOrEmpty(id))
{ {
// Process images // Process images
var xmlPath = GetFanartXmlPath(id); var path = GetFanartJsonPath(id);
var fileInfo = new FileInfo(xmlPath); var fileInfo = new FileInfo(path);
return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > date; return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > date;
} }
@ -364,12 +233,12 @@ namespace MediaBrowser.Providers.Movies
/// <summary> /// <summary>
/// Gets the movie data path. /// Gets the movie data path.
/// </summary> /// </summary>
/// <param name="appPaths">The app paths.</param> /// <param name="appPaths">The application paths.</param>
/// <param name="tmdbId">The TMDB id.</param> /// <param name="id">The identifier.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
internal static string GetMovieDataPath(IApplicationPaths appPaths, string tmdbId) internal static string GetMovieDataPath(IApplicationPaths appPaths, string id)
{ {
var dataPath = Path.Combine(GetMoviesDataPath(appPaths), tmdbId); var dataPath = Path.Combine(GetMoviesDataPath(appPaths), id);
return dataPath; return dataPath;
} }
@ -386,27 +255,27 @@ namespace MediaBrowser.Providers.Movies
return dataPath; return dataPath;
} }
public string GetFanartXmlPath(string tmdbId) public string GetFanartJsonPath(string id)
{ {
var movieDataPath = GetMovieDataPath(_config.ApplicationPaths, tmdbId); var movieDataPath = GetMovieDataPath(_config.ApplicationPaths, id);
return Path.Combine(movieDataPath, "fanart.xml"); return Path.Combine(movieDataPath, "fanart.json");
} }
/// <summary> /// <summary>
/// Downloads the movie XML. /// Downloads the movie json.
/// </summary> /// </summary>
/// <param name="tmdbId">The TMDB id.</param> /// <param name="id">The identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
internal async Task DownloadMovieXml(string tmdbId, CancellationToken cancellationToken) internal async Task DownloadMovieJson(string id, CancellationToken cancellationToken)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
var url = string.Format(FanArtBaseUrl, FanartArtistProvider.ApiKey, tmdbId); var url = string.Format(FanArtBaseUrl, FanartArtistProvider.ApiKey, id);
var xmlPath = GetFanartXmlPath(tmdbId); var path = GetFanartJsonPath(id);
Directory.CreateDirectory(Path.GetDirectoryName(xmlPath)); Directory.CreateDirectory(Path.GetDirectoryName(path));
using (var response = await _httpClient.Get(new HttpRequestOptions using (var response = await _httpClient.Get(new HttpRequestOptions
{ {
@ -416,17 +285,17 @@ namespace MediaBrowser.Providers.Movies
}).ConfigureAwait(false)) }).ConfigureAwait(false))
{ {
using (var xmlFileStream = _fileSystem.GetFileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
{ {
await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); await response.CopyToAsync(fileStream).ConfigureAwait(false);
} }
} }
} }
private readonly Task _cachedTask = Task.FromResult(true); private readonly Task _cachedTask = Task.FromResult(true);
internal Task EnsureMovieXml(string tmdbId, CancellationToken cancellationToken) internal Task EnsureMovieJson(string id, CancellationToken cancellationToken)
{ {
var path = GetFanartXmlPath(tmdbId); var path = GetFanartJsonPath(id);
var fileInfo = _fileSystem.GetFileSystemInfo(path); var fileInfo = _fileSystem.GetFileSystemInfo(path);
@ -438,7 +307,31 @@ namespace MediaBrowser.Providers.Movies
} }
} }
return DownloadMovieXml(tmdbId, cancellationToken); return DownloadMovieJson(id, cancellationToken);
}
public class Image
{
public string id { get; set; }
public string url { get; set; }
public string lang { get; set; }
public string likes { get; set; }
}
public class RootObject
{
public string name { get; set; }
public string tmdb_id { get; set; }
public string imdb_id { get; set; }
public List<Image> hdmovielogo { get; set; }
public List<Image> moviedisc { get; set; }
public List<Image> movielogo { get; set; }
public List<Image> movieposter { get; set; }
public List<Image> hdmovieclearart { get; set; }
public List<Image> movieart { get; set; }
public List<Image> moviebackground { get; set; }
public List<Image> moviebanner { get; set; }
public List<Image> moviethumb { get; set; }
} }
} }
} }

@ -7,16 +7,15 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Providers.Music; using MediaBrowser.Providers.Music;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml;
namespace MediaBrowser.Providers.TV namespace MediaBrowser.Providers.TV
{ {
@ -26,12 +25,14 @@ namespace MediaBrowser.Providers.TV
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _json;
public FanArtSeasonProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) public FanArtSeasonProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IJsonSerializer json)
{ {
_config = config; _config = config;
_httpClient = httpClient; _httpClient = httpClient;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_json = json;
} }
public string Name public string Name
@ -71,14 +72,14 @@ namespace MediaBrowser.Providers.TV
if (!string.IsNullOrEmpty(id) && season.IndexNumber.HasValue) if (!string.IsNullOrEmpty(id) && season.IndexNumber.HasValue)
{ {
await FanartSeriesProvider.Current.EnsureSeriesXml(id, cancellationToken).ConfigureAwait(false); await FanartSeriesProvider.Current.EnsureSeriesJson(id, cancellationToken).ConfigureAwait(false);
var xmlPath = FanartSeriesProvider.Current.GetFanartXmlPath(id); var path = FanartSeriesProvider.Current.GetFanartJsonPath(id);
try try
{ {
int seasonNumber = AdjustForSeriesOffset(series, season.IndexNumber.Value); int seasonNumber = AdjustForSeriesOffset(series, season.IndexNumber.Value);
AddImages(list, seasonNumber, xmlPath, cancellationToken); AddImages(list, seasonNumber, path, cancellationToken);
} }
catch (FileNotFoundException) catch (FileNotFoundException)
{ {
@ -125,142 +126,67 @@ namespace MediaBrowser.Providers.TV
return seasonNumber; return seasonNumber;
} }
private void AddImages(List<RemoteImageInfo> list, int seasonNumber, string xmlPath, CancellationToken cancellationToken) private void AddImages(List<RemoteImageInfo> list, int seasonNumber, string path, CancellationToken cancellationToken)
{ {
using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8)) var root = _json.DeserializeFromFile<FanartSeriesProvider.RootObject>(path);
{
// Use XmlReader for best performance
using (var reader = XmlReader.Create(streamReader, new XmlReaderSettings
{
CheckCharacters = false,
IgnoreProcessingInstructions = true,
IgnoreComments = true,
ValidationType = ValidationType.None
}))
{
reader.MoveToContent();
// Loop through each element AddImages(list, root, seasonNumber, cancellationToken);
while (reader.Read())
{
cancellationToken.ThrowIfCancellationRequested();
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "series":
{
using (var subReader = reader.ReadSubtree())
{
AddImages(list, subReader, seasonNumber, cancellationToken);
}
break;
}
default:
reader.Skip();
break;
}
}
}
}
}
} }
private void AddImages(List<RemoteImageInfo> list, XmlReader reader, int seasonNumber, CancellationToken cancellationToken) private void AddImages(List<RemoteImageInfo> list, FanartSeriesProvider.RootObject obj, int seasonNumber, CancellationToken cancellationToken)
{ {
reader.MoveToContent(); PopulateImages(list, obj.seasonthumb, ImageType.Thumb, 500, 281, seasonNumber);
PopulateImages(list, obj.showbackground, ImageType.Backdrop, 1920, 1080, seasonNumber);
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "seasonthumbs":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Thumb, 500, 281, seasonNumber);
}
break;
}
case "showbackgrounds":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Backdrop, 1920, 1080, seasonNumber);
}
break;
}
default:
{
using (reader.ReadSubtree())
{
}
break;
}
}
}
}
} }
private void PopulateImageCategory(List<RemoteImageInfo> list, XmlReader reader, CancellationToken cancellationToken, ImageType type, int width, int height, int seasonNumber) private void PopulateImages(List<RemoteImageInfo> list,
List<FanartSeriesProvider.Image> images,
ImageType type,
int width,
int height,
int seasonNumber)
{ {
reader.MoveToContent(); if (images == null)
{
return;
}
while (reader.Read()) list.AddRange(images.Select(i =>
{ {
cancellationToken.ThrowIfCancellationRequested(); var url = i.url;
var season = i.season;
if (reader.NodeType == XmlNodeType.Element) int imageSeasonNumber;
if (!string.IsNullOrEmpty(url) &&
!string.IsNullOrEmpty(season) &&
int.TryParse(season, NumberStyles.Any, _usCulture, out imageSeasonNumber) &&
seasonNumber == imageSeasonNumber)
{ {
switch (reader.Name) var likesString = i.likes;
int likes;
var info = new RemoteImageInfo
{
RatingType = RatingType.Likes,
Type = type,
Width = width,
Height = height,
ProviderName = Name,
Url = url,
Language = i.lang
};
if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Any, _usCulture, out likes))
{ {
case "seasonthumb": info.CommunityRating = likes;
case "showbackground":
{
var url = reader.GetAttribute("url");
var season = reader.GetAttribute("season");
int imageSeasonNumber;
if (!string.IsNullOrEmpty(url) &&
!string.IsNullOrEmpty(season) &&
int.TryParse(season, NumberStyles.Any, _usCulture, out imageSeasonNumber) &&
seasonNumber == imageSeasonNumber)
{
var likesString = reader.GetAttribute("likes");
int likes;
var info = new RemoteImageInfo
{
RatingType = RatingType.Likes,
Type = type,
Width = width,
Height = height,
ProviderName = Name,
Url = url,
Language = reader.GetAttribute("lang")
};
if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Any, _usCulture, out likes))
{
info.CommunityRating = likes;
}
list.Add(info);
}
break;
}
default:
reader.Skip();
break;
} }
return info;
} }
}
return null;
}).Where(i => i != null));
} }
public int Order public int Order
@ -298,9 +224,9 @@ namespace MediaBrowser.Providers.TV
if (!String.IsNullOrEmpty(tvdbId)) if (!String.IsNullOrEmpty(tvdbId))
{ {
// Process images // Process images
var imagesXmlPath = FanartSeriesProvider.Current.GetFanartXmlPath(tvdbId); var imagesFilePath = FanartSeriesProvider.Current.GetFanartJsonPath(tvdbId);
var fileInfo = new FileInfo(imagesXmlPath); var fileInfo = new FileInfo(imagesFilePath);
return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > date; return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > date;
} }

@ -18,7 +18,7 @@ namespace MediaBrowser.Providers.TV
{ {
class FanArtTvUpdatesPostScanTask : ILibraryPostScanTask class FanArtTvUpdatesPostScanTask : ILibraryPostScanTask
{ {
private const string UpdatesUrl = "http://api.fanart.tv/webservice/newtv/{0}/{1}/"; private const string UpdatesUrl = "http://webservice.fanart.tv/v3/tv/latest?api_key={0}&date={1}";
/// <summary> /// <summary>
/// The _HTTP client /// The _HTTP client
@ -150,7 +150,7 @@ namespace MediaBrowser.Providers.TV
{ {
_logger.Info("Updating series " + id); _logger.Info("Updating series " + id);
await FanartSeriesProvider.Current.DownloadSeriesXml(id, cancellationToken).ConfigureAwait(false); await FanartSeriesProvider.Current.DownloadSeriesJson(id, cancellationToken).ConfigureAwait(false);
numComplete++; numComplete++;
double percent = numComplete; double percent = numComplete;

@ -8,16 +8,15 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Providers.Music; using MediaBrowser.Providers.Music;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml;
namespace MediaBrowser.Providers.TV namespace MediaBrowser.Providers.TV
{ {
@ -27,16 +26,19 @@ namespace MediaBrowser.Providers.TV
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _json;
protected string FanArtBaseUrl = "http://api.fanart.tv/webservice/series/{0}/{1}/xml/all/1/1"; private const string FanArtBaseUrl = "http://webservice.fanart.tv/v3/tv/{1}?api_key={0}";
// &client_key=52c813aa7b8c8b3bb87f4797532a2f8c
internal static FanartSeriesProvider Current { get; private set; } internal static FanartSeriesProvider Current { get; private set; }
public FanartSeriesProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) public FanartSeriesProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IJsonSerializer json)
{ {
_config = config; _config = config;
_httpClient = httpClient; _httpClient = httpClient;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_json = json;
Current = this; Current = this;
} }
@ -79,13 +81,13 @@ namespace MediaBrowser.Providers.TV
if (!string.IsNullOrEmpty(id)) if (!string.IsNullOrEmpty(id))
{ {
await EnsureSeriesXml(id, cancellationToken).ConfigureAwait(false); await EnsureSeriesJson(id, cancellationToken).ConfigureAwait(false);
var xmlPath = GetFanartXmlPath(id); var path = GetFanartJsonPath(id);
try try
{ {
AddImages(list, xmlPath, cancellationToken); AddImages(list, path, cancellationToken);
} }
catch (FileNotFoundException) catch (FileNotFoundException)
{ {
@ -122,203 +124,72 @@ namespace MediaBrowser.Providers.TV
.ThenByDescending(i => i.VoteCount ?? 0); .ThenByDescending(i => i.VoteCount ?? 0);
} }
private void AddImages(List<RemoteImageInfo> list, string xmlPath, CancellationToken cancellationToken) private void AddImages(List<RemoteImageInfo> list, string path, CancellationToken cancellationToken)
{ {
using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8)) var root = _json.DeserializeFromFile<RootObject>(path);
{
// Use XmlReader for best performance
using (var reader = XmlReader.Create(streamReader, new XmlReaderSettings
{
CheckCharacters = false,
IgnoreProcessingInstructions = true,
IgnoreComments = true,
ValidationType = ValidationType.None
}))
{
reader.MoveToContent();
// Loop through each element
while (reader.Read())
{
cancellationToken.ThrowIfCancellationRequested();
if (reader.NodeType == XmlNodeType.Element) AddImages(list, root, cancellationToken);
{
switch (reader.Name)
{
case "series":
{
using (var subReader = reader.ReadSubtree())
{
AddImages(list, subReader, cancellationToken);
}
break;
}
default:
reader.Skip();
break;
}
}
}
}
}
} }
private void AddImages(List<RemoteImageInfo> list, XmlReader reader, CancellationToken cancellationToken) private void AddImages(List<RemoteImageInfo> list, RootObject obj, CancellationToken cancellationToken)
{ {
reader.MoveToContent(); PopulateImages(list, obj.hdtvlogo, ImageType.Logo, 800, 310);
PopulateImages(list, obj.hdclearart, ImageType.Art, 1000, 562);
while (reader.Read()) PopulateImages(list, obj.clearlogo, ImageType.Logo, 400, 155);
{ PopulateImages(list, obj.clearart, ImageType.Art, 500, 281);
if (reader.NodeType == XmlNodeType.Element) PopulateImages(list, obj.showbackground, ImageType.Backdrop, 1920, 1080, true);
{ PopulateImages(list, obj.seasonthumb, ImageType.Thumb, 500, 281);
switch (reader.Name) PopulateImages(list, obj.tvthumb, ImageType.Thumb, 500, 281);
{ PopulateImages(list, obj.tvbanner, ImageType.Banner, 1000, 185);
case "hdtvlogos": PopulateImages(list, obj.tvposter, ImageType.Primary, 1000, 1426);
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Logo, 800, 310);
}
break;
}
case "hdcleararts":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Art, 1000, 562);
}
break;
}
case "clearlogos":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Logo, 400, 155);
}
break;
}
case "cleararts":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Art, 500, 281);
}
break;
}
case "showbackgrounds":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Backdrop, 1920, 1080, true);
}
break;
}
case "seasonthumbs":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Thumb, 500, 281);
}
break;
}
case "tvthumbs":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Thumb, 500, 281);
}
break;
}
case "tvbanners":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Banner, 1000, 185);
}
break;
}
case "tvposters":
{
using (var subReader = reader.ReadSubtree())
{
PopulateImageCategory(list, subReader, cancellationToken, ImageType.Primary, 1000, 1426);
}
break;
}
default:
{
using (reader.ReadSubtree())
{
}
break;
}
}
}
}
} }
private void PopulateImageCategory(List<RemoteImageInfo> list, XmlReader reader, CancellationToken cancellationToken, ImageType type, int width, int height, bool allowSeasonAll = false) private void PopulateImages(List<RemoteImageInfo> list,
List<Image> images,
ImageType type,
int width,
int height,
bool allowSeasonAll = false)
{ {
reader.MoveToContent(); if (images == null)
{
return;
}
while (reader.Read()) list.AddRange(images.Select(i =>
{ {
cancellationToken.ThrowIfCancellationRequested(); var url = i.url;
var season = i.season;
var isSeasonValid = string.IsNullOrEmpty(season) ||
(allowSeasonAll && string.Equals(season, "all", StringComparison.OrdinalIgnoreCase));
if (reader.NodeType == XmlNodeType.Element) if (!string.IsNullOrEmpty(url) && isSeasonValid)
{ {
switch (reader.Name) var likesString = i.likes;
int likes;
var info = new RemoteImageInfo
{
RatingType = RatingType.Likes,
Type = type,
Width = width,
Height = height,
ProviderName = Name,
Url = url,
Language = i.lang
};
if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Any, _usCulture, out likes))
{ {
case "hdtvlogo": info.CommunityRating = likes;
case "hdclearart":
case "clearlogo":
case "clearart":
case "showbackground":
case "seasonthumb":
case "tvthumb":
case "tvbanner":
case "tvposter":
{
var url = reader.GetAttribute("url");
var season = reader.GetAttribute("season");
var isSeasonValid = string.IsNullOrEmpty(season) ||
(allowSeasonAll && string.Equals(season, "all", StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(url) && isSeasonValid)
{
var likesString = reader.GetAttribute("likes");
int likes;
var info = new RemoteImageInfo
{
RatingType = RatingType.Likes,
Type = type,
Width = width,
Height = height,
ProviderName = Name,
Url = url,
Language = reader.GetAttribute("lang")
};
if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Any, _usCulture, out likes))
{
info.CommunityRating = likes;
}
list.Add(info);
}
break;
}
default:
reader.Skip();
break;
} }
return info;
} }
}
return null;
}).Where(i => i != null));
} }
public int Order public int Order
@ -361,23 +232,23 @@ namespace MediaBrowser.Providers.TV
return dataPath; return dataPath;
} }
public string GetFanartXmlPath(string tvdbId) public string GetFanartJsonPath(string tvdbId)
{ {
var dataPath = GetSeriesDataPath(_config.ApplicationPaths, tvdbId); var dataPath = GetSeriesDataPath(_config.ApplicationPaths, tvdbId);
return Path.Combine(dataPath, "fanart.xml"); return Path.Combine(dataPath, "fanart.json");
} }
private readonly SemaphoreSlim _ensureSemaphore = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _ensureSemaphore = new SemaphoreSlim(1, 1);
internal async Task EnsureSeriesXml(string tvdbId, CancellationToken cancellationToken) internal async Task EnsureSeriesJson(string tvdbId, CancellationToken cancellationToken)
{ {
var xmlPath = GetFanartXmlPath(tvdbId); var path = GetFanartJsonPath(tvdbId);
// Only allow one thread in here at a time since every season will be calling this method, possibly concurrently // Only allow one thread in here at a time since every season will be calling this method, possibly concurrently
await _ensureSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); await _ensureSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try try
{ {
var fileInfo = _fileSystem.GetFileSystemInfo(xmlPath); var fileInfo = _fileSystem.GetFileSystemInfo(path);
if (fileInfo.Exists) if (fileInfo.Exists)
{ {
@ -387,7 +258,7 @@ namespace MediaBrowser.Providers.TV
} }
} }
await DownloadSeriesXml(tvdbId, cancellationToken).ConfigureAwait(false); await DownloadSeriesJson(tvdbId, cancellationToken).ConfigureAwait(false);
} }
finally finally
{ {
@ -396,20 +267,20 @@ namespace MediaBrowser.Providers.TV
} }
/// <summary> /// <summary>
/// Downloads the series XML. /// Downloads the series json.
/// </summary> /// </summary>
/// <param name="tvdbId">The TVDB id.</param> /// <param name="tvdbId">The TVDB identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
internal async Task DownloadSeriesXml(string tvdbId, CancellationToken cancellationToken) internal async Task DownloadSeriesJson(string tvdbId, CancellationToken cancellationToken)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
var url = string.Format(FanArtBaseUrl, FanartArtistProvider.ApiKey, tvdbId); var url = string.Format(FanArtBaseUrl, FanartArtistProvider.ApiKey, tvdbId);
var xmlPath = GetFanartXmlPath(tvdbId); var path = GetFanartJsonPath(tvdbId);
Directory.CreateDirectory(Path.GetDirectoryName(xmlPath)); Directory.CreateDirectory(Path.GetDirectoryName(path));
using (var response = await _httpClient.Get(new HttpRequestOptions using (var response = await _httpClient.Get(new HttpRequestOptions
{ {
@ -419,9 +290,9 @@ namespace MediaBrowser.Providers.TV
}).ConfigureAwait(false)) }).ConfigureAwait(false))
{ {
using (var xmlFileStream = _fileSystem.GetFileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
{ {
await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); await response.CopyToAsync(fileStream).ConfigureAwait(false);
} }
} }
} }
@ -438,14 +309,41 @@ namespace MediaBrowser.Providers.TV
if (!String.IsNullOrEmpty(tvdbId)) if (!String.IsNullOrEmpty(tvdbId))
{ {
// Process images // Process images
var imagesXmlPath = GetFanartXmlPath(tvdbId); var imagesFilePath = GetFanartJsonPath(tvdbId);
var fileInfo = new FileInfo(imagesXmlPath); var fileInfo = new FileInfo(imagesFilePath);
return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > date; return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > date;
} }
return false; return false;
} }
public class Image
{
public string id { get; set; }
public string url { get; set; }
public string lang { get; set; }
public string likes { get; set; }
public string season { get; set; }
}
public class RootObject
{
public string name { get; set; }
public string thetvdb_id { get; set; }
public List<Image> clearlogo { get; set; }
public List<Image> hdtvlogo { get; set; }
public List<Image> clearart { get; set; }
public List<Image> showbackground { get; set; }
public List<Image> tvthumb { get; set; }
public List<Image> seasonposter { get; set; }
public List<Image> seasonthumb { get; set; }
public List<Image> hdclearart { get; set; }
public List<Image> tvbanner { get; set; }
public List<Image> characterart { get; set; }
public List<Image> tvposter { get; set; }
public List<Image> seasonbanner { get; set; }
}
} }
} }

@ -3,6 +3,8 @@ using MediaBrowser.Common.Events;
using MediaBrowser.Common.Implementations.Configuration; using MediaBrowser.Common.Implementations.Configuration;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
@ -214,6 +216,11 @@ namespace MediaBrowser.Server.Implementations.Configuration
DisableMetadataService(typeof(Movie), Configuration, service); DisableMetadataService(typeof(Movie), Configuration, service);
DisableMetadataService(typeof(Episode), Configuration, service); DisableMetadataService(typeof(Episode), Configuration, service);
DisableMetadataService(typeof(Series), Configuration, service); DisableMetadataService(typeof(Series), Configuration, service);
DisableMetadataService(typeof(Season), Configuration, service);
DisableMetadataService(typeof(MusicArtist), Configuration, service);
DisableMetadataService(typeof(MusicAlbum), Configuration, service);
DisableMetadataService(typeof(MusicVideo), Configuration, service);
DisableMetadataService(typeof(Video), Configuration, service);
} }
private void DisableMetadataService(Type type, ServerConfiguration config, string service) private void DisableMetadataService(Type type, ServerConfiguration config, string service)

@ -141,6 +141,8 @@ namespace MediaBrowser.Server.Implementations.Connect
try try
{ {
var localAddress = _appHost.GetSystemInfo().LocalAddress;
var hasExistingRecord = !string.IsNullOrWhiteSpace(ConnectServerId) && var hasExistingRecord = !string.IsNullOrWhiteSpace(ConnectServerId) &&
!string.IsNullOrWhiteSpace(ConnectAccessKey); !string.IsNullOrWhiteSpace(ConnectAccessKey);
@ -150,11 +152,12 @@ namespace MediaBrowser.Server.Implementations.Connect
{ {
try try
{ {
await UpdateServerRegistration(wanApiAddress).ConfigureAwait(false); await UpdateServerRegistration(wanApiAddress, localAddress).ConfigureAwait(false);
} }
catch (HttpException ex) catch (HttpException ex)
{ {
if (!ex.StatusCode.HasValue || !new[] { HttpStatusCode.NotFound, HttpStatusCode.Unauthorized }.Contains(ex.StatusCode.Value)) if (!ex.StatusCode.HasValue ||
!new[] { HttpStatusCode.NotFound, HttpStatusCode.Unauthorized }.Contains(ex.StatusCode.Value))
{ {
throw; throw;
} }
@ -165,7 +168,7 @@ namespace MediaBrowser.Server.Implementations.Connect
if (createNewRegistration) if (createNewRegistration)
{ {
await CreateServerRegistration(wanApiAddress).ConfigureAwait(false); await CreateServerRegistration(wanApiAddress, localAddress).ConfigureAwait(false);
} }
await RefreshAuthorizationsInternal(true, CancellationToken.None).ConfigureAwait(false); await RefreshAuthorizationsInternal(true, CancellationToken.None).ConfigureAwait(false);
@ -176,7 +179,7 @@ namespace MediaBrowser.Server.Implementations.Connect
} }
} }
private async Task CreateServerRegistration(string wanApiAddress) private async Task CreateServerRegistration(string wanApiAddress, string localAddress)
{ {
var url = "Servers"; var url = "Servers";
url = GetConnectUrl(url); url = GetConnectUrl(url);
@ -188,6 +191,11 @@ namespace MediaBrowser.Server.Implementations.Connect
{"systemId", _appHost.SystemId} {"systemId", _appHost.SystemId}
}; };
if (!string.IsNullOrWhiteSpace(localAddress))
{
postData["localAddress"] = localAddress;
}
using (var stream = await _httpClient.Post(url, postData, CancellationToken.None).ConfigureAwait(false)) using (var stream = await _httpClient.Post(url, postData, CancellationToken.None).ConfigureAwait(false))
{ {
var data = _json.DeserializeFromStream<ServerRegistrationResponse>(stream); var data = _json.DeserializeFromStream<ServerRegistrationResponse>(stream);
@ -199,24 +207,31 @@ namespace MediaBrowser.Server.Implementations.Connect
} }
} }
private async Task UpdateServerRegistration(string wanApiAddress) private async Task UpdateServerRegistration(string wanApiAddress, string localAddress)
{ {
var url = "Servers"; var url = "Servers";
url = GetConnectUrl(url); url = GetConnectUrl(url);
url += "?id=" + ConnectServerId; url += "?id=" + ConnectServerId;
var postData = new Dictionary<string, string>
{
{"name", _appHost.FriendlyName},
{"url", wanApiAddress},
{"systemId", _appHost.SystemId}
};
if (!string.IsNullOrWhiteSpace(localAddress))
{
postData["localAddress"] = localAddress;
}
var options = new HttpRequestOptions var options = new HttpRequestOptions
{ {
Url = url, Url = url,
CancellationToken = CancellationToken.None CancellationToken = CancellationToken.None
}; };
options.SetPostData(new Dictionary<string, string> options.SetPostData(postData);
{
{"name", _appHost.FriendlyName},
{"url", wanApiAddress},
{"systemId", _appHost.SystemId}
});
SetServerAccessToken(options); SetServerAccessToken(options);
@ -405,15 +420,46 @@ namespace MediaBrowser.Server.Implementations.Connect
throw new ArgumentNullException("connectUsername"); throw new ArgumentNullException("connectUsername");
} }
var connectUser = await GetConnectUser(new ConnectUserQuery string connectUserId = null;
var result = new UserLinkResult();
try
{ {
Name = connectUsername var connectUser = await GetConnectUser(new ConnectUserQuery
{
Name = connectUsername
}, CancellationToken.None).ConfigureAwait(false); }, CancellationToken.None).ConfigureAwait(false);
if (!connectUser.IsActive) if (!connectUser.IsActive)
{
throw new ArgumentException("The Media Browser account has been disabled.");
}
connectUserId = connectUser.Id;
result.GuestDisplayName = connectUser.Name;
}
catch (HttpException ex)
{ {
throw new ArgumentException("The Media Browser account has been disabled."); if (!ex.StatusCode.HasValue ||
ex.StatusCode.Value != HttpStatusCode.NotFound ||
!Validator.EmailIsValid(connectUsername))
{
throw;
}
}
var sendingUser = GetUser(sendingUserId);
var requesterUserName = sendingUser.ConnectUserName;
if (string.IsNullOrWhiteSpace(requesterUserName))
{
requesterUserName = sendingUser.Name;
}
if (string.IsNullOrWhiteSpace(connectUserId))
{
return await SendNewUserInvitation(requesterUserName, connectUsername).ConfigureAwait(false);
} }
var url = GetConnectUrl("ServerAuthorizations"); var url = GetConnectUrl("ServerAuthorizations");
@ -425,18 +471,11 @@ namespace MediaBrowser.Server.Implementations.Connect
}; };
var accessToken = Guid.NewGuid().ToString("N"); var accessToken = Guid.NewGuid().ToString("N");
var sendingUser = GetUser(sendingUserId);
var requesterUserName = sendingUser.ConnectUserName;
if (string.IsNullOrWhiteSpace(requesterUserName))
{
requesterUserName = sendingUser.Name;
}
var postData = new Dictionary<string, string> var postData = new Dictionary<string, string>
{ {
{"serverId", ConnectServerId}, {"serverId", ConnectServerId},
{"userId", connectUser.Id}, {"userId", connectUserId},
{"userType", "Guest"}, {"userType", "Guest"},
{"accessToken", accessToken}, {"accessToken", accessToken},
{"requesterUserName", requesterUserName} {"requesterUserName", requesterUserName}
@ -446,8 +485,6 @@ namespace MediaBrowser.Server.Implementations.Connect
SetServerAccessToken(options); SetServerAccessToken(options);
var result = new UserLinkResult();
// No need to examine the response // No need to examine the response
using (var stream = (await _httpClient.Post(options).ConfigureAwait(false)).Content) using (var stream = (await _httpClient.Post(options).ConfigureAwait(false)).Content)
{ {
@ -461,6 +498,36 @@ namespace MediaBrowser.Server.Implementations.Connect
return result; return result;
} }
private async Task<UserLinkResult> SendNewUserInvitation(string fromName, string email)
{
var url = GetConnectUrl("users/invite");
var options = new HttpRequestOptions
{
Url = url,
CancellationToken = CancellationToken.None
};
var postData = new Dictionary<string, string>
{
{"email", email},
{"requesterUserName", fromName}
};
options.SetPostData(postData);
// No need to examine the response
using (var stream = (await _httpClient.Post(options).ConfigureAwait(false)).Content)
{
}
return new UserLinkResult
{
IsNewUserInvitation = true,
GuestDisplayName = email
};
}
public Task RemoveConnect(string userId) public Task RemoveConnect(string userId)
{ {
var user = GetUser(userId); var user = GetUser(userId);
@ -586,6 +653,9 @@ namespace MediaBrowser.Server.Implementations.Connect
if (connectEntry == null) if (connectEntry == null)
{ {
var deleteUser = user.ConnectLinkType.HasValue &&
user.ConnectLinkType.Value == UserLinkType.Guest;
user.ConnectUserId = null; user.ConnectUserId = null;
user.ConnectAccessKey = null; user.ConnectAccessKey = null;
user.ConnectUserName = null; user.ConnectUserName = null;
@ -593,7 +663,7 @@ namespace MediaBrowser.Server.Implementations.Connect
await _userManager.UpdateUser(user).ConfigureAwait(false); await _userManager.UpdateUser(user).ConfigureAwait(false);
if (user.ConnectLinkType.HasValue && user.ConnectLinkType.Value == UserLinkType.Guest) if (deleteUser)
{ {
_logger.Debug("Deleting guest user {0}", user.Name); _logger.Debug("Deleting guest user {0}", user.Name);
await _userManager.DeleteUser(user).ConfigureAwait(false); await _userManager.DeleteUser(user).ConfigureAwait(false);

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Connect
{
public static class Validator
{
static Regex ValidEmailRegex = CreateValidEmailRegex();
/// <summary>
/// Taken from http://haacked.com/archive/2007/08/21/i-knew-how-to-validate-an-email-address-until-i.aspx
/// </summary>
/// <returns></returns>
private static Regex CreateValidEmailRegex()
{
string validEmailPattern = @"^(?!\.)(""([^""\r\\]|\\[""\r\\])*""|"
+ @"([-a-z0-9!#$%&'*+/=?^_`{|}~]|(?<!\.)\.)*)(?<!\.)"
+ @"@[a-z0-9][\w\.-]*[a-z0-9]\.[a-z][a-z\.]*[a-z]$";
return new Regex(validEmailPattern, RegexOptions.IgnoreCase);
}
internal static bool EmailIsValid(string emailAddress)
{
bool isValid = ValidEmailRegex.IsMatch(emailAddress);
return isValid;
}
}
}

@ -205,8 +205,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
HostContext.Config.HandlerFactoryPath = ListenerRequest.GetHandlerPathIfAny(UrlPrefixes.First()); HostContext.Config.HandlerFactoryPath = ListenerRequest.GetHandlerPathIfAny(UrlPrefixes.First());
_listener = _supportsNativeWebSocket && NativeWebSocket.IsSupported _listener = _supportsNativeWebSocket && NativeWebSocket.IsSupported
//? _listener = new HttpListenerServer(_logger, OnRequestReceived) ? _listener = new HttpListenerServer(_logger, OnRequestReceived)
? _listener = new WebSocketSharpListener(_logger, OnRequestReceived) //? _listener = new WebSocketSharpListener(_logger, OnRequestReceived)
: _listener = new WebSocketSharpListener(_logger, OnRequestReceived); : _listener = new WebSocketSharpListener(_logger, OnRequestReceived);
_listener.WebSocketHandler = WebSocketHandler; _listener.WebSocketHandler = WebSocketHandler;

@ -3,7 +3,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Resolvers;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;

@ -530,20 +530,12 @@ namespace MediaBrowser.Server.Implementations.Library
return item; return item;
} }
public BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null) public BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null, string collectionType = null)
{ {
return ResolvePath(fileInfo, new DirectoryService(_logger), parent); return ResolvePath(fileInfo, new DirectoryService(_logger), parent, collectionType);
} }
/// <summary> public BaseItem ResolvePath(FileSystemInfo fileInfo, IDirectoryService directoryService, Folder parent = null, string collectionType = null)
/// Resolves a path into a BaseItem
/// </summary>
/// <param name="fileInfo">The file info.</param>
/// <param name="directoryService">The directory service.</param>
/// <param name="parent">The parent.</param>
/// <returns>BaseItem.</returns>
/// <exception cref="System.ArgumentNullException">fileInfo</exception>
public BaseItem ResolvePath(FileSystemInfo fileInfo, IDirectoryService directoryService, Folder parent = null)
{ {
if (fileInfo == null) if (fileInfo == null)
{ {
@ -554,7 +546,8 @@ namespace MediaBrowser.Server.Implementations.Library
{ {
Parent = parent, Parent = parent,
Path = fileInfo.FullName, Path = fileInfo.FullName,
FileInfo = fileInfo FileInfo = fileInfo,
CollectionType = collectionType
}; };
// Return null if ignore rules deem that we should do so // Return null if ignore rules deem that we should do so
@ -622,15 +615,7 @@ namespace MediaBrowser.Server.Implementations.Library
return !args.ContainsFileSystemEntryByName(".ignore"); return !args.ContainsFileSystemEntryByName(".ignore");
} }
/// <summary> public List<T> ResolvePaths<T>(IEnumerable<FileSystemInfo> files, IDirectoryService directoryService, Folder parent, string collectionType = null)
/// Resolves a set of files into a list of BaseItem
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="files">The files.</param>
/// <param name="directoryService">The directory service.</param>
/// <param name="parent">The parent.</param>
/// <returns>List{``0}.</returns>
public List<T> ResolvePaths<T>(IEnumerable<FileSystemInfo> files, IDirectoryService directoryService, Folder parent)
where T : BaseItem where T : BaseItem
{ {
var list = new List<T>(); var list = new List<T>();
@ -639,7 +624,7 @@ namespace MediaBrowser.Server.Implementations.Library
{ {
try try
{ {
var item = ResolvePath(f, directoryService, parent) as T; var item = ResolvePath(f, directoryService, parent, collectionType) as T;
if (item != null) if (item != null)
{ {
@ -1190,6 +1175,20 @@ namespace MediaBrowser.Server.Implementations.Library
return item; return item;
} }
public BaseItem GetMemoryItemById(Guid id)
{
if (id == Guid.Empty)
{
throw new ArgumentNullException("id");
}
BaseItem item;
LibraryItemsCache.TryGetValue(id, out item);
return item;
}
/// <summary> /// <summary>
/// Gets the intros. /// Gets the intros.
/// </summary> /// </summary>
@ -1497,14 +1496,10 @@ namespace MediaBrowser.Server.Implementations.Library
return null; return null;
} }
var collectionTypes = _userManager.Users var collectionTypes = GetUserRootFolder().Children
.Select(i => i.RootFolder)
.Distinct()
.SelectMany(i => i.Children)
.OfType<ICollectionFolder>() .OfType<ICollectionFolder>()
.Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path)) .Where(i => !string.IsNullOrEmpty(i.CollectionType) && (string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path)))
.Select(i => i.CollectionType) .Select(i => i.CollectionType)
.Where(i => !string.IsNullOrEmpty(i))
.Distinct() .Distinct()
.ToList(); .ToList();

@ -82,29 +82,29 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
{ {
if (string.Equals(collectionType, CollectionType.Trailers, StringComparison.OrdinalIgnoreCase)) if (string.Equals(collectionType, CollectionType.Trailers, StringComparison.OrdinalIgnoreCase))
{ {
return FindMovie<Trailer>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, false, false); return FindMovie<Trailer>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, false, false, collectionType);
} }
if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase)) if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
{ {
return FindMovie<MusicVideo>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, false, false); return FindMovie<MusicVideo>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, false, false, collectionType);
} }
if (string.Equals(collectionType, CollectionType.AdultVideos, StringComparison.OrdinalIgnoreCase)) if (string.Equals(collectionType, CollectionType.AdultVideos, StringComparison.OrdinalIgnoreCase))
{ {
return FindMovie<Video>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, true, false); return FindMovie<Video>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, true, false, collectionType);
} }
if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase)) if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
{ {
return FindMovie<Video>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, true, false); return FindMovie<Video>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, true, false, collectionType);
} }
if (string.IsNullOrEmpty(collectionType) || if (string.IsNullOrEmpty(collectionType) ||
string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase) || string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase) ||
string.Equals(collectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase)) string.Equals(collectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
{ {
return FindMovie<Movie>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, true, true); return FindMovie<Movie>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, true, true, collectionType);
} }
return null; return null;
@ -190,7 +190,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
/// <param name="directoryService">The directory service.</param> /// <param name="directoryService">The directory service.</param>
/// <param name="supportMultiFileItems">if set to <c>true</c> [support multi file items].</param> /// <param name="supportMultiFileItems">if set to <c>true</c> [support multi file items].</param>
/// <returns>Movie.</returns> /// <returns>Movie.</returns>
private T FindMovie<T>(string path, Folder parent, List<FileSystemInfo> fileSystemEntries, IDirectoryService directoryService, bool supportMultiFileItems, bool supportsMultipleSources) private T FindMovie<T>(string path, Folder parent, List<FileSystemInfo> fileSystemEntries, IDirectoryService directoryService, bool supportMultiFileItems, bool supportsMultipleSources, string collectionType)
where T : Video, new() where T : Video, new()
{ {
var movies = new List<T>(); var movies = new List<T>();
@ -239,7 +239,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
{ {
FileInfo = child, FileInfo = child,
Path = child.FullName, Path = child.FullName,
Parent = parent Parent = parent,
CollectionType = collectionType
}; };
var item = ResolveVideo<T>(childArgs); var item = ResolveVideo<T>(childArgs);

@ -75,7 +75,7 @@
"ButtonClose": "Close", "ButtonClose": "Close",
"LabelAllPlaysSentToPlayer": "All plays will be sent to the selected player.", "LabelAllPlaysSentToPlayer": "All plays will be sent to the selected player.",
"MessageInvalidUser": "Invalid username or password. Please try again.", "MessageInvalidUser": "Invalid username or password. Please try again.",
"HeaderLoginFailure": "Login Failure", "HeaderLoginFailure": "Login Failure",
"HeaderAllRecordings": "All Recordings", "HeaderAllRecordings": "All Recordings",
"RecommendationBecauseYouLike": "Because you like {0}", "RecommendationBecauseYouLike": "Because you like {0}",
"RecommendationBecauseYouWatched": "Because you watched {0}", "RecommendationBecauseYouWatched": "Because you watched {0}",
@ -613,5 +613,8 @@
"LabelEnableCameraUploadForHelp": "Uploads will occur automatically in the background when signed into Media Browser.", "LabelEnableCameraUploadForHelp": "Uploads will occur automatically in the background when signed into Media Browser.",
"ErrorMessageStartHourGreaterThanEnd": "End time must be greater than the start time.", "ErrorMessageStartHourGreaterThanEnd": "End time must be greater than the start time.",
"ButtonLibraryAccess": "Library access", "ButtonLibraryAccess": "Library access",
"ButtonParentalControl": "Parental control" "ButtonParentalControl": "Parental control",
"HeaderInvitationSent": "Invitation Sent",
"MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.",
"MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Media Browser."
} }

@ -1166,6 +1166,7 @@
"OptionSaveMetadataAsHidden": "Save metadata and images as hidden files", "OptionSaveMetadataAsHidden": "Save metadata and images as hidden files",
"LabelExtractChaptersDuringLibraryScan": "Extract chapter images during the library scan", "LabelExtractChaptersDuringLibraryScan": "Extract chapter images during the library scan",
"LabelExtractChaptersDuringLibraryScanHelp": "If enabled, chapter images will be extracted when videos are imported during the library scan. If disabled they will be extracted during the chapter images scheduled task, allowing the regular library scan to complete faster.", "LabelExtractChaptersDuringLibraryScanHelp": "If enabled, chapter images will be extracted when videos are imported during the library scan. If disabled they will be extracted during the chapter images scheduled task, allowing the regular library scan to complete faster.",
"LabelConnectGuestUserName": "Their Media Browser username or email address:",
"LabelConnectUserName": "Media Browser username/email:", "LabelConnectUserName": "Media Browser username/email:",
"LabelConnectUserNameHelp": "Connect this user to a Media Browser account to enable easy sign-in access from Media Browser any app without having to know the server ip address.", "LabelConnectUserNameHelp": "Connect this user to a Media Browser account to enable easy sign-in access from Media Browser any app without having to know the server ip address.",
"ButtonLearnMoreAboutMediaBrowserConnect": "Learn more about Media Browser Connect", "ButtonLearnMoreAboutMediaBrowserConnect": "Learn more about Media Browser Connect",
@ -1229,7 +1230,7 @@
"LabelCustomDeviceDisplayName": "Display name:", "LabelCustomDeviceDisplayName": "Display name:",
"LabelCustomDeviceDisplayNameHelp": "Supply a custom display name or leave empty to use the name reported by the device.", "LabelCustomDeviceDisplayNameHelp": "Supply a custom display name or leave empty to use the name reported by the device.",
"HeaderInviteUser": "Invite User", "HeaderInviteUser": "Invite User",
"LabelConnectInviteUserHelp": "This is the username or email that your friend uses to sign in to the Media Browser website.", "LabelConnectGuestUserNameHelp": "This is the username that your friend uses to sign in to the Media Browser website, or their email address.",
"HeaderInviteUserHelp": "Sharing your media with friends is easier than ever before with Media Browser Connect.", "HeaderInviteUserHelp": "Sharing your media with friends is easier than ever before with Media Browser Connect.",
"ButtonSendInvitation": "Send Invitation", "ButtonSendInvitation": "Send Invitation",
"HeaderGuests": "Guests", "HeaderGuests": "Guests",

@ -119,6 +119,7 @@
<Compile Include="Connect\ConnectData.cs" /> <Compile Include="Connect\ConnectData.cs" />
<Compile Include="Connect\ConnectManager.cs" /> <Compile Include="Connect\ConnectManager.cs" />
<Compile Include="Connect\Responses.cs" /> <Compile Include="Connect\Responses.cs" />
<Compile Include="Connect\Validator.cs" />
<Compile Include="Devices\DeviceManager.cs" /> <Compile Include="Devices\DeviceManager.cs" />
<Compile Include="Devices\DeviceRepository.cs" /> <Compile Include="Devices\DeviceRepository.cs" />
<Compile Include="Devices\CameraUploadsFolder.cs" /> <Compile Include="Devices\CameraUploadsFolder.cs" />

@ -257,7 +257,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
_writeLock.Release(); _writeLock.Release();
} }
} }
/// <summary> /// <summary>
/// Internal retrieve from items or users table /// Internal retrieve from items or users table
/// </summary> /// </summary>
@ -473,6 +473,34 @@ namespace MediaBrowser.Server.Implementations.Persistence
} }
} }
public IEnumerable<BaseItem> GetChildrenItems(Guid parentId)
{
if (parentId == Guid.Empty)
{
throw new ArgumentNullException("parentId");
}
using (var cmd = _connection.CreateCommand())
{
cmd.CommandText = "select type,data from TypedBaseItems where guid in (select ItemId from ChildrenIds where ParentId = @ParentId)";
cmd.Parameters.Add(cmd, "@ParentId", DbType.Guid).Value = parentId;
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
{
while (reader.Read())
{
var item = GetItem(reader);
if (item != null)
{
yield return item;
}
}
}
}
}
public IEnumerable<BaseItem> GetItemsOfType(Type type) public IEnumerable<BaseItem> GetItemsOfType(Type type)
{ {
if (type == null) if (type == null)

@ -40,7 +40,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
// Add PixelFormat column // Add PixelFormat column
createTableCommand += "(ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, PRIMARY KEY (ItemId, StreamIndex))"; createTableCommand += "(ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, IsCabac BIT NULL, PRIMARY KEY (ItemId, StreamIndex))";
string[] queries = { string[] queries = {
@ -59,6 +59,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
AddPixelFormatColumnCommand(); AddPixelFormatColumnCommand();
AddBitDepthCommand(); AddBitDepthCommand();
AddIsAnamorphicColumn(); AddIsAnamorphicColumn();
AddIsCabacColumn();
AddRefFramesCommand(); AddRefFramesCommand();
PrepareStatements(); PrepareStatements();
@ -159,6 +160,37 @@ namespace MediaBrowser.Server.Implementations.Persistence
_connection.RunQueries(new[] { builder.ToString() }, _logger); _connection.RunQueries(new[] { builder.ToString() }, _logger);
} }
private void AddIsCabacColumn()
{
using (var cmd = _connection.CreateCommand())
{
cmd.CommandText = "PRAGMA table_info(mediastreams)";
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
{
while (reader.Read())
{
if (!reader.IsDBNull(1))
{
var name = reader.GetString(1);
if (string.Equals(name, "IsCabac", StringComparison.OrdinalIgnoreCase))
{
return;
}
}
}
}
}
var builder = new StringBuilder();
builder.AppendLine("alter table mediastreams");
builder.AppendLine("add column IsCabac BIT NULL");
_connection.RunQueries(new[] { builder.ToString() }, _logger);
}
private void AddIsAnamorphicColumn() private void AddIsAnamorphicColumn()
{ {
using (var cmd = _connection.CreateCommand()) using (var cmd = _connection.CreateCommand())
@ -216,7 +248,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
"PixelFormat", "PixelFormat",
"BitDepth", "BitDepth",
"IsAnamorphic", "IsAnamorphic",
"RefFrames" "RefFrames",
"IsCabac"
}; };
/// <summary> /// <summary>
@ -395,6 +428,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
item.RefFrames = reader.GetInt32(24); item.RefFrames = reader.GetInt32(24);
} }
if (!reader.IsDBNull(25))
{
item.IsCabac = reader.GetBoolean(25);
}
return item; return item;
} }
@ -460,6 +498,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
_saveStreamCommand.GetParameter(22).Value = stream.BitDepth; _saveStreamCommand.GetParameter(22).Value = stream.BitDepth;
_saveStreamCommand.GetParameter(23).Value = stream.IsAnamorphic; _saveStreamCommand.GetParameter(23).Value = stream.IsAnamorphic;
_saveStreamCommand.GetParameter(24).Value = stream.RefFrames; _saveStreamCommand.GetParameter(24).Value = stream.RefFrames;
_saveStreamCommand.GetParameter(25).Value = stream.IsCabac;
_saveStreamCommand.Transaction = transaction; _saveStreamCommand.Transaction = transaction;
_saveStreamCommand.ExecuteNonQuery(); _saveStreamCommand.ExecuteNonQuery();

@ -359,6 +359,7 @@ namespace MediaBrowser.Server.Implementations.Session
session.NowPlayingItem = info.Item; session.NowPlayingItem = info.Item;
session.LastActivityDate = DateTime.UtcNow; session.LastActivityDate = DateTime.UtcNow;
session.LastPlaybackCheckIn = DateTime.UtcNow;
session.PlayState.IsPaused = info.IsPaused; session.PlayState.IsPaused = info.IsPaused;
session.PlayState.PositionTicks = info.PositionTicks; session.PlayState.PositionTicks = info.PositionTicks;
@ -498,6 +499,65 @@ namespace MediaBrowser.Server.Implementations.Session
return users; return users;
} }
private Timer _idleTimer;
private void StartIdleCheckTimer()
{
if (_idleTimer == null)
{
_idleTimer = new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
}
}
private void StopIdleCheckTimer()
{
if (_idleTimer != null)
{
_idleTimer.Dispose();
_idleTimer = null;
}
}
private async void CheckForIdlePlayback(object state)
{
var playingSessions = Sessions.Where(i => i.NowPlayingItem != null)
.ToList();
if (playingSessions.Count > 0)
{
var idle = playingSessions
.Where(i => (DateTime.UtcNow - i.LastPlaybackCheckIn).TotalMinutes > 5)
.ToList();
foreach (var session in idle)
{
_logger.Debug("Session {0} has gone idle while playing", session.Id);
try
{
await OnPlaybackStopped(new PlaybackStopInfo
{
Item = session.NowPlayingItem,
ItemId = (session.NowPlayingItem == null ? null : session.NowPlayingItem.Id),
SessionId = session.Id,
MediaSourceId = (session.PlayState == null ? null : session.PlayState.MediaSourceId)
});
}
catch (Exception ex)
{
_logger.Debug("Error calling OnPlaybackStopped", ex);
}
}
playingSessions = Sessions.Where(i => i.NowPlayingItem != null)
.ToList();
}
if (playingSessions.Count == 0)
{
StopIdleCheckTimer();
}
}
/// <summary> /// <summary>
/// Used to report that playback has started for an item /// Used to report that playback has started for an item
/// </summary> /// </summary>
@ -553,6 +613,8 @@ namespace MediaBrowser.Server.Implementations.Session
}, _logger); }, _logger);
await SendPlaybackStartNotification(session, CancellationToken.None).ConfigureAwait(false); await SendPlaybackStartNotification(session, CancellationToken.None).ConfigureAwait(false);
StartIdleCheckTimer();
} }
/// <summary> /// <summary>
@ -623,6 +685,8 @@ namespace MediaBrowser.Server.Implementations.Session
DeviceId = session.DeviceId DeviceId = session.DeviceId
}, _logger); }, _logger);
StartIdleCheckTimer();
} }
private async Task OnPlaybackProgress(Guid userId, string userDataKey, BaseItem item, long? positionTicks) private async Task OnPlaybackProgress(Guid userId, string userDataKey, BaseItem item, long? positionTicks)

@ -452,7 +452,6 @@ namespace MediaBrowser.WebDashboard.Api
"tvstudios.js", "tvstudios.js",
"tvupcoming.js", "tvupcoming.js",
"useredit.js", "useredit.js",
"userpassword.js",
"myprofile.js", "myprofile.js",
"userprofilespage.js", "userprofilespage.js",
"userparentalcontrol.js", "userparentalcontrol.js",

@ -1906,11 +1906,6 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="dashboard-ui\userpassword.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="dashboard-ui\myprofile.html"> <Content Include="dashboard-ui\myprofile.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@ -1956,11 +1951,6 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="dashboard-ui\scripts\userpassword.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="dashboard-ui\scripts\advancedconfigurationpage.js"> <Content Include="dashboard-ui\scripts\advancedconfigurationpage.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

@ -777,4 +777,7 @@ Global
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
EndGlobal EndGlobal

Loading…
Cancel
Save