mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-09-06 11:34:57 +02:00
commit
b0b65b17a4
6 changed files with 81 additions and 48 deletions
|
@ -20,9 +20,6 @@ namespace Emby.Dlna.PlayTo
|
||||||
{
|
{
|
||||||
public class Device : IDisposable
|
public class Device : IDisposable
|
||||||
{
|
{
|
||||||
const string ServiceAvtransportType = "urn:schemas-upnp-org:service:AVTransport:1";
|
|
||||||
const string ServiceRenderingType = "urn:schemas-upnp-org:service:RenderingControl:1";
|
|
||||||
|
|
||||||
#region Fields & Properties
|
#region Fields & Properties
|
||||||
|
|
||||||
private ITimer _timer;
|
private ITimer _timer;
|
||||||
|
@ -254,13 +251,29 @@ namespace Emby.Dlna.PlayTo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DeviceService GetServiceRenderingControl()
|
||||||
|
{
|
||||||
|
var services = Properties.Services;
|
||||||
|
|
||||||
|
return services.FirstOrDefault(s => string.Equals(s.ServiceType, "urn:schemas-upnp-org:service:RenderingControl:1", StringComparison.OrdinalIgnoreCase)) ??
|
||||||
|
services.FirstOrDefault(s => (s.ServiceType ?? string.Empty).StartsWith("urn:schemas-upnp-org:service:RenderingControl", StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeviceService GetAvTransportService()
|
||||||
|
{
|
||||||
|
var services = Properties.Services;
|
||||||
|
|
||||||
|
return services.FirstOrDefault(s => string.Equals(s.ServiceType, "urn:schemas-upnp-org:service:AVTransport:1", StringComparison.OrdinalIgnoreCase)) ??
|
||||||
|
services.FirstOrDefault(s => (s.ServiceType ?? string.Empty).StartsWith("urn:schemas-upnp-org:service:AVTransport", StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<bool> SetMute(bool mute)
|
private async Task<bool> SetMute(bool mute)
|
||||||
{
|
{
|
||||||
var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetMute");
|
var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetMute");
|
||||||
if (command == null)
|
if (command == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType);
|
var service = GetServiceRenderingControl();
|
||||||
|
|
||||||
if (service == null)
|
if (service == null)
|
||||||
{
|
{
|
||||||
|
@ -287,7 +300,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
if (command == null)
|
if (command == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType);
|
var service = GetServiceRenderingControl();
|
||||||
|
|
||||||
if (service == null)
|
if (service == null)
|
||||||
{
|
{
|
||||||
|
@ -308,7 +321,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
if (command == null)
|
if (command == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
|
var service = GetAvTransportService();
|
||||||
|
|
||||||
if (service == null)
|
if (service == null)
|
||||||
{
|
{
|
||||||
|
@ -333,7 +346,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
{"CurrentURIMetaData", CreateDidlMeta(metaData)}
|
{"CurrentURIMetaData", CreateDidlMeta(metaData)}
|
||||||
};
|
};
|
||||||
|
|
||||||
var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
|
var service = GetAvTransportService();
|
||||||
|
|
||||||
if (service == null)
|
if (service == null)
|
||||||
{
|
{
|
||||||
|
@ -373,7 +386,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
if (command == null)
|
if (command == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
|
var service = GetAvTransportService();
|
||||||
|
|
||||||
if (service == null)
|
if (service == null)
|
||||||
{
|
{
|
||||||
|
@ -390,7 +403,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
if (command == null)
|
if (command == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var service = Properties.Services.First(s => s.ServiceType == ServiceAvtransportType);
|
var service = GetAvTransportService();
|
||||||
|
|
||||||
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
|
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
@ -402,7 +415,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
if (command == null)
|
if (command == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var service = Properties.Services.First(s => s.ServiceType == ServiceAvtransportType);
|
var service = GetAvTransportService();
|
||||||
|
|
||||||
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
|
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
@ -521,7 +534,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
if (command == null)
|
if (command == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType);
|
var service = GetServiceRenderingControl();
|
||||||
|
|
||||||
if (service == null)
|
if (service == null)
|
||||||
{
|
{
|
||||||
|
@ -554,7 +567,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
if (command == null)
|
if (command == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType);
|
var service = GetServiceRenderingControl();
|
||||||
|
|
||||||
if (service == null)
|
if (service == null)
|
||||||
{
|
{
|
||||||
|
@ -579,7 +592,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
if (command == null)
|
if (command == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
|
var service = GetAvTransportService();
|
||||||
if (service == null)
|
if (service == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -613,7 +626,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
if (command == null)
|
if (command == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
|
var service = GetAvTransportService();
|
||||||
|
|
||||||
if (service == null)
|
if (service == null)
|
||||||
{
|
{
|
||||||
|
@ -644,7 +657,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
if (command == null)
|
if (command == null)
|
||||||
return new Tuple<bool, uBaseObject>(false, null);
|
return new Tuple<bool, uBaseObject>(false, null);
|
||||||
|
|
||||||
var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
|
var service = GetAvTransportService();
|
||||||
|
|
||||||
if (service == null)
|
if (service == null)
|
||||||
{
|
{
|
||||||
|
@ -780,28 +793,28 @@ namespace Emby.Dlna.PlayTo
|
||||||
|
|
||||||
private async Task GetAVProtocolAsync()
|
private async Task GetAVProtocolAsync()
|
||||||
{
|
{
|
||||||
var avService = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
|
var avService = GetAvTransportService();
|
||||||
if (avService == null)
|
if (avService == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
|
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
|
||||||
|
|
||||||
var httpClient = new SsdpHttpClient(_httpClient, _config);
|
var httpClient = new SsdpHttpClient(_httpClient, _config);
|
||||||
var document = await httpClient.GetDataAsync(url);
|
var document = await httpClient.GetDataAsync(url).ConfigureAwait(false);
|
||||||
|
|
||||||
AvCommands = TransportCommands.Create(document);
|
AvCommands = TransportCommands.Create(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task GetRenderingProtocolAsync()
|
private async Task GetRenderingProtocolAsync()
|
||||||
{
|
{
|
||||||
var avService = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType);
|
var avService = GetServiceRenderingControl();
|
||||||
|
|
||||||
if (avService == null)
|
if (avService == null)
|
||||||
return;
|
return;
|
||||||
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
|
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
|
||||||
|
|
||||||
var httpClient = new SsdpHttpClient(_httpClient, _config);
|
var httpClient = new SsdpHttpClient(_httpClient, _config);
|
||||||
var document = await httpClient.GetDataAsync(url);
|
var document = await httpClient.GetDataAsync(url).ConfigureAwait(false);
|
||||||
|
|
||||||
RendererCommands = TransportCommands.Create(document);
|
RendererCommands = TransportCommands.Create(document);
|
||||||
}
|
}
|
||||||
|
@ -899,8 +912,6 @@ namespace Emby.Dlna.PlayTo
|
||||||
deviceProperties.Icon = CreateIcon(icon);
|
deviceProperties.Icon = CreateIcon(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
var isRenderer = false;
|
|
||||||
|
|
||||||
foreach (var services in document.Descendants(uPnpNamespaces.ud.GetName("serviceList")))
|
foreach (var services in document.Descendants(uPnpNamespaces.ud.GetName("serviceList")))
|
||||||
{
|
{
|
||||||
if (services == null)
|
if (services == null)
|
||||||
|
@ -918,17 +929,13 @@ namespace Emby.Dlna.PlayTo
|
||||||
if (service != null)
|
if (service != null)
|
||||||
{
|
{
|
||||||
deviceProperties.Services.Add(service);
|
deviceProperties.Services.Add(service);
|
||||||
if (service.ServiceType == ServiceAvtransportType)
|
|
||||||
{
|
|
||||||
isRenderer = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var device = new Device(deviceProperties, httpClient, logger, config, timerFactory);
|
var device = new Device(deviceProperties, httpClient, logger, config, timerFactory);
|
||||||
|
|
||||||
if (isRenderer)
|
if (device.GetAvTransportService() != null)
|
||||||
{
|
{
|
||||||
await device.GetRenderingProtocolAsync().ConfigureAwait(false);
|
await device.GetRenderingProtocolAsync().ConfigureAwait(false);
|
||||||
await device.GetAVProtocolAsync().ConfigureAwait(false);
|
await device.GetAVProtocolAsync().ConfigureAwait(false);
|
||||||
|
|
|
@ -389,6 +389,8 @@ namespace Emby.Server.Implementations.Data
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GC.Collect();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1113,16 +1113,21 @@ namespace Emby.Server.Implementations.Library
|
||||||
|
|
||||||
progress.Report(1);
|
progress.Report(1);
|
||||||
|
|
||||||
var userRoot = GetUserRootFolder();
|
await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
await userRoot.RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
await GetUserRootFolder().ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false).ConfigureAwait(false);
|
||||||
|
|
||||||
await userRoot.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false).ConfigureAwait(false);
|
|
||||||
progress.Report(2);
|
progress.Report(2);
|
||||||
|
|
||||||
|
// Quickly scan CollectionFolders for changes
|
||||||
|
foreach (var folder in GetUserRootFolder().Children.OfType<Folder>().ToList())
|
||||||
|
{
|
||||||
|
await folder.RefreshMetadata(cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
progress.Report(3);
|
||||||
|
|
||||||
var innerProgress = new ActionableProgress<double>();
|
var innerProgress = new ActionableProgress<double>();
|
||||||
|
|
||||||
innerProgress.RegisterAction(pct => progress.Report(2 + pct * .73));
|
innerProgress.RegisterAction(pct => progress.Report(3 + pct * .72));
|
||||||
|
|
||||||
// Now validate the entire media library
|
// Now validate the entire media library
|
||||||
await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: true).ConfigureAwait(false);
|
await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: true).ConfigureAwait(false);
|
||||||
|
@ -1291,7 +1296,6 @@ namespace Emby.Server.Implementations.Library
|
||||||
if (parent != null)
|
if (parent != null)
|
||||||
{
|
{
|
||||||
SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
|
SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
|
||||||
query.ParentId = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1311,7 +1315,6 @@ namespace Emby.Server.Implementations.Library
|
||||||
if (parent != null)
|
if (parent != null)
|
||||||
{
|
{
|
||||||
SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
|
SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
|
||||||
query.ParentId = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1456,6 +1459,12 @@ namespace Emby.Server.Implementations.Library
|
||||||
// Optimize by querying against top level views
|
// Optimize by querying against top level views
|
||||||
query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).Select(i => i.ToString("N")).ToArray();
|
query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).Select(i => i.ToString("N")).ToArray();
|
||||||
query.AncestorIds = new string[] { };
|
query.AncestorIds = new string[] { };
|
||||||
|
|
||||||
|
// Prevent searching in all libraries due to empty filter
|
||||||
|
if (query.TopParentIds.Length == 0)
|
||||||
|
{
|
||||||
|
query.TopParentIds = new[] { Guid.NewGuid().ToString("N") };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1478,7 +1487,6 @@ namespace Emby.Server.Implementations.Library
|
||||||
if (parent != null)
|
if (parent != null)
|
||||||
{
|
{
|
||||||
SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
|
SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
|
||||||
query.ParentId = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1514,14 +1522,28 @@ namespace Emby.Server.Implementations.Library
|
||||||
{
|
{
|
||||||
// Optimize by querying against top level views
|
// Optimize by querying against top level views
|
||||||
query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).Select(i => i.ToString("N")).ToArray();
|
query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).Select(i => i.ToString("N")).ToArray();
|
||||||
|
|
||||||
|
// Prevent searching in all libraries due to empty filter
|
||||||
|
if (query.TopParentIds.Length == 0)
|
||||||
|
{
|
||||||
|
query.TopParentIds = new[] { Guid.NewGuid().ToString("N") };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// We need to be able to query from any arbitrary ancestor up the tree
|
// We need to be able to query from any arbitrary ancestor up the tree
|
||||||
query.AncestorIds = parents.SelectMany(i => i.GetIdsForAncestorQuery()).Select(i => i.ToString("N")).ToArray();
|
query.AncestorIds = parents.SelectMany(i => i.GetIdsForAncestorQuery()).Select(i => i.ToString("N")).ToArray();
|
||||||
|
|
||||||
|
// Prevent searching in all libraries due to empty filter
|
||||||
|
if (query.AncestorIds.Length == 0)
|
||||||
|
{
|
||||||
|
query.AncestorIds = new[] { Guid.NewGuid().ToString("N") };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query.ParentId = null;
|
||||||
|
}
|
||||||
|
|
||||||
private void AddUserToQuery(InternalItemsQuery query, User user)
|
private void AddUserToQuery(InternalItemsQuery query, User user)
|
||||||
{
|
{
|
||||||
if (query.AncestorIds.Length == 0 &&
|
if (query.AncestorIds.Length == 0 &&
|
||||||
|
|
|
@ -144,23 +144,16 @@ namespace Emby.Server.Implementations.TV
|
||||||
// If viewing all next up for all series, remove first episodes
|
// If viewing all next up for all series, remove first episodes
|
||||||
// But if that returns empty, keep those first episodes (avoid completely empty view)
|
// But if that returns empty, keep those first episodes (avoid completely empty view)
|
||||||
var alwaysEnableFirstEpisode = !string.IsNullOrWhiteSpace(request.SeriesId);
|
var alwaysEnableFirstEpisode = !string.IsNullOrWhiteSpace(request.SeriesId);
|
||||||
var isFirstItemAFirstEpisode = true;
|
|
||||||
|
|
||||||
return allNextUp
|
return allNextUp
|
||||||
.Where(i =>
|
.Where(i =>
|
||||||
{
|
{
|
||||||
if (alwaysEnableFirstEpisode || i.Item1 != DateTime.MinValue)
|
if (alwaysEnableFirstEpisode || i.Item1 != DateTime.MinValue)
|
||||||
{
|
{
|
||||||
isFirstItemAFirstEpisode = false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFirstItemAFirstEpisode)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
})
|
})
|
||||||
.Select(i => i.Item2())
|
.Select(i => i.Item2())
|
||||||
.Where(i => i != null)
|
.Where(i => i != null)
|
||||||
|
|
|
@ -145,7 +145,8 @@ namespace MediaBrowser.Api
|
||||||
client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 ||
|
client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 ||
|
||||||
client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1 ||
|
client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1 ||
|
||||||
client.IndexOf("roku", StringComparison.OrdinalIgnoreCase) != -1 ||
|
client.IndexOf("roku", StringComparison.OrdinalIgnoreCase) != -1 ||
|
||||||
client.IndexOf("samsung", StringComparison.OrdinalIgnoreCase) != -1)
|
client.IndexOf("samsung", StringComparison.OrdinalIgnoreCase) != -1 ||
|
||||||
|
client.IndexOf("androidtv", StringComparison.OrdinalIgnoreCase) != -1)
|
||||||
{
|
{
|
||||||
options.Fields.Add(Model.Querying.ItemFields.ChildCount);
|
options.Fields.Add(Model.Querying.ItemFields.ChildCount);
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,6 +201,11 @@ namespace MediaBrowser.Controller.Entities
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool RefreshLinkedChildren(IEnumerable<FileSystemMetadata> fileSystemChildren)
|
protected override bool RefreshLinkedChildren(IEnumerable<FileSystemMetadata> fileSystemChildren)
|
||||||
|
{
|
||||||
|
return RefreshLinkedChildrenInternal(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool RefreshLinkedChildrenInternal(bool setFolders)
|
||||||
{
|
{
|
||||||
var physicalFolders = GetPhysicalFolders(false)
|
var physicalFolders = GetPhysicalFolders(false)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
@ -219,8 +224,11 @@ namespace MediaBrowser.Controller.Entities
|
||||||
if (!folderIds.SequenceEqual(newFolderIds))
|
if (!folderIds.SequenceEqual(newFolderIds))
|
||||||
{
|
{
|
||||||
changed = true;
|
changed = true;
|
||||||
|
if (setFolders)
|
||||||
|
{
|
||||||
PhysicalFolderIds = newFolderIds.ToList();
|
PhysicalFolderIds = newFolderIds.ToList();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue