@ -1,13 +1,18 @@
using System.Collections.Generic ;
using System.Threading ;
using System.Threading.Tasks ;
using MediaBrowser.Common.IO ;
using MediaBrowser.Controller.Configuration ;
using MediaBrowser.Controller.Dto ;
using MediaBrowser.Controller.Entities ;
using MediaBrowser.Controller.Library ;
using MediaBrowser.Controller.Persistence ;
using MediaBrowser.Model.Dto ;
using MediaBrowser.Model.Entities ;
using MediaBrowser.Model.Querying ;
using ServiceStack ;
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Threading ;
using System.Threading.Tasks ;
namespace MediaBrowser.Api
{
@ -26,28 +31,10 @@ namespace MediaBrowser.Api
public string Id { get ; set ; }
}
[Route("/Videos/{Id}/ Alternate Versions", "GET")]
[Api(Description = "Gets al ternate versions of a video.")]
public class Get AlternateVersions : IReturn < ItemsResult >
[Route("/Videos/{Id}/ Versions", "GET")]
[Api(Description = "Gets al l versions of a video.")]
public class Get MediaVersions : IReturn < List < MediaVersionInfo > >
{
[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 ; }
}
[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>
@ -60,18 +47,16 @@ namespace MediaBrowser.Api
[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")]
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Id { get ; set ; }
}
[ApiMember(Name = "IsAlternateEncoding", Description = "Filter by versions that are considered alternate encodings of the original.", IsRequired = true, DataType = "bool", ParameterType = "path", Verb = "GET")]
public bool? IsAlternateEncoding { get ; set ; }
[Route("/Videos/MergeVersions", "POST")]
[Api(Description = "Merges videos into a single record")]
public class MergeVersions : IReturnVoid
{
[ApiMember(Name = "Ids", Description = "Item id list. This allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]
public string Ids { get ; set ; }
}
public class VideosService : BaseApiService
@ -79,12 +64,18 @@ namespace MediaBrowser.Api
private readonly ILibraryManager _libraryManager ;
private readonly IUserManager _userManager ;
private readonly IDtoService _dtoService ;
private readonly IFileSystem _fileSystem ;
private readonly IItemRepository _itemRepo ;
private readonly IServerConfigurationManager _config ;
public VideosService ( ILibraryManager libraryManager , IUserManager userManager , IDtoService dtoService )
public VideosService ( ILibraryManager libraryManager , IUserManager userManager , IDtoService dtoService , IItemRepository itemRepo , IFileSystem fileSystem , IServerConfigurationManager config )
{
_libraryManager = libraryManager ;
_userManager = userManager ;
_dtoService = dtoService ;
_itemRepo = itemRepo ;
_fileSystem = fileSystem ;
_config = config ;
}
/// <summary>
@ -122,90 +113,249 @@ namespace MediaBrowser.Api
return ToOptimizedSerializedResultUsingCache ( result ) ;
}
public object Get ( Get Alternate Versions request )
public object Get ( Get Media Versions request )
{
var user = request . UserId . HasValue ? _userManager . GetUserById ( request . UserId . Value ) : null ;
var item = _libraryManager . GetItemById ( new Guid ( request . Id ) ) ;
var item = string . IsNullOrEmpty ( request . Id )
? ( request . UserId . HasValue
? user . RootFolder
: _libraryManager . RootFolder )
: _dtoService . GetItemByDtoId ( request . Id , request . UserId ) ;
var video = ( Video ) item ;
// Get everything
var fields = Enum . GetNames ( typeof ( ItemFields ) )
. Select ( i = > ( ItemFields ) Enum . Parse ( typeof ( ItemFields ) , i , true ) )
. ToList ( ) ;
var result = video . GetAlternateVersions ( ) . Select ( GetVersionInfo ) . ToList ( ) ;
var video = ( Video ) item ;
result . Add ( GetVersionInfo ( video ) ) ;
var items = video . GetAlternateVersions ( )
. Select ( i = > _dtoService . GetBaseItemDto ( i , fields , user , video ) )
. ToArray ( ) ;
result = result . OrderBy ( i = >
{
if ( video . VideoType = = VideoType . VideoFile )
{
return 0 ;
}
var result = new ItemsResult
return 1 ;
} ) . ThenBy ( i = > i . Video3DFormat . HasValue ? 1 : 0 )
. ThenByDescending ( i = >
{
Items = items ,
TotalRecordCount = items . Length
} ;
var stream = i . MediaStreams . FirstOrDefault ( m = > m . Type = = MediaStreamType . Video ) ;
return stream = = null | | stream . Width = = null ? 0 : stream . Width . Value ;
} )
. ToList ( ) ;
return ToOptimizedSerializedResultUsingCache ( result ) ;
}
p ublic void Post ( PostAlternateVersions request )
p rivate MediaVersionInfo GetVersionInfo ( Video i )
{
var task = AddAlternateVersions ( request ) ;
Task . WaitAll ( task ) ;
return new MediaVersionInfo
{
Chapters = _itemRepo . GetChapters ( i . Id ) . Select ( c = > _dtoService . GetChapterInfoDto ( c , i ) ) . ToList ( ) ,
Id = i . Id . ToString ( "N" ) ,
IsoType = i . IsoType ,
LocationType = i . LocationType ,
MediaStreams = _itemRepo . GetMediaStreams ( new MediaStreamQuery { ItemId = i . Id } ) . ToList ( ) ,
Name = GetAlternateVersionName ( i ) ,
Path = GetMappedPath ( i ) ,
RunTimeTicks = i . RunTimeTicks ,
Video3DFormat = i . Video3DFormat ,
VideoType = i . VideoType ,
IsHD = i . IsHD
} ;
}
public void Delete ( DeleteAlternateVersions request )
p rivate string GetMappedPath ( Video video )
{
var task = RemoveAlternateVersions ( request ) ;
var path = video . Path ;
Task . WaitAll ( task ) ;
var locationType = video . LocationType ;
if ( locationType ! = LocationType . FileSystem & & locationType ! = LocationType . Offline )
{
return path ;
}
foreach ( var map in _config . Configuration . PathSubstitutions )
{
path = _fileSystem . SubstitutePath ( path , map . From , map . To ) ;
}
return path ;
}
private async Task AddAlternateVersions ( PostAlternateVersions request )
private string GetAlternateVersionName ( Video video )
{
var video = ( Video ) _dtoService . GetItemByDtoId ( request . Id ) ;
var name = "" ;
var list = new List < LinkedChild > ( ) ;
var currentAlternateVersions = video . GetAlternateVersions ( ) . ToList ( ) ;
var stream = video . GetDefaultVideoStream ( ) ;
foreach ( var itemId in request . AlternateVersionIds . Split ( ',' ) . Select ( i = > new Guid ( i ) ) )
if ( video . Video3DFormat . HasValue )
{
var item = _libraryManager . GetItemById ( itemId ) as Video ;
name = "3D " + name ;
name = name . Trim ( ) ;
}
if ( item = = null )
if ( video . VideoType = = VideoType . BluRay )
{
name = name + " " + "Bluray" ;
name = name . Trim ( ) ;
}
else if ( video . VideoType = = VideoType . Dvd )
{
name = name + " " + "DVD" ;
name = name . Trim ( ) ;
}
else if ( video . VideoType = = VideoType . HdDvd )
{
name = name + " " + "HD-DVD" ;
name = name . Trim ( ) ;
}
else if ( video . VideoType = = VideoType . Iso )
{
if ( video . IsoType . HasValue )
{
throw new ArgumentException ( "No item exists with the supplied Id" ) ;
if ( video . IsoType . Value = = IsoType . BluRay )
{
name = name + " " + "Bluray" ;
}
else if ( video . IsoType . Value = = IsoType . Dvd )
{
name = name + " " + "DVD" ;
}
}
if ( currentAlternateVersions . Any ( i = > i . Id = = itemId ) )
else
{
throw new ArgumentException ( "Item already exists." ) ;
name = name + " " + "ISO" ;
}
list . Add ( new LinkedChild
name = name . Trim ( ) ;
}
else if ( video . VideoType = = VideoType . VideoFile )
{
if ( stream ! = null )
{
Path = item . Path ,
Type = LinkedChildType . Manual
} ) ;
if ( stream . Width . HasValue )
{
if ( stream . Width . Value > = 1900 )
{
name = name + " " + "1080P" ;
name = name . Trim ( ) ;
}
else if ( stream . Width . Value > = 1270 )
{
name = name + " " + "720P" ;
name = name . Trim ( ) ;
}
else if ( stream . Width . Value > = 700 )
{
name = name + " " + "480p" ;
name = name . Trim ( ) ;
}
else
{
name = name + " " + "SD" ;
name = name . Trim ( ) ;
}
}
}
}
item . PrimaryVersionId = video . Id ;
if ( stream ! = null & & ! string . IsNullOrWhiteSpace ( stream . Codec ) )
{
name = name + " " + stream . Codec . ToUpper ( ) ;
name = name . Trim ( ) ;
}
video . LinkedAlternateVersions . AddRange ( list ) ;
if ( string . IsNullOrWhiteSpace ( name ) )
{
return video . Name ;
}
await video . UpdateToRepository ( ItemUpdateType . MetadataEdit , CancellationToken . None ) . ConfigureAwait ( false ) ;
return name ;
}
public void Delete ( DeleteAlternateVersions request )
{
var task = RemoveAlternateVersions ( request ) ;
await video . RefreshMetadata ( CancellationToken . None ) . ConfigureAwait ( false ) ;
Task . WaitAll ( task ) ;
}
private async Task RemoveAlternateVersions ( DeleteAlternateVersions request )
{
var video = ( Video ) _dtoService . GetItemByDtoId ( request . Id ) ;
foreach ( var link in video . GetLinkedAlternateVersions ( ) )
{
link . PrimaryVersionId = null ;
await link . UpdateToRepository ( ItemUpdateType . MetadataEdit , CancellationToken . None ) . ConfigureAwait ( false ) ;
}
video . LinkedAlternateVersions . Clear ( ) ;
await video . UpdateToRepository ( ItemUpdateType . MetadataEdit , CancellationToken . None ) . ConfigureAwait ( false ) ;
}
public void Post ( MergeVersions request )
{
var task = MergeVersions ( request ) ;
Task . WaitAll ( task ) ;
}
private async Task MergeVersions ( MergeVersions request )
{
var items = request . Ids . Split ( ',' )
. Select ( i = > new Guid ( i ) )
. Select ( i = > _libraryManager . GetItemById ( i ) )
. ToList ( ) ;
if ( items . Count < 2 )
{
throw new ArgumentException ( "Please supply at least two videos to merge." ) ;
}
if ( items . Any ( i = > ! ( i is Video ) ) )
{
throw new ArgumentException ( "Only videos can be grouped together." ) ;
}
var videos = items . Cast < Video > ( ) . ToList ( ) ;
var videosWithVersions = videos . Where ( i = > i . AlternateVersionCount > 0 )
. ToList ( ) ;
if ( videosWithVersions . Count > 1 )
{
throw new ArgumentException ( "Videos with sub-versions cannot be merged." ) ;
}
var primaryVersion = videosWithVersions . FirstOrDefault ( ) ;
if ( primaryVersion = = null )
{
primaryVersion = videos . OrderByDescending ( i = >
{
var stream = i . GetDefaultVideoStream ( ) ;
return stream = = null | | stream . Width = = null ? 0 : stream . Width . Value ;
} ) . ThenBy ( i = > i . Name . Length )
. First ( ) ;
}
foreach ( var item in videos . Where ( i = > i . Id ! = primaryVersion . Id ) )
{
item . PrimaryVersionId = primaryVersion . Id ;
await item . UpdateToRepository ( ItemUpdateType . MetadataEdit , CancellationToken . None ) . ConfigureAwait ( false ) ;
primaryVersion . LinkedAlternateVersions . Add ( new LinkedChild
{
Path = item . Path ,
ItemId = item . Id
} ) ;
}
await primaryVersion . UpdateToRepository ( ItemUpdateType . MetadataEdit , CancellationToken . None ) . ConfigureAwait ( false ) ;
}
}
}