mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-07-09 15:20:34 +02:00
Added poor man's multi-file movie support
This commit is contained in:
parent
455de48a65
commit
def3428199
|
@ -415,26 +415,24 @@ namespace MediaBrowser.Api
|
||||||
: DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, request.UserId);
|
: DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, request.UserId);
|
||||||
|
|
||||||
// Get everything
|
// Get everything
|
||||||
var fields =
|
var fields = Enum.GetNames(typeof(ItemFields))
|
||||||
Enum.GetNames(typeof(ItemFields))
|
|
||||||
.Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
|
.Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository);
|
var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository);
|
||||||
|
|
||||||
var items =
|
var items = _itemRepo.GetItems(item.ThemeSongIds)
|
||||||
_itemRepo.GetItems(item.ThemeSongIds)
|
|
||||||
.OrderBy(i => i.SortName)
|
.OrderBy(i => i.SortName)
|
||||||
.Select(i => dtoBuilder.GetBaseItemDto(i, fields, user))
|
.Select(i => dtoBuilder.GetBaseItemDto(i, fields, user))
|
||||||
.Select(t => t.Result)
|
.Select(t => t.Result)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
var result = new ThemeSongsResult
|
var result = new ThemeSongsResult
|
||||||
{
|
{
|
||||||
Items = items,
|
Items = items,
|
||||||
TotalRecordCount = items.Length,
|
TotalRecordCount = items.Length,
|
||||||
OwnerId = DtoBuilder.GetClientItemId(item)
|
OwnerId = DtoBuilder.GetClientItemId(item)
|
||||||
};
|
};
|
||||||
|
|
||||||
return ToOptimizedResult(result);
|
return ToOptimizedResult(result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,6 +114,7 @@
|
||||||
<Compile Include="UserLibrary\YearsService.cs" />
|
<Compile Include="UserLibrary\YearsService.cs" />
|
||||||
<Compile Include="UserService.cs" />
|
<Compile Include="UserService.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="VideosService.cs" />
|
||||||
<Compile Include="WeatherService.cs" />
|
<Compile Include="WeatherService.cs" />
|
||||||
<Compile Include="WebSocket\LogFileWebSocketListener.cs" />
|
<Compile Include="WebSocket\LogFileWebSocketListener.cs" />
|
||||||
<Compile Include="WebSocket\SessionInfoWebSocketListener.cs" />
|
<Compile Include="WebSocket\SessionInfoWebSocketListener.cs" />
|
||||||
|
|
82
MediaBrowser.Api/VideosService.cs
Normal file
82
MediaBrowser.Api/VideosService.cs
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
using MediaBrowser.Controller.Dto;
|
||||||
|
using MediaBrowser.Controller.Entities;
|
||||||
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.Persistence;
|
||||||
|
using MediaBrowser.Model.Querying;
|
||||||
|
using ServiceStack.ServiceHost;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Api
|
||||||
|
{
|
||||||
|
[Route("/Videos/{Id}/AdditionalParts", "GET")]
|
||||||
|
[Api(Description = "Gets additional parts for a video.")]
|
||||||
|
public class GetAdditionalParts : IReturn<ItemsResult>
|
||||||
|
{
|
||||||
|
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||||
|
public Guid? UserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the id.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The id.</value>
|
||||||
|
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class VideosService : BaseApiService
|
||||||
|
{
|
||||||
|
private readonly IItemRepository _itemRepo;
|
||||||
|
|
||||||
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
private readonly IUserManager _userManager;
|
||||||
|
private readonly IUserDataRepository _userDataRepository;
|
||||||
|
|
||||||
|
public VideosService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager, IUserDataRepository userDataRepository)
|
||||||
|
{
|
||||||
|
_itemRepo = itemRepo;
|
||||||
|
_libraryManager = libraryManager;
|
||||||
|
_userManager = userManager;
|
||||||
|
_userDataRepository = userDataRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the specified request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">The request.</param>
|
||||||
|
/// <returns>System.Object.</returns>
|
||||||
|
public object Get(GetAdditionalParts request)
|
||||||
|
{
|
||||||
|
var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
|
||||||
|
|
||||||
|
var item = string.IsNullOrEmpty(request.Id)
|
||||||
|
? (request.UserId.HasValue
|
||||||
|
? user.RootFolder
|
||||||
|
: (Folder)_libraryManager.RootFolder)
|
||||||
|
: DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, request.UserId);
|
||||||
|
|
||||||
|
// Get everything
|
||||||
|
var fields = Enum.GetNames(typeof(ItemFields))
|
||||||
|
.Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository);
|
||||||
|
|
||||||
|
var video = (Video)item;
|
||||||
|
|
||||||
|
var items = _itemRepo.GetItems(video.AdditionalPartIds)
|
||||||
|
.OrderBy(i => i.SortName)
|
||||||
|
.Select(i => dtoBuilder.GetBaseItemDto(i, fields, user))
|
||||||
|
.Select(t => t.Result)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var result = new ItemsResult
|
||||||
|
{
|
||||||
|
Items = items,
|
||||||
|
TotalRecordCount = items.Length
|
||||||
|
};
|
||||||
|
|
||||||
|
return ToOptimizedResult(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -183,7 +183,7 @@ namespace MediaBrowser.Controller.Dto
|
||||||
}
|
}
|
||||||
|
|
||||||
dto.OriginalPrimaryImageAspectRatio = size.Width / size.Height;
|
dto.OriginalPrimaryImageAspectRatio = size.Width / size.Height;
|
||||||
|
|
||||||
var supportedEnhancers = Kernel.Instance.ImageManager.ImageEnhancers.Where(i =>
|
var supportedEnhancers = Kernel.Instance.ImageManager.ImageEnhancers.Where(i =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -239,7 +239,7 @@ namespace MediaBrowser.Controller.Dto
|
||||||
dto.LockedImages = item.LockedImages;
|
dto.LockedImages = item.LockedImages;
|
||||||
dto.EnableInternetProviders = !item.DontFetchMeta;
|
dto.EnableInternetProviders = !item.DontFetchMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields.Contains(ItemFields.Budget))
|
if (fields.Contains(ItemFields.Budget))
|
||||||
{
|
{
|
||||||
dto.Budget = item.Budget;
|
dto.Budget = item.Budget;
|
||||||
|
@ -264,7 +264,7 @@ namespace MediaBrowser.Controller.Dto
|
||||||
{
|
{
|
||||||
dto.Tags = item.Tags;
|
dto.Tags = item.Tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields.Contains(ItemFields.ProductionLocations))
|
if (fields.Contains(ItemFields.ProductionLocations))
|
||||||
{
|
{
|
||||||
dto.ProductionLocations = item.ProductionLocations;
|
dto.ProductionLocations = item.ProductionLocations;
|
||||||
|
@ -441,6 +441,8 @@ namespace MediaBrowser.Controller.Dto
|
||||||
dto.VideoFormat = video.VideoFormat;
|
dto.VideoFormat = video.VideoFormat;
|
||||||
dto.IsoType = video.IsoType;
|
dto.IsoType = video.IsoType;
|
||||||
|
|
||||||
|
dto.PartCount = video.AdditionalPartIds.Count + 1;
|
||||||
|
|
||||||
if (fields.Contains(ItemFields.Chapters) && video.Chapters != null)
|
if (fields.Contains(ItemFields.Chapters) && video.Chapters != null)
|
||||||
{
|
{
|
||||||
dto.Chapters = video.Chapters.Select(c => GetChapterInfoDto(c, item)).ToList();
|
dto.Chapters = video.Chapters.Select(c => GetChapterInfoDto(c, item)).ToList();
|
||||||
|
|
|
@ -753,7 +753,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
// Support xbmc trailers (-trailer suffix on video file names)
|
// Support xbmc trailers (-trailer suffix on video file names)
|
||||||
files.AddRange(resolveArgs.FileSystemChildren.Where(i =>
|
files.AddRange(resolveArgs.FileSystemChildren.Where(i =>
|
||||||
{
|
{
|
||||||
if (!i.Attributes.HasFlag(FileAttributes.Directory))
|
if ((i.Attributes & FileAttributes.Directory) != FileAttributes.Directory)
|
||||||
{
|
{
|
||||||
if (System.IO.Path.GetFileNameWithoutExtension(i.Name).EndsWith(XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) && !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase))
|
if (System.IO.Path.GetFileNameWithoutExtension(i.Name).EndsWith(XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) && !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
@ -916,14 +916,11 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// <param name="forceSave">if set to <c>true</c> [is new item].</param>
|
/// <param name="forceSave">if set to <c>true</c> [is new item].</param>
|
||||||
/// <param name="forceRefresh">if set to <c>true</c> [force].</param>
|
/// <param name="forceRefresh">if set to <c>true</c> [force].</param>
|
||||||
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
|
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
|
||||||
/// <param name="resetResolveArgs">if set to <c>true</c> [reset resolve args].</param>
|
|
||||||
/// <returns>true if a provider reports we changed</returns>
|
/// <returns>true if a provider reports we changed</returns>
|
||||||
public virtual async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true)
|
public virtual async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
|
||||||
{
|
{
|
||||||
if (resetResolveArgs)
|
// Reload this
|
||||||
{
|
ResolveArgs = null;
|
||||||
ResolveArgs = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh for the item
|
// Refresh for the item
|
||||||
var itemRefreshTask = ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh, allowSlowProviders);
|
var itemRefreshTask = ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh, allowSlowProviders);
|
||||||
|
|
|
@ -768,7 +768,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
var child = currentTuple.Item1;
|
var child = currentTuple.Item1;
|
||||||
|
|
||||||
//refresh it
|
//refresh it
|
||||||
await child.RefreshMetadata(cancellationToken, resetResolveArgs: child.IsFolder, forceSave: currentTuple.Item2, forceRefresh: forceRefreshMetadata).ConfigureAwait(false);
|
await child.RefreshMetadata(cancellationToken, forceSave: currentTuple.Item2, forceRefresh: forceRefreshMetadata).ConfigureAwait(false);
|
||||||
|
|
||||||
// Refresh children if a folder and the item changed or recursive is set to true
|
// Refresh children if a folder and the item changed or recursive is set to true
|
||||||
var refreshChildren = child.IsFolder && (currentTuple.Item2 || (recursive.HasValue && recursive.Value));
|
var refreshChildren = child.IsFolder && (currentTuple.Item2 || (recursive.HasValue && recursive.Value));
|
||||||
|
|
|
@ -195,9 +195,8 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// <param name="forceSave">if set to <c>true</c> [is new item].</param>
|
/// <param name="forceSave">if set to <c>true</c> [is new item].</param>
|
||||||
/// <param name="forceRefresh">if set to <c>true</c> [force].</param>
|
/// <param name="forceRefresh">if set to <c>true</c> [force].</param>
|
||||||
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
|
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
|
||||||
/// <param name="resetResolveArgs">if set to <c>true</c> [reset resolve args].</param>
|
|
||||||
/// <returns>Task{System.Boolean}.</returns>
|
/// <returns>Task{System.Boolean}.</returns>
|
||||||
public override Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true)
|
public override Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
|
||||||
{
|
{
|
||||||
// We should never get in here since these are not part of the library
|
// We should never get in here since these are not part of the library
|
||||||
return Task.FromResult(false);
|
return Task.FromResult(false);
|
||||||
|
|
|
@ -62,12 +62,11 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||||
/// <param name="forceSave">if set to <c>true</c> [is new item].</param>
|
/// <param name="forceSave">if set to <c>true</c> [is new item].</param>
|
||||||
/// <param name="forceRefresh">if set to <c>true</c> [force].</param>
|
/// <param name="forceRefresh">if set to <c>true</c> [force].</param>
|
||||||
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
|
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
|
||||||
/// <param name="resetResolveArgs">if set to <c>true</c> [reset resolve args].</param>
|
|
||||||
/// <returns>Task{System.Boolean}.</returns>
|
/// <returns>Task{System.Boolean}.</returns>
|
||||||
public override async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true)
|
public override async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
|
||||||
{
|
{
|
||||||
// Kick off a task to refresh the main item
|
// Kick off a task to refresh the main item
|
||||||
var result = await base.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs).ConfigureAwait(false);
|
var result = await base.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
|
||||||
|
|
||||||
var specialFeaturesChanged = await RefreshSpecialFeatures(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
|
var specialFeaturesChanged = await RefreshSpecialFeatures(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -127,7 +126,7 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||||
}
|
}
|
||||||
catch (IOException ex)
|
catch (IOException ex)
|
||||||
{
|
{
|
||||||
Logger.ErrorException("Error loading trailers for {0}", ex, Name);
|
Logger.ErrorException("Error loading special features for {0}", ex, Name);
|
||||||
return new List<Video>();
|
return new List<Video>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -322,14 +322,11 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// <param name="forceSave">if set to <c>true</c> [is new item].</param>
|
/// <param name="forceSave">if set to <c>true</c> [is new item].</param>
|
||||||
/// <param name="forceRefresh">if set to <c>true</c> [force].</param>
|
/// <param name="forceRefresh">if set to <c>true</c> [force].</param>
|
||||||
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
|
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
|
||||||
/// <param name="resetResolveArgs">if set to <c>true</c> [reset resolve args].</param>
|
|
||||||
/// <returns>true if a provider reports we changed</returns>
|
/// <returns>true if a provider reports we changed</returns>
|
||||||
public override async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true)
|
public override async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
|
||||||
{
|
{
|
||||||
if (resetResolveArgs)
|
// Reload this
|
||||||
{
|
ResolveArgs = null;
|
||||||
ResolveArgs = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var changed = await ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh, allowSlowProviders).ConfigureAwait(false);
|
var changed = await ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh, allowSlowProviders).ConfigureAwait(false);
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.Resolvers;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
|
@ -11,11 +16,16 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Video : BaseItem, IHasMediaStreams
|
public class Video : BaseItem, IHasMediaStreams
|
||||||
{
|
{
|
||||||
|
public bool IsMultiPart { get; set; }
|
||||||
|
|
||||||
|
public List<Guid> AdditionalPartIds { get; set; }
|
||||||
|
|
||||||
public Video()
|
public Video()
|
||||||
{
|
{
|
||||||
MediaStreams = new List<MediaStream>();
|
MediaStreams = new List<MediaStream>();
|
||||||
Chapters = new List<ChapterInfo>();
|
Chapters = new List<ChapterInfo>();
|
||||||
PlayableStreamFileNames = new List<string>();
|
PlayableStreamFileNames = new List<string>();
|
||||||
|
AdditionalPartIds = new List<Guid>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -61,7 +71,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
return GetPlayableStreamFiles(Path);
|
return GetPlayableStreamFiles(Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the playable stream files.
|
/// Gets the playable stream files.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -112,5 +122,102 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return Model.Entities.MediaType.Video;
|
return Model.Entities.MediaType.Video;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the base implementation to refresh metadata for local trailers
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <param name="forceSave">if set to <c>true</c> [is new item].</param>
|
||||||
|
/// <param name="forceRefresh">if set to <c>true</c> [force].</param>
|
||||||
|
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
|
||||||
|
/// <returns>true if a provider reports we changed</returns>
|
||||||
|
public override async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
|
||||||
|
{
|
||||||
|
// Kick off a task to refresh the main item
|
||||||
|
var result = await base.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var additionalPartsChanged = await RefreshAdditionalParts(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return additionalPartsChanged || result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Refreshes the additional parts.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <param name="forceSave">if set to <c>true</c> [force save].</param>
|
||||||
|
/// <param name="forceRefresh">if set to <c>true</c> [force refresh].</param>
|
||||||
|
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
|
||||||
|
/// <returns>Task{System.Boolean}.</returns>
|
||||||
|
private async Task<bool> RefreshAdditionalParts(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
|
||||||
|
{
|
||||||
|
var newItems = LoadAdditionalParts().ToList();
|
||||||
|
var newItemIds = newItems.Select(i => i.Id).ToList();
|
||||||
|
|
||||||
|
var itemsChanged = !AdditionalPartIds.SequenceEqual(newItemIds);
|
||||||
|
|
||||||
|
var tasks = newItems.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders));
|
||||||
|
|
||||||
|
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||||
|
|
||||||
|
AdditionalPartIds = newItemIds;
|
||||||
|
|
||||||
|
return itemsChanged || results.Contains(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the additional parts.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>IEnumerable{Video}.</returns>
|
||||||
|
private IEnumerable<Video> LoadAdditionalParts()
|
||||||
|
{
|
||||||
|
if (!IsMultiPart || LocationType != LocationType.FileSystem)
|
||||||
|
{
|
||||||
|
return new List<Video>();
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemResolveArgs resolveArgs;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
resolveArgs = ResolveArgs;
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
Logger.ErrorException("Error getting ResolveArgs for {0}", ex, Path);
|
||||||
|
return new List<Video>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resolveArgs.IsDirectory)
|
||||||
|
{
|
||||||
|
return new List<Video>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var files = resolveArgs.FileSystemChildren.Where(i =>
|
||||||
|
{
|
||||||
|
if ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !string.Equals(i.FullName, Path, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsVideoFile(i.FullName) && EntityResolutionHelper.IsMultiPartFile(i.FullName);
|
||||||
|
});
|
||||||
|
|
||||||
|
return LibraryManager.ResolvePaths<Video>(files, null).Select(video =>
|
||||||
|
{
|
||||||
|
// Try to retrieve it from the db. If we don't find it, use the resolved version
|
||||||
|
var dbItem = LibraryManager.RetrieveItem(video.Id) as Video;
|
||||||
|
|
||||||
|
if (dbItem != null)
|
||||||
|
{
|
||||||
|
dbItem.ResolveArgs = video.ResolveArgs;
|
||||||
|
video = dbItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return video;
|
||||||
|
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using MediaBrowser.Controller.Entities;
|
using System.Text.RegularExpressions;
|
||||||
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.IO;
|
using MediaBrowser.Controller.IO;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -45,6 +46,20 @@ namespace MediaBrowser.Controller.Resolvers
|
||||||
".mts"
|
".mts"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static readonly Regex MultiFileRegex = new Regex(
|
||||||
|
@"(.*?)([ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck]|d)[ _.-]*[0-9]+)(.*?)(\.[^.]+)$",
|
||||||
|
RegexOptions.Compiled);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether [is multi part file] [the specified path].
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
|
/// <returns><c>true</c> if [is multi part file] [the specified path]; otherwise, <c>false</c>.</returns>
|
||||||
|
public static bool IsMultiPartFile(string path)
|
||||||
|
{
|
||||||
|
return MultiFileRegex.Match(path).Success;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The audio file extensions
|
/// The audio file extensions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -53,7 +53,7 @@ namespace MediaBrowser.Model.Dto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The critic rating summary.</value>
|
/// <value>The critic rating summary.</value>
|
||||||
public string CriticRatingSummary { get; set; }
|
public string CriticRatingSummary { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the path.
|
/// Gets or sets the path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -71,7 +71,7 @@ namespace MediaBrowser.Model.Dto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The custom rating.</value>
|
/// <value>The custom rating.</value>
|
||||||
public string CustomRating { get; set; }
|
public string CustomRating { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the overview.
|
/// Gets or sets the overview.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -119,7 +119,7 @@ namespace MediaBrowser.Model.Dto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The players.</value>
|
/// <value>The players.</value>
|
||||||
public int? Players { get; set; }
|
public int? Players { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the index number.
|
/// Gets or sets the index number.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -131,7 +131,7 @@ namespace MediaBrowser.Model.Dto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The index number end.</value>
|
/// <value>The index number end.</value>
|
||||||
public int? IndexNumberEnd { get; set; }
|
public int? IndexNumberEnd { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the parent index number.
|
/// Gets or sets the parent index number.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -239,7 +239,7 @@ namespace MediaBrowser.Model.Dto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The recursive unplayed item count.</value>
|
/// <value>The recursive unplayed item count.</value>
|
||||||
public int? RecursiveUnplayedItemCount { get; set; }
|
public int? RecursiveUnplayedItemCount { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the child count.
|
/// Gets or sets the child count.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -299,7 +299,7 @@ namespace MediaBrowser.Model.Dto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The tags.</value>
|
/// <value>The tags.</value>
|
||||||
public List<string> Tags { get; set; }
|
public List<string> Tags { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the primary image aspect ratio, after image enhancements.
|
/// Gets or sets the primary image aspect ratio, after image enhancements.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -311,7 +311,7 @@ namespace MediaBrowser.Model.Dto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The original primary image aspect ratio.</value>
|
/// <value>The original primary image aspect ratio.</value>
|
||||||
public double? OriginalPrimaryImageAspectRatio { get; set; }
|
public double? OriginalPrimaryImageAspectRatio { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the artists.
|
/// Gets or sets the artists.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -348,6 +348,12 @@ namespace MediaBrowser.Model.Dto
|
||||||
/// <value>The display type of the media.</value>
|
/// <value>The display type of the media.</value>
|
||||||
public string DisplayMediaType { get; set; }
|
public string DisplayMediaType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the part count.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The part count.</value>
|
||||||
|
public int? PartCount { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified type is type.
|
/// Determines whether the specified type is type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -385,7 +391,7 @@ namespace MediaBrowser.Model.Dto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The screenshot image tags.</value>
|
/// <value>The screenshot image tags.</value>
|
||||||
public List<Guid> ScreenshotImageTags { get; set; }
|
public List<Guid> ScreenshotImageTags { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the parent logo image tag.
|
/// Gets or sets the parent logo image tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -515,7 +521,7 @@ namespace MediaBrowser.Model.Dto
|
||||||
{
|
{
|
||||||
get { return ScreenshotImageTags == null ? 0 : ScreenshotImageTags.Count; }
|
get { return ScreenshotImageTags == null ? 0 : ScreenshotImageTags.Count; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this instance has banner.
|
/// Gets a value indicating whether this instance has banner.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -8,6 +8,7 @@ using MediaBrowser.Model.Entities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||||
{
|
{
|
||||||
|
@ -17,7 +18,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||||
public class MovieResolver : BaseVideoResolver<Video>
|
public class MovieResolver : BaseVideoResolver<Video>
|
||||||
{
|
{
|
||||||
private IServerApplicationPaths ApplicationPaths { get; set; }
|
private IServerApplicationPaths ApplicationPaths { get; set; }
|
||||||
|
|
||||||
public MovieResolver(IServerApplicationPaths appPaths)
|
public MovieResolver(IServerApplicationPaths appPaths)
|
||||||
{
|
{
|
||||||
ApplicationPaths = appPaths;
|
ApplicationPaths = appPaths;
|
||||||
|
@ -196,10 +197,41 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are multiple video files, return null, and let the VideoResolver catch them later as plain videos
|
if (movies.Count > 1)
|
||||||
|
{
|
||||||
|
return GetMultiFileMovie(movies);
|
||||||
|
}
|
||||||
|
|
||||||
return movies.Count == 1 ? movies[0] : null;
|
return movies.Count == 1 ? movies[0] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the multi file movie.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="movies">The movies.</param>
|
||||||
|
/// <returns>``0.</returns>
|
||||||
|
private T GetMultiFileMovie<T>(List<T> movies)
|
||||||
|
where T : Video, new()
|
||||||
|
{
|
||||||
|
var multiPartMovies = movies.OrderBy(i => i.Path)
|
||||||
|
.Where(i => EntityResolutionHelper.IsMultiPartFile(i.Path))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// They must all be part of the sequence
|
||||||
|
if (multiPartMovies.Count != movies.Count)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstPart = multiPartMovies[0];
|
||||||
|
|
||||||
|
firstPart.IsMultiPart = true;
|
||||||
|
|
||||||
|
return firstPart;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether [is DVD directory] [the specified directory name].
|
/// Determines whether [is DVD directory] [the specified directory name].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -209,6 +241,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||||
{
|
{
|
||||||
return directoryName.Equals("video_ts", StringComparison.OrdinalIgnoreCase);
|
return directoryName.Equals("video_ts", StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether [is hd DVD directory] [the specified directory name].
|
/// Determines whether [is hd DVD directory] [the specified directory name].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -142,9 +142,15 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
|
||||||
|
|
||||||
var video = item as Video;
|
var video = item as Video;
|
||||||
|
|
||||||
if (video != null && video.Chapters != null)
|
if (video != null)
|
||||||
{
|
{
|
||||||
images = images.Concat(video.Chapters.Where(i => !string.IsNullOrEmpty(i.ImagePath)).Select(i => i.ImagePath));
|
if (video.Chapters != null)
|
||||||
|
{
|
||||||
|
images = images.Concat(video.Chapters.Where(i => !string.IsNullOrEmpty(i.ImagePath)).Select(i => i.ImagePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
var additionalParts = _itemRepo.GetItems(video.AdditionalPartIds).ToList();
|
||||||
|
images = additionalParts.Aggregate(images, (current, subItem) => current.Concat(GetPathsInUse(subItem)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var movie = item as Movie;
|
var movie = item as Movie;
|
||||||
|
|
|
@ -222,6 +222,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
|
||||||
|
|
||||||
items.AddRange(themeVideos);
|
items.AddRange(themeVideos);
|
||||||
|
|
||||||
|
items.AddRange(videos.SelectMany(i => _itemRepo.GetItems(i.AdditionalPartIds).Cast<Video>()).ToList());
|
||||||
items.AddRange(videos.OfType<Movie>().SelectMany(i => _itemRepo.GetItems(i.SpecialFeatureIds).Cast<Video>()).ToList());
|
items.AddRange(videos.OfType<Movie>().SelectMany(i => _itemRepo.GetItems(i.SpecialFeatureIds).Cast<Video>()).ToList());
|
||||||
|
|
||||||
return items.Where(i =>
|
return items.Where(i =>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
@ -6,8 +6,8 @@
|
||||||
<ProjectGuid>{E22BFD35-0FCD-4A85-978A-C22DCD73A081}</ProjectGuid>
|
<ProjectGuid>{E22BFD35-0FCD-4A85-978A-C22DCD73A081}</ProjectGuid>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>MediaBrowser.Specs</RootNamespace>
|
<RootNamespace>MediaBrowser.Tests</RootNamespace>
|
||||||
<AssemblyName>MediaBrowser.Specs</AssemblyName>
|
<AssemblyName>MediaBrowser.Tests</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
|
@ -50,7 +50,8 @@
|
||||||
</Otherwise>
|
</Otherwise>
|
||||||
</Choose>
|
</Choose>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Controller\Library\TvUtilTests.cs" />
|
<Compile Include="Resolvers\MovieResolverTests.cs" />
|
||||||
|
<Compile Include="Resolvers\TvUtilTests.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -58,6 +59,10 @@
|
||||||
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
|
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
|
||||||
<Name>MediaBrowser.Controller</Name>
|
<Name>MediaBrowser.Controller</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
|
||||||
|
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
|
||||||
|
<Name>MediaBrowser.Model</Name>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Choose>
|
<Choose>
|
||||||
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
|
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
|
||||||
|
|
30
MediaBrowser.Tests/Resolvers/MovieResolverTests.cs
Normal file
30
MediaBrowser.Tests/Resolvers/MovieResolverTests.cs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
using MediaBrowser.Controller.Resolvers;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Tests.Resolvers
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class MovieResolverTests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void TestMultiPartFiles()
|
||||||
|
{
|
||||||
|
Assert.IsFalse(EntityResolutionHelper.IsMultiPartFile(@"blah blah.mkv"));
|
||||||
|
|
||||||
|
Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - cd1.mkv"));
|
||||||
|
Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - disc1.mkv"));
|
||||||
|
Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - disk1.mkv"));
|
||||||
|
Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - pt1.mkv"));
|
||||||
|
Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - part1.mkv"));
|
||||||
|
Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - dvd1.mkv"));
|
||||||
|
|
||||||
|
// Add a space
|
||||||
|
Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - cd 1.mkv"));
|
||||||
|
Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - disc 1.mkv"));
|
||||||
|
Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - disk 1.mkv"));
|
||||||
|
Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - pt 1.mkv"));
|
||||||
|
Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - part 1.mkv"));
|
||||||
|
Assert.IsTrue(EntityResolutionHelper.IsMultiPartFile(@"blah blah - dvd 1.mkv"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
namespace MediaBrowser.Tests.Controller.Library
|
namespace MediaBrowser.Tests.Resolvers
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class TvUtilTests
|
public class TvUtilTests
|
|
@ -2047,6 +2047,27 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.getAdditionalVideoParts = function (userId, itemId) {
|
||||||
|
|
||||||
|
if (!itemId) {
|
||||||
|
throw new Error("null itemId");
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = {};
|
||||||
|
|
||||||
|
if (userId) {
|
||||||
|
options.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = self.getUrl("Videos/" + itemId + "/AdditionalParts", options);
|
||||||
|
|
||||||
|
return self.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: url,
|
||||||
|
dataType: "json"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets theme songs for an item
|
* Gets theme songs for an item
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.123" targetFramework="net45" />
|
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.124" targetFramework="net45" />
|
||||||
<package id="ServiceStack.Common" version="3.9.46" targetFramework="net45" />
|
<package id="ServiceStack.Common" version="3.9.46" targetFramework="net45" />
|
||||||
<package id="ServiceStack.Text" version="3.9.45" targetFramework="net45" />
|
<package id="ServiceStack.Text" version="3.9.45" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
Loading…
Reference in a new issue