more sync movement
This commit is contained in:
parent
3fb40eb02e
commit
ab3da46113
|
@ -1,25 +1,19 @@
|
|||
using MediaBrowser.Controller.Devices;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Devices;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Session;
|
||||
using ServiceStack;
|
||||
using ServiceStack.Web;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.Devices
|
||||
{
|
||||
[Route("/Devices", "GET", Summary = "Gets all devices")]
|
||||
[Authenticated(Roles = "Admin")]
|
||||
public class GetDevices : IReturn<List<DeviceInfo>>
|
||||
public class GetDevices : DeviceQuery, IReturn<QueryResult<DeviceInfo>>
|
||||
{
|
||||
[ApiMember(Name = "SupportsContentUploading", Description = "SupportsContentUploading", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public bool? SupportsContentUploading { get; set; }
|
||||
|
||||
[ApiMember(Name = "SupportsDeviceId", Description = "SupportsDeviceId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public bool? SupportsDeviceId { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Devices", "DELETE", Summary = "Deletes a device")]
|
||||
|
@ -112,23 +106,7 @@ namespace MediaBrowser.Api.Devices
|
|||
|
||||
public object Get(GetDevices request)
|
||||
{
|
||||
var devices = _deviceManager.GetDevices();
|
||||
|
||||
if (request.SupportsContentUploading.HasValue)
|
||||
{
|
||||
var val = request.SupportsContentUploading.Value;
|
||||
|
||||
devices = devices.Where(i => _deviceManager.GetCapabilities(i.Id).SupportsContentUploading == val);
|
||||
}
|
||||
|
||||
if (request.SupportsDeviceId.HasValue)
|
||||
{
|
||||
var val = request.SupportsDeviceId.Value;
|
||||
|
||||
devices = devices.Where(i => _deviceManager.GetCapabilities(i.Id).SupportsDeviceId == val);
|
||||
}
|
||||
|
||||
return ToOptimizedResult(devices.ToList());
|
||||
return ToOptimizedResult(_deviceManager.GetDevices(request));
|
||||
}
|
||||
|
||||
public object Get(GetCameraUploads request)
|
||||
|
|
|
@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities;
|
|||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Playlists;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using ServiceStack;
|
||||
using System.Collections.Generic;
|
||||
|
@ -20,6 +21,11 @@ namespace MediaBrowser.Api.Music
|
|||
{
|
||||
}
|
||||
|
||||
[Route("/Playlists/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given playlist")]
|
||||
public class GetInstantMixFromPlaylist : BaseGetSimilarItemsFromItem
|
||||
{
|
||||
}
|
||||
|
||||
[Route("/Artists/{Name}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given artist")]
|
||||
public class GetInstantMixFromArtist : BaseGetSimilarItems
|
||||
{
|
||||
|
@ -109,6 +115,17 @@ namespace MediaBrowser.Api.Music
|
|||
return GetResult(items, user, request);
|
||||
}
|
||||
|
||||
public object Get(GetInstantMixFromPlaylist request)
|
||||
{
|
||||
var playlist = (Playlist)_libraryManager.GetItemById(request.Id);
|
||||
|
||||
var user = _userManager.GetUserById(request.UserId.Value);
|
||||
|
||||
var items = _musicManager.GetInstantMixFromPlaylist(playlist, user);
|
||||
|
||||
return GetResult(items, user, request);
|
||||
}
|
||||
|
||||
public object Get(GetInstantMixFromMusicGenre request)
|
||||
{
|
||||
var user = _userManager.GetUserById(request.UserId.Value);
|
||||
|
|
|
@ -239,8 +239,11 @@ namespace MediaBrowser.Api.Session
|
|||
[ApiMember(Name = "SupportsContentUploading", Description = "Determines whether camera upload is supported.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
|
||||
public bool SupportsContentUploading { get; set; }
|
||||
|
||||
[ApiMember(Name = "SupportsDeviceId", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
|
||||
public bool SupportsDeviceId { get; set; }
|
||||
[ApiMember(Name = "SupportsSync", Description = "Determines whether sync is supported.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
|
||||
public bool SupportsSync { get; set; }
|
||||
|
||||
[ApiMember(Name = "SupportsUniqueIdentifier", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
|
||||
public bool SupportsUniqueIdentifier { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Sessions/Logout", "POST", Summary = "Reports that a session has ended")]
|
||||
|
@ -521,7 +524,9 @@ namespace MediaBrowser.Api.Session
|
|||
|
||||
SupportsContentUploading = request.SupportsContentUploading,
|
||||
|
||||
SupportsDeviceId = request.SupportsDeviceId
|
||||
SupportsSync = request.SupportsSync,
|
||||
|
||||
SupportsUniqueIdentifier = request.SupportsUniqueIdentifier
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,11 +15,13 @@ namespace MediaBrowser.Controller.Collections
|
|||
public Dictionary<string, string> ProviderIds { get; set; }
|
||||
|
||||
public List<Guid> ItemIdList { get; set; }
|
||||
public List<Guid> UserIds { get; set; }
|
||||
|
||||
public CollectionCreationOptions()
|
||||
{
|
||||
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
ItemIdList = new List<Guid>();
|
||||
UserIds = new List<Guid>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using MediaBrowser.Model.Devices;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Session;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -58,8 +59,9 @@ namespace MediaBrowser.Controller.Devices
|
|||
/// <summary>
|
||||
/// Gets the devices.
|
||||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>IEnumerable<DeviceInfo>.</returns>
|
||||
IEnumerable<DeviceInfo> GetDevices();
|
||||
QueryResult<DeviceInfo> GetDevices(DeviceQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the device.
|
||||
|
|
|
@ -15,8 +15,10 @@ namespace MediaBrowser.Controller.Entities.Movies
|
|||
/// <summary>
|
||||
/// Class BoxSet
|
||||
/// </summary>
|
||||
public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer
|
||||
public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer, IHasShares
|
||||
{
|
||||
public List<Share> Shares { get; set; }
|
||||
|
||||
public BoxSet()
|
||||
{
|
||||
RemoteTrailers = new List<MediaUrl>();
|
||||
|
@ -25,6 +27,7 @@ namespace MediaBrowser.Controller.Entities.Movies
|
|||
|
||||
DisplayOrder = ItemSortBy.PremiereDate;
|
||||
Keywords = new List<string>();
|
||||
Shares = new List<Share>();
|
||||
}
|
||||
|
||||
protected override bool FilterLinkedChildrenPerUser
|
||||
|
@ -160,5 +163,20 @@ namespace MediaBrowser.Controller.Entities.Movies
|
|||
|
||||
progress.Report(100);
|
||||
}
|
||||
|
||||
public override bool IsVisible(User user)
|
||||
{
|
||||
if (base.IsVisible(user))
|
||||
{
|
||||
var userId = user.Id.ToString("N");
|
||||
|
||||
return Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)) ||
|
||||
|
||||
// Need to support this for boxsets created prior to the creation of Shares
|
||||
Shares.Count == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
15
MediaBrowser.Controller/Entities/Share.cs
Normal file
15
MediaBrowser.Controller/Entities/Share.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
public interface IHasShares
|
||||
{
|
||||
List<Share> Shares { get; set; }
|
||||
}
|
||||
|
||||
public class Share
|
||||
{
|
||||
public string UserId { get; set; }
|
||||
public bool CanEdit { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Playlists;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Controller.Library
|
||||
|
@ -28,6 +29,13 @@ namespace MediaBrowser.Controller.Library
|
|||
/// <returns>IEnumerable{Audio}.</returns>
|
||||
IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user);
|
||||
/// <summary>
|
||||
/// Gets the instant mix from playlist.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>IEnumerable<Audio>.</returns>
|
||||
IEnumerable<Audio> GetInstantMixFromPlaylist(Playlist item, User user);
|
||||
/// <summary>
|
||||
/// Gets the instant mix from genre.
|
||||
/// </summary>
|
||||
/// <param name="genres">The genres.</param>
|
||||
|
|
|
@ -162,6 +162,7 @@
|
|||
<Compile Include="Entities\IHasAwards.cs" />
|
||||
<Compile Include="Entities\Photo.cs" />
|
||||
<Compile Include="Entities\PhotoAlbum.cs" />
|
||||
<Compile Include="Entities\Share.cs" />
|
||||
<Compile Include="Entities\UserView.cs" />
|
||||
<Compile Include="Entities\UserViewBuilder.cs" />
|
||||
<Compile Include="FileOrganization\IFileOrganizationService.cs" />
|
||||
|
|
|
@ -10,12 +10,6 @@ namespace MediaBrowser.Controller.Persistence
|
|||
/// </summary>
|
||||
public interface IUserRepository : IRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Opens the connection to the repository
|
||||
/// </summary>
|
||||
/// <returns>Task.</returns>
|
||||
Task Initialize();
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the user.
|
||||
/// </summary>
|
||||
|
|
|
@ -11,10 +11,17 @@ using System.Runtime.Serialization;
|
|||
|
||||
namespace MediaBrowser.Controller.Playlists
|
||||
{
|
||||
public class Playlist : Folder
|
||||
public class Playlist : Folder, IHasShares
|
||||
{
|
||||
public string OwnerUserId { get; set; }
|
||||
|
||||
public List<Share> Shares { get; set; }
|
||||
|
||||
public Playlist()
|
||||
{
|
||||
Shares = new List<Share>();
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
protected override bool FilterLinkedChildrenPerUser
|
||||
{
|
||||
|
@ -166,7 +173,15 @@ namespace MediaBrowser.Controller.Playlists
|
|||
|
||||
public override bool IsVisible(User user)
|
||||
{
|
||||
return base.IsVisible(user) && string.Equals(user.Id.ToString("N"), OwnerUserId);
|
||||
if (base.IsVisible(user))
|
||||
{
|
||||
var userId = user.Id.ToString("N");
|
||||
|
||||
return Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)) ||
|
||||
string.Equals(OwnerUserId, userId, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -819,6 +819,19 @@ namespace MediaBrowser.Controller.Providers
|
|||
break;
|
||||
}
|
||||
|
||||
case "Shares":
|
||||
{
|
||||
using (var subtree = reader.ReadSubtree())
|
||||
{
|
||||
var hasShares = item as IHasShares;
|
||||
if (hasShares != null)
|
||||
{
|
||||
FetchFromSharesNode(subtree, hasShares);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "Format3D":
|
||||
{
|
||||
var video = item as Video;
|
||||
|
@ -853,6 +866,71 @@ namespace MediaBrowser.Controller.Providers
|
|||
}
|
||||
}
|
||||
|
||||
private void FetchFromSharesNode(XmlReader reader, IHasShares item)
|
||||
{
|
||||
reader.MoveToContent();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "Share":
|
||||
{
|
||||
using (var subtree = reader.ReadSubtree())
|
||||
{
|
||||
var share = GetShareFromNode(subtree);
|
||||
if (share != null)
|
||||
{
|
||||
item.Shares.Add(share);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Share GetShareFromNode(XmlReader reader)
|
||||
{
|
||||
var share = new Share();
|
||||
|
||||
reader.MoveToContent();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "UserId":
|
||||
{
|
||||
share.UserId = reader.ReadElementContentAsString();
|
||||
break;
|
||||
}
|
||||
|
||||
case "CanEdit":
|
||||
{
|
||||
share.CanEdit = string.Equals(reader.ReadElementContentAsString(), true.ToString(), StringComparison.OrdinalIgnoreCase);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return share;
|
||||
}
|
||||
|
||||
private void FetchFromCountriesNode(XmlReader reader, T item)
|
||||
{
|
||||
reader.MoveToContent();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Sync;
|
||||
using System.Collections.Generic;
|
||||
|
@ -51,5 +52,12 @@ namespace MediaBrowser.Controller.Sync
|
|||
/// <param name="item">The item.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||
bool SupportsSync(BaseItem item);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device profile.
|
||||
/// </summary>
|
||||
/// <param name="targetId">The target identifier.</param>
|
||||
/// <returns>DeviceProfile.</returns>
|
||||
DeviceProfile GetDeviceProfile(string targetId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Sync;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Controller.Sync
|
||||
|
@ -66,8 +65,8 @@ namespace MediaBrowser.Controller.Sync
|
|||
/// <summary>
|
||||
/// Gets the job items.
|
||||
/// </summary>
|
||||
/// <param name="jobId">The job identifier.</param>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>IEnumerable<SyncJobItem>.</returns>
|
||||
IEnumerable<SyncJobItem> GetJobItems(string jobId);
|
||||
QueryResult<SyncJobItem> GetJobItems(SyncJobItemQuery query);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
using MediaBrowser.Controller.Playlists;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
|
||||
namespace MediaBrowser.LocalMetadata.Parsers
|
||||
|
@ -20,7 +22,15 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|||
{
|
||||
case "OwnerUserId":
|
||||
{
|
||||
item.OwnerUserId = reader.ReadElementContentAsString();
|
||||
var userId = reader.ReadElementContentAsString();
|
||||
if (!item.Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
item.Shares.Add(new Share
|
||||
{
|
||||
UserId = userId,
|
||||
CanEdit = true
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -57,11 +57,6 @@ namespace MediaBrowser.LocalMetadata.Savers
|
|||
|
||||
builder.Append("<Item>");
|
||||
|
||||
if (!string.IsNullOrEmpty(playlist.OwnerUserId))
|
||||
{
|
||||
builder.Append("<OwnerUserId>" + SecurityElement.Escape(playlist.OwnerUserId) + "</OwnerUserId>");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(playlist.PlaylistMediaType))
|
||||
{
|
||||
builder.Append("<PlaylistMediaType>" + SecurityElement.Escape(playlist.PlaylistMediaType) + "</PlaylistMediaType>");
|
||||
|
|
|
@ -645,6 +645,29 @@ namespace MediaBrowser.LocalMetadata.Savers
|
|||
{
|
||||
AddLinkedChildren(playlist, builder, "PlaylistItems", "PlaylistItem");
|
||||
}
|
||||
|
||||
var hasShares = item as IHasShares;
|
||||
if (hasShares != null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddShares(IHasShares item, StringBuilder builder)
|
||||
{
|
||||
builder.Append("<Shares>");
|
||||
|
||||
foreach (var share in item.Shares)
|
||||
{
|
||||
builder.Append("<Share>");
|
||||
|
||||
builder.Append("<UserId>" + SecurityElement.Escape(share.UserId) + "</UserId>");
|
||||
builder.Append("<CanEdit>" + SecurityElement.Escape(share.CanEdit.ToString().ToLower()) + "</CanEdit>");
|
||||
|
||||
builder.Append("</Share>");
|
||||
}
|
||||
|
||||
builder.Append("</Shares>");
|
||||
}
|
||||
|
||||
public static void AddChapters(Video item, StringBuilder builder, IItemRepository repository)
|
||||
|
|
|
@ -275,6 +275,9 @@
|
|||
<Compile Include="..\MediaBrowser.Model\Devices\DeviceOptions.cs">
|
||||
<Link>Devices\DeviceOptions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Devices\DeviceQuery.cs">
|
||||
<Link>Devices\DeviceQuery.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Devices\DevicesOptions.cs">
|
||||
<Link>Devices\DevicesOptions.cs</Link>
|
||||
</Compile>
|
||||
|
@ -1034,6 +1037,12 @@
|
|||
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItem.cs">
|
||||
<Link>Sync\SyncJobItem.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemQuery.cs">
|
||||
<Link>SyncJobItemQuery.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemStatus.cs">
|
||||
<Link>Sync\SyncJobItemStatus.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobQuery.cs">
|
||||
<Link>Sync\SyncJobQuery.cs</Link>
|
||||
</Compile>
|
||||
|
|
|
@ -240,6 +240,9 @@
|
|||
<Compile Include="..\MediaBrowser.Model\Devices\DeviceOptions.cs">
|
||||
<Link>Devices\DeviceOptions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Devices\DeviceQuery.cs">
|
||||
<Link>Devices\DeviceQuery.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Devices\DevicesOptions.cs">
|
||||
<Link>Devices\DevicesOptions.cs</Link>
|
||||
</Compile>
|
||||
|
@ -993,6 +996,12 @@
|
|||
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItem.cs">
|
||||
<Link>Sync\SyncJobItem.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemQuery.cs">
|
||||
<Link>Sync\SyncJobItemQuery.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemStatus.cs">
|
||||
<Link>Sync\SyncJobItemStatus.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobQuery.cs">
|
||||
<Link>Sync\SyncJobQuery.cs</Link>
|
||||
</Compile>
|
||||
|
|
17
MediaBrowser.Model/Devices/DeviceQuery.cs
Normal file
17
MediaBrowser.Model/Devices/DeviceQuery.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
|
||||
namespace MediaBrowser.Model.Devices
|
||||
{
|
||||
public class DeviceQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether [supports content uploading].
|
||||
/// </summary>
|
||||
/// <value><c>null</c> if [supports content uploading] contains no value, <c>true</c> if [supports content uploading]; otherwise, <c>false</c>.</value>
|
||||
public bool? SupportsContentUploading { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether [supports unique identifier].
|
||||
/// </summary>
|
||||
/// <value><c>null</c> if [supports unique identifier] contains no value, <c>true</c> if [supports unique identifier]; otherwise, <c>false</c>.</value>
|
||||
public bool? SupportsUniqueIdentifier { get; set; }
|
||||
}
|
||||
}
|
|
@ -187,6 +187,12 @@ namespace MediaBrowser.Model.Dto
|
|||
/// <value>The genres.</value>
|
||||
public List<string> Genres { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the series genres.
|
||||
/// </summary>
|
||||
/// <value>The series genres.</value>
|
||||
public List<string> SeriesGenres { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the community rating.
|
||||
/// </summary>
|
||||
|
|
|
@ -113,6 +113,7 @@
|
|||
<Compile Include="Connect\PinStatusResult.cs" />
|
||||
<Compile Include="Connect\UserLinkType.cs" />
|
||||
<Compile Include="Devices\DeviceOptions.cs" />
|
||||
<Compile Include="Devices\DeviceQuery.cs" />
|
||||
<Compile Include="Devices\LocalFileInfo.cs" />
|
||||
<Compile Include="Devices\DeviceInfo.cs" />
|
||||
<Compile Include="Devices\DevicesOptions.cs" />
|
||||
|
@ -365,6 +366,8 @@
|
|||
<Compile Include="Sync\SyncJob.cs" />
|
||||
<Compile Include="Sync\SyncJobCreationResult.cs" />
|
||||
<Compile Include="Sync\SyncJobItem.cs" />
|
||||
<Compile Include="Sync\SyncJobItemQuery.cs" />
|
||||
<Compile Include="Sync\SyncJobItemStatus.cs" />
|
||||
<Compile Include="Sync\SyncJobQuery.cs" />
|
||||
<Compile Include="Sync\SyncJobRequest.cs" />
|
||||
<Compile Include="Sync\SyncJobStatus.cs" />
|
||||
|
|
|
@ -161,6 +161,11 @@ namespace MediaBrowser.Model.Querying
|
|||
/// </summary>
|
||||
ScreenshotImageTags,
|
||||
|
||||
/// <summary>
|
||||
/// The series genres
|
||||
/// </summary>
|
||||
SeriesGenres,
|
||||
|
||||
/// <summary>
|
||||
/// The series studio
|
||||
/// </summary>
|
||||
|
|
|
@ -13,13 +13,14 @@ namespace MediaBrowser.Model.Session
|
|||
public string MessageCallbackUrl { get; set; }
|
||||
|
||||
public bool SupportsContentUploading { get; set; }
|
||||
public bool SupportsDeviceId { get; set; }
|
||||
public bool SupportsUniqueIdentifier { get; set; }
|
||||
public bool SupportsSync { get; set; }
|
||||
|
||||
public ClientCapabilities()
|
||||
{
|
||||
PlayableMediaTypes = new List<string>();
|
||||
SupportedCommands = new List<string>();
|
||||
SupportsDeviceId = true;
|
||||
SupportsUniqueIdentifier = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Model.Sync
|
||||
{
|
||||
public class SyncJobItem
|
||||
|
@ -37,12 +38,18 @@ namespace MediaBrowser.Model.Sync
|
|||
/// Gets or sets the status.
|
||||
/// </summary>
|
||||
/// <value>The status.</value>
|
||||
public SyncJobStatus Status { get; set; }
|
||||
public SyncJobItemStatus Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current progress.
|
||||
/// </summary>
|
||||
/// <value>The current progress.</value>
|
||||
public double? CurrentProgress { get; set; }
|
||||
public double? Progress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date created.
|
||||
/// </summary>
|
||||
/// <value>The date created.</value>
|
||||
public DateTime DateCreated { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
27
MediaBrowser.Model/Sync/SyncJobItemQuery.cs
Normal file
27
MediaBrowser.Model/Sync/SyncJobItemQuery.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
|
||||
namespace MediaBrowser.Model.Sync
|
||||
{
|
||||
public class SyncJobItemQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the start index.
|
||||
/// </summary>
|
||||
/// <value>The start index.</value>
|
||||
public int? StartIndex { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the limit.
|
||||
/// </summary>
|
||||
/// <value>The limit.</value>
|
||||
public int? Limit { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the job identifier.
|
||||
/// </summary>
|
||||
/// <value>The job identifier.</value>
|
||||
public string JobId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is completed.
|
||||
/// </summary>
|
||||
/// <value><c>null</c> if [is completed] contains no value, <c>true</c> if [is completed]; otherwise, <c>false</c>.</value>
|
||||
public bool? IsCompleted { get; set; }
|
||||
}
|
||||
}
|
12
MediaBrowser.Model/Sync/SyncJobItemStatus.cs
Normal file
12
MediaBrowser.Model/Sync/SyncJobItemStatus.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
namespace MediaBrowser.Model.Sync
|
||||
{
|
||||
public enum SyncJobItemStatus
|
||||
{
|
||||
Queued = 0,
|
||||
Converting = 1,
|
||||
Transferring = 2,
|
||||
Completed = 3,
|
||||
Failed = 4
|
||||
}
|
||||
}
|
|
@ -13,5 +13,10 @@ namespace MediaBrowser.Model.Sync
|
|||
/// </summary>
|
||||
/// <value>The limit.</value>
|
||||
public int? Limit { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is completed.
|
||||
/// </summary>
|
||||
/// <value><c>null</c> if [is completed] contains no value, <c>true</c> if [is completed]; otherwise, <c>false</c>.</value>
|
||||
public bool? IsCompleted { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,8 @@ namespace MediaBrowser.Model.Sync
|
|||
public enum SyncJobStatus
|
||||
{
|
||||
Queued = 0,
|
||||
Converting = 1,
|
||||
Transferring = 2,
|
||||
Completed = 3,
|
||||
Cancelled = 4
|
||||
InProgress = 1,
|
||||
Completed = 2,
|
||||
CompletedWithError = 3
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,11 @@ namespace MediaBrowser.Providers.BoxSets
|
|||
|
||||
target.LinkedChildren = list;
|
||||
}
|
||||
|
||||
if (replaceData || target.Shares.Count == 0)
|
||||
{
|
||||
target.Shares = source.Shares;
|
||||
}
|
||||
}
|
||||
|
||||
protected override ItemUpdateType BeforeSave(BoxSet item)
|
||||
|
|
|
@ -7,7 +7,6 @@ using MediaBrowser.Model.Entities;
|
|||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Providers.Manager;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Providers.Playlists
|
||||
{
|
||||
|
@ -34,18 +33,14 @@ namespace MediaBrowser.Providers.Playlists
|
|||
target.PlaylistMediaType = source.PlaylistMediaType;
|
||||
}
|
||||
|
||||
if (replaceData || string.IsNullOrEmpty(target.OwnerUserId))
|
||||
if (replaceData || target.Shares.Count == 0)
|
||||
{
|
||||
target.OwnerUserId = source.OwnerUserId;
|
||||
target.Shares = source.Shares;
|
||||
}
|
||||
|
||||
if (mergeMetadataSettings)
|
||||
{
|
||||
var list = source.LinkedChildren.ToList();
|
||||
|
||||
list.AddRange(target.LinkedChildren);
|
||||
|
||||
target.LinkedChildren = list;
|
||||
target.LinkedChildren = source.LinkedChildren;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,12 @@ namespace MediaBrowser.Server.Implementations.Collections
|
|||
DisplayMediaType = "Collection",
|
||||
Path = path,
|
||||
IsLocked = options.IsLocked,
|
||||
ProviderIds = options.ProviderIds
|
||||
ProviderIds = options.ProviderIds,
|
||||
Shares = options.UserIds.Select(i => new Share
|
||||
{
|
||||
UserId = i.ToString("N")
|
||||
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
await parentFolder.AddChild(collection, CancellationToken.None).ConfigureAwait(false);
|
||||
|
|
|
@ -6,6 +6,7 @@ using MediaBrowser.Controller.Library;
|
|||
using MediaBrowser.Model.Devices;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Session;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -79,9 +80,30 @@ namespace MediaBrowser.Server.Implementations.Devices
|
|||
return _repo.GetDevice(id);
|
||||
}
|
||||
|
||||
public IEnumerable<DeviceInfo> GetDevices()
|
||||
public QueryResult<DeviceInfo> GetDevices(DeviceQuery query)
|
||||
{
|
||||
return _repo.GetDevices().OrderByDescending(i => i.DateLastModified);
|
||||
IEnumerable<DeviceInfo> devices = _repo.GetDevices().OrderByDescending(i => i.DateLastModified);
|
||||
|
||||
if (query.SupportsContentUploading.HasValue)
|
||||
{
|
||||
var val = query.SupportsContentUploading.Value;
|
||||
|
||||
devices = devices.Where(i => GetCapabilities(i.Id).SupportsContentUploading == val);
|
||||
}
|
||||
|
||||
if (query.SupportsUniqueIdentifier.HasValue)
|
||||
{
|
||||
var val = query.SupportsUniqueIdentifier.Value;
|
||||
|
||||
devices = devices.Where(i => GetCapabilities(i.Id).SupportsUniqueIdentifier == val);
|
||||
}
|
||||
|
||||
var array = devices.ToArray();
|
||||
return new QueryResult<DeviceInfo>
|
||||
{
|
||||
Items = array,
|
||||
TotalRecordCount = array.Length
|
||||
};
|
||||
}
|
||||
|
||||
public Task DeleteDevice(string id)
|
||||
|
|
|
@ -1142,6 +1142,15 @@ namespace MediaBrowser.Server.Implementations.Dto
|
|||
dto.SeasonId = episodeSeason.Id.ToString("N");
|
||||
dto.SeasonName = episodeSeason.Name;
|
||||
}
|
||||
|
||||
if (fields.Contains(ItemFields.SeriesGenres))
|
||||
{
|
||||
var episodeseries = episode.Series;
|
||||
if (episodeseries != null)
|
||||
{
|
||||
dto.SeriesGenres = episodeseries.Genres.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add SeriesInfo
|
||||
|
|
|
@ -736,7 +736,12 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
}
|
||||
|
||||
private UserRootFolder _userRootFolder;
|
||||
private readonly object _syncLock = new object();
|
||||
public Folder GetUserRootFolder()
|
||||
{
|
||||
if (_userRootFolder == null)
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (_userRootFolder == null)
|
||||
{
|
||||
|
@ -744,8 +749,14 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
|
||||
Directory.CreateDirectory(userRootPath);
|
||||
|
||||
_userRootFolder = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder ??
|
||||
(UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath));
|
||||
_userRootFolder = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder;
|
||||
|
||||
if (_userRootFolder == null)
|
||||
{
|
||||
_userRootFolder = (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _userRootFolder;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Playlists;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
@ -53,6 +54,18 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
return GetInstantMixFromGenres(genres, user);
|
||||
}
|
||||
|
||||
public IEnumerable<Audio> GetInstantMixFromPlaylist(Playlist item, User user)
|
||||
{
|
||||
var genres = item
|
||||
.GetRecursiveChildren(user, true)
|
||||
.OfType<Audio>()
|
||||
.SelectMany(i => i.Genres)
|
||||
.Concat(item.Genres)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
return GetInstantMixFromGenres(genres, user);
|
||||
}
|
||||
|
||||
public IEnumerable<Audio> GetInstantMixFromGenres(IEnumerable<string> genres, User user)
|
||||
{
|
||||
var inputItems = user.RootFolder.GetRecursiveChildren(user);
|
||||
|
|
|
@ -168,7 +168,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
|||
{
|
||||
if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return FindMovie<MusicVideo>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType);
|
||||
return FindMovie<MusicVideo>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType, false);
|
||||
}
|
||||
|
||||
if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
|
||||
|
|
|
@ -638,5 +638,14 @@
|
|||
"ButtonSync": "Sync",
|
||||
"SyncMedia": "Sync Media",
|
||||
"HeaderCancelSyncJob": "Cancel Sync",
|
||||
"CancelSyncJobConfirmation": "Are you sure you wish to cancel this sync job?"
|
||||
"CancelSyncJobConfirmation": "Are you sure you wish to cancel this sync job?",
|
||||
"TabSync": "Sync",
|
||||
"MessagePleaseSelectDeviceToSyncTo": "Please select a device to sync to.",
|
||||
"MessageSyncJobCreated": "Sync job created.",
|
||||
"LabelSyncTo": "Sync to:",
|
||||
"LabelSyncJobName": "Sync job name:",
|
||||
"LabelQuality": "Quality:",
|
||||
"OptionHigh": "High",
|
||||
"OptionMedium": "Medium",
|
||||
"OptionLow": "Low"
|
||||
}
|
||||
|
|
|
@ -1293,5 +1293,7 @@
|
|||
"LabelBlockItemsWithTags": "Block items with tags:",
|
||||
"LabelTag": "Tag:",
|
||||
"LabelEnableSingleImageInDidlLimit": "Limit to single embedded image",
|
||||
"LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl."
|
||||
"LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl.",
|
||||
"TabActivity": "Activity",
|
||||
"TitleSync": "Sync"
|
||||
}
|
||||
|
|
|
@ -306,6 +306,7 @@
|
|||
<Compile Include="Sync\SyncJobProcessor.cs" />
|
||||
<Compile Include="Sync\SyncManager.cs" />
|
||||
<Compile Include="Sync\SyncRepository.cs" />
|
||||
<Compile Include="Sync\SyncScheduledTask.cs" />
|
||||
<Compile Include="Themes\AppThemeManager.cs" />
|
||||
<Compile Include="TV\TVSeriesManager.cs" />
|
||||
<Compile Include="Udp\UdpMessageReceivedEventArgs.cs" />
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace MediaBrowser.Server.Implementations.Playlists
|
|||
{
|
||||
return base.IsVisible(user) && GetRecursiveChildren(user, false)
|
||||
.OfType<Playlist>()
|
||||
.Any(i => string.Equals(i.OwnerUserId, user.Id.ToString("N")));
|
||||
.Any(i => i.IsVisible(user));
|
||||
}
|
||||
|
||||
protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
|
||||
|
|
|
@ -115,10 +115,15 @@ namespace MediaBrowser.Server.Implementations.Playlists
|
|||
{
|
||||
Name = name,
|
||||
Parent = parentFolder,
|
||||
Path = path,
|
||||
OwnerUserId = options.UserId
|
||||
Path = path
|
||||
};
|
||||
|
||||
playlist.Shares.Add(new Share
|
||||
{
|
||||
UserId = options.UserId,
|
||||
CanEdit = true
|
||||
});
|
||||
|
||||
playlist.SetMediaType(options.MediaType);
|
||||
|
||||
await parentFolder.AddChild(playlist, CancellationToken.None).ConfigureAwait(false);
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Sync;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using MediaBrowser.Model.Session;
|
||||
using MediaBrowser.Model.Sync;
|
||||
using MoreLinq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.Sync
|
||||
|
@ -14,11 +21,17 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ISyncRepository _syncRepo;
|
||||
private readonly ISyncManager _syncManager;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
public SyncJobProcessor(ILibraryManager libraryManager, ISyncRepository syncRepo)
|
||||
public SyncJobProcessor(ILibraryManager libraryManager, ISyncRepository syncRepo, ISyncManager syncManager, ILogger logger, IUserManager userManager)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_syncRepo = syncRepo;
|
||||
_syncManager = syncManager;
|
||||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public void ProcessJobItem(SyncJob job, SyncJobItem jobItem, SyncTarget target)
|
||||
|
@ -28,13 +41,21 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
|
||||
public async Task EnsureJobItems(SyncJob job)
|
||||
{
|
||||
var items = GetItemsForSync(job.RequestedItemIds)
|
||||
var user = _userManager.GetUserById(job.UserId);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot proceed with sync because user no longer exists.");
|
||||
}
|
||||
|
||||
var items = GetItemsForSync(job.RequestedItemIds, user)
|
||||
.ToList();
|
||||
|
||||
var jobItems = _syncRepo.GetJobItems(job.Id)
|
||||
.ToList();
|
||||
var jobItems = _syncRepo.GetJobItems(new SyncJobItemQuery
|
||||
{
|
||||
JobId = job.Id
|
||||
|
||||
var created = 0;
|
||||
}).Items.ToList();
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
|
@ -52,24 +73,97 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
Id = Guid.NewGuid().ToString("N"),
|
||||
ItemId = itemId,
|
||||
JobId = job.Id,
|
||||
TargetId = job.TargetId
|
||||
TargetId = job.TargetId,
|
||||
DateCreated = DateTime.UtcNow
|
||||
};
|
||||
|
||||
await _syncRepo.Create(jobItem).ConfigureAwait(false);
|
||||
|
||||
created++;
|
||||
jobItems.Add(jobItem);
|
||||
}
|
||||
|
||||
job.ItemCount = jobItems.Count + created;
|
||||
await _syncRepo.Update(job).ConfigureAwait(false);
|
||||
jobItems = jobItems
|
||||
.OrderBy(i => i.DateCreated)
|
||||
.ToList();
|
||||
|
||||
await UpdateJobStatus(job, jobItems).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public IEnumerable<BaseItem> GetItemsForSync(IEnumerable<string> itemIds)
|
||||
private Task UpdateJobStatus(SyncJob job)
|
||||
{
|
||||
return itemIds.SelectMany(GetItemsForSync).DistinctBy(i => i.Id);
|
||||
if (job == null)
|
||||
{
|
||||
throw new ArgumentNullException("job");
|
||||
}
|
||||
|
||||
private IEnumerable<BaseItem> GetItemsForSync(string id)
|
||||
var result = _syncRepo.GetJobItems(new SyncJobItemQuery
|
||||
{
|
||||
JobId = job.Id
|
||||
});
|
||||
|
||||
return UpdateJobStatus(job, result.Items.ToList());
|
||||
}
|
||||
|
||||
private Task UpdateJobStatus(SyncJob job, List<SyncJobItem> jobItems)
|
||||
{
|
||||
job.ItemCount = jobItems.Count;
|
||||
|
||||
double pct = 0;
|
||||
|
||||
foreach (var item in jobItems)
|
||||
{
|
||||
if (item.Status == SyncJobItemStatus.Failed || item.Status == SyncJobItemStatus.Completed)
|
||||
{
|
||||
pct += 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
pct += item.Progress ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (job.ItemCount > 0)
|
||||
{
|
||||
pct /= job.ItemCount;
|
||||
job.Progress = pct;
|
||||
}
|
||||
else
|
||||
{
|
||||
job.Progress = null;
|
||||
}
|
||||
|
||||
if (pct >= 100)
|
||||
{
|
||||
if (jobItems.Any(i => i.Status == SyncJobItemStatus.Failed))
|
||||
{
|
||||
job.Status = SyncJobStatus.CompletedWithError;
|
||||
}
|
||||
else
|
||||
{
|
||||
job.Status = SyncJobStatus.Completed;
|
||||
}
|
||||
}
|
||||
else if (pct.Equals(0))
|
||||
{
|
||||
job.Status = SyncJobStatus.Queued;
|
||||
}
|
||||
else
|
||||
{
|
||||
job.Status = SyncJobStatus.InProgress;
|
||||
}
|
||||
|
||||
return _syncRepo.Update(job);
|
||||
}
|
||||
|
||||
public IEnumerable<BaseItem> GetItemsForSync(IEnumerable<string> itemIds, User user)
|
||||
{
|
||||
return itemIds
|
||||
.SelectMany(i => GetItemsForSync(i, user))
|
||||
.Where(_syncManager.SupportsSync)
|
||||
.DistinctBy(i => i.Id);
|
||||
}
|
||||
|
||||
private IEnumerable<BaseItem> GetItemsForSync(string id, User user)
|
||||
{
|
||||
var item = _libraryManager.GetItemById(id);
|
||||
|
||||
|
@ -78,12 +172,224 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
return new List<BaseItem>();
|
||||
}
|
||||
|
||||
return GetItemsForSync(item);
|
||||
return GetItemsForSync(item, user);
|
||||
}
|
||||
|
||||
private IEnumerable<BaseItem> GetItemsForSync(BaseItem item)
|
||||
private IEnumerable<BaseItem> GetItemsForSync(BaseItem item, User user)
|
||||
{
|
||||
var itemByName = item as IItemByName;
|
||||
if (itemByName != null)
|
||||
{
|
||||
var items = user.RootFolder
|
||||
.GetRecursiveChildren(user);
|
||||
|
||||
return itemByName.GetTaggedItems(items);
|
||||
}
|
||||
|
||||
if (item.IsFolder)
|
||||
{
|
||||
var folder = (Folder)item;
|
||||
var items = folder.GetRecursiveChildren(user);
|
||||
|
||||
items = items.Where(i => !i.IsFolder);
|
||||
|
||||
if (!folder.IsPreSorted)
|
||||
{
|
||||
items = items.OrderBy(i => i.SortName);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
return new[] { item };
|
||||
}
|
||||
|
||||
public async Task EnsureSyncJobs(CancellationToken cancellationToken)
|
||||
{
|
||||
var jobResult = _syncRepo.GetJobs(new SyncJobQuery
|
||||
{
|
||||
IsCompleted = false
|
||||
});
|
||||
|
||||
foreach (var job in jobResult.Items)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (job.SyncNewContent)
|
||||
{
|
||||
await EnsureJobItems(job).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Sync(IProgress<double> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
await EnsureSyncJobs(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var result = _syncRepo.GetJobItems(new SyncJobItemQuery
|
||||
{
|
||||
IsCompleted = false
|
||||
});
|
||||
|
||||
var jobItems = result.Items;
|
||||
var index = 0;
|
||||
|
||||
foreach (var item in jobItems)
|
||||
{
|
||||
double percent = index;
|
||||
percent /= result.TotalRecordCount;
|
||||
|
||||
progress.Report(100 * percent);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (item.Status == SyncJobItemStatus.Queued)
|
||||
{
|
||||
await ProcessJobItem(item, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var job = _syncRepo.GetJob(item.JobId);
|
||||
await UpdateJobStatus(job).ConfigureAwait(false);
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessJobItem(SyncJobItem jobItem, CancellationToken cancellationToken)
|
||||
{
|
||||
var item = _libraryManager.GetItemById(jobItem.ItemId);
|
||||
if (item == null)
|
||||
{
|
||||
jobItem.Status = SyncJobItemStatus.Failed;
|
||||
_logger.Error("Unable to locate library item for JobItem {0}, ItemId {1}", jobItem.Id, jobItem.ItemId);
|
||||
await _syncRepo.Update(jobItem).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var deviceProfile = _syncManager.GetDeviceProfile(jobItem.TargetId);
|
||||
if (deviceProfile == null)
|
||||
{
|
||||
jobItem.Status = SyncJobItemStatus.Failed;
|
||||
_logger.Error("Unable to locate SyncTarget for JobItem {0}, SyncTargetId {1}", jobItem.Id, jobItem.TargetId);
|
||||
await _syncRepo.Update(jobItem).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
jobItem.Progress = 0;
|
||||
jobItem.Status = SyncJobItemStatus.Converting;
|
||||
|
||||
var video = item as Video;
|
||||
if (video != null)
|
||||
{
|
||||
jobItem.OutputPath = await Sync(jobItem, video, deviceProfile, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
else if (item is Audio)
|
||||
{
|
||||
jobItem.OutputPath = await Sync(jobItem, (Audio)item, deviceProfile, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
else if (item is Photo)
|
||||
{
|
||||
jobItem.OutputPath = await Sync(jobItem, (Photo)item, deviceProfile, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
else if (item is Game)
|
||||
{
|
||||
jobItem.OutputPath = await Sync(jobItem, (Game)item, deviceProfile, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
else if (item is Book)
|
||||
{
|
||||
jobItem.OutputPath = await Sync(jobItem, (Book)item, deviceProfile, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
jobItem.Progress = 50;
|
||||
jobItem.Status = SyncJobItemStatus.Transferring;
|
||||
await _syncRepo.Update(jobItem).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<string> Sync(SyncJobItem jobItem, Video item, DeviceProfile profile, CancellationToken cancellationToken)
|
||||
{
|
||||
var options = new VideoOptions
|
||||
{
|
||||
Context = EncodingContext.Streaming,
|
||||
ItemId = item.Id.ToString("N"),
|
||||
DeviceId = jobItem.TargetId,
|
||||
Profile = profile,
|
||||
MediaSources = item.GetMediaSources(false).ToList()
|
||||
};
|
||||
|
||||
var streamInfo = new StreamBuilder().BuildVideoItem(options);
|
||||
var mediaSource = streamInfo.MediaSource;
|
||||
|
||||
if (streamInfo.PlayMethod != PlayMethod.Transcode)
|
||||
{
|
||||
if (mediaSource.Protocol == MediaProtocol.File)
|
||||
{
|
||||
return mediaSource.Path;
|
||||
}
|
||||
if (mediaSource.Protocol == MediaProtocol.Http)
|
||||
{
|
||||
return await DownloadFile(jobItem, mediaSource, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
throw new InvalidOperationException(string.Format("Cannot direct stream {0} protocol", mediaSource.Protocol));
|
||||
}
|
||||
|
||||
// TODO: Transcode
|
||||
return mediaSource.Path;
|
||||
}
|
||||
|
||||
private async Task<string> Sync(SyncJobItem jobItem, Audio item, DeviceProfile profile, CancellationToken cancellationToken)
|
||||
{
|
||||
var options = new AudioOptions
|
||||
{
|
||||
Context = EncodingContext.Streaming,
|
||||
ItemId = item.Id.ToString("N"),
|
||||
DeviceId = jobItem.TargetId,
|
||||
Profile = profile,
|
||||
MediaSources = item.GetMediaSources(false).ToList()
|
||||
};
|
||||
|
||||
var streamInfo = new StreamBuilder().BuildAudioItem(options);
|
||||
var mediaSource = streamInfo.MediaSource;
|
||||
|
||||
if (streamInfo.PlayMethod != PlayMethod.Transcode)
|
||||
{
|
||||
if (mediaSource.Protocol == MediaProtocol.File)
|
||||
{
|
||||
return mediaSource.Path;
|
||||
}
|
||||
if (mediaSource.Protocol == MediaProtocol.Http)
|
||||
{
|
||||
return await DownloadFile(jobItem, mediaSource, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
throw new InvalidOperationException(string.Format("Cannot direct stream {0} protocol", mediaSource.Protocol));
|
||||
}
|
||||
|
||||
// TODO: Transcode
|
||||
return mediaSource.Path;
|
||||
}
|
||||
|
||||
private async Task<string> Sync(SyncJobItem jobItem, Photo item, DeviceProfile profile, CancellationToken cancellationToken)
|
||||
{
|
||||
return item.Path;
|
||||
}
|
||||
|
||||
private async Task<string> Sync(SyncJobItem jobItem, Game item, DeviceProfile profile, CancellationToken cancellationToken)
|
||||
{
|
||||
return item.Path;
|
||||
}
|
||||
|
||||
private async Task<string> Sync(SyncJobItem jobItem, Book item, DeviceProfile profile, CancellationToken cancellationToken)
|
||||
{
|
||||
return item.Path;
|
||||
}
|
||||
|
||||
private async Task<string> DownloadFile(SyncJobItem jobItem, MediaSourceInfo mediaSource, CancellationToken cancellationToken)
|
||||
{
|
||||
// TODO: Download
|
||||
return mediaSource.Path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ using MediaBrowser.Controller.Entities.Audio;
|
|||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Sync;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Querying;
|
||||
|
@ -23,15 +24,17 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
private readonly ISyncRepository _repo;
|
||||
private readonly IImageProcessor _imageProcessor;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
private ISyncProvider[] _providers = { };
|
||||
|
||||
public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger)
|
||||
public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger, IUserManager userManager)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_repo = repo;
|
||||
_imageProcessor = imageProcessor;
|
||||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public void AddParts(IEnumerable<ISyncProvider> providers)
|
||||
|
@ -41,8 +44,12 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
|
||||
public async Task<SyncJobCreationResult> CreateJob(SyncJobRequest request)
|
||||
{
|
||||
var items = new SyncJobProcessor(_libraryManager, _repo)
|
||||
.GetItemsForSync(request.ItemIds)
|
||||
var processor = new SyncJobProcessor(_libraryManager, _repo, this, _logger, _userManager);
|
||||
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var items = processor
|
||||
.GetItemsForSync(request.ItemIds, user)
|
||||
.ToList();
|
||||
|
||||
if (items.Any(i => !SupportsSync(i)))
|
||||
|
@ -50,9 +57,9 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
throw new ArgumentException("Item does not support sync.");
|
||||
}
|
||||
|
||||
if (items.Count == 1)
|
||||
if (string.IsNullOrWhiteSpace(request.Name) && request.ItemIds.Count == 1)
|
||||
{
|
||||
request.Name = GetDefaultName(items[0]);
|
||||
request.Name = GetDefaultName(_libraryManager.GetItemById(request.ItemIds[0]));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.Name))
|
||||
|
@ -82,8 +89,16 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
Quality = request.Quality
|
||||
};
|
||||
|
||||
// It's just a static list
|
||||
if (!items.Any(i => i.IsFolder || i is IItemByName))
|
||||
{
|
||||
job.SyncNewContent = false;
|
||||
}
|
||||
|
||||
await _repo.Create(job).ConfigureAwait(false);
|
||||
|
||||
await processor.EnsureJobItems(job).ConfigureAwait(false);
|
||||
|
||||
return new SyncJobCreationResult
|
||||
{
|
||||
Job = GetJob(jobId)
|
||||
|
@ -101,9 +116,9 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
|
||||
private void FillMetadata(SyncJob job)
|
||||
{
|
||||
var item = new SyncJobProcessor(_libraryManager, _repo)
|
||||
.GetItemsForSync(job.RequestedItemIds)
|
||||
.FirstOrDefault();
|
||||
var item = job.RequestedItemIds
|
||||
.Select(_libraryManager.GetItemById)
|
||||
.FirstOrDefault(i => i != null);
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
|
@ -139,10 +154,6 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
|
||||
public Task CancelJob(string id)
|
||||
{
|
||||
var job = GetJob(id);
|
||||
|
||||
job.Status = SyncJobStatus.Cancelled;
|
||||
|
||||
return _repo.DeleteJob(id);
|
||||
}
|
||||
|
||||
|
@ -165,10 +176,15 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
return provider.GetSyncTargets().Select(i => new SyncTarget
|
||||
{
|
||||
Name = i.Name,
|
||||
Id = providerId + "-" + i.Id
|
||||
Id = GetSyncTargetId(providerId, i)
|
||||
});
|
||||
}
|
||||
|
||||
private string GetSyncTargetId(string providerId, SyncTarget target)
|
||||
{
|
||||
return (providerId + "-" + target.Id).GetMD5().ToString("N");
|
||||
}
|
||||
|
||||
private ISyncProvider GetSyncProvider(SyncTarget target)
|
||||
{
|
||||
var providerId = target.Id.Split(new[] { '-' }, 2).First();
|
||||
|
@ -183,18 +199,23 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
|
||||
public bool SupportsSync(BaseItem item)
|
||||
{
|
||||
if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(item.MediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(item.MediaType, MediaType.Book, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (item.LocationType == LocationType.Virtual)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.RunTimeTicks.HasValue)
|
||||
if (!item.RunTimeTicks.HasValue)
|
||||
{
|
||||
var video = item as Video;
|
||||
return false;
|
||||
}
|
||||
|
||||
var video = item as Video;
|
||||
if (video != null)
|
||||
{
|
||||
if (video.VideoType == VideoType.Iso)
|
||||
|
@ -208,10 +229,16 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
var game = item as Game;
|
||||
if (game != null)
|
||||
{
|
||||
if (game.IsMultiPart)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return item.LocationType == LocationType.FileSystem || item is Season;
|
||||
|
@ -221,5 +248,21 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
{
|
||||
return item.Name;
|
||||
}
|
||||
|
||||
public DeviceProfile GetDeviceProfile(string targetId)
|
||||
{
|
||||
foreach (var provider in _providers)
|
||||
{
|
||||
foreach (var target in GetSyncTargets(provider, null))
|
||||
{
|
||||
if (string.Equals(target.Id, targetId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return provider.GetDeviceProfile(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
|
||||
public async Task Initialize()
|
||||
{
|
||||
var dbFile = Path.Combine(_appPaths.DataPath, "sync2.db");
|
||||
var dbFile = Path.Combine(_appPaths.DataPath, "sync4.db");
|
||||
|
||||
_connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false);
|
||||
|
||||
|
@ -44,7 +44,7 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
"create table if not exists SyncJobs (Id GUID PRIMARY KEY, TargetId TEXT NOT NULL, Name TEXT NOT NULL, Quality TEXT NOT NULL, Status TEXT NOT NULL, Progress FLOAT, UserId TEXT NOT NULL, ItemIds TEXT NOT NULL, UnwatchedOnly BIT, ItemLimit INT, RemoveWhenWatched BIT, SyncNewContent BIT, DateCreated DateTime, DateLastModified DateTime, ItemCount int)",
|
||||
"create index if not exists idx_SyncJobs on SyncJobs(Id)",
|
||||
|
||||
"create table if not exists SyncJobItems (Id GUID PRIMARY KEY, ItemId TEXT, JobId TEXT, OutputPath TEXT, Status TEXT, TargetId TEXT)",
|
||||
"create table if not exists SyncJobItems (Id GUID PRIMARY KEY, ItemId TEXT, JobId TEXT, OutputPath TEXT, Status TEXT, TargetId TEXT, DateCreated DateTime, Progress FLOAT)",
|
||||
"create index if not exists idx_SyncJobItems on SyncJobs(Id)",
|
||||
|
||||
//pragmas
|
||||
|
@ -84,17 +84,20 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
_saveJobCommand.Parameters.Add(_saveJobCommand, "@ItemCount");
|
||||
|
||||
_saveJobItemCommand = _connection.CreateCommand();
|
||||
_saveJobItemCommand.CommandText = "replace into SyncJobItems (Id, ItemId, JobId, OutputPath, Status, TargetId) values (@Id, @ItemId, @JobId, @OutputPath, @Status, @TargetId)";
|
||||
_saveJobItemCommand.CommandText = "replace into SyncJobItems (Id, ItemId, JobId, OutputPath, Status, TargetId, DateCreated, Progress) values (@Id, @ItemId, @JobId, @OutputPath, @Status, @TargetId, @DateCreated, @Progress)";
|
||||
|
||||
_saveJobItemCommand.Parameters.Add(_saveJobCommand, "@Id");
|
||||
_saveJobItemCommand.Parameters.Add(_saveJobCommand, "@ItemId");
|
||||
_saveJobItemCommand.Parameters.Add(_saveJobCommand, "@JobId");
|
||||
_saveJobItemCommand.Parameters.Add(_saveJobCommand, "@OutputPath");
|
||||
_saveJobItemCommand.Parameters.Add(_saveJobCommand, "@Status");
|
||||
_saveJobItemCommand.Parameters.Add(_saveJobCommand, "@TargetId");
|
||||
_saveJobItemCommand.Parameters.Add(_saveJobCommand, "@DateCreated");
|
||||
_saveJobItemCommand.Parameters.Add(_saveJobCommand, "@Progress");
|
||||
}
|
||||
|
||||
private const string BaseJobSelectText = "select Id, TargetId, Name, Quality, Status, Progress, UserId, ItemIds, UnwatchedOnly, ItemLimit, RemoveWhenWatched, SyncNewContent, DateCreated, DateLastModified, ItemCount from SyncJobs";
|
||||
private const string BaseJobItemSelectText = "select Id, ItemId, JobId, OutputPath, Status, TargetId from SyncJobItems";
|
||||
private const string BaseJobItemSelectText = "select Id, ItemId, JobId, OutputPath, Status, TargetId, DateCreated, Progress from SyncJobItems";
|
||||
|
||||
public SyncJob GetJob(string id)
|
||||
{
|
||||
|
@ -105,6 +108,11 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
|
||||
var guid = new Guid(id);
|
||||
|
||||
if (guid == Guid.Empty)
|
||||
{
|
||||
throw new ArgumentNullException("id");
|
||||
}
|
||||
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = BaseJobSelectText + " where Id=@Id";
|
||||
|
@ -321,8 +329,24 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
|
||||
var whereClauses = new List<string>();
|
||||
|
||||
var startIndex = query.StartIndex ?? 0;
|
||||
if (query.IsCompleted.HasValue)
|
||||
{
|
||||
if (query.IsCompleted.Value)
|
||||
{
|
||||
whereClauses.Add("Status=@Status");
|
||||
}
|
||||
else
|
||||
{
|
||||
whereClauses.Add("Status<>@Status");
|
||||
}
|
||||
cmd.Parameters.Add(cmd, "@Status", DbType.String).Value = SyncJobStatus.Completed.ToString();
|
||||
}
|
||||
|
||||
var whereTextWithoutPaging = whereClauses.Count == 0 ?
|
||||
string.Empty :
|
||||
" where " + string.Join(" AND ", whereClauses.ToArray());
|
||||
|
||||
var startIndex = query.StartIndex ?? 0;
|
||||
if (startIndex > 0)
|
||||
{
|
||||
whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM SyncJobs ORDER BY DateLastModified DESC LIMIT {0})",
|
||||
|
@ -341,7 +365,7 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
|
||||
}
|
||||
|
||||
cmd.CommandText += "; select count (Id) from SyncJobs";
|
||||
cmd.CommandText += "; select count (Id) from SyncJobs" + whereTextWithoutPaging;
|
||||
|
||||
var list = new List<SyncJob>();
|
||||
var count = 0;
|
||||
|
@ -386,7 +410,7 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
return GetSyncJobItem(reader);
|
||||
return GetJobItem(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -394,28 +418,84 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<SyncJobItem> GetJobItems(string jobId)
|
||||
public QueryResult<SyncJobItem> GetJobItems(SyncJobItemQuery query)
|
||||
{
|
||||
if (string.IsNullOrEmpty(jobId))
|
||||
if (query == null)
|
||||
{
|
||||
throw new ArgumentNullException("jobId");
|
||||
throw new ArgumentNullException("query");
|
||||
}
|
||||
|
||||
var guid = new Guid(jobId);
|
||||
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = BaseJobItemSelectText + " where JobId=@Id";
|
||||
cmd.CommandText = BaseJobItemSelectText;
|
||||
|
||||
cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = guid;
|
||||
var whereClauses = new List<string>();
|
||||
|
||||
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
|
||||
if (!string.IsNullOrWhiteSpace(query.JobId))
|
||||
{
|
||||
whereClauses.Add("JobId=@JobId");
|
||||
cmd.Parameters.Add(cmd, "@JobId", DbType.String).Value = query.JobId;
|
||||
}
|
||||
|
||||
if (query.IsCompleted.HasValue)
|
||||
{
|
||||
if (query.IsCompleted.Value)
|
||||
{
|
||||
whereClauses.Add("Status=@Status");
|
||||
}
|
||||
else
|
||||
{
|
||||
whereClauses.Add("Status<>@Status");
|
||||
}
|
||||
cmd.Parameters.Add(cmd, "@Status", DbType.String).Value = SyncJobStatus.Completed.ToString();
|
||||
}
|
||||
|
||||
var whereTextWithoutPaging = whereClauses.Count == 0 ?
|
||||
string.Empty :
|
||||
" where " + string.Join(" AND ", whereClauses.ToArray());
|
||||
|
||||
var startIndex = query.StartIndex ?? 0;
|
||||
if (startIndex > 0)
|
||||
{
|
||||
whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM SyncJobItems ORDER BY DateCreated LIMIT {0})",
|
||||
startIndex.ToString(_usCulture)));
|
||||
}
|
||||
|
||||
if (whereClauses.Count > 0)
|
||||
{
|
||||
cmd.CommandText += " where " + string.Join(" AND ", whereClauses.ToArray());
|
||||
}
|
||||
|
||||
cmd.CommandText += " ORDER BY DateCreated";
|
||||
|
||||
if (query.Limit.HasValue)
|
||||
{
|
||||
cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
|
||||
}
|
||||
|
||||
cmd.CommandText += "; select count (Id) from SyncJobItems" + whereTextWithoutPaging;
|
||||
|
||||
var list = new List<SyncJobItem>();
|
||||
var count = 0;
|
||||
|
||||
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
yield return GetSyncJobItem(reader);
|
||||
list.Add(GetJobItem(reader));
|
||||
}
|
||||
|
||||
if (reader.NextResult() && reader.Read())
|
||||
{
|
||||
count = reader.GetInt32(0);
|
||||
}
|
||||
}
|
||||
|
||||
return new QueryResult<SyncJobItem>()
|
||||
{
|
||||
Items = list.ToArray(),
|
||||
TotalRecordCount = count
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -447,6 +527,8 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
_saveJobItemCommand.GetParameter(index++).Value = jobItem.OutputPath;
|
||||
_saveJobItemCommand.GetParameter(index++).Value = jobItem.Status;
|
||||
_saveJobItemCommand.GetParameter(index++).Value = jobItem.TargetId;
|
||||
_saveJobItemCommand.GetParameter(index++).Value = jobItem.DateCreated;
|
||||
_saveJobItemCommand.GetParameter(index++).Value = jobItem.Progress;
|
||||
|
||||
_saveJobItemCommand.Transaction = transaction;
|
||||
|
||||
|
@ -485,7 +567,7 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
}
|
||||
}
|
||||
|
||||
private SyncJobItem GetSyncJobItem(IDataReader reader)
|
||||
private SyncJobItem GetJobItem(IDataReader reader)
|
||||
{
|
||||
var info = new SyncJobItem
|
||||
{
|
||||
|
@ -501,11 +583,18 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|||
|
||||
if (!reader.IsDBNull(4))
|
||||
{
|
||||
info.Status = (SyncJobStatus)Enum.Parse(typeof(SyncJobStatus), reader.GetString(4), true);
|
||||
info.Status = (SyncJobItemStatus)Enum.Parse(typeof(SyncJobItemStatus), reader.GetString(4), true);
|
||||
}
|
||||
|
||||
info.TargetId = reader.GetString(5);
|
||||
|
||||
info.DateCreated = reader.GetDateTime(6);
|
||||
|
||||
if (!reader.IsDBNull(7))
|
||||
{
|
||||
info.Progress = reader.GetDouble(7);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
using MediaBrowser.Common.ScheduledTasks;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Sync;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.Sync
|
||||
{
|
||||
public class SyncScheduledTask : IScheduledTask
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ISyncRepository _syncRepo;
|
||||
private readonly ISyncManager _syncManager;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
public SyncScheduledTask(ILibraryManager libraryManager, ISyncRepository syncRepo, ISyncManager syncManager, ILogger logger, IUserManager userManager)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_syncRepo = syncRepo;
|
||||
_syncManager = syncManager;
|
||||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return "Sync"; }
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
get { return "Runs scheduled sync jobs"; }
|
||||
}
|
||||
|
||||
public string Category
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Library";
|
||||
}
|
||||
}
|
||||
|
||||
public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
|
||||
{
|
||||
return new SyncJobProcessor(_libraryManager, _syncRepo, _syncManager, _logger, _userManager).Sync(progress,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
public IEnumerable<ITaskTrigger> GetDefaultTriggers()
|
||||
{
|
||||
return new ITaskTrigger[]
|
||||
{
|
||||
new IntervalTrigger { Interval = TimeSpan.FromHours(3) },
|
||||
new StartupTrigger{ DelayMs = Convert.ToInt32(TimeSpan.FromMinutes(5).TotalMilliseconds)}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -428,7 +428,7 @@ namespace MediaBrowser.Server.Startup.Common
|
|||
ImageProcessor = new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, MediaEncoder);
|
||||
RegisterSingleInstance(ImageProcessor);
|
||||
|
||||
SyncManager = new SyncManager(LibraryManager, SyncRepository, ImageProcessor, LogManager.GetLogger("SyncManager"));
|
||||
SyncManager = new SyncManager(LibraryManager, SyncRepository, ImageProcessor, LogManager.GetLogger("SyncManager"), UserManager);
|
||||
RegisterSingleInstance(SyncManager);
|
||||
|
||||
DtoService = new DtoService(Logger, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager, () => ChannelManager, SyncManager, this);
|
||||
|
|
|
@ -354,7 +354,6 @@ namespace MediaBrowser.WebDashboard.Api
|
|||
"connectlogin.js",
|
||||
"dashboardgeneral.js",
|
||||
"dashboardpage.js",
|
||||
"dashboardsync.js",
|
||||
"device.js",
|
||||
"devices.js",
|
||||
"devicesupload.js",
|
||||
|
@ -451,6 +450,8 @@ namespace MediaBrowser.WebDashboard.Api
|
|||
"songs.js",
|
||||
"supporterkeypage.js",
|
||||
"supporterpage.js",
|
||||
"syncactivity.js",
|
||||
"syncsettings.js",
|
||||
"episodes.js",
|
||||
"thememediaplayer.js",
|
||||
"tvgenres.js",
|
||||
|
|
|
@ -102,6 +102,9 @@
|
|||
<Content Include="dashboard-ui\scripts\selectserver.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\scripts\syncsettings.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\scripts\usernew.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
@ -111,6 +114,9 @@
|
|||
<Content Include="dashboard-ui\selectserver.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\syncsettings.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\thirdparty\apiclient\connectservice.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
@ -399,7 +405,7 @@
|
|||
<Content Include="dashboard-ui\dashboardgeneral.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\dashboardsync.html">
|
||||
<Content Include="dashboard-ui\syncactivity.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\device.html">
|
||||
|
@ -735,7 +741,7 @@
|
|||
<Content Include="dashboard-ui\scripts\dashboardgeneral.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\scripts\dashboardsync.js">
|
||||
<Content Include="dashboard-ui\scripts\syncactivity.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\scripts\device.js">
|
||||
|
|
Loading…
Reference in New Issue
Block a user