mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-11-16 02:18:54 -07:00
Added poor man's multi-file movie support
This commit is contained in:
parent
455de48a65
commit
def3428199
@ -415,15 +415,13 @@ namespace MediaBrowser.Api
|
||||
: DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, request.UserId);
|
||||
|
||||
// Get everything
|
||||
var fields =
|
||||
Enum.GetNames(typeof(ItemFields))
|
||||
var fields = Enum.GetNames(typeof(ItemFields))
|
||||
.Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
|
||||
.ToList();
|
||||
|
||||
var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository);
|
||||
|
||||
var items =
|
||||
_itemRepo.GetItems(item.ThemeSongIds)
|
||||
var items = _itemRepo.GetItems(item.ThemeSongIds)
|
||||
.OrderBy(i => i.SortName)
|
||||
.Select(i => dtoBuilder.GetBaseItemDto(i, fields, user))
|
||||
.Select(t => t.Result)
|
||||
|
@ -114,6 +114,7 @@
|
||||
<Compile Include="UserLibrary\YearsService.cs" />
|
||||
<Compile Include="UserService.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="VideosService.cs" />
|
||||
<Compile Include="WeatherService.cs" />
|
||||
<Compile Include="WebSocket\LogFileWebSocketListener.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);
|
||||
}
|
||||
}
|
||||
}
|
@ -441,6 +441,8 @@ namespace MediaBrowser.Controller.Dto
|
||||
dto.VideoFormat = video.VideoFormat;
|
||||
dto.IsoType = video.IsoType;
|
||||
|
||||
dto.PartCount = video.AdditionalPartIds.Count + 1;
|
||||
|
||||
if (fields.Contains(ItemFields.Chapters) && video.Chapters != null)
|
||||
{
|
||||
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)
|
||||
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))
|
||||
{
|
||||
@ -916,14 +916,11 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <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>
|
||||
/// <param name="resetResolveArgs">if set to <c>true</c> [reset resolve args].</param>
|
||||
/// <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)
|
||||
{
|
||||
if (resetResolveArgs)
|
||||
public virtual async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
|
||||
{
|
||||
// Reload this
|
||||
ResolveArgs = null;
|
||||
}
|
||||
|
||||
// Refresh for the item
|
||||
var itemRefreshTask = ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh, allowSlowProviders);
|
||||
|
@ -768,7 +768,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
var child = currentTuple.Item1;
|
||||
|
||||
//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
|
||||
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="forceRefresh">if set to <c>true</c> [force].</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>
|
||||
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
|
||||
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="forceRefresh">if set to <c>true</c> [force].</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>
|
||||
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
|
||||
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);
|
||||
|
||||
@ -127,7 +126,7 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||
}
|
||||
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>();
|
||||
}
|
||||
|
||||
|
@ -322,14 +322,11 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <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>
|
||||
/// <param name="resetResolveArgs">if set to <c>true</c> [reset resolve args].</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, bool resetResolveArgs = true)
|
||||
{
|
||||
if (resetResolveArgs)
|
||||
public override async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
|
||||
{
|
||||
// Reload this
|
||||
ResolveArgs = null;
|
||||
}
|
||||
|
||||
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.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
@ -11,11 +16,16 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// </summary>
|
||||
public class Video : BaseItem, IHasMediaStreams
|
||||
{
|
||||
public bool IsMultiPart { get; set; }
|
||||
|
||||
public List<Guid> AdditionalPartIds { get; set; }
|
||||
|
||||
public Video()
|
||||
{
|
||||
MediaStreams = new List<MediaStream>();
|
||||
Chapters = new List<ChapterInfo>();
|
||||
PlayableStreamFileNames = new List<string>();
|
||||
AdditionalPartIds = new List<Guid>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -112,5 +122,102 @@ namespace MediaBrowser.Controller.Entities
|
||||
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 System;
|
||||
using System.Collections.Generic;
|
||||
@ -45,6 +46,20 @@ namespace MediaBrowser.Controller.Resolvers
|
||||
".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>
|
||||
/// The audio file extensions
|
||||
/// </summary>
|
||||
|
@ -348,6 +348,12 @@ namespace MediaBrowser.Model.Dto
|
||||
/// <value>The display type of the media.</value>
|
||||
public string DisplayMediaType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the part count.
|
||||
/// </summary>
|
||||
/// <value>The part count.</value>
|
||||
public int? PartCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified type is type.
|
||||
/// </summary>
|
||||
|
@ -8,6 +8,7 @@ using MediaBrowser.Model.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
/// <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>
|
||||
/// Determines whether [is DVD directory] [the specified directory name].
|
||||
/// </summary>
|
||||
@ -209,6 +241,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||
{
|
||||
return directoryName.Equals("video_ts", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether [is hd DVD directory] [the specified directory name].
|
||||
/// </summary>
|
||||
|
@ -142,11 +142,17 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
|
||||
|
||||
var video = item as Video;
|
||||
|
||||
if (video != null && video.Chapters != null)
|
||||
if (video != null)
|
||||
{
|
||||
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;
|
||||
|
||||
if (movie != null)
|
||||
|
@ -222,6 +222,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
|
||||
|
||||
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());
|
||||
|
||||
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">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
@ -6,8 +6,8 @@
|
||||
<ProjectGuid>{E22BFD35-0FCD-4A85-978A-C22DCD73A081}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MediaBrowser.Specs</RootNamespace>
|
||||
<AssemblyName>MediaBrowser.Specs</AssemblyName>
|
||||
<RootNamespace>MediaBrowser.Tests</RootNamespace>
|
||||
<AssemblyName>MediaBrowser.Tests</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
@ -50,7 +50,8 @@
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
<ItemGroup>
|
||||
<Compile Include="Controller\Library\TvUtilTests.cs" />
|
||||
<Compile Include="Resolvers\MovieResolverTests.cs" />
|
||||
<Compile Include="Resolvers\TvUtilTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@ -58,6 +59,10 @@
|
||||
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
|
||||
<Name>MediaBrowser.Controller</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
|
||||
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
|
||||
<Name>MediaBrowser.Model</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Choose>
|
||||
<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 Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace MediaBrowser.Tests.Controller.Library
|
||||
namespace MediaBrowser.Tests.Resolvers
|
||||
{
|
||||
[TestClass]
|
||||
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
|
||||
*/
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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.Text" version="3.9.45" targetFramework="net45" />
|
||||
</packages>
|
Loading…
Reference in New Issue
Block a user