aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2014-03-16 00:23:58 -0400
committerLuke Pulverenti <luke.pulverenti@gmail.com>2014-03-16 00:23:58 -0400
commitb36aea4ff74052ae40c27db057f50f645659aa57 (patch)
treef98c438363d961243fcf743c9a240a05429a2848
parent4e6d306d0021cda1e909da2647b803ea7d505d4a (diff)
#712 - Support grouping multiple versions of a movie
-rw-r--r--MediaBrowser.Api/VideosService.cs95
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs86
-rw-r--r--MediaBrowser.Dlna/PlayTo/DlnaController.cs21
-rw-r--r--MediaBrowser.Dlna/PlayTo/PlayToManager.cs2
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs3
-rw-r--r--MediaBrowser.Model/Session/PlayRequest.cs6
-rw-r--r--MediaBrowser.Model/Session/PlaystateCommand.cs6
-rw-r--r--MediaBrowser.Providers/All/LocalImageProvider.cs15
-rw-r--r--MediaBrowser.Server.Implementations/Collections/CollectionManager.cs3
-rw-r--r--MediaBrowser.Server.Implementations/Dto/DtoService.cs7
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionManager.cs10
12 files changed, 217 insertions, 39 deletions
diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs
index 31fda199e..af200895e 100644
--- a/MediaBrowser.Api/VideosService.cs
+++ b/MediaBrowser.Api/VideosService.cs
@@ -1,4 +1,7 @@
-using MediaBrowser.Controller.Dto;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Querying;
@@ -37,14 +40,44 @@ namespace MediaBrowser.Api
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
-
+
+ [Route("/Videos/{Id}/AlternateVersions", "POST")]
+ [Api(Description = "Assigns videos as alternates of antoher.")]
+ public class PostAlternateVersions : IReturnVoid
+ {
+ [ApiMember(Name = "AlternateVersionIds", Description = "Item id, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string AlternateVersionIds { 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; }
+ }
+
+ [Route("/Videos/{Id}/AlternateVersions", "DELETE")]
+ [Api(Description = "Assigns videos as alternates of antoher.")]
+ public class DeleteAlternateVersions : IReturnVoid
+ {
+ [ApiMember(Name = "AlternateVersionIds", Description = "Item id, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string AlternateVersionIds { 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 ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
private readonly IDtoService _dtoService;
- public VideosService( ILibraryManager libraryManager, IUserManager userManager, IDtoService dtoService)
+ public VideosService(ILibraryManager libraryManager, IUserManager userManager, IDtoService dtoService)
{
_libraryManager = libraryManager;
_userManager = userManager;
@@ -115,5 +148,61 @@ namespace MediaBrowser.Api
return ToOptimizedSerializedResultUsingCache(result);
}
+
+ public void Post(PostAlternateVersions request)
+ {
+ var task = AddAlternateVersions(request);
+
+ Task.WaitAll(task);
+ }
+
+ public void Delete(DeleteAlternateVersions request)
+ {
+ var task = RemoveAlternateVersions(request);
+
+ Task.WaitAll(task);
+ }
+
+ private async Task AddAlternateVersions(PostAlternateVersions request)
+ {
+ var video = (Video)_dtoService.GetItemByDtoId(request.Id);
+
+ var list = new List<LinkedChild>();
+ var currentAlternateVersions = video.GetAlternateVersions().ToList();
+
+ foreach (var itemId in request.AlternateVersionIds.Split(',').Select(i => new Guid(i)))
+ {
+ var item = _libraryManager.GetItemById(itemId) as Video;
+
+ if (item == null)
+ {
+ throw new ArgumentException("No item exists with the supplied Id");
+ }
+
+ if (currentAlternateVersions.Any(i => i.Id == itemId))
+ {
+ throw new ArgumentException("Item already exists.");
+ }
+
+ list.Add(new LinkedChild
+ {
+ Path = item.Path,
+ Type = LinkedChildType.Manual
+ });
+
+ item.PrimaryVersionId = video.Id;
+ }
+
+ video.LinkedAlternateVersions.AddRange(list);
+
+ await video.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
+
+ await video.RefreshMetadata(CancellationToken.None).ConfigureAwait(false);
+ }
+
+ private async Task RemoveAlternateVersions(DeleteAlternateVersions request)
+ {
+ var video = (Video)_dtoService.GetItemByDtoId(request.Id);
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index e30458dd8..18db21f38 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
@@ -20,26 +21,27 @@ namespace MediaBrowser.Controller.Entities
{
public bool IsMultiPart { get; set; }
public bool HasLocalAlternateVersions { get; set; }
+ public Guid? PrimaryVersionId { get; set; }
public List<Guid> AdditionalPartIds { get; set; }
- public List<Guid> AlternateVersionIds { get; set; }
+ public List<Guid> LocalAlternateVersionIds { get; set; }
public Video()
{
PlayableStreamFileNames = new List<string>();
AdditionalPartIds = new List<Guid>();
- AlternateVersionIds = new List<Guid>();
+ LocalAlternateVersionIds = new List<Guid>();
Tags = new List<string>();
SubtitleFiles = new List<string>();
LinkedAlternateVersions = new List<LinkedChild>();
}
[IgnoreDataMember]
- public bool HasAlternateVersions
+ public int AlternateVersionCount
{
get
{
- return HasLocalAlternateVersions || LinkedAlternateVersions.Count > 0;
+ return LinkedAlternateVersions.Count + LocalAlternateVersionIds.Count;
}
}
@@ -51,7 +53,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>IEnumerable{BaseItem}.</returns>
public IEnumerable<BaseItem> GetAlternateVersions()
{
- var filesWithinSameDirectory = AlternateVersionIds
+ var filesWithinSameDirectory = LocalAlternateVersionIds
.Select(i => LibraryManager.GetItemById(i))
.Where(i => i != null)
.OfType<Video>();
@@ -233,14 +235,11 @@ namespace MediaBrowser.Controller.Entities
{
RefreshLinkedAlternateVersions();
- if (HasLocalAlternateVersions)
- {
- var additionalPartsChanged = await RefreshAlternateVersionsWithinSameDirectory(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
+ var additionalPartsChanged = await RefreshAlternateVersionsWithinSameDirectory(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
- if (additionalPartsChanged)
- {
- hasChanges = true;
- }
+ if (additionalPartsChanged)
+ {
+ hasChanges = true;
}
}
}
@@ -339,21 +338,72 @@ namespace MediaBrowser.Controller.Entities
private async Task<bool> RefreshAlternateVersionsWithinSameDirectory(MetadataRefreshOptions options, IEnumerable<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- var newItems = LoadAlternateVersionsWithinSameDirectory(fileSystemChildren, options.DirectoryService).ToList();
+ var newItems = HasLocalAlternateVersions ?
+ LoadAlternateVersionsWithinSameDirectory(fileSystemChildren, options.DirectoryService).ToList() :
+ new List<Video>();
var newItemIds = newItems.Select(i => i.Id).ToList();
- var itemsChanged = !AlternateVersionIds.SequenceEqual(newItemIds);
+ var itemsChanged = !LocalAlternateVersionIds.SequenceEqual(newItemIds);
- var tasks = newItems.Select(i => i.RefreshMetadata(options, cancellationToken));
+ var tasks = newItems.Select(i => RefreshAlternateVersion(options, i, cancellationToken));
await Task.WhenAll(tasks).ConfigureAwait(false);
- AlternateVersionIds = newItemIds;
+ LocalAlternateVersionIds = newItemIds;
return itemsChanged;
}
+ private Task RefreshAlternateVersion(MetadataRefreshOptions options, Video video, CancellationToken cancellationToken)
+ {
+ var currentImagePath = video.GetImagePath(ImageType.Primary);
+ var ownerImagePath = this.GetImagePath(ImageType.Primary);
+
+ var newOptions = new MetadataRefreshOptions
+ {
+ DirectoryService = options.DirectoryService,
+ ImageRefreshMode = options.ImageRefreshMode,
+ MetadataRefreshMode = options.MetadataRefreshMode,
+ ReplaceAllMetadata = options.ReplaceAllMetadata
+ };
+
+ if (!string.Equals(currentImagePath, ownerImagePath, StringComparison.OrdinalIgnoreCase))
+ {
+ newOptions.ForceSave = true;
+
+ if (string.IsNullOrWhiteSpace(ownerImagePath))
+ {
+ video.ImageInfos.Clear();
+ }
+ else
+ {
+ video.SetImagePath(ImageType.Primary, ownerImagePath);
+ }
+ }
+
+ return video.RefreshMetadata(newOptions, cancellationToken);
+ }
+
+ public override async Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
+ {
+ await base.UpdateToRepository(updateReason, cancellationToken).ConfigureAwait(false);
+
+ foreach (var item in LocalAlternateVersionIds.Select(i => LibraryManager.GetItemById(i)))
+ {
+ item.ImageInfos = ImageInfos;
+ item.Overview = Overview;
+ item.ProductionYear = ProductionYear;
+ item.PremiereDate = PremiereDate;
+ item.CommunityRating = CommunityRating;
+ item.OfficialRating = OfficialRating;
+ item.Genres = Genres;
+ item.ProviderIds = ProviderIds;
+
+ await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
/// <summary>
/// Loads the additional parts.
/// </summary>
@@ -395,7 +445,7 @@ namespace MediaBrowser.Controller.Entities
video = dbItem;
}
- video.ImageInfos = ImageInfos;
+ video.PrimaryVersionId = Id;
return video;
diff --git a/MediaBrowser.Dlna/PlayTo/DlnaController.cs b/MediaBrowser.Dlna/PlayTo/DlnaController.cs
index 7885ee481..2d500db89 100644
--- a/MediaBrowser.Dlna/PlayTo/DlnaController.cs
+++ b/MediaBrowser.Dlna/PlayTo/DlnaController.cs
@@ -28,6 +28,7 @@ namespace MediaBrowser.Dlna.PlayTo
private readonly INetworkManager _networkManager;
private readonly ILogger _logger;
private readonly IDlnaManager _dlnaManager;
+ private readonly IUserManager _userManager;
private bool _playbackStarted = false;
public bool SupportsMediaRemoteControl
@@ -46,7 +47,7 @@ namespace MediaBrowser.Dlna.PlayTo
}
}
- public PlayToController(SessionInfo session, ISessionManager sessionManager, IItemRepository itemRepository, ILibraryManager libraryManager, ILogger logger, INetworkManager networkManager, IDlnaManager dlnaManager)
+ public PlayToController(SessionInfo session, ISessionManager sessionManager, IItemRepository itemRepository, ILibraryManager libraryManager, ILogger logger, INetworkManager networkManager, IDlnaManager dlnaManager, IUserManager userManager)
{
_session = session;
_itemRepository = itemRepository;
@@ -54,6 +55,7 @@ namespace MediaBrowser.Dlna.PlayTo
_libraryManager = libraryManager;
_networkManager = networkManager;
_dlnaManager = dlnaManager;
+ _userManager = userManager;
_logger = logger;
}
@@ -194,7 +196,7 @@ namespace MediaBrowser.Dlna.PlayTo
#region SendCommands
- public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
+ public async Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
{
_logger.Debug("{0} - Received PlayRequest: {1}", this._session.DeviceName, command.PlayCommand);
@@ -227,16 +229,25 @@ namespace MediaBrowser.Dlna.PlayTo
if (command.PlayCommand == PlayCommand.PlayLast)
{
AddItemsToPlaylist(playlist);
- return Task.FromResult(true);
}
if (command.PlayCommand == PlayCommand.PlayNext)
{
AddItemsToPlaylist(playlist);
- return Task.FromResult(true);
}
_logger.Debug("{0} - Playing {1} items", _session.DeviceName, playlist.Count);
- return PlayItems(playlist);
+
+ if (!string.IsNullOrWhiteSpace(command.ControllingUserId))
+ {
+ var userId = new Guid(command.ControllingUserId);
+
+ var user = _userManager.GetUserById(userId);
+
+ await _sessionManager.LogSessionActivity(_session.Client, _session.ApplicationVersion, _session.DeviceId,
+ _session.DeviceName, _session.RemoteEndPoint, user).ConfigureAwait(false);
+ }
+
+ await PlayItems(playlist).ConfigureAwait(false);
}
public Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs
index cb30498e6..0eab41b1b 100644
--- a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs
+++ b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs
@@ -227,7 +227,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (controller == null)
{
- sessionInfo.SessionController = controller = new PlayToController(sessionInfo, _sessionManager, _itemRepository, _libraryManager, _logger, _networkManager, _dlnaManager);
+ sessionInfo.SessionController = controller = new PlayToController(sessionInfo, _sessionManager, _itemRepository, _libraryManager, _logger, _networkManager, _dlnaManager, _userManager);
}
controller.Init(device);
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index d571c233b..857332b06 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -494,7 +494,8 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value>The part count.</value>
public int? PartCount { get; set; }
- public bool? HasAlternateVersions { get; set; }
+ public int? AlternateVersionCount { get; set; }
+ public string PrimaryVersionId { get; set; }
/// <summary>
/// Determines whether the specified type is type.
diff --git a/MediaBrowser.Model/Session/PlayRequest.cs b/MediaBrowser.Model/Session/PlayRequest.cs
index 57f6c37f5..949274a5d 100644
--- a/MediaBrowser.Model/Session/PlayRequest.cs
+++ b/MediaBrowser.Model/Session/PlayRequest.cs
@@ -23,6 +23,12 @@ namespace MediaBrowser.Model.Session
/// </summary>
/// <value>The play command.</value>
public PlayCommand PlayCommand { get; set; }
+
+ /// <summary>
+ /// Gets or sets the controlling user identifier.
+ /// </summary>
+ /// <value>The controlling user identifier.</value>
+ public string ControllingUserId { get; set; }
}
/// <summary>
diff --git a/MediaBrowser.Model/Session/PlaystateCommand.cs b/MediaBrowser.Model/Session/PlaystateCommand.cs
index 918f4f70f..b0dec66d4 100644
--- a/MediaBrowser.Model/Session/PlaystateCommand.cs
+++ b/MediaBrowser.Model/Session/PlaystateCommand.cs
@@ -37,5 +37,11 @@ namespace MediaBrowser.Model.Session
public PlaystateCommand Command { get; set; }
public long? SeekPositionTicks { get; set; }
+
+ /// <summary>
+ /// Gets or sets the controlling user identifier.
+ /// </summary>
+ /// <value>The controlling user identifier.</value>
+ public string ControllingUserId { get; set; }
}
} \ No newline at end of file
diff --git a/MediaBrowser.Providers/All/LocalImageProvider.cs b/MediaBrowser.Providers/All/LocalImageProvider.cs
index 6ef5f6bf3..1d10fcfa3 100644
--- a/MediaBrowser.Providers/All/LocalImageProvider.cs
+++ b/MediaBrowser.Providers/All/LocalImageProvider.cs
@@ -94,14 +94,13 @@ namespace MediaBrowser.Providers.All
public List<LocalImageInfo> GetImages(IHasImages item, IEnumerable<string> paths, IDirectoryService directoryService)
{
var files = paths.SelectMany(directoryService.GetFiles)
- .Where(i =>
- {
- var ext = i.Extension;
-
- return !string.IsNullOrEmpty(ext) &&
- BaseItem.SupportedImageExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
- })
- .Cast<FileSystemInfo>()
+ .Where(i =>
+ {
+ var ext = i.Extension;
+
+ return !string.IsNullOrEmpty(ext) &&
+ BaseItem.SupportedImageExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
+ })
.ToList();
var list = new List<LocalImageInfo>();
diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs
index 60d631c1a..c18856293 100644
--- a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs
+++ b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs
@@ -111,6 +111,7 @@ namespace MediaBrowser.Server.Implementations.Collections
}
var list = new List<LinkedChild>();
+ var currentLinkedChildren = collection.GetLinkedChildren().ToList();
foreach (var itemId in ids)
{
@@ -121,7 +122,7 @@ namespace MediaBrowser.Server.Implementations.Collections
throw new ArgumentException("No item exists with the supplied Id");
}
- if (collection.LinkedChildren.Any(i => i.ItemId.HasValue && i.ItemId == itemId))
+ if (currentLinkedChildren.Any(i => i.Id == itemId))
{
throw new ArgumentException("Item already exists in collection");
}
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
index 6f6a3f043..19d834c20 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
@@ -1082,7 +1082,12 @@ namespace MediaBrowser.Server.Implementations.Dto
dto.IsHD = video.IsHD;
dto.PartCount = video.AdditionalPartIds.Count + 1;
- dto.HasAlternateVersions = video.HasAlternateVersions;
+ dto.AlternateVersionCount = video.AlternateVersionCount;
+
+ if (video.PrimaryVersionId.HasValue)
+ {
+ dto.PrimaryVersionId = video.PrimaryVersionId.Value.ToString("N");
+ }
if (fields.Contains(ItemFields.Chapters))
{
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index de10e669e..b11457ec5 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -410,7 +410,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
if (!string.IsNullOrWhiteSpace(filenamePrefix))
{
- if (sortedMovies.All(i => Path.GetFileNameWithoutExtension(i.Path).StartsWith(filenamePrefix, StringComparison.OrdinalIgnoreCase)))
+ if (sortedMovies.Skip(1).All(i => Path.GetFileNameWithoutExtension(i.Path).StartsWith(filenamePrefix + " - ", StringComparison.OrdinalIgnoreCase)))
{
firstMovie.HasLocalAlternateVersions = true;
diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
index 71d95b97b..cea70a8b4 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs
+++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
@@ -690,6 +690,11 @@ namespace MediaBrowser.Server.Implementations.Session
}
}
+ if (session.UserId.HasValue)
+ {
+ command.ControllingUserId = session.UserId.Value.ToString("N");
+ }
+
return session.SessionController.SendPlayCommand(command, cancellationToken);
}
@@ -723,6 +728,11 @@ namespace MediaBrowser.Server.Implementations.Session
throw new ArgumentException(string.Format("Session {0} is unable to seek.", session.Id));
}
+ if (session.UserId.HasValue)
+ {
+ command.ControllingUserId = session.UserId.Value.ToString("N");
+ }
+
return session.SessionController.SendPlaystateCommand(command, cancellationToken);
}