Merge pull request #2677 from MediaBrowser/beta

Beta
This commit is contained in:
Luke
2017-05-31 15:40:34 -04:00
committed by GitHub
483 changed files with 6802 additions and 6720 deletions

View File

@@ -23,7 +23,7 @@ using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
@@ -120,7 +120,7 @@ namespace Emby.Server.Implementations.Channels
if (query.IsFavorite.HasValue)
{
var val = query.IsFavorite.Value;
channels = channels.Where(i => _userDataManager.GetUserData(user, i).IsFavorite == val)
channels = channels.Where(i => _userDataManager.GetUserData(user, i).IsFavorite == val)
.ToList();
}
@@ -263,7 +263,7 @@ namespace Emby.Server.Implementations.Channels
}
catch
{
}
return;
}
@@ -273,7 +273,7 @@ namespace Emby.Server.Implementations.Channels
_jsonSerializer.SerializeToFile(mediaSources, path);
}
public async Task<IEnumerable<MediaSourceInfo>> GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken)
public IEnumerable<MediaSourceInfo> GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken)
{
IEnumerable<ChannelMediaInfo> results = GetSavedMediaSources(item);
@@ -460,12 +460,12 @@ namespace Emby.Server.Implementations.Channels
public IEnumerable<ChannelFeatures> GetAllChannelFeatures()
{
return _libraryManager.GetItemList(new InternalItemsQuery
return _libraryManager.GetItemIds(new InternalItemsQuery
{
IncludeItemTypes = new[] { typeof(Channel).Name },
SortBy = new[] { ItemSortBy.SortName }
}).Select(i => GetChannelFeatures(i.Id.ToString("N")));
}).Select(i => GetChannelFeatures(i.ToString("N")));
}
public ChannelFeatures GetChannelFeatures(string id)
@@ -963,7 +963,7 @@ namespace Emby.Server.Implementations.Channels
}
}
return await GetReturnItems(internalItems, providerTotalRecordCount, user, query).ConfigureAwait(false);
return GetReturnItems(internalItems, providerTotalRecordCount, user, query);
}
public async Task<QueryResult<BaseItemDto>> GetChannelItems(ChannelItemQuery query, CancellationToken cancellationToken)
@@ -1154,7 +1154,7 @@ namespace Emby.Server.Implementations.Channels
filename + ".json");
}
private async Task<QueryResult<BaseItem>> GetReturnItems(IEnumerable<BaseItem> items,
private QueryResult<BaseItem> GetReturnItems(IEnumerable<BaseItem> items,
int? totalCountFromProvider,
User user,
ChannelItemQuery query)

View File

@@ -32,7 +32,7 @@ namespace Emby.Server.Implementations.Collections
return base.Supports(item);
}
protected override Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
protected override List<BaseItem> GetItemsWithImages(IHasImages item)
{
var playlist = (BoxSet)item;
@@ -73,10 +73,10 @@ namespace Emby.Server.Implementations.Collections
.DistinctBy(i => i.Id)
.ToList();
return Task.FromResult(GetFinalItems(items, 2));
return GetFinalItems(items, 2);
}
protected override Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
protected override string CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
{
return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
}

View File

@@ -170,6 +170,11 @@ namespace Emby.Server.Implementations.Collections
{
var item = _libraryManager.GetItemById(itemId);
if (string.IsNullOrWhiteSpace(item.Path))
{
continue;
}
if (item == null)
{
throw new ArgumentException("No item exists with the supplied Id");

View File

@@ -1,7 +1,7 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using System.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.IO;

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Providers;

View File

@@ -16,7 +16,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.IO;

View File

@@ -24,7 +24,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Extensions;
@@ -107,7 +107,7 @@ namespace Emby.Server.Implementations.Dto
foreach (var item in items)
{
var dto = await GetBaseItemDtoInternal(item, options, user, owner).ConfigureAwait(false);
var dto = GetBaseItemDtoInternal(item, options, user, owner);
var tvChannel = item as LiveTvChannel;
if (tvChannel != null)
@@ -127,7 +127,11 @@ namespace Emby.Server.Implementations.Dto
{
var libraryItems = byName.GetTaggedItems(new InternalItemsQuery(user)
{
Recursive = true
Recursive = true,
DtoOptions = new DtoOptions(false)
{
EnableImages = false
}
});
SetItemByNameInfo(item, dto, libraryItems.ToList(), user);
@@ -156,7 +160,7 @@ namespace Emby.Server.Implementations.Dto
{
var syncDictionary = GetSyncedItemProgress(options);
var dto = GetBaseItemDtoInternal(item, options, user, owner).Result;
var dto = GetBaseItemDtoInternal(item, options, user, owner);
var tvChannel = item as LiveTvChannel;
if (tvChannel != null)
{
@@ -177,7 +181,11 @@ namespace Emby.Server.Implementations.Dto
{
if (options.Fields.Contains(ItemFields.ItemCounts))
{
SetItemByNameInfo(item, dto, GetTaggedItems(byName, user), user);
SetItemByNameInfo(item, dto, GetTaggedItems(byName, user, new DtoOptions(false)
{
EnableImages = false
}), user);
}
FillSyncInfo(dto, item, options, user, syncDictionary);
@@ -189,11 +197,12 @@ namespace Emby.Server.Implementations.Dto
return dto;
}
private List<BaseItem> GetTaggedItems(IItemByName byName, User user)
private List<BaseItem> GetTaggedItems(IItemByName byName, User user, DtoOptions options)
{
var items = byName.GetTaggedItems(new InternalItemsQuery(user)
{
Recursive = true
Recursive = true,
DtoOptions = options
}).ToList();
@@ -283,7 +292,7 @@ namespace Emby.Server.Implementations.Dto
}
}
private async Task<BaseItemDto> GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
{
var fields = options.Fields;
@@ -332,7 +341,7 @@ namespace Emby.Server.Implementations.Dto
if (user != null)
{
await AttachUserSpecificInfo(dto, item, user, options).ConfigureAwait(false);
AttachUserSpecificInfo(dto, item, user, options);
}
var hasMediaSources = item as IHasMediaSources;
@@ -393,7 +402,7 @@ namespace Emby.Server.Implementations.Dto
public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, Dictionary<string, SyncedItemProgress> syncProgress, User user = null)
{
var dto = GetBaseItemDtoInternal(item, options, user).Result;
var dto = GetBaseItemDtoInternal(item, options, user);
if (taggedItems != null && options.Fields.Contains(ItemFields.ItemCounts))
{
@@ -446,7 +455,7 @@ namespace Emby.Server.Implementations.Dto
/// <summary>
/// Attaches the user specific info.
/// </summary>
private async Task AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, DtoOptions dtoOptions)
private void AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, DtoOptions dtoOptions)
{
var fields = dtoOptions.Fields;
@@ -456,7 +465,7 @@ namespace Emby.Server.Implementations.Dto
if (dtoOptions.EnableUserData)
{
dto.UserData = await _userDataRepository.GetUserDataDto(item, dto, user, dtoOptions.Fields).ConfigureAwait(false);
dto.UserData = _userDataRepository.GetUserDataDto(item, dto, user, dtoOptions.Fields);
}
if (!dto.ChildCount.HasValue && item.SourceType == SourceType.Library)
@@ -488,7 +497,7 @@ namespace Emby.Server.Implementations.Dto
{
if (dtoOptions.EnableUserData)
{
dto.UserData = await _userDataRepository.GetUserDataDto(item, user).ConfigureAwait(false);
dto.UserData = _userDataRepository.GetUserDataDto(item, user);
}
}
@@ -595,16 +604,17 @@ namespace Emby.Server.Implementations.Dto
{
if (!string.IsNullOrEmpty(item.Album))
{
var parentAlbum = _libraryManager.GetItemList(new InternalItemsQuery
var parentAlbumIds = _libraryManager.GetItemIds(new InternalItemsQuery
{
IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
Name = item.Album
Name = item.Album,
Limit = 1
}).FirstOrDefault();
});
if (parentAlbum != null)
if (parentAlbumIds.Count > 0)
{
dto.AlbumId = GetDtoId(parentAlbum);
dto.AlbumId = parentAlbumIds[0].ToString("N");
}
}
@@ -751,45 +761,41 @@ namespace Emby.Server.Implementations.Dto
/// <returns>Task.</returns>
private void AttachStudios(BaseItemDto dto, BaseItem item)
{
var studios = item.Studios.ToList();
dto.Studios = item.Studios
.Where(i => !string.IsNullOrWhiteSpace(i))
.Select(i => new NameIdPair
{
Name = i,
Id = _libraryManager.GetStudioId(i).ToString("N")
})
.ToArray();
}
dto.Studios = new StudioDto[studios.Count];
private void AttachGenreItems(BaseItemDto dto, BaseItem item)
{
dto.GenreItems = item.Genres
.Where(i => !string.IsNullOrWhiteSpace(i))
.Select(i => new NameIdPair
{
Name = i,
Id = GetStudioId(i, item)
})
.ToArray();
}
var dictionary = studios.Distinct(StringComparer.OrdinalIgnoreCase).Select(name =>
private string GetStudioId(string name, BaseItem owner)
{
if (owner is IHasMusicGenres)
{
try
{
return _libraryManager.GetStudio(name);
}
catch (IOException ex)
{
_logger.ErrorException("Error getting studio {0}", ex, name);
return null;
}
})
.Where(i => i != null)
.DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
.ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
for (var i = 0; i < studios.Count; i++)
{
var studio = studios[i];
var studioDto = new StudioDto
{
Name = studio
};
Studio entity;
if (dictionary.TryGetValue(studio, out entity))
{
studioDto.Id = entity.Id.ToString("N");
studioDto.PrimaryImageTag = GetImageCacheTag(entity, ImageType.Primary);
}
dto.Studios[i] = studioDto;
return _libraryManager.GetGameGenreId(name).ToString("N");
}
if (owner is Game || owner is GameSystem)
{
return _libraryManager.GetGameGenreId(name).ToString("N");
}
return _libraryManager.GetGenreId(name).ToString("N");
}
/// <summary>
@@ -901,6 +907,7 @@ namespace Emby.Server.Implementations.Dto
if (fields.Contains(ItemFields.Genres))
{
dto.Genres = item.Genres;
AttachGenreItems(dto, item);
}
if (options.EnableImages)
@@ -1130,7 +1137,10 @@ namespace Emby.Server.Implementations.Dto
return null;
}
var artist = _libraryManager.GetArtist(i);
var artist = _libraryManager.GetArtist(i, new DtoOptions(false)
{
EnableImages = false
});
if (artist != null)
{
return new NameIdPair
@@ -1179,7 +1189,10 @@ namespace Emby.Server.Implementations.Dto
return null;
}
var artist = _libraryManager.GetArtist(i);
var artist = _libraryManager.GetArtist(i, new DtoOptions(false)
{
EnableImages = false
});
if (artist != null)
{
return new NameIdPair
@@ -1449,7 +1462,7 @@ namespace Emby.Server.Implementations.Dto
var musicAlbum = item as MusicAlbum;
if (musicAlbum != null)
{
var artist = musicAlbum.MusicArtist;
var artist = musicAlbum.GetMusicArtist(new DtoOptions(false));
if (artist != null)
{
return artist;
@@ -1582,24 +1595,30 @@ namespace Emby.Server.Implementations.Dto
ImageSize size;
if (supportedEnhancers.Count == 0)
{
var defaultAspectRatio = item.GetDefaultPrimaryImageAspectRatio();
var defaultAspectRatio = item.GetDefaultPrimaryImageAspectRatio();
if (defaultAspectRatio.HasValue)
if (defaultAspectRatio.HasValue)
{
if (supportedEnhancers.Count == 0)
{
return defaultAspectRatio.Value;
}
}
try
{
size = _imageProcessor.GetImageSize(imageInfo);
double dummyWidth = 200;
double dummyHeight = dummyWidth / defaultAspectRatio.Value;
size = new ImageSize(dummyWidth, dummyHeight);
}
catch
else
{
//_logger.ErrorException("Failed to determine primary image aspect ratio for {0}", ex, path);
return null;
try
{
size = _imageProcessor.GetImageSize(imageInfo);
}
catch
{
//_logger.ErrorException("Failed to determine primary image aspect ratio for {0}", ex, path);
return null;
}
}
foreach (var enhancer in supportedEnhancers)

View File

@@ -10,9 +10,8 @@
<RootNamespace>Emby.Server.Implementations</RootNamespace>
<AssemblyName>Emby.Server.Implementations</AssemblyName>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkProfile />
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -103,6 +102,7 @@
<Compile Include="HttpServer\SocketSharp\WebSocketSharpResponse.cs" />
<Compile Include="HttpServer\StreamWriter.cs" />
<Compile Include="Images\BaseDynamicImageProvider.cs" />
<Compile Include="IO\AsyncStreamCopier.cs" />
<Compile Include="IO\FileRefresher.cs" />
<Compile Include="IO\MbLinkShortcutHandler.cs" />
<Compile Include="IO\ThrottledStream.cs" />
@@ -182,7 +182,6 @@
<Compile Include="Migrations\IVersionMigration.cs" />
<Compile Include="Migrations\LibraryScanMigration.cs" />
<Compile Include="Migrations\GuideMigration.cs" />
<Compile Include="Migrations\UpdateLevelMigration.cs" />
<Compile Include="News\NewsEntryPoint.cs" />
<Compile Include="News\NewsService.cs" />
<Compile Include="Notifications\CoreNotificationTypes.cs" />
@@ -291,9 +290,9 @@
<Project>{2e781478-814d-4a48-9d80-bff206441a65}</Project>
<Name>MediaBrowser.Server.Implementations</Name>
</ProjectReference>
<ProjectReference Include="..\SocketHttpListener.Portable\SocketHttpListener.Portable.csproj">
<Project>{4f26d5d8-a7b0-42b3-ba42-7cb7d245934e}</Project>
<Name>SocketHttpListener.Portable</Name>
<ProjectReference Include="..\SocketHttpListener\SocketHttpListener.csproj">
<Project>{1d74413b-e7cf-455b-b021-f52bdf881542}</Project>
<Name>SocketHttpListener</Name>
</ProjectReference>
<Reference Include="Emby.XmlTv, Version=1.0.6299.28292, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Emby.XmlTv.1.0.8\lib\portable-net45+win8\Emby.XmlTv.dll</HintPath>
@@ -312,6 +311,17 @@
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Localization\Core\ar.json" />
<EmbeddedResource Include="Localization\Core\bg-BG.json" />
@@ -410,7 +420,7 @@
<ItemGroup>
<EmbeddedResource Include="Localization\Ratings\uk.txt" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@@ -122,7 +122,7 @@ namespace Emby.Server.Implementations.EntryPoints
.DistinctBy(i => i.Id)
.Select(i =>
{
var dto = _userDataManager.GetUserDataDto(i, user).Result;
var dto = _userDataManager.GetUserDataDto(i, user);
dto.ItemId = i.Id.ToString("N");
return dto;
})

View File

@@ -15,7 +15,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.Library;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
@@ -588,7 +588,8 @@ namespace Emby.Server.Implementations.FileOrganization
var series = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new[] { typeof(Series).Name },
Recursive = true
Recursive = true,
DtoOptions = new DtoOptions(true)
})
.Cast<Series>()
.Select(i => NameUtils.GetMatchScore(nameWithoutYear, yearInName, i))
@@ -607,7 +608,8 @@ namespace Emby.Server.Implementations.FileOrganization
{
IncludeItemTypes = new[] { typeof(Series).Name },
Recursive = true,
Name = info.ItemName
Name = info.ItemName,
DtoOptions = new DtoOptions(true)
}).Cast<Series>().FirstOrDefault();
}

View File

@@ -17,7 +17,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Events;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Tasks;

View File

@@ -8,7 +8,7 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;

View File

@@ -10,7 +10,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;

View File

@@ -8,6 +8,7 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.HttpServer;
using Emby.Server.Implementations.HttpServer.SocketSharp;
@@ -445,10 +446,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary>
/// Overridable method that can be used to implement a custom hnandler
/// </summary>
/// <param name="httpReq">The HTTP req.</param>
/// <param name="url">The URL.</param>
/// <returns>Task.</returns>
protected async Task RequestHandler(IHttpRequest httpReq, Uri url)
protected async Task RequestHandler(IHttpRequest httpReq, Uri url, CancellationToken cancellationToken)
{
var date = DateTime.Now;
var httpRes = httpReq.Response;
@@ -589,7 +587,7 @@ namespace Emby.Server.Implementations.HttpServer
if (handler != null)
{
await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, operationName).ConfigureAwait(false);
await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, operationName, cancellationToken).ConfigureAwait(false);
}
else
{

View File

@@ -58,6 +58,18 @@ namespace Emby.Server.Implementations.HttpServer
return GetHttpResult(content, contentType, true, responseHeaders);
}
public object GetRedirectResult(string url)
{
var responseHeaders = new Dictionary<string, string>();
responseHeaders["Location"] = url;
var result = new HttpResult(new byte[] { }, "text/plain", HttpStatusCode.Redirect);
AddResponseHeaders(result, responseHeaders);
return result;
}
/// <summary>
/// Gets the HTTP result.
/// </summary>
@@ -599,9 +611,9 @@ namespace Emby.Server.Implementations.HttpServer
}
}
private async Task<IHasHeaders> GetCompressedResult(Stream stream,
string requestedCompressionType,
IDictionary<string,string> responseHeaders,
private async Task<IHasHeaders> GetCompressedResult(Stream stream,
string requestedCompressionType,
IDictionary<string, string> responseHeaders,
bool isHeadRequest,
string contentType)
{

View File

@@ -1,6 +1,7 @@
using MediaBrowser.Controller.Net;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
@@ -18,7 +19,7 @@ namespace Emby.Server.Implementations.HttpServer
/// Gets or sets the request handler.
/// </summary>
/// <value>The request handler.</value>
Func<IHttpRequest, Uri, Task> RequestHandler { get; set; }
Func<IHttpRequest, Uri, CancellationToken, Task> RequestHandler { get; set; }
/// <summary>
/// Gets or sets the web socket handler.

View File

@@ -4,6 +4,7 @@ using SocketHttpListener.Net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Cryptography;
@@ -33,6 +34,9 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
private readonly bool _enableDualMode;
private readonly IEnvironmentInfo _environment;
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
private CancellationToken _disposeCancellationToken;
public WebSocketSharpListener(ILogger logger, ICertificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, bool enableDualMode, Func<HttpListenerContext, IHttpRequest> httpRequestFactory, IFileSystem fileSystem, IEnvironmentInfo environment)
{
_logger = logger;
@@ -47,10 +51,12 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
_httpRequestFactory = httpRequestFactory;
_fileSystem = fileSystem;
_environment = environment;
_disposeCancellationToken = _disposeCancellationTokenSource.Token;
}
public Action<Exception, IRequest, bool> ErrorHandler { get; set; }
public Func<IHttpRequest, Uri, Task> RequestHandler { get; set; }
public Func<IHttpRequest, Uri, CancellationToken, Task> RequestHandler { get; set; }
public Action<WebSocketConnectingEventArgs> WebSocketConnecting { get; set; }
@@ -81,11 +87,11 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
private void ProcessContext(HttpListenerContext context)
{
//Task.Factory.StartNew(() => InitTask(context), TaskCreationOptions.DenyChildAttach | TaskCreationOptions.PreferFairness);
Task.Run(() => InitTask(context));
InitTask(context, _disposeCancellationToken);
//Task.Run(() => InitTask(context, _disposeCancellationToken));
}
private Task InitTask(HttpListenerContext context)
private Task InitTask(HttpListenerContext context, CancellationToken cancellationToken)
{
IHttpRequest httpReq = null;
var request = context.Request;
@@ -111,7 +117,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
return Task.FromResult(true);
}
return RequestHandler(httpReq, request.Url);
return RequestHandler(httpReq, request.Url, cancellationToken);
}
private void ProcessWebSocketRequest(HttpListenerContext ctx)
@@ -172,6 +178,8 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
public void Stop()
{
_disposeCancellationTokenSource.Cancel();
if (_listener != null)
{
foreach (var prefix in _listener.Prefixes.ToList())

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using Emby.Server.Implementations.HttpServer;
using Emby.Server.Implementations.HttpServer.SocketSharp;
@@ -374,7 +373,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
this.pathInfo = request.RawUrl;
}
this.pathInfo = WebUtility.UrlDecode(pathInfo);
this.pathInfo = System.Net.WebUtility.UrlDecode(pathInfo);
this.pathInfo = NormalizePathInfo(pathInfo, mode);
}
return this.pathInfo;
@@ -440,7 +439,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
cookies = new Dictionary<string, System.Net.Cookie>();
foreach (var cookie in this.request.Cookies)
{
var httpCookie = (Cookie) cookie;
var httpCookie = (System.Net.Cookie) cookie;
cookies[httpCookie.Name] = new System.Net.Cookie(httpCookie.Name, httpCookie.Value, httpCookie.Path, httpCookie.Domain);
}
}

View File

@@ -114,15 +114,9 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
var outputStream = response.OutputStream;
// This is needed with compression
if (outputStream is ResponseStream)
{
//if (!string.IsNullOrWhiteSpace(GetHeader("Content-Encoding")))
{
outputStream.Flush();
}
outputStream.Flush();
outputStream.Dispose();
outputStream.Dispose();
}
response.Close();
}
catch (Exception ex)

View File

@@ -5,7 +5,7 @@ using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.Services;
namespace Emby.Server.Implementations.HttpServer

View File

@@ -0,0 +1,459 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Emby.Server.Implementations.IO
{
public class AsyncStreamCopier : IDisposable
{
// size in bytes of the buffers in the buffer pool
private const int DefaultBufferSize = 81920;
private readonly int _bufferSize;
// number of buffers in the pool
private const int DefaultBufferCount = 4;
private readonly int _bufferCount;
// indexes of the next buffer to read into/write from
private int _nextReadBuffer = -1;
private int _nextWriteBuffer = -1;
// the buffer pool, implemented as an array, and used in a cyclic way
private readonly byte[][] _buffers;
// sizes in bytes of the available (read) data in the buffers
private readonly int[] _sizes;
// the streams...
private Stream _source;
private Stream _target;
private readonly bool _closeStreamsOnEnd;
// number of buffers that are ready to be written
private int _buffersToWrite;
// flag indicating that there is still a read operation to be scheduled
// (source end of stream not reached)
private volatile bool _moreDataToRead;
// the result of the whole operation, returned by BeginCopy()
private AsyncResult _asyncResult;
// any exception that occurs during an async operation
// stored here for rethrow
private Exception _exception;
public TaskCompletionSource<long> TaskCompletionSource;
private long _bytesToRead;
private long _totalBytesWritten;
private CancellationToken _cancellationToken;
public int IndividualReadOffset = 0;
public AsyncStreamCopier(Stream source,
Stream target,
long bytesToRead,
CancellationToken cancellationToken,
bool closeStreamsOnEnd = false,
int bufferSize = DefaultBufferSize,
int bufferCount = DefaultBufferCount)
{
if (source == null)
throw new ArgumentNullException("source");
if (target == null)
throw new ArgumentNullException("target");
if (!source.CanRead)
throw new ArgumentException("Cannot copy from a non-readable stream.");
if (!target.CanWrite)
throw new ArgumentException("Cannot copy to a non-writable stream.");
_source = source;
_target = target;
_moreDataToRead = true;
_closeStreamsOnEnd = closeStreamsOnEnd;
_bufferSize = bufferSize;
_bufferCount = bufferCount;
_buffers = new byte[_bufferCount][];
_sizes = new int[_bufferCount];
_bytesToRead = bytesToRead;
_cancellationToken = cancellationToken;
}
~AsyncStreamCopier()
{
// ensure any exception cannot be ignored
ThrowExceptionIfNeeded();
}
public static Task<long> CopyStream(Stream source, Stream target, int bufferSize, int bufferCount, CancellationToken cancellationToken)
{
return CopyStream(source, target, 0, bufferSize, bufferCount, cancellationToken);
}
public static Task<long> CopyStream(Stream source, Stream target, long size, int bufferSize, int bufferCount, CancellationToken cancellationToken)
{
var copier = new AsyncStreamCopier(source, target, size, cancellationToken, false, bufferSize, bufferCount);
var taskCompletion = new TaskCompletionSource<long>();
copier.TaskCompletionSource = taskCompletion;
var result = copier.BeginCopy(StreamCopyCallback, copier);
if (result.CompletedSynchronously)
{
StreamCopyCallback(result);
}
cancellationToken.Register(() => taskCompletion.TrySetCanceled());
return taskCompletion.Task;
}
private static void StreamCopyCallback(IAsyncResult result)
{
var copier = (AsyncStreamCopier)result.AsyncState;
var taskCompletion = copier.TaskCompletionSource;
try
{
copier.EndCopy(result);
taskCompletion.TrySetResult(copier._totalBytesWritten);
}
catch (Exception ex)
{
taskCompletion.TrySetException(ex);
}
}
public void Dispose()
{
if (_asyncResult != null)
_asyncResult.Dispose();
if (_closeStreamsOnEnd)
{
if (_source != null)
{
_source.Dispose();
_source = null;
}
if (_target != null)
{
_target.Dispose();
_target = null;
}
}
GC.SuppressFinalize(this);
ThrowExceptionIfNeeded();
}
public IAsyncResult BeginCopy(AsyncCallback callback, object state)
{
// avoid concurrent start of the copy on separate threads
if (Interlocked.CompareExchange(ref _asyncResult, new AsyncResult(callback, state), null) != null)
throw new InvalidOperationException("A copy operation has already been started on this object.");
// allocate buffers
for (int i = 0; i < _bufferCount; i++)
_buffers[i] = new byte[_bufferSize];
// we pass false to BeginRead() to avoid completing the async result
// immediately which would result in invoking the callback
// when the method fails synchronously
BeginRead(false);
// throw exception synchronously if there is one
ThrowExceptionIfNeeded();
return _asyncResult;
}
public void EndCopy(IAsyncResult ar)
{
if (ar != _asyncResult)
throw new InvalidOperationException("Invalid IAsyncResult object.");
if (!_asyncResult.IsCompleted)
_asyncResult.AsyncWaitHandle.WaitOne();
if (_closeStreamsOnEnd)
{
_source.Close();
_source = null;
_target.Close();
_target = null;
}
//_logger.Info("AsyncStreamCopier {0} bytes requested. {1} bytes transferred", _bytesToRead, _totalBytesWritten);
ThrowExceptionIfNeeded();
}
/// <summary>
/// Here we'll throw a pending exception if there is one,
/// and remove it from our instance, so we know it has been consumed.
/// </summary>
private void ThrowExceptionIfNeeded()
{
if (_exception != null)
{
var exception = _exception;
_exception = null;
throw exception;
}
}
private void BeginRead(bool completeOnError = true)
{
if (!_moreDataToRead)
{
return;
}
if (_asyncResult.IsCompleted)
return;
int bufferIndex = Interlocked.Increment(ref _nextReadBuffer) % _bufferCount;
try
{
_source.BeginRead(_buffers[bufferIndex], 0, _bufferSize, EndRead, bufferIndex);
}
catch (Exception exception)
{
_exception = exception;
if (completeOnError)
_asyncResult.Complete(false);
}
}
private void BeginWrite()
{
if (_asyncResult.IsCompleted)
return;
// this method can actually be called concurrently!!
// indeed, let's say we call a BeginWrite, and the thread gets interrupted
// just after making the IO request.
// At that moment, the thread is still in the method. And then the IO request
// ends (extremely fast io, or caching...), EndWrite gets called
// on another thread, and calls BeginWrite again! There we have it!
// That is the reason why an Interlocked is needed here.
int bufferIndex = Interlocked.Increment(ref _nextWriteBuffer) % _bufferCount;
try
{
int bytesToWrite;
if (_bytesToRead > 0)
{
var bytesLeftToWrite = _bytesToRead - _totalBytesWritten;
bytesToWrite = Convert.ToInt32(Math.Min(_sizes[bufferIndex], bytesLeftToWrite));
}
else
{
bytesToWrite = _sizes[bufferIndex];
}
_target.BeginWrite(_buffers[bufferIndex], IndividualReadOffset, bytesToWrite - IndividualReadOffset, EndWrite, null);
_totalBytesWritten += bytesToWrite;
}
catch (Exception exception)
{
_exception = exception;
_asyncResult.Complete(false);
}
}
private void EndRead(IAsyncResult ar)
{
try
{
int read = _source.EndRead(ar);
_moreDataToRead = read > 0;
var bufferIndex = (int)ar.AsyncState;
_sizes[bufferIndex] = read;
}
catch (Exception exception)
{
_exception = exception;
_asyncResult.Complete(false);
return;
}
if (_moreDataToRead && !_cancellationToken.IsCancellationRequested)
{
int usedBuffers = Interlocked.Increment(ref _buffersToWrite);
// if we incremented from zero to one, then it means we just
// added the single buffer to write, so a writer could not
// be busy, and we have to schedule one.
if (usedBuffers == 1)
BeginWrite();
// test if there is at least a free buffer, and schedule
// a read, as we have read some data
if (usedBuffers < _bufferCount)
BeginRead();
}
else
{
// we did not add a buffer, because no data was read, and
// there is no buffer left to write so this is the end...
if (Thread.VolatileRead(ref _buffersToWrite) == 0)
{
_asyncResult.Complete(false);
}
}
}
private void EndWrite(IAsyncResult ar)
{
try
{
_target.EndWrite(ar);
}
catch (Exception exception)
{
_exception = exception;
_asyncResult.Complete(false);
return;
}
int buffersLeftToWrite = Interlocked.Decrement(ref _buffersToWrite);
// no reader could be active if all buffers were full of data waiting to be written
bool noReaderIsBusy = buffersLeftToWrite == _bufferCount - 1;
// note that it is possible that both a reader and
// a writer see the end of the copy and call Complete
// on the _asyncResult object. That race condition is handled by
// Complete that ensures it is only executed fully once.
long bytesLeftToWrite;
if (_bytesToRead > 0)
{
bytesLeftToWrite = _bytesToRead - _totalBytesWritten;
}
else
{
bytesLeftToWrite = 1;
}
if (!_moreDataToRead || bytesLeftToWrite <= 0 || _cancellationToken.IsCancellationRequested)
{
// at this point we know no reader can schedule a read or write
if (Thread.VolatileRead(ref _buffersToWrite) == 0)
{
// nothing left to write, so it is the end
_asyncResult.Complete(false);
return;
}
}
else
// here, we know we have something left to read,
// so schedule a read if no read is busy
if (noReaderIsBusy)
BeginRead();
// also schedule a write if we are sure we did not write the last buffer
// note that if buffersLeftToWrite is zero and a reader has put another
// buffer to write between the time we decremented _buffersToWrite
// and now, that reader will also schedule another write,
// as it will increment _buffersToWrite from zero to one
if (buffersLeftToWrite > 0)
BeginWrite();
}
}
internal class AsyncResult : IAsyncResult, IDisposable
{
// Fields set at construction which never change while
// operation is pending
private readonly AsyncCallback _asyncCallback;
private readonly object _asyncState;
// Fields set at construction which do change after
// operation completes
private const int StatePending = 0;
private const int StateCompletedSynchronously = 1;
private const int StateCompletedAsynchronously = 2;
private int _completedState = StatePending;
// Field that may or may not get set depending on usage
private ManualResetEvent _waitHandle;
internal AsyncResult(
AsyncCallback asyncCallback,
object state)
{
_asyncCallback = asyncCallback;
_asyncState = state;
}
internal bool Complete(bool completedSynchronously)
{
bool result = false;
// The _completedState field MUST be set prior calling the callback
int prevState = Interlocked.CompareExchange(ref _completedState,
completedSynchronously ? StateCompletedSynchronously :
StateCompletedAsynchronously, StatePending);
if (prevState == StatePending)
{
// If the event exists, set it
if (_waitHandle != null)
_waitHandle.Set();
if (_asyncCallback != null)
_asyncCallback(this);
result = true;
}
return result;
}
#region Implementation of IAsyncResult
public Object AsyncState { get { return _asyncState; } }
public bool CompletedSynchronously
{
get
{
return Thread.VolatileRead(ref _completedState) ==
StateCompletedSynchronously;
}
}
public WaitHandle AsyncWaitHandle
{
get
{
if (_waitHandle == null)
{
bool done = IsCompleted;
var mre = new ManualResetEvent(done);
if (Interlocked.CompareExchange(ref _waitHandle,
mre, null) != null)
{
// Another thread created this object's event; dispose
// the event we just created
mre.Close();
}
else
{
if (!done && IsCompleted)
{
// If the operation wasn't done when we created
// the event but now it is done, set the event
_waitHandle.Set();
}
}
}
return _waitHandle;
}
}
public bool IsCompleted
{
get
{
return Thread.VolatileRead(ref _completedState) !=
StatePending;
}
}
#endregion
public void Dispose()
{
if (_waitHandle != null)
{
_waitHandle.Dispose();
_waitHandle = null;
}
}
}
}

View File

@@ -6,7 +6,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
@@ -180,7 +180,7 @@ namespace Emby.Server.Implementations.IO
try
{
await item.ChangedExternally().ConfigureAwait(false);
item.ChangedExternally();
}
catch (IOException ex)
{
@@ -231,7 +231,7 @@ namespace Emby.Server.Implementations.IO
private bool IsFileLocked(string path)
{
if (_environmentInfo.OperatingSystem != OperatingSystem.Windows)
if (_environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows)
{
// Causing lockups on linux
return false;
@@ -282,11 +282,11 @@ namespace Emby.Server.Implementations.IO
return false;
}
}
//catch (DirectoryNotFoundException)
//{
// // File may have been deleted
// return false;
//}
catch (DirectoryNotFoundException)
{
// File may have been deleted
return false;
}
catch (FileNotFoundException)
{
// File may have been deleted

View File

@@ -1,6 +1,6 @@
using System;
using System.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;

View File

@@ -12,7 +12,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.IO;
@@ -59,33 +59,6 @@ namespace Emby.Server.Implementations.Images
//return GetSupportedImages(item).Where(i => IsEnabled(options, i, item)).ToList();
}
private bool IsEnabled(MetadataOptions options, ImageType type, IHasImages item)
{
if (type == ImageType.Backdrop)
{
if (item.LockedFields.Contains(MetadataFields.Backdrops))
{
return false;
}
}
else if (type == ImageType.Screenshot)
{
if (item.LockedFields.Contains(MetadataFields.Screenshots))
{
return false;
}
}
else
{
if (item.LockedFields.Contains(MetadataFields.Images))
{
return false;
}
}
return options.IsEnabled(type);
}
public async Task<ItemUpdateType> FetchAsync(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
if (!Supports(item))
@@ -128,7 +101,7 @@ namespace Emby.Server.Implementations.Images
}
}
var items = await GetItemsWithImages(item).ConfigureAwait(false);
var items = GetItemsWithImages(item);
return await FetchToFileInternal(item, items, imageType, cancellationToken).ConfigureAwait(false);
}
@@ -140,7 +113,7 @@ namespace Emby.Server.Implementations.Images
{
var outputPathWithoutExtension = Path.Combine(ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N"));
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(outputPathWithoutExtension));
string outputPath = await CreateImage(item, itemsWithImages, outputPathWithoutExtension, imageType, 0).ConfigureAwait(false);
string outputPath = CreateImage(item, itemsWithImages, outputPathWithoutExtension, imageType, 0);
if (string.IsNullOrWhiteSpace(outputPath))
{
@@ -159,9 +132,9 @@ namespace Emby.Server.Implementations.Images
return ItemUpdateType.ImageUpdate;
}
protected abstract Task<List<BaseItem>> GetItemsWithImages(IHasImages item);
protected abstract List<BaseItem> GetItemsWithImages(IHasImages item);
protected Task<string> CreateThumbCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath)
protected string CreateThumbCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath)
{
return CreateCollage(primaryItem, items, outputPath, 640, 360);
}
@@ -188,22 +161,22 @@ namespace Emby.Server.Implementations.Images
.Where(i => !string.IsNullOrWhiteSpace(i));
}
protected Task<string> CreatePosterCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath)
protected string CreatePosterCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath)
{
return CreateCollage(primaryItem, items, outputPath, 400, 600);
}
protected Task<string> CreateSquareCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath)
protected string CreateSquareCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath)
{
return CreateCollage(primaryItem, items, outputPath, 600, 600);
}
protected Task<string> CreateThumbCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath, int width, int height)
protected string CreateThumbCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath, int width, int height)
{
return CreateCollage(primaryItem, items, outputPath, width, height);
}
private async Task<string> CreateCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath, int width, int height)
private string CreateCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath, int width, int height)
{
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(outputPath));
@@ -225,7 +198,7 @@ namespace Emby.Server.Implementations.Images
return null;
}
await ImageProcessor.CreateImageCollage(options).ConfigureAwait(false);
ImageProcessor.CreateImageCollage(options);
return outputPath;
}
@@ -234,7 +207,7 @@ namespace Emby.Server.Implementations.Images
get { return "Dynamic Image Provider"; }
}
protected virtual async Task<string> CreateImage(IHasImages item,
protected virtual string CreateImage(IHasImages item,
List<BaseItem> itemsWithImages,
string outputPathWithoutExtension,
ImageType imageType,
@@ -249,20 +222,20 @@ namespace Emby.Server.Implementations.Images
if (imageType == ImageType.Thumb)
{
return await CreateThumbCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
return CreateThumbCollage(item, itemsWithImages, outputPath);
}
if (imageType == ImageType.Primary)
{
if (item is UserView)
{
return await CreateSquareCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
return CreateSquareCollage(item, itemsWithImages, outputPath);
}
if (item is Playlist || item is MusicGenre || item is Genre || item is GameGenre || item is PhotoAlbum)
{
return await CreateSquareCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
return CreateSquareCollage(item, itemsWithImages, outputPath);
}
return await CreatePosterCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
return CreatePosterCollage(item, itemsWithImages, outputPath);
}
throw new ArgumentException("Unexpected image type");
@@ -346,7 +319,7 @@ namespace Emby.Server.Implementations.Images
}
}
protected async Task<string> CreateSingleImage(List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType)
protected string CreateSingleImage(List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType)
{
var image = itemsWithImages
.Where(i => i.HasImage(imageType) && i.GetImageInfo(imageType, 0).IsLocalFile && Path.HasExtension(i.GetImagePath(imageType)))

View File

@@ -6,7 +6,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;

View File

@@ -40,7 +40,8 @@ using MediaBrowser.Model.Net;
using SortOrder = MediaBrowser.Model.Entities.SortOrder;
using VideoResolver = MediaBrowser.Naming.Video.VideoResolver;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Tasks;
@@ -313,7 +314,8 @@ namespace Emby.Server.Implementations.Library
{
IncludeItemTypes = new[] { typeof(Season).Name },
Recursive = true,
IndexNumber = 0
IndexNumber = 0,
DtoOptions = new DtoOptions(true)
}).Cast<Season>()
.Where(i => !string.Equals(i.Name, newName, StringComparison.Ordinal))
@@ -342,7 +344,7 @@ namespace Emby.Server.Implementations.Library
}
if (item is IItemByName)
{
if (!(item is MusicArtist) && !(item is Studio))
if (!(item is MusicArtist))
{
return;
}
@@ -862,13 +864,19 @@ namespace Emby.Server.Implementations.Library
// If this returns multiple items it could be tricky figuring out which one is correct.
// In most cases, the newest one will be and the others obsolete but not yet cleaned up
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentNullException("path");
}
var query = new InternalItemsQuery
{
Path = path,
IsFolder = isFolder,
SortBy = new[] { ItemSortBy.DateCreated },
SortOrder = SortOrder.Descending,
Limit = 1
Limit = 1,
DtoOptions = new DtoOptions(true)
};
return GetItemList(query)
@@ -882,7 +890,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task{Person}.</returns>
public Person GetPerson(string name)
{
return CreateItemByName<Person>(Person.GetPath, name);
return CreateItemByName<Person>(Person.GetPath, name, new DtoOptions(true));
}
/// <summary>
@@ -892,7 +900,27 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task{Studio}.</returns>
public Studio GetStudio(string name)
{
return CreateItemByName<Studio>(Studio.GetPath, name);
return CreateItemByName<Studio>(Studio.GetPath, name, new DtoOptions(true));
}
public Guid GetStudioId(string name)
{
return GetItemByNameId<Studio>(Studio.GetPath, name);
}
public Guid GetGenreId(string name)
{
return GetItemByNameId<Genre>(Genre.GetPath, name);
}
public Guid GetMusicGenreId(string name)
{
return GetItemByNameId<MusicGenre>(MusicGenre.GetPath, name);
}
public Guid GetGameGenreId(string name)
{
return GetItemByNameId<GameGenre>(GameGenre.GetPath, name);
}
/// <summary>
@@ -902,7 +930,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task{Genre}.</returns>
public Genre GetGenre(string name)
{
return CreateItemByName<Genre>(Genre.GetPath, name);
return CreateItemByName<Genre>(Genre.GetPath, name, new DtoOptions(true));
}
/// <summary>
@@ -912,7 +940,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task{MusicGenre}.</returns>
public MusicGenre GetMusicGenre(string name)
{
return CreateItemByName<MusicGenre>(MusicGenre.GetPath, name);
return CreateItemByName<MusicGenre>(MusicGenre.GetPath, name, new DtoOptions(true));
}
/// <summary>
@@ -922,7 +950,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task{GameGenre}.</returns>
public GameGenre GetGameGenre(string name)
{
return CreateItemByName<GameGenre>(GameGenre.GetPath, name);
return CreateItemByName<GameGenre>(GameGenre.GetPath, name, new DtoOptions(true));
}
/// <summary>
@@ -940,7 +968,7 @@ namespace Emby.Server.Implementations.Library
var name = value.ToString(CultureInfo.InvariantCulture);
return CreateItemByName<Year>(Year.GetPath, name);
return CreateItemByName<Year>(Year.GetPath, name, new DtoOptions(true));
}
/// <summary>
@@ -950,10 +978,15 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task{Genre}.</returns>
public MusicArtist GetArtist(string name)
{
return CreateItemByName<MusicArtist>(MusicArtist.GetPath, name);
return GetArtist(name, new DtoOptions(true));
}
private T CreateItemByName<T>(Func<string, string> getPathFn, string name)
public MusicArtist GetArtist(string name, DtoOptions options)
{
return CreateItemByName<MusicArtist>(MusicArtist.GetPath, name, options);
}
private T CreateItemByName<T>(Func<string, string> getPathFn, string name, DtoOptions options)
where T : BaseItem, new()
{
if (typeof(T) == typeof(MusicArtist))
@@ -961,7 +994,8 @@ namespace Emby.Server.Implementations.Library
var existing = GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new[] { typeof(T).Name },
Name = name
Name = name,
DtoOptions = options
}).Cast<MusicArtist>()
.OrderBy(i => i.IsAccessedByName ? 1 : 0)
@@ -974,14 +1008,13 @@ namespace Emby.Server.Implementations.Library
}
}
var path = getPathFn(name);
var forceCaseInsensitiveId = ConfigurationManager.Configuration.EnableNormalizedItemByNameIds;
var id = GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId);
var id = GetItemByNameId<T>(getPathFn, name);
var item = GetItemById(id) as T;
if (item == null)
{
var path = getPathFn(name);
item = new T
{
Name = name,
@@ -998,52 +1031,12 @@ namespace Emby.Server.Implementations.Library
return item;
}
public IEnumerable<MusicArtist> GetAlbumArtists(IEnumerable<IHasAlbumArtist> items)
private Guid GetItemByNameId<T>(Func<string, string> getPathFn, string name)
where T : BaseItem, new()
{
var names = items
.SelectMany(i => i.AlbumArtists)
.DistinctNames()
.Select(i =>
{
try
{
var artist = GetArtist(i);
return artist;
}
catch
{
// Already logged at lower levels
return null;
}
})
.Where(i => i != null);
return names;
}
public IEnumerable<MusicArtist> GetArtists(IEnumerable<IHasArtist> items)
{
var names = items
.SelectMany(i => i.AllArtists)
.DistinctNames()
.Select(i =>
{
try
{
var artist = GetArtist(i);
return artist;
}
catch
{
// Already logged at lower levels
return null;
}
})
.Where(i => i != null);
return names;
var path = getPathFn(name);
var forceCaseInsensitiveId = ConfigurationManager.Configuration.EnableNormalizedItemByNameIds;
return GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId);
}
/// <summary>
@@ -1098,12 +1091,6 @@ namespace Emby.Server.Implementations.Library
try
{
await PerformLibraryValidation(progress, cancellationToken).ConfigureAwait(false);
if (!ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey)
{
ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey = true;
ConfigurationManager.SaveConfiguration();
}
}
finally
{
@@ -1558,7 +1545,7 @@ namespace Emby.Server.Implementations.Library
}
}
query.ParentId = null;
query.Parent = null;
}
private void AddUserToQuery(InternalItemsQuery query, User user)

View File

@@ -6,6 +6,7 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
@@ -27,7 +28,8 @@ namespace Emby.Server.Implementations.Library
var items = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new[] { typeof(BoxSet).Name, typeof(Game).Name, typeof(Movie).Name, typeof(Series).Name },
Recursive = true
Recursive = true,
DtoOptions = new DtoOptions(true)
}).OfType<IHasTrailers>().ToList();
@@ -40,7 +42,8 @@ namespace Emby.Server.Implementations.Library
{
IncludeItemTypes = new[] { typeof(Trailer).Name },
TrailerTypes = trailerTypes,
Recursive = true
Recursive = true,
DtoOptions = new DtoOptions(false)
}).ToArray();

View File

@@ -5,6 +5,7 @@ using MediaBrowser.Controller.Playlists;
using System;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Model.Querying;
namespace Emby.Server.Implementations.Library
@@ -18,47 +19,48 @@ namespace Emby.Server.Implementations.Library
_libraryManager = libraryManager;
}
public IEnumerable<Audio> GetInstantMixFromSong(Audio item, User user)
public IEnumerable<Audio> GetInstantMixFromSong(Audio item, User user, DtoOptions dtoOptions)
{
var list = new List<Audio>
{
item
};
return list.Concat(GetInstantMixFromGenres(item.Genres, user));
return list.Concat(GetInstantMixFromGenres(item.Genres, user, dtoOptions));
}
public IEnumerable<Audio> GetInstantMixFromArtist(MusicArtist item, User user)
public IEnumerable<Audio> GetInstantMixFromArtist(MusicArtist item, User user, DtoOptions dtoOptions)
{
return GetInstantMixFromGenres(item.Genres, user);
return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
}
public IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user)
public IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user, DtoOptions dtoOptions)
{
return GetInstantMixFromGenres(item.Genres, user);
return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
}
public IEnumerable<Audio> GetInstantMixFromFolder(Folder item, User user)
public IEnumerable<Audio> GetInstantMixFromFolder(Folder item, User user, DtoOptions dtoOptions)
{
var genres = item
.GetRecursiveChildren(user, new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { typeof(Audio).Name }
IncludeItemTypes = new[] { typeof(Audio).Name },
DtoOptions = dtoOptions
})
.Cast<Audio>()
.SelectMany(i => i.Genres)
.Concat(item.Genres)
.DistinctNames();
return GetInstantMixFromGenres(genres, user);
return GetInstantMixFromGenres(genres, user, dtoOptions);
}
public IEnumerable<Audio> GetInstantMixFromPlaylist(Playlist item, User user)
public IEnumerable<Audio> GetInstantMixFromPlaylist(Playlist item, User user, DtoOptions dtoOptions)
{
return GetInstantMixFromGenres(item.Genres, user);
return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
}
public IEnumerable<Audio> GetInstantMixFromGenres(IEnumerable<string> genres, User user)
public IEnumerable<Audio> GetInstantMixFromGenres(IEnumerable<string> genres, User user, DtoOptions dtoOptions)
{
var genreIds = genres.DistinctNames().Select(i =>
{
@@ -73,10 +75,10 @@ namespace Emby.Server.Implementations.Library
}).Where(i => i != null);
return GetInstantMixFromGenreIds(genreIds, user);
return GetInstantMixFromGenreIds(genreIds, user, dtoOptions);
}
public IEnumerable<Audio> GetInstantMixFromGenreIds(IEnumerable<string> genreIds, User user)
public IEnumerable<Audio> GetInstantMixFromGenreIds(IEnumerable<string> genreIds, User user, DtoOptions dtoOptions)
{
return _libraryManager.GetItemList(new InternalItemsQuery(user)
{
@@ -86,47 +88,49 @@ namespace Emby.Server.Implementations.Library
Limit = 200,
SortBy = new[] { ItemSortBy.Random }
SortBy = new[] { ItemSortBy.Random },
DtoOptions = dtoOptions
}).Cast<Audio>();
}
public IEnumerable<Audio> GetInstantMixFromItem(BaseItem item, User user)
public IEnumerable<Audio> GetInstantMixFromItem(BaseItem item, User user, DtoOptions dtoOptions)
{
var genre = item as MusicGenre;
if (genre != null)
{
return GetInstantMixFromGenreIds(new[] { item.Id.ToString("N") }, user);
return GetInstantMixFromGenreIds(new[] { item.Id.ToString("N") }, user, dtoOptions);
}
var playlist = item as Playlist;
if (playlist != null)
{
return GetInstantMixFromPlaylist(playlist, user);
return GetInstantMixFromPlaylist(playlist, user, dtoOptions);
}
var album = item as MusicAlbum;
if (album != null)
{
return GetInstantMixFromAlbum(album, user);
return GetInstantMixFromAlbum(album, user, dtoOptions);
}
var artist = item as MusicArtist;
if (artist != null)
{
return GetInstantMixFromArtist(artist, user);
return GetInstantMixFromArtist(artist, user, dtoOptions);
}
var song = item as Audio;
if (song != null)
{
return GetInstantMixFromSong(song, user);
return GetInstantMixFromSong(song, user, dtoOptions);
}
var folder = item as Folder;
if (folder != null)
{
return GetInstantMixFromFolder(folder, user);
return GetInstantMixFromFolder(folder, user, dtoOptions);
}
return new Audio[] { };

View File

@@ -5,8 +5,6 @@ using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library

View File

@@ -8,7 +8,7 @@ using MediaBrowser.Naming.Audio;
using System;
using System.Collections.Generic;
using System.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.IO;

View File

@@ -6,7 +6,7 @@ using MediaBrowser.Model.Logging;
using System;
using System.IO;
using System.Linq;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.IO;

View File

@@ -11,7 +11,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;

View File

@@ -5,7 +5,7 @@ using MediaBrowser.Controller.Resolvers;
using System;
using System.IO;
using System.Linq;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;

View File

@@ -10,7 +10,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.IO;

View File

@@ -8,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Extensions;
namespace Emby.Server.Implementations.Library
@@ -175,7 +176,17 @@ namespace Emby.Server.Implementations.Library
IsNews = query.IsNews,
IsSeries = query.IsSeries,
IsSports = query.IsSports,
MediaTypes = query.MediaTypes
MediaTypes = query.MediaTypes,
DtoOptions = new DtoOptions
{
Fields = new List<ItemFields>
{
ItemFields.AirTime,
ItemFields.DateCreated,
ItemFields.ChannelInfo
}
}
});
// Add search hints based on item name

View File

@@ -182,21 +182,21 @@ namespace Emby.Server.Implementations.Library
return GetUserData(userId, item.Id, item.GetUserDataKeys());
}
public async Task<UserItemDataDto> GetUserDataDto(IHasUserData item, User user)
public UserItemDataDto GetUserDataDto(IHasUserData item, User user)
{
var userData = GetUserData(user.Id, item);
var dto = GetUserItemDataDto(userData);
await item.FillUserDataDtoValues(dto, userData, null, user, new List<ItemFields>()).ConfigureAwait(false);
item.FillUserDataDtoValues(dto, userData, null, user, new List<ItemFields>());
return dto;
}
public async Task<UserItemDataDto> GetUserDataDto(IHasUserData item, BaseItemDto itemDto, User user, List<ItemFields> fields)
public UserItemDataDto GetUserDataDto(IHasUserData item, BaseItemDto itemDto, User user, List<ItemFields> fields)
{
var userData = GetUserData(user.Id, item);
var dto = GetUserItemDataDto(userData);
await item.FillUserDataDtoValues(dto, userData, itemDto, user, fields).ConfigureAwait(false);
item.FillUserDataDtoValues(dto, userData, itemDto, user, fields);
return dto;
}

View File

@@ -12,6 +12,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Model.Globalization;
@@ -190,11 +191,11 @@ namespace Emby.Server.Implementations.Library
return _libraryManager.GetShadowView(parent, viewType, sortName, cancellationToken);
}
public List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request)
public List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request, DtoOptions options)
{
var user = _userManager.GetUserById(request.UserId);
var libraryItems = GetItemsForLatestItems(user, request);
var libraryItems = GetItemsForLatestItems(user, request, options);
var list = new List<Tuple<BaseItem, List<BaseItem>>>();
@@ -230,7 +231,7 @@ namespace Emby.Server.Implementations.Library
return list;
}
private IEnumerable<BaseItem> GetItemsForLatestItems(User user, LatestItemsQuery request)
private IEnumerable<BaseItem> GetItemsForLatestItems(User user, LatestItemsQuery request, DtoOptions options)
{
var parentId = request.ParentId;
@@ -289,7 +290,8 @@ namespace Emby.Server.Implementations.Library
IsVirtualItem = false,
Limit = limit * 5,
SourceTypes = parents.Count == 0 ? new[] { SourceType.Library } : new SourceType[] { },
IsPlayed = isPlayed
IsPlayed = isPlayed,
DtoOptions = options
}, parents);
}

View File

@@ -11,7 +11,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;

View File

@@ -3,9 +3,10 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Logging;
@@ -29,7 +30,35 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return targetFile;
}
public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
public Task Record(IDirectStreamProvider directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
{
if (directStreamProvider != null)
{
return RecordFromDirectStreamProvider(directStreamProvider, targetFile, duration, onStarted, cancellationToken);
}
return RecordFromMediaSource(mediaSource, targetFile, duration, onStarted, cancellationToken);
}
private async Task RecordFromDirectStreamProvider(IDirectStreamProvider directStreamProvider, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
{
using (var output = _fileSystem.GetFileStream(targetFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
onStarted();
_logger.Info("Copying recording stream to file {0}", targetFile);
// The media source if infinite so we need to handle stopping ourselves
var durationToken = new CancellationTokenSource(duration);
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
await directStreamProvider.CopyToAsync(output, cancellationToken).ConfigureAwait(false);
}
_logger.Info("Recording completed to file {0}", targetFile);
}
private async Task RecordFromMediaSource(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
{
var httpRequestOptions = new HttpRequestOptions
{

View File

@@ -28,8 +28,9 @@ using System.Xml;
using MediaBrowser.Model.IO;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.IO;
@@ -1232,7 +1233,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
RequiresClosing = false,
Protocol = MediaBrowser.Model.MediaInfo.MediaProtocol.Http,
BufferMs = 0,
IgnoreDts = true
IgnoreDts = true,
IgnoreIndex = true
};
var isAudio = false;
@@ -1517,7 +1519,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
EnforceKeepUpTo(timer, seriesPath);
};
await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken).ConfigureAwait(false);
await recorder.Record(liveStreamInfo.Item1 as IDirectStreamProvider, mediaStreamInfo, recordPath, duration, onStarted, cancellationToken).ConfigureAwait(false);
recordingStatus = RecordingStatus.Completed;
_logger.Info("Recording completed: {0}", recordPath);
@@ -1634,15 +1636,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return;
}
var episodesToDelete = (await librarySeries.GetItems(new InternalItemsQuery
var episodesToDelete = (librarySeries.GetItems(new InternalItemsQuery
{
SortBy = new[] { ItemSortBy.DateCreated },
SortOrder = SortOrder.Descending,
IsVirtualItem = false,
IsFolder = false,
Recursive = true
Recursive = true,
DtoOptions = new DtoOptions(true)
}).ConfigureAwait(false))
}))
.Items
.Where(i => i.LocationType == LocationType.FileSystem && _fileSystem.FileExists(i.Path))
.Skip(seriesTimer.KeepUpTo - 1)
@@ -1759,20 +1762,30 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var config = GetConfiguration();
if (config.EnableRecordingEncoding)
{
var regInfo = await _liveTvManager.GetRegistrationInfo("embytvrecordingconversion").ConfigureAwait(false);
var regInfo = await _liveTvManager.GetRegistrationInfo("embytvrecordingconversion").ConfigureAwait(false);
if (regInfo.IsValid)
if (regInfo.IsValid)
{
if (config.EnableRecordingEncoding)
{
return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, config, _httpClient, _processFactory, _config);
}
return new DirectRecorder(_logger, _httpClient, _fileSystem);
//var options = new LiveTvOptions
//{
// EnableOriginalAudioWithEncodedRecordings = true,
// RecordedVideoCodec = "copy",
// RecordingEncodingFormat = "ts"
//};
//return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, options, _httpClient, _processFactory, _config);
}
return new DirectRecorder(_logger, _httpClient, _fileSystem);
throw new InvalidOperationException("Emby DVR Requires an active Emby Premiere subscription.");
}
private async void OnSuccessfulRecording(TimerInfo timer, string path)
private void OnSuccessfulRecording(TimerInfo timer, string path)
{
//if (timer.IsProgramSeries && GetConfiguration().EnableAutoOrganize)
//{
@@ -1967,7 +1980,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
Limit = 1,
ExternalId = timer.ProgramId
ExternalId = timer.ProgramId,
DtoOptions = new DtoOptions(true)
}).FirstOrDefault() as LiveTvProgram;
@@ -2501,16 +2515,17 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (program.EpisodeNumber.HasValue && program.SeasonNumber.HasValue)
{
var result = _libraryManager.GetItemsResult(new InternalItemsQuery
var result = _libraryManager.GetItemIds(new InternalItemsQuery
{
IncludeItemTypes = new[] { typeof(Episode).Name },
ParentIndexNumber = program.SeasonNumber.Value,
IndexNumber = program.EpisodeNumber.Value,
AncestorIds = seriesIds,
IsVirtualItem = false
IsVirtualItem = false,
Limit = 1
});
if (result.TotalRecordCount > 0)
if (result.Count > 0)
{
return true;
}

View File

@@ -8,7 +8,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
@@ -21,6 +21,7 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Library;
namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
@@ -64,6 +65,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
return "mkv";
}
if (string.Equals(format, "ts", StringComparison.OrdinalIgnoreCase))
{
return "ts";
}
return "mp4";
}
@@ -90,7 +95,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return Path.ChangeExtension(targetFile, "." + extension);
}
public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
public async Task Record(IDirectStreamProvider directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
{
//var durationToken = new CancellationTokenSource(duration);
//cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
@@ -177,6 +182,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
videoArgs = "-codec:v:0 copy";
}
videoArgs += " -fflags +genpts";
var durationParam = " -t " + _mediaEncoder.GetTimeParameter(duration.Ticks);
var flags = new List<string>();
@@ -188,28 +195,37 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
flags.Add("+ignidx");
}
if (mediaSource.GenPtsInput)
{
flags.Add("+genpts");
}
var inputModifiers = "-async 1 -vsync -1";
var inputModifier = "-async 1 -vsync -1";
if (flags.Count > 0)
{
inputModifiers += " -fflags " + string.Join("", flags.ToArray());
inputModifier += " -fflags " + string.Join("", flags.ToArray());
}
if (!string.IsNullOrWhiteSpace(GetEncodingOptions().HardwareAccelerationType))
{
inputModifiers += " -hwaccel auto";
inputModifier += " -hwaccel auto";
}
if (mediaSource.ReadAtNativeFramerate)
{
inputModifiers += " -re";
inputModifier += " -re";
}
if (mediaSource.RequiresLooping)
{
inputModifier += " -stream_loop -1";
}
var analyzeDurationSeconds = 5;
var analyzeDuration = " -analyzeduration " +
(analyzeDurationSeconds * 1000000).ToString(CultureInfo.InvariantCulture);
inputModifiers += analyzeDuration;
inputModifier += analyzeDuration;
var subtitleArgs = CopySubtitles ? " -codec:s copy" : " -sn";
@@ -228,7 +244,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
durationParam,
outputParam);
return inputModifiers + " " + commandLineArgs;
return inputModifier + " " + commandLineArgs;
}
private string GetAudioArgs(MediaSourceInfo mediaSource)

View File

@@ -1,6 +1,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
namespace Emby.Server.Implementations.LiveTv.EmbyTV
@@ -10,13 +11,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
/// <summary>
/// Records the specified media source.
/// </summary>
/// <param name="mediaSource">The media source.</param>
/// <param name="targetFile">The target file.</param>
/// <param name="duration">The duration.</param>
/// <param name="onStarted">The on started.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken);
Task Record(IDirectStreamProvider directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken);
string GetOutputPath(MediaSourceInfo mediaSource, string targetFile);
}

View File

@@ -4,7 +4,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;

View File

@@ -105,7 +105,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
else
{
name += " " + info.StartDate.ToString("yyyy-MM-dd") + " " + info.Id;
name += " " + info.StartDate.ToString("yyyy-MM-dd");
}
return name;

View File

@@ -2,8 +2,6 @@
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using System;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.LiveTv.EmbyTV

View File

@@ -8,7 +8,7 @@ using System.Collections.Concurrent;
using System.Globalization;
using System.Linq;
using System.Threading;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;

View File

@@ -167,10 +167,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
var programEntry = programDict[schedule.programID];
var data = images[imageIndex].data ?? new List<ScheduleDirect.ImageData>();
data = data.OrderByDescending(GetSizeOrder).ToList();
var allImages = (images[imageIndex].data ?? new List<ScheduleDirect.ImageData>()).OrderByDescending(GetSizeOrder).ToList();
var imagesWithText = allImages.Where(i => string.Equals(i.text, "yes", StringComparison.OrdinalIgnoreCase)).ToList();
programEntry.primaryImage = GetProgramImage(ApiUrl, imagesWithText, "Logo", true, 600) ??
GetProgramImage(ApiUrl, allImages, "Logo", true, 600);
programEntry.primaryImage = GetProgramImage(ApiUrl, data, "Logo", true, 600);
//programEntry.thumbImage = GetProgramImage(ApiUrl, data, "Iconic", false);
//programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
// GetProgramImage(ApiUrl, data, "Banner-L1", false) ??

View File

@@ -149,7 +149,11 @@ namespace Emby.Server.Implementations.LiveTv
IncludeItemTypes = new string[] { typeof(Series).Name },
Name = seriesName,
Limit = 1,
ImageTypes = new ImageType[] { ImageType.Thumb }
ImageTypes = new ImageType[] { ImageType.Thumb },
DtoOptions = new DtoOptions
{
Fields = new List<MediaBrowser.Model.Querying.ItemFields>()
}
}).FirstOrDefault();
@@ -191,7 +195,11 @@ namespace Emby.Server.Implementations.LiveTv
IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
ExternalSeriesId = programSeriesId,
Limit = 1,
ImageTypes = new ImageType[] { ImageType.Primary }
ImageTypes = new ImageType[] { ImageType.Primary },
DtoOptions = new DtoOptions
{
Fields = new List<MediaBrowser.Model.Querying.ItemFields>()
}
}).FirstOrDefault();
@@ -239,7 +247,11 @@ namespace Emby.Server.Implementations.LiveTv
IncludeItemTypes = new string[] { typeof(Series).Name },
Name = seriesName,
Limit = 1,
ImageTypes = new ImageType[] { ImageType.Thumb }
ImageTypes = new ImageType[] { ImageType.Thumb },
DtoOptions = new DtoOptions
{
Fields = new List<MediaBrowser.Model.Querying.ItemFields>()
}
}).FirstOrDefault();
@@ -281,14 +293,22 @@ namespace Emby.Server.Implementations.LiveTv
IncludeItemTypes = new string[] { typeof(Series).Name },
Name = seriesName,
Limit = 1,
ImageTypes = new ImageType[] { ImageType.Primary }
ImageTypes = new ImageType[] { ImageType.Primary },
DtoOptions = new DtoOptions
{
Fields = new List<MediaBrowser.Model.Querying.ItemFields>()
}
}).FirstOrDefault() ?? _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
ExternalSeriesId = programSeriesId,
Limit = 1,
ImageTypes = new ImageType[] { ImageType.Primary }
ImageTypes = new ImageType[] { ImageType.Primary },
DtoOptions = new DtoOptions
{
Fields = new List<MediaBrowser.Model.Querying.ItemFields>()
}
}).FirstOrDefault();

View File

@@ -26,7 +26,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Security;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
@@ -173,7 +173,7 @@ namespace Emby.Server.Implementations.LiveTv
}
}
public async Task<QueryResult<LiveTvChannel>> GetInternalChannels(LiveTvChannelQuery query, CancellationToken cancellationToken)
public async Task<QueryResult<LiveTvChannel>> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken)
{
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
@@ -192,7 +192,8 @@ namespace Emby.Server.Implementations.LiveTv
IsFavorite = query.IsFavorite,
IsLiked = query.IsLiked,
StartIndex = query.StartIndex,
Limit = query.Limit
Limit = query.Limit,
DtoOptions = dtoOptions
};
internalQuery.OrderBy.AddRange(query.SortBy.Select(i => new Tuple<string, SortOrder>(i, query.SortOrder ?? SortOrder.Ascending)));
@@ -249,7 +250,7 @@ namespace Emby.Server.Implementations.LiveTv
{
Id = id
}, cancellationToken).ConfigureAwait(false);
}, new DtoOptions(), cancellationToken).ConfigureAwait(false);
return result.Items.FirstOrDefault();
}
@@ -477,7 +478,8 @@ namespace Emby.Server.Implementations.LiveTv
if (!(service is EmbyTV.EmbyTV))
{
// We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
// We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
//mediaSource.SupportsDirectPlay = false;
mediaSource.SupportsDirectStream = false;
mediaSource.SupportsTranscoding = true;
foreach (var stream in mediaSource.MediaStreams)
@@ -864,13 +866,6 @@ namespace Emby.Server.Implementations.LiveTv
return item.Id;
}
private string GetExternalSeriesIdLegacy(BaseItem item)
{
return item.GetProviderId("ProviderExternalSeriesId");
}
public async Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
{
var program = GetInternalProgram(id);
@@ -881,11 +876,6 @@ namespace Emby.Server.Implementations.LiveTv
var externalSeriesId = program.ExternalSeriesId;
if (string.IsNullOrWhiteSpace(externalSeriesId))
{
externalSeriesId = GetExternalSeriesIdLegacy(program);
}
list.Add(new Tuple<BaseItemDto, string, string, string>(dto, program.ServiceName, GetItemExternalId(program), externalSeriesId));
await AddRecordingInfo(list, cancellationToken).ConfigureAwait(false);
@@ -905,6 +895,8 @@ namespace Emby.Server.Implementations.LiveTv
query.SortBy = new[] { ItemSortBy.StartDate };
}
RemoveFields(options);
var internalQuery = new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
@@ -964,8 +956,6 @@ namespace Emby.Server.Implementations.LiveTv
var queryResult = _libraryManager.QueryItems(internalQuery);
RemoveFields(options);
var returnArray = (await _dtoService.GetBaseItemDtos(queryResult.Items, options, user).ConfigureAwait(false)).ToArray();
var result = new QueryResult<BaseItemDto>
@@ -1044,12 +1034,12 @@ namespace Emby.Server.Implementations.LiveTv
public async Task<QueryResult<BaseItemDto>> GetRecommendedPrograms(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken)
{
RemoveFields(options);
var internalResult = await GetRecommendedProgramsInternal(query, options, cancellationToken).ConfigureAwait(false);
var user = _userManager.GetUserById(query.UserId);
RemoveFields(options);
var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user).ConfigureAwait(false)).ToArray();
var result = new QueryResult<BaseItemDto>
@@ -1332,7 +1322,8 @@ namespace Emby.Server.Implementations.LiveTv
{
IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
ChannelIds = new string[] { currentChannel.Id.ToString("N") }
ChannelIds = new string[] { currentChannel.Id.ToString("N") },
DtoOptions = new DtoOptions(true)
}).Cast<LiveTvProgram>().ToDictionary(i => i.Id);
@@ -1435,7 +1426,8 @@ namespace Emby.Server.Implementations.LiveTv
{
var list = _itemRepo.GetItemIdsList(new InternalItemsQuery
{
IncludeItemTypes = validTypes
IncludeItemTypes = validTypes,
DtoOptions = new DtoOptions(false)
}).ToList();
@@ -1662,6 +1654,8 @@ namespace Emby.Server.Implementations.LiveTv
includeItemTypes.Add(typeof(Series).Name);
RemoveFields(options);
var internalResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{
Recursive = true,
@@ -1671,11 +1665,10 @@ namespace Emby.Server.Implementations.LiveTv
SortOrder = SortOrder.Descending,
EnableTotalRecordCount = query.EnableTotalRecordCount,
IncludeItemTypes = includeItemTypes.ToArray(),
ExcludeItemTypes = excludeItemTypes.ToArray()
ExcludeItemTypes = excludeItemTypes.ToArray(),
DtoOptions = options
});
RemoveFields(options);
var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user).ConfigureAwait(false)).ToArray();
return new QueryResult<BaseItemDto>
@@ -1685,7 +1678,7 @@ namespace Emby.Server.Implementations.LiveTv
};
}
public async Task<QueryResult<BaseItem>> GetInternalRecordings(RecordingQuery query, CancellationToken cancellationToken)
public async Task<QueryResult<BaseItem>> GetInternalRecordings(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken)
{
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
if (user != null && !IsLiveTvEnabled(user))
@@ -1695,14 +1688,15 @@ namespace Emby.Server.Implementations.LiveTv
if (_services.Count == 1 && !(query.IsInProgress ?? false) && (!query.IsLibraryItem.HasValue || query.IsLibraryItem.Value))
{
return GetEmbyRecordings(query, new DtoOptions(), user);
return GetEmbyRecordings(query, options, user);
}
await RefreshRecordings(cancellationToken).ConfigureAwait(false);
var internalQuery = new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name }
IncludeItemTypes = new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name },
DtoOptions = options
};
if (!string.IsNullOrEmpty(query.ChannelId))
@@ -1871,11 +1865,6 @@ namespace Emby.Server.Implementations.LiveTv
var externalSeriesId = program.ExternalSeriesId;
if (string.IsNullOrWhiteSpace(externalSeriesId))
{
externalSeriesId = GetExternalSeriesIdLegacy(program);
}
programTuples.Add(new Tuple<BaseItemDto, string, string, string>(dto, serviceName, GetItemExternalId(program), externalSeriesId));
}
@@ -1952,10 +1941,10 @@ namespace Emby.Server.Implementations.LiveTv
{
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
var internalResult = await GetInternalRecordings(query, cancellationToken).ConfigureAwait(false);
RemoveFields(options);
var internalResult = await GetInternalRecordings(query, options, cancellationToken).ConfigureAwait(false);
var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user).ConfigureAwait(false)).ToArray();
return new QueryResult<BaseItemDto>
@@ -2298,7 +2287,8 @@ namespace Emby.Server.Implementations.LiveTv
MinEndDate = now,
Limit = channelIds.Length,
SortBy = new[] { "StartDate" },
TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Result.Id.ToString("N") }
TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Result.Id.ToString("N") },
DtoOptions = options
}).ToList() : new List<BaseItem>();
@@ -2600,7 +2590,7 @@ namespace Emby.Server.Implementations.LiveTv
{
UserId = query.UserId
}, cancellationToken).ConfigureAwait(false);
}, new DtoOptions(), cancellationToken).ConfigureAwait(false);
var recordings = recordingResult.Items.OfType<ILiveTvRecording>().ToList();

View File

@@ -421,7 +421,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
SupportsDirectStream = true,
SupportsTranscoding = true,
IsInfiniteStream = true,
IgnoreDts = true
IgnoreDts = true,
//IgnoreIndex = true,
ReadAtNativeFramerate = true
};
mediaSource.InferTotalBitrate();
@@ -505,12 +507,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
if (hdhomerunChannel != null && hdhomerunChannel.IsLegacyTuner)
{
return new HdHomerunUdpStream(mediaSource, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Url), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager);
return new HdHomerunUdpStream(mediaSource, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Url), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager, _environment);
}
// The UDP method is not working reliably on OSX, and on BSD it hasn't been tested yet
var enableHttpStream = _environment.OperatingSystem == OperatingSystem.OSX ||
_environment.OperatingSystem == OperatingSystem.BSD;
var enableHttpStream = _environment.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX
|| _environment.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.BSD;
enableHttpStream = true;
if (enableHttpStream)
{
@@ -519,17 +521,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var httpUrl = GetApiUrl(info, true) + "/auto/v" + hdhrId;
// If raw was used, the tuner doesn't support params
if (!string.IsNullOrWhiteSpace(profile)
&& !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase))
if (!string.IsNullOrWhiteSpace(profile) && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase))
{
httpUrl += "?transcode=" + profile;
}
mediaSource.Path = httpUrl;
return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost);
return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _environment);
}
return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager);
return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number, profile), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager, _environment);
}
public async Task Validate(TunerHostInfo info)
@@ -596,10 +597,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
// Need a way to set the Receive timeout on the socket otherwise this might never timeout?
try
{
await udpClient.SendAsync(discBytes, discBytes.Length, new IpEndPointInfo(new IpAddressInfo("255.255.255.255", IpAddressFamily.InterNetwork), 65001), cancellationToken);
await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IpEndPointInfo(new IpAddressInfo("255.255.255.255", IpAddressFamily.InterNetwork), 65001), cancellationToken);
var receiveBuffer = new byte[8192];
while (!cancellationToken.IsCancellationRequested)
{
var response = await udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false);
var response = await udpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
var deviceIp = response.RemoteEndPoint.IpAddress.Address;
// check to make sure we have enough bytes received to be a valid message and make sure the 2nd byte is the discover reply byte

View File

@@ -2,6 +2,7 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
@@ -10,6 +11,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.System;
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
@@ -17,24 +19,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem;
private readonly IServerApplicationPaths _appPaths;
private readonly IServerApplicationHost _appHost;
private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource();
private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
private readonly MulticastStream _multicastStream;
public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost)
: base(mediaSource)
private readonly string _tempFilePath;
public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, IEnvironmentInfo environment)
: base(mediaSource, environment, fileSystem)
{
_fileSystem = fileSystem;
_httpClient = httpClient;
_logger = logger;
_appPaths = appPaths;
_appHost = appHost;
OriginalStreamId = originalStreamId;
_multicastStream = new MulticastStream(_logger);
_tempFilePath = Path.Combine(appPaths.TranscodingTempPath, UniqueId + ".ts");
}
protected override async Task OpenInternal(CancellationToken openCancellationToken)
@@ -57,9 +57,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
OpenedMediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
OpenedMediaSource.Protocol = MediaProtocol.Http;
OpenedMediaSource.SupportsDirectPlay = false;
OpenedMediaSource.SupportsDirectStream = true;
OpenedMediaSource.SupportsTranscoding = true;
//OpenedMediaSource.SupportsDirectPlay = false;
//OpenedMediaSource.SupportsDirectStream = true;
//OpenedMediaSource.SupportsTranscoding = true;
await taskCompletionSource.Task.ConfigureAwait(false);
@@ -74,9 +74,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
return _liveStreamTaskCompletionSource.Task;
}
private async Task StartStreaming(string url, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
private Task StartStreaming(string url, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
{
await Task.Run(async () =>
return Task.Run(async () =>
{
var isFirstAttempt = true;
@@ -101,13 +101,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
_logger.Info("Beginning multicastStream.CopyUntilCancelled");
Action onStarted = null;
if (isFirstAttempt)
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(_tempFilePath));
using (var fileStream = FileSystem.GetFileStream(_tempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.Asynchronous))
{
onStarted = () => openTaskCompletionSource.TrySetResult(true);
}
ResolveAfterDelay(3000, openTaskCompletionSource);
await _multicastStream.CopyUntilCancelled(response.Content, onStarted, cancellationToken).ConfigureAwait(false);
//await response.Content.CopyToAsync(fileStream, 81920, cancellationToken).ConfigureAwait(false);
await AsyncStreamCopier.CopyStream(response.Content, fileStream, 81920, 4, cancellationToken).ConfigureAwait(false);
}
}
}
}
@@ -131,13 +132,60 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
_liveStreamTaskCompletionSource.TrySetResult(true);
await DeleteTempFile(_tempFilePath).ConfigureAwait(false);
});
}
}).ConfigureAwait(false);
private void ResolveAfterDelay(int delayMs, TaskCompletionSource<bool> openTaskCompletionSource)
{
Task.Run(async () =>
{
await Task.Delay(delayMs).ConfigureAwait(false);
openTaskCompletionSource.TrySetResult(true);
});
}
public Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
{
return _multicastStream.CopyToAsync(stream);
return CopyFileTo(_tempFilePath, false, stream, cancellationToken);
}
protected async Task CopyFileTo(string path, bool allowEndOfFile, Stream outputStream, CancellationToken cancellationToken)
{
var eofCount = 0;
long startPosition = -25000;
if (startPosition < 0)
{
var length = FileSystem.GetFileInfo(path).Length;
startPosition = Math.Max(length - startPosition, 0);
}
using (var inputStream = GetInputStream(path, startPosition, true))
{
if (startPosition > 0)
{
inputStream.Position = startPosition;
}
while (eofCount < 20 || !allowEndOfFile)
{
var bytesRead = await AsyncStreamCopier.CopyStream(inputStream, outputStream, 81920, 4, cancellationToken).ConfigureAwait(false);
//var position = fs.Position;
//_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
if (bytesRead == 0)
{
eofCount++;
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
}
else
{
eofCount = 0;
}
}
}
}
}
}

View File

@@ -46,10 +46,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public class HdHomerunChannelCommands : IHdHomerunChannelCommands
{
private string _channel;
private string _profile;
public HdHomerunChannelCommands(string channel)
public HdHomerunChannelCommands(string channel, string profile)
{
_channel = channel;
_profile = profile;
}
public IEnumerable<Tuple<string, string>> GetCommands()
@@ -57,7 +59,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var commands = new List<Tuple<string, string>>();
if (!String.IsNullOrEmpty(_channel))
commands.Add(Tuple.Create("vchannel", _channel));
{
if (!string.IsNullOrWhiteSpace(_profile) && !string.Equals(_profile, "native", StringComparison.OrdinalIgnoreCase))
{
commands.Add(Tuple.Create("vchannel", String.Format("{0} transcode={1}", _channel, _profile)));
}
else
{
commands.Add(Tuple.Create("vchannel", _channel));
}
}
return commands;
}
@@ -103,8 +114,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var ipEndPoint = new IpEndPointInfo(remoteIp, HdHomeRunPort);
var lockkeyMsg = CreateGetMessage(tuner, "lockkey");
await socket.SendAsync(lockkeyMsg, lockkeyMsg.Length, ipEndPoint, cancellationToken);
var response = await socket.ReceiveAsync(cancellationToken).ConfigureAwait(false);
await socket.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken);
var receiveBuffer = new byte[8192];
var response = await socket.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
string returnVal;
ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal);
@@ -117,6 +131,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
using (var tcpClient = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort))
{
var receiveBuffer = new byte[8192];
if (!_lockkey.HasValue)
{
var rand = new Random();
@@ -133,8 +149,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
_activeTuner = i;
var lockKeyString = String.Format("{0:d}", _lockkey.Value);
var lockkeyMsg = CreateSetMessage(i, "lockkey", lockKeyString, null);
await tcpClient.SendAsync(lockkeyMsg, lockkeyMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
var response = await tcpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false);
await tcpClient.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
string returnVal;
// parse response to make sure it worked
if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
@@ -144,8 +160,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
foreach(Tuple<string,string> command in commandList)
{
var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, _lockkey.Value);
await tcpClient.SendAsync(channelMsg, channelMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
response = await tcpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false);
await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
// parse response to make sure it worked
if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
{
@@ -158,8 +174,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var targetValue = String.Format("rtp://{0}:{1}", localIp, localPort);
var targetMsg = CreateSetMessage(i, "target", targetValue, _lockkey.Value);
await tcpClient.SendAsync(targetMsg, targetMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
response = await tcpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false);
await tcpClient.SendToAsync(targetMsg, 0, targetMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
// parse response to make sure it worked
if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
{
@@ -180,11 +196,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
using (var tcpClient = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort))
{
var commandList = commands.GetCommands();
var receiveBuffer = new byte[8192];
foreach (Tuple<string, string> command in commandList)
{
var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey.Value);
await tcpClient.SendAsync(channelMsg, channelMsg.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), cancellationToken).ConfigureAwait(false);
var response = await tcpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false);
await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), cancellationToken).ConfigureAwait(false);
var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
// parse response to make sure it worked
string returnVal;
if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
@@ -209,12 +227,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private async Task ReleaseLockkey(ISocket tcpClient)
{
var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", _lockkey);
await tcpClient.SendAsync(releaseTarget, releaseTarget.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), CancellationToken.None).ConfigureAwait(false);
await tcpClient.ReceiveAsync(CancellationToken.None).ConfigureAwait(false);
await tcpClient.SendToAsync(releaseTarget, 0, releaseTarget.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), CancellationToken.None).ConfigureAwait(false);
var receiveBuffer = new byte[8192];
await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false);
var releaseKeyMsg = CreateSetMessage(_activeTuner, "lockkey", "none", _lockkey);
_lockkey = null;
await tcpClient.SendAsync(releaseKeyMsg, releaseKeyMsg.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), CancellationToken.None).ConfigureAwait(false);
await tcpClient.ReceiveAsync(CancellationToken.None).ConfigureAwait(false);
await tcpClient.SendToAsync(releaseKeyMsg, 0, releaseKeyMsg.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), CancellationToken.None).ConfigureAwait(false);
await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, CancellationToken.None).ConfigureAwait(false);
}
private static byte[] CreateGetMessage(int tuner, string name)

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;
@@ -14,39 +15,35 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.System;
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
public class HdHomerunUdpStream : LiveStream, IDirectStreamProvider
{
private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem;
private readonly IServerApplicationPaths _appPaths;
private readonly IServerApplicationHost _appHost;
private readonly ISocketFactory _socketFactory;
private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource();
private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
private readonly MulticastStream _multicastStream;
private readonly IHdHomerunChannelCommands _channelCommands;
private readonly int _numTuners;
private readonly INetworkManager _networkManager;
public HdHomerunUdpStream(MediaSourceInfo mediaSource, string originalStreamId, IHdHomerunChannelCommands channelCommands, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager)
: base(mediaSource)
private readonly string _tempFilePath;
public HdHomerunUdpStream(MediaSourceInfo mediaSource, string originalStreamId, IHdHomerunChannelCommands channelCommands, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager, IEnvironmentInfo environment)
: base(mediaSource, environment, fileSystem)
{
_fileSystem = fileSystem;
_httpClient = httpClient;
_logger = logger;
_appPaths = appPaths;
_appHost = appHost;
_socketFactory = socketFactory;
_networkManager = networkManager;
OriginalStreamId = originalStreamId;
_multicastStream = new MulticastStream(_logger);
_channelCommands = channelCommands;
_numTuners = numTuners;
_tempFilePath = Path.Combine(appPaths.TranscodingTempPath, UniqueId + ".ts");
}
protected override async Task OpenInternal(CancellationToken openCancellationToken)
@@ -70,9 +67,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
OpenedMediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
OpenedMediaSource.Protocol = MediaProtocol.Http;
OpenedMediaSource.SupportsDirectPlay = false;
OpenedMediaSource.SupportsDirectStream = true;
OpenedMediaSource.SupportsTranscoding = true;
//OpenedMediaSource.SupportsDirectPlay = false;
//OpenedMediaSource.SupportsDirectStream = true;
//OpenedMediaSource.SupportsTranscoding = true;
await taskCompletionSource.Task.ConfigureAwait(false);
@@ -87,9 +84,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
return _liveStreamTaskCompletionSource.Task;
}
private async Task StartStreaming(string remoteIp, int localPort, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
private Task StartStreaming(string remoteIp, int localPort, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
{
await Task.Run(async () =>
return Task.Run(async () =>
{
var isFirstAttempt = true;
using (var udpClient = _socketFactory.CreateUdpSocket(localPort))
@@ -124,13 +121,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
if (!cancellationToken.IsCancellationRequested)
{
Action onStarted = null;
if (isFirstAttempt)
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(_tempFilePath));
using (var fileStream = FileSystem.GetFileStream(_tempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.Asynchronous))
{
onStarted = () => openTaskCompletionSource.TrySetResult(true);
await CopyTo(udpClient, fileStream, openTaskCompletionSource, cancellationToken).ConfigureAwait(false);
}
await _multicastStream.CopyUntilCancelled(new UdpClientStream(udpClient), onStarted, cancellationToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException ex)
@@ -159,135 +154,109 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
}).ConfigureAwait(false);
await DeleteTempFile(_tempFilePath).ConfigureAwait(false);
});
}
private void Resolve(TaskCompletionSource<bool> openTaskCompletionSource)
{
Task.Run(() =>
{
openTaskCompletionSource.TrySetResult(true);
});
}
public Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
{
return _multicastStream.CopyToAsync(stream);
return CopyFileTo(_tempFilePath, false, stream, cancellationToken);
}
protected async Task CopyFileTo(string path, bool allowEndOfFile, Stream outputStream, CancellationToken cancellationToken)
{
var eofCount = 0;
long startPosition = -25000;
if (startPosition < 0)
{
var length = FileSystem.GetFileInfo(path).Length;
startPosition = Math.Max(length - startPosition, 0);
}
using (var inputStream = GetInputStream(path, startPosition, true))
{
if (startPosition > 0)
{
inputStream.Position = startPosition;
}
while (eofCount < 20 || !allowEndOfFile)
{
var bytesRead = await AsyncStreamCopier.CopyStream(inputStream, outputStream, 81920, 4, cancellationToken).ConfigureAwait(false);
//var position = fs.Position;
//_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
if (bytesRead == 0)
{
eofCount++;
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
}
else
{
eofCount = 0;
}
}
}
}
}
// This handles the ReadAsync function only of a Stream object
// This is used to wrap a UDP socket into a stream for MulticastStream which only uses ReadAsync
public class UdpClientStream : Stream
{
private static int RtpHeaderBytes = 12;
private static int PacketSize = 1316;
private readonly ISocket _udpClient;
bool disposed;
public UdpClientStream(ISocket udpClient) : base()
private Task CopyTo(ISocket udpClient, Stream outputStream, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
{
_udpClient = udpClient;
return CopyStream(_socketFactory.CreateNetworkStream(udpClient, false), outputStream, 81920, 4, openTaskCompletionSource, cancellationToken);
}
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
private Task CopyStream(Stream source, Stream target, int bufferSize, int bufferCount, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
var copier = new AsyncStreamCopier(source, target, 0, cancellationToken, false, bufferSize, bufferCount);
copier.IndividualReadOffset = RtpHeaderBytes;
if (offset + count < 0)
throw new ArgumentOutOfRangeException("offset + count must not be negative", "offset+count");
var taskCompletion = new TaskCompletionSource<long>();
if (offset + count > buffer.Length)
throw new ArgumentException("offset + count must not be greater than the length of buffer", "offset+count");
copier.TaskCompletionSource = taskCompletion;
if (disposed)
throw new ObjectDisposedException(typeof(UdpClientStream).ToString());
var result = copier.BeginCopy(StreamCopyCallback, copier);
// This will always receive a 1328 packet size (PacketSize + RtpHeaderSize)
// The RTP header will be stripped so see how many reads we need to make to fill the buffer.
int numReads = count / PacketSize;
int totalBytesRead = 0;
for (int i = 0; i < numReads; ++i)
if (openTaskCompletionSource != null)
{
var data = await _udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false);
var bytesRead = data.ReceivedBytes - RtpHeaderBytes;
// remove rtp header
Buffer.BlockCopy(data.Buffer, RtpHeaderBytes, buffer, offset, bytesRead);
offset += bytesRead;
totalBytesRead += bytesRead;
Resolve(openTaskCompletionSource);
openTaskCompletionSource = null;
}
return totalBytesRead;
}
protected override void Dispose(bool disposing)
{
disposed = true;
}
public override bool CanRead
{
get
if (result.CompletedSynchronously)
{
throw new NotImplementedException();
StreamCopyCallback(result);
}
cancellationToken.Register(() => taskCompletion.TrySetCanceled());
return taskCompletion.Task;
}
private void StreamCopyCallback(IAsyncResult result)
{
var copier = (AsyncStreamCopier)result.AsyncState;
var taskCompletion = copier.TaskCompletionSource;
try
{
copier.EndCopy(result);
taskCompletion.TrySetResult(0);
}
catch (Exception ex)
{
taskCompletion.TrySetException(ex);
}
}
public override bool CanSeek
{
get
{
throw new NotImplementedException();
}
}
public override bool CanWrite
{
get
{
throw new NotImplementedException();
}
}
public override long Length
{
get
{
throw new NotImplementedException();
}
}
public override long Position
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override void Flush()
{
throw new NotImplementedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
}
}

View File

@@ -11,7 +11,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
@@ -19,6 +19,7 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.System;
namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
@@ -27,13 +28,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
private readonly IFileSystem _fileSystem;
private readonly IHttpClient _httpClient;
private readonly IServerApplicationHost _appHost;
private readonly IEnvironmentInfo _environment;
public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost)
public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, IEnvironmentInfo environment)
: base(config, logger, jsonSerializer, mediaEncoder)
{
_fileSystem = fileSystem;
_httpClient = httpClient;
_appHost = appHost;
_environment = environment;
}
public override string Type
@@ -73,7 +76,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
var sources = await GetChannelStreamMediaSources(info, channelId, cancellationToken).ConfigureAwait(false);
var liveStream = new LiveStream(sources.First());
var liveStream = new LiveStream(sources.First(), _environment, _fileSystem);
return liveStream;
}

View File

@@ -8,7 +8,6 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.IO;

View File

@@ -15,7 +15,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
private readonly ConcurrentDictionary<Guid,QueueStream> _outputStreams = new ConcurrentDictionary<Guid, QueueStream>();
private const int BufferSize = 81920;
private CancellationToken _cancellationToken;
private readonly ILogger _logger;
public MulticastStream(ILogger logger)
@@ -25,8 +24,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public async Task CopyUntilCancelled(Stream source, Action onStarted, CancellationToken cancellationToken)
{
_cancellationToken = cancellationToken;
byte[] buffer = new byte[BufferSize];
if (source == null)
@@ -72,59 +69,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
}
private static int RtpHeaderBytes = 12;
public async Task CopyUntilCancelled(ISocket udpClient, Action onStarted, CancellationToken cancellationToken)
{
_cancellationToken = cancellationToken;
while (!cancellationToken.IsCancellationRequested)
{
var receiveToken = cancellationToken;
// On the first connection attempt, put a timeout to avoid being stuck indefinitely in the event of failure
if (onStarted != null)
{
receiveToken = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(5000).Token, cancellationToken).Token;
}
var data = await udpClient.ReceiveAsync(receiveToken).ConfigureAwait(false);
var bytesRead = data.ReceivedBytes - RtpHeaderBytes;
if (bytesRead > 0)
{
var allStreams = _outputStreams.ToList();
if (allStreams.Count == 1)
{
await allStreams[0].Value.WriteAsync(data.Buffer, 0, bytesRead).ConfigureAwait(false);
}
else
{
byte[] copy = new byte[bytesRead];
Buffer.BlockCopy(data.Buffer, RtpHeaderBytes, copy, 0, bytesRead);
foreach (var stream in allStreams)
{
stream.Value.Queue(copy, 0, copy.Length);
}
}
if (onStarted != null)
{
var onStartedCopy = onStarted;
onStarted = null;
Task.Run(onStartedCopy);
}
}
else
{
await Task.Delay(100).ConfigureAwait(false);
}
}
}
public Task CopyToAsync(Stream stream)
public Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
{
var result = new QueueStream(stream, _logger)
{
@@ -133,7 +78,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
_outputStreams.TryAdd(result.Id, result);
result.Start(_cancellationToken);
result.Start(cancellationToken);
return result.TaskCompletion.Task;
}

View File

@@ -13,7 +13,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Library;

View File

@@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.Migrations
_taskManager = taskManager;
}
public async Task Run()
public Task Run()
{
var name = "GuideRefresh3";
@@ -42,6 +42,8 @@ namespace Emby.Server.Implementations.Migrations
_config.Configuration.Migrations = list.ToArray();
_config.SaveConfiguration();
}
return Task.FromResult(true);
}
}
}

View File

@@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.Migrations
_taskManager = taskManager;
}
public async Task Run()
public Task Run()
{
var name = "LibraryScan6";
@@ -42,6 +42,8 @@ namespace Emby.Server.Implementations.Migrations
_config.Configuration.Migrations = list.ToArray();
_config.SaveConfiguration();
}
return Task.FromResult(true);
}
}
}

View File

@@ -1,130 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates;
namespace Emby.Server.Implementations.Migrations
{
public class UpdateLevelMigration : IVersionMigration
{
private readonly IServerConfigurationManager _config;
private readonly IServerApplicationHost _appHost;
private readonly IHttpClient _httpClient;
private readonly IJsonSerializer _jsonSerializer;
private readonly string _releaseAssetFilename;
private readonly ILogger _logger;
public UpdateLevelMigration(IServerConfigurationManager config, IServerApplicationHost appHost, IHttpClient httpClient, IJsonSerializer jsonSerializer, string releaseAssetFilename, ILogger logger)
{
_config = config;
_appHost = appHost;
_httpClient = httpClient;
_jsonSerializer = jsonSerializer;
_releaseAssetFilename = releaseAssetFilename;
_logger = logger;
}
public async Task Run()
{
var lastVersion = _config.Configuration.LastVersion;
var currentVersion = _appHost.ApplicationVersion;
if (string.Equals(lastVersion, currentVersion.ToString(), StringComparison.OrdinalIgnoreCase))
{
return;
}
try
{
var updateLevel = _config.Configuration.SystemUpdateLevel;
await CheckVersion(currentVersion, updateLevel, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error in update migration", ex);
}
}
private async Task CheckVersion(Version currentVersion, PackageVersionClass currentUpdateLevel, CancellationToken cancellationToken)
{
var releases = await new GithubUpdater(_httpClient, _jsonSerializer)
.GetLatestReleases("MediaBrowser", "Emby", _releaseAssetFilename, cancellationToken).ConfigureAwait(false);
var newUpdateLevel = GetNewUpdateLevel(currentVersion, currentUpdateLevel, releases);
if (newUpdateLevel != currentUpdateLevel)
{
_config.Configuration.SystemUpdateLevel = newUpdateLevel;
_config.SaveConfiguration();
}
}
private PackageVersionClass GetNewUpdateLevel(Version currentVersion, PackageVersionClass currentUpdateLevel, List<GithubUpdater.RootObject> releases)
{
var newUpdateLevel = currentUpdateLevel;
// If the current version is later than current stable, set the update level to beta
if (releases.Count >= 1)
{
var release = releases[0];
var version = ParseVersion(release.tag_name);
if (version != null)
{
if (currentVersion > version)
{
newUpdateLevel = PackageVersionClass.Beta;
}
else
{
return PackageVersionClass.Release;
}
}
}
// If the current version is later than current beta, set the update level to dev
if (releases.Count >= 2)
{
var release = releases[1];
var version = ParseVersion(release.tag_name);
if (version != null)
{
if (currentVersion > version)
{
newUpdateLevel = PackageVersionClass.Dev;
}
else
{
return PackageVersionClass.Beta;
}
}
}
return newUpdateLevel;
}
private Version ParseVersion(string versionString)
{
if (!string.IsNullOrWhiteSpace(versionString))
{
var parts = versionString.Split('.');
if (parts.Length == 3)
{
versionString += ".0";
}
}
Version version;
Version.TryParse(versionString, out version);
return version;
}
}
}

View File

@@ -18,15 +18,15 @@ namespace Emby.Server.Implementations.Photos
{
}
protected override Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
protected override List<BaseItem> GetItemsWithImages(IHasImages item)
{
var photoAlbum = (PhotoAlbum)item;
var items = GetFinalItems(photoAlbum.Children.ToList());
return Task.FromResult(items);
return items;
}
protected override Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
protected string CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
{
return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
}

View File

@@ -10,7 +10,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Emby.Server.Implementations.Images;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
@@ -26,7 +27,7 @@ namespace Emby.Server.Implementations.Playlists
{
}
protected override Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
protected override List<BaseItem> GetItemsWithImages(IHasImages item)
{
var playlist = (Playlist)item;
@@ -67,7 +68,7 @@ namespace Emby.Server.Implementations.Playlists
.DistinctBy(i => i.Id)
.ToList();
return Task.FromResult(GetFinalItems(items));
return GetFinalItems(items);
}
}
@@ -80,7 +81,7 @@ namespace Emby.Server.Implementations.Playlists
_libraryManager = libraryManager;
}
protected override Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
protected override List<BaseItem> GetItemsWithImages(IHasImages item)
{
var items = _libraryManager.GetItemList(new InternalItemsQuery
{
@@ -89,11 +90,12 @@ namespace Emby.Server.Implementations.Playlists
SortBy = new[] { ItemSortBy.Random },
Limit = 4,
Recursive = true,
ImageTypes = new[] { ImageType.Primary }
ImageTypes = new[] { ImageType.Primary },
DtoOptions = new DtoOptions(false)
}).ToList();
return Task.FromResult(GetFinalItems(items));
return GetFinalItems(items);
}
//protected override Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
@@ -111,7 +113,7 @@ namespace Emby.Server.Implementations.Playlists
_libraryManager = libraryManager;
}
protected override Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
protected override List<BaseItem> GetItemsWithImages(IHasImages item)
{
var items = _libraryManager.GetItemList(new InternalItemsQuery
{
@@ -120,11 +122,12 @@ namespace Emby.Server.Implementations.Playlists
SortBy = new[] { ItemSortBy.Random },
Limit = 4,
Recursive = true,
ImageTypes = new[] { ImageType.Primary }
ImageTypes = new[] { ImageType.Primary },
DtoOptions = new DtoOptions(false)
}).ToList();
return Task.FromResult(GetFinalItems(items));
return GetFinalItems(items);
}
//protected override Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)

View File

@@ -12,7 +12,8 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
@@ -135,7 +136,10 @@ namespace Emby.Server.Implementations.Playlists
if (options.ItemIdList.Count > 0)
{
await AddToPlaylistInternal(playlist.Id.ToString("N"), options.ItemIdList, user);
await AddToPlaylistInternal(playlist.Id.ToString("N"), options.ItemIdList, user, new DtoOptions(false)
{
EnableImages = true
});
}
return new PlaylistCreationResult
@@ -160,21 +164,24 @@ namespace Emby.Server.Implementations.Playlists
return path;
}
private Task<IEnumerable<BaseItem>> GetPlaylistItems(IEnumerable<string> itemIds, string playlistMediaType, User user)
private IEnumerable<BaseItem> GetPlaylistItems(IEnumerable<string> itemIds, string playlistMediaType, User user, DtoOptions options)
{
var items = itemIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null);
return Playlist.GetPlaylistItems(playlistMediaType, items, user);
return Playlist.GetPlaylistItems(playlistMediaType, items, user, options);
}
public Task AddToPlaylist(string playlistId, IEnumerable<string> itemIds, string userId)
{
var user = string.IsNullOrWhiteSpace(userId) ? null : _userManager.GetUserById(userId);
return AddToPlaylistInternal(playlistId, itemIds, user);
return AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false)
{
EnableImages = true
});
}
private async Task AddToPlaylistInternal(string playlistId, IEnumerable<string> itemIds, User user)
private async Task AddToPlaylistInternal(string playlistId, IEnumerable<string> itemIds, User user, DtoOptions options)
{
var playlist = _libraryManager.GetItemById(playlistId) as Playlist;
@@ -185,12 +192,17 @@ namespace Emby.Server.Implementations.Playlists
var list = new List<LinkedChild>();
var items = (await GetPlaylistItems(itemIds, playlist.MediaType, user).ConfigureAwait(false))
var items = (GetPlaylistItems(itemIds, playlist.MediaType, user, options))
.Where(i => i.SupportsAddingToPlaylist)
.ToList();
foreach (var item in items)
{
if (string.IsNullOrWhiteSpace(item.Path))
{
continue;
}
list.Add(LinkedChild.Create(item));
}
@@ -271,7 +283,7 @@ namespace Emby.Server.Implementations.Playlists
{
var typeName = "PlaylistsFolder";
return _libraryManager.RootFolder.Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, typeName, StringComparison.Ordinal)) ??
return _libraryManager.RootFolder.Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, typeName, StringComparison.Ordinal)) ??
_libraryManager.GetUserRootFolder().Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, typeName, StringComparison.Ordinal));
}
}

View File

@@ -10,7 +10,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Entities;
@@ -85,7 +85,9 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
MediaTypes = new[] { MediaType.Video },
IsFolder = false,
Recursive = true
Recursive = true,
DtoOptions = new DtoOptions(false)
})
.OfType<Video>()
.ToList();

View File

@@ -4,7 +4,7 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;

View File

@@ -12,7 +12,7 @@ using System.Collections.Specialized;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.Text;

View File

@@ -9,7 +9,6 @@ using System.Collections.Specialized;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.Text;

View File

@@ -12,43 +12,28 @@ namespace Emby.Server.Implementations.Services
{
public static class ResponseHelper
{
public static Task WriteToResponse(IResponse httpRes, IRequest httpReq, object result)
public static async Task WriteToResponse(IResponse response, IRequest request, object result, CancellationToken cancellationToken)
{
if (result == null)
{
if (httpRes.StatusCode == (int)HttpStatusCode.OK)
if (response.StatusCode == (int)HttpStatusCode.OK)
{
httpRes.StatusCode = (int)HttpStatusCode.NoContent;
response.StatusCode = (int)HttpStatusCode.NoContent;
}
httpRes.SetContentLength(0);
return Task.FromResult(true);
response.SetContentLength(0);
return;
}
var httpResult = result as IHttpResult;
if (httpResult != null)
{
httpResult.RequestContext = httpReq;
httpReq.ResponseContentType = httpResult.ContentType ?? httpReq.ResponseContentType;
return WriteToResponseInternal(httpRes, httpResult, httpReq);
httpResult.RequestContext = request;
request.ResponseContentType = httpResult.ContentType ?? request.ResponseContentType;
}
return WriteToResponseInternal(httpRes, result, httpReq);
}
/// <summary>
/// Writes to response.
/// Response headers are customizable by implementing IHasHeaders an returning Dictionary of Http headers.
/// </summary>
/// <param name="response">The response.</param>
/// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
/// <param name="request">The serialization context.</param>
/// <returns></returns>
private static async Task WriteToResponseInternal(IResponse response, object result, IRequest request)
{
var defaultContentType = request.ResponseContentType;
var httpResult = result as IHttpResult;
if (httpResult != null)
{
if (httpResult.RequestContext == null)
@@ -105,7 +90,7 @@ namespace Emby.Server.Implementations.Services
var asyncStreamWriter = result as IAsyncStreamWriter;
if (asyncStreamWriter != null)
{
await asyncStreamWriter.WriteToAsync(response.OutputStream, CancellationToken.None).ConfigureAwait(false);
await asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken).ConfigureAwait(false);
return;
}
@@ -119,7 +104,7 @@ namespace Emby.Server.Implementations.Services
var fileWriter = result as FileWriter;
if (fileWriter != null)
{
await fileWriter.WriteToAsync(response, CancellationToken.None).ConfigureAwait(false);
await fileWriter.WriteToAsync(response, cancellationToken).ConfigureAwait(false);
return;
}
@@ -139,7 +124,7 @@ namespace Emby.Server.Implementations.Services
response.ContentType = "application/octet-stream";
response.SetContentLength(bytes.Length);
await response.OutputStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
await response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false);
return;
}
@@ -148,7 +133,7 @@ namespace Emby.Server.Implementations.Services
{
bytes = Encoding.UTF8.GetBytes(responseText);
response.SetContentLength(bytes.Length);
await response.OutputStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
await response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false);
return;
}

View File

@@ -187,7 +187,7 @@ namespace Emby.Server.Implementations.Services
return null;
}
public async Task<object> Execute(HttpListenerHost appHost, object requestDto, IRequest req)
public Task<object> Execute(HttpListenerHost appHost, object requestDto, IRequest req)
{
req.Dto = requestDto;
var requestType = requestDto.GetType();
@@ -209,9 +209,7 @@ namespace Emby.Server.Implementations.Services
req.Dto = requestDto;
//Executes the service and returns the result
var response = await ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName()).ConfigureAwait(false);
return response;
return ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName());
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.HttpServer;
using MediaBrowser.Model.Logging;
@@ -123,7 +124,7 @@ namespace Emby.Server.Implementations.Services
// Set from SSHHF.GetHandlerForPathInfo()
public string ResponseContentType { get; set; }
public async Task ProcessRequestAsync(HttpListenerHost appHost, IRequest httpReq, IResponse httpRes, ILogger logger, string operationName)
public async Task ProcessRequestAsync(HttpListenerHost appHost, IRequest httpReq, IResponse httpRes, ILogger logger, string operationName, CancellationToken cancellationToken)
{
var restPath = GetRestPath(httpReq.Verb, httpReq.PathInfo);
if (restPath == null)
@@ -142,7 +143,8 @@ namespace Emby.Server.Implementations.Services
var rawResponse = await appHost.ServiceController.Execute(appHost, request, httpReq).ConfigureAwait(false);
var response = await HandleResponseAsync(rawResponse).ConfigureAwait(false);
//var response = await HandleResponseAsync(rawResponse).ConfigureAwait(false);
var response = rawResponse;
// Apply response filters
foreach (var responseFilter in appHost.ResponseFilters)
@@ -150,7 +152,7 @@ namespace Emby.Server.Implementations.Services
responseFilter(httpReq, httpRes, response);
}
await ResponseHelper.WriteToResponse(httpRes, httpReq, response).ConfigureAwait(false);
await ResponseHelper.WriteToResponse(httpRes, httpReq, response, cancellationToken).ConfigureAwait(false);
}
public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, ILogger logger)

View File

@@ -66,19 +66,19 @@ namespace Emby.Server.Implementations.Session
return SendMessage(name, new Dictionary<string, string>(), cancellationToken);
}
private async Task SendMessage(string name,
private Task SendMessage(string name,
Dictionary<string, string> args,
CancellationToken cancellationToken)
{
var url = PostUrl + "/" + name + ToQueryString(args);
await _httpClient.Post(new HttpRequestOptions
return _httpClient.Post(new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken,
BufferContent = false
}).ConfigureAwait(false);
});
}
public Task SendSessionEndedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
@@ -159,8 +159,24 @@ namespace Emby.Server.Implementations.Session
public Task SendMessage<T>(string name, T data, CancellationToken cancellationToken)
{
// Not supported or needed right now
return Task.FromResult(true);
var url = PostUrl + "/" + name;
var options = new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken,
BufferContent = false
};
options.RequestContent = _json.SerializeToString(data);
options.RequestContentType = "application/json";
return _httpClient.Post(new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken,
BufferContent = false
});
}
private string ToQueryString(Dictionary<string, string> nvc)

View File

@@ -30,6 +30,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Threading;
namespace Emby.Server.Implementations.Session
@@ -984,7 +985,7 @@ namespace Emby.Server.Implementations.Session
var list = new List<BaseItem>();
foreach (var itemId in command.ItemIds)
{
var subItems = await TranslateItemForPlayback(itemId, user).ConfigureAwait(false);
var subItems = TranslateItemForPlayback(itemId, user);
list.AddRange(subItems);
}
@@ -1022,7 +1023,10 @@ namespace Emby.Server.Implementations.Session
var series = episode.Series;
if (series != null)
{
var episodes = series.GetEpisodes(user)
var episodes = series.GetEpisodes(user, new DtoOptions(false)
{
EnableImages = false
})
.Where(i => !i.IsVirtualItem)
.SkipWhile(i => i.Id != episode.Id)
.ToList();
@@ -1048,7 +1052,7 @@ namespace Emby.Server.Implementations.Session
await session.SessionController.SendPlayCommand(command, cancellationToken).ConfigureAwait(false);
}
private async Task<List<BaseItem>> TranslateItemForPlayback(string id, User user)
private List<BaseItem> TranslateItemForPlayback(string id, User user)
{
var item = _libraryManager.GetItemById(id);
@@ -1065,7 +1069,15 @@ namespace Emby.Server.Implementations.Session
var items = byName.GetTaggedItems(new InternalItemsQuery(user)
{
IsFolder = false,
Recursive = true
Recursive = true,
DtoOptions = new DtoOptions(false)
{
EnableImages = false,
Fields = new List<ItemFields>
{
ItemFields.SortName
}
}
});
return FilterToSingleMediaType(items)
@@ -1077,12 +1089,20 @@ namespace Emby.Server.Implementations.Session
{
var folder = (Folder)item;
var itemsResult = await folder.GetItems(new InternalItemsQuery(user)
var itemsResult = folder.GetItems(new InternalItemsQuery(user)
{
Recursive = true,
IsFolder = false
IsFolder = false,
DtoOptions = new DtoOptions(false)
{
EnableImages = false,
Fields = new List<ItemFields>
{
ItemFields.SortName
}
}
}).ConfigureAwait(false);
});
return FilterToSingleMediaType(itemsResult.Items)
.OrderBy(i => i.SortName)
@@ -1111,7 +1131,7 @@ namespace Emby.Server.Implementations.Session
return new List<BaseItem>();
}
return _musicManager.GetInstantMixFromItem(item, user);
return _musicManager.GetInstantMixFromItem(item, user, new DtoOptions(false) { EnableImages = false });
}
public Task SendBrowseCommand(string controllingSessionId, string sessionId, BrowseRequest command, CancellationToken cancellationToken)

View File

@@ -57,7 +57,6 @@ namespace Emby.Server.Implementations.Session
_json = json;
_httpServer = httpServer;
_serverManager = serverManager;
httpServer.WebSocketConnecting += _httpServer_WebSocketConnecting;
serverManager.WebSocketConnected += _serverManager_WebSocketConnected;
}
@@ -84,27 +83,6 @@ namespace Emby.Server.Implementations.Session
}
}
async void _httpServer_WebSocketConnecting(object sender, WebSocketConnectingEventArgs e)
{
//var token = e.QueryString["api_key"];
//if (!string.IsNullOrWhiteSpace(token))
//{
// try
// {
// var session = await GetSession(e.QueryString, e.Endpoint).ConfigureAwait(false);
// if (session == null)
// {
// e.AllowConnection = false;
// }
// }
// catch (Exception ex)
// {
// _logger.ErrorException("Error getting session info", ex);
// }
//}
}
private Task<SessionInfo> GetSession(QueryParamCollection queryString, string remoteEndpoint)
{
if (queryString == null)
@@ -123,7 +101,6 @@ namespace Emby.Server.Implementations.Session
public void Dispose()
{
_httpServer.WebSocketConnecting -= _httpServer_WebSocketConnecting;
_serverManager.WebSocketConnected -= _serverManager_WebSocketConnected;
}

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -58,7 +59,8 @@ namespace Emby.Server.Implementations.TV
{
IncludeItemTypes = new[] { typeof(Series).Name },
Recursive = true,
GroupByPresentationUniqueKey = false
GroupByPresentationUniqueKey = false,
DtoOptions = new DtoOptions(true)
}).Cast<Series>().ToList();
@@ -188,7 +190,8 @@ namespace Emby.Server.Implementations.TV
{
IncludeItemTypes = new[] { typeof(Series).Name },
Recursive = true,
GroupByPresentationUniqueKey = false
GroupByPresentationUniqueKey = false,
DtoOptions = new DtoOptions(true)
}).Cast<Series>().ToList();

View File

@@ -8,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
namespace Emby.Server.Implementations.TV
{
@@ -26,7 +27,7 @@ namespace Emby.Server.Implementations.TV
_config = config;
}
public QueryResult<BaseItem> GetNextUp(NextUpQuery request)
public QueryResult<BaseItem> GetNextUp(NextUpQuery request, DtoOptions dtoOptions)
{
var user = _userManager.GetUserById(request.UserId);
@@ -68,19 +69,19 @@ namespace Emby.Server.Implementations.TV
{
Fields = new List<ItemFields>
{
ItemFields.PresentationUniqueKey
}
}
}).Cast<Series>().Select(GetUniqueSeriesKey);
// Avoid implicitly captured closure
var episodes = GetNextUpEpisodes(request, user, items);
var episodes = GetNextUpEpisodes(request, user, items, dtoOptions);
return GetResult(episodes, request);
}
public QueryResult<BaseItem> GetNextUp(NextUpQuery request, List<Folder> parentsFolders)
public QueryResult<BaseItem> GetNextUp(NextUpQuery request, List<Folder> parentsFolders, DtoOptions dtoOptions)
{
var user = _userManager.GetUserById(request.UserId);
@@ -118,7 +119,7 @@ namespace Emby.Server.Implementations.TV
{
Fields = new List<ItemFields>
{
ItemFields.PresentationUniqueKey
},
EnableImages = false
}
@@ -126,18 +127,18 @@ namespace Emby.Server.Implementations.TV
}, parentsFolders.Cast<BaseItem>().ToList()).Cast<Series>().Select(GetUniqueSeriesKey);
// Avoid implicitly captured closure
var episodes = GetNextUpEpisodes(request, user, items);
var episodes = GetNextUpEpisodes(request, user, items, dtoOptions);
return GetResult(episodes, request);
}
public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IEnumerable<string> seriesKeys)
public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IEnumerable<string> seriesKeys, DtoOptions dtoOptions)
{
// Avoid implicitly captured closure
var currentUser = user;
var allNextUp = seriesKeys
.Select(i => GetNextUp(i, currentUser));
.Select(i => GetNextUp(i, currentUser, dtoOptions));
//allNextUp = allNextUp.OrderByDescending(i => i.Item1);
@@ -175,14 +176,12 @@ namespace Emby.Server.Implementations.TV
/// Gets the next up.
/// </summary>
/// <returns>Task{Episode}.</returns>
private Tuple<DateTime, Func<Episode>> GetNextUp(string seriesKey, User user)
private Tuple<DateTime, Func<Episode>> GetNextUp(string seriesKey, User user, DtoOptions dtoOptions)
{
var enableSeriesPresentationKey = _config.Configuration.EnableSeriesPresentationUniqueKey;
var lastWatchedEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey,
SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null,
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
IncludeItemTypes = new[] { typeof(Episode).Name },
SortBy = new[] { ItemSortBy.SortName },
SortOrder = SortOrder.Descending,
@@ -193,7 +192,7 @@ namespace Emby.Server.Implementations.TV
{
Fields = new List<ItemFields>
{
ItemFields.SortName
},
EnableImages = false
}
@@ -204,8 +203,8 @@ namespace Emby.Server.Implementations.TV
{
return _libraryManager.GetItemList(new InternalItemsQuery(user)
{
AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey,
SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null,
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
IncludeItemTypes = new[] { typeof(Episode).Name },
SortBy = new[] { ItemSortBy.SortName },
SortOrder = SortOrder.Ascending,
@@ -213,7 +212,8 @@ namespace Emby.Server.Implementations.TV
IsPlayed = false,
IsVirtualItem = false,
ParentIndexNumberNotEquals = 0,
MinSortName = lastWatchedEpisode == null ? null : lastWatchedEpisode.SortName
MinSortName = lastWatchedEpisode == null ? null : lastWatchedEpisode.SortName,
DtoOptions = dtoOptions
}).Cast<Episode>().FirstOrDefault();
};

View File

@@ -139,30 +139,58 @@ namespace Emby.Server.Implementations.Udp
{
_udpClient = _socketFactory.CreateUdpSocket(port);
Task.Run(() => StartListening());
Task.Run(() => BeginReceive());
}
private async void StartListening()
{
while (!_isDisposed)
{
try
{
var result = await _udpClient.ReceiveAsync(CancellationToken.None).ConfigureAwait(false);
private readonly byte[] _receiveBuffer = new byte[8192];
OnMessageReceived(result);
}
catch (ObjectDisposedException)
private void BeginReceive()
{
if (_isDisposed)
{
return;
}
try
{
var result = _udpClient.BeginReceive(_receiveBuffer, 0, _receiveBuffer.Length, OnReceiveResult);
if (result.CompletedSynchronously)
{
}
catch (OperationCanceledException)
{
}
catch (Exception ex)
{
_logger.ErrorException("Error receiving udp message", ex);
OnReceiveResult(result);
}
}
catch (ObjectDisposedException)
{
}
catch (Exception ex)
{
_logger.ErrorException("Error receiving udp message", ex);
}
}
private void OnReceiveResult(IAsyncResult result)
{
if (_isDisposed)
{
return;
}
try
{
var socketResult = _udpClient.EndReceive(result);
OnMessageReceived(socketResult);
}
catch (ObjectDisposedException)
{
}
catch (Exception ex)
{
_logger.ErrorException("Error receiving udp message", ex);
}
BeginReceive();
}
/// <summary>
@@ -239,13 +267,13 @@ namespace Emby.Server.Implementations.Udp
try
{
await _udpClient.SendWithLockAsync(bytes, bytes.Length, remoteEndPoint, CancellationToken.None).ConfigureAwait(false);
await _udpClient.SendToAsync(bytes, 0, bytes.Length, remoteEndPoint, CancellationToken.None).ConfigureAwait(false);
_logger.Info("Udp message sent to {0}", remoteEndPoint);
}
catch (OperationCanceledException)
{
}
catch (Exception ex)
{

View File

@@ -11,9 +11,9 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Emby.Server.Implementations.Images;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
@@ -36,19 +36,20 @@ namespace Emby.Server.Implementations.UserViews
};
}
protected override async Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
protected override List<BaseItem> GetItemsWithImages(IHasImages item)
{
var view = (CollectionFolder)item;
var recursive = !new[] { CollectionType.Playlists, CollectionType.Channels }.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
var result = await view.GetItems(new InternalItemsQuery
var result = view.GetItems(new InternalItemsQuery
{
CollapseBoxSetItems = false,
Recursive = recursive,
ExcludeItemTypes = new[] { "UserView", "CollectionFolder", "Playlist" }
ExcludeItemTypes = new[] { "UserView", "CollectionFolder", "Playlist" },
DtoOptions = new DtoOptions(false)
}).ConfigureAwait(false);
});
var items = result.Items.Select(i =>
{
@@ -98,7 +99,7 @@ namespace Emby.Server.Implementations.UserViews
return item is CollectionFolder;
}
protected override async Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
protected override string CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
{
var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png");
@@ -109,10 +110,10 @@ namespace Emby.Server.Implementations.UserViews
return null;
}
return await CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540).ConfigureAwait(false);
return CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540);
}
return await base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex).ConfigureAwait(false);
return base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex);
}
}
@@ -133,7 +134,7 @@ namespace Emby.Server.Implementations.UserViews
};
}
protected override async Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
protected override List<BaseItem> GetItemsWithImages(IHasImages item)
{
var view = (ManualCollectionsFolder)item;
@@ -144,7 +145,8 @@ namespace Emby.Server.Implementations.UserViews
Recursive = recursive,
IncludeItemTypes = new[] { typeof(BoxSet).Name },
Limit = 20,
SortBy = new[] { ItemSortBy.Random }
SortBy = new[] { ItemSortBy.Random },
DtoOptions = new DtoOptions(false)
});
return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)).ToList(), 8);
@@ -155,7 +157,7 @@ namespace Emby.Server.Implementations.UserViews
return item is ManualCollectionsFolder;
}
protected override async Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
protected override string CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
{
var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png");
@@ -166,10 +168,10 @@ namespace Emby.Server.Implementations.UserViews
return null;
}
return await CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540).ConfigureAwait(false);
return CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540);
}
return await base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex).ConfigureAwait(false);
return base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex);
}
}

View File

@@ -12,6 +12,7 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Emby.Server.Implementations.Images;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Extensions;
@@ -47,7 +48,7 @@ namespace Emby.Server.Implementations.UserViews
};
}
protected override async Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
protected override List<BaseItem> GetItemsWithImages(IHasImages item)
{
var view = (UserView)item;
@@ -58,7 +59,9 @@ namespace Emby.Server.Implementations.UserViews
IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
ImageTypes = new[] { ImageType.Primary },
Limit = 30,
IsMovie = true
IsMovie = true,
DtoOptions = new DtoOptions(false)
}).ToList();
return GetFinalItems(programs).ToList();
@@ -67,9 +70,10 @@ namespace Emby.Server.Implementations.UserViews
if (string.Equals(view.ViewType, SpecialFolder.MovieGenre, StringComparison.OrdinalIgnoreCase) ||
string.Equals(view.ViewType, SpecialFolder.TvGenre, StringComparison.OrdinalIgnoreCase))
{
var userItemsResult = await view.GetItems(new InternalItemsQuery
var userItemsResult = view.GetItems(new InternalItemsQuery
{
CollapseBoxSetItems = false
CollapseBoxSetItems = false,
DtoOptions = new DtoOptions(false)
});
return userItemsResult.Items.ToList();
@@ -78,14 +82,14 @@ namespace Emby.Server.Implementations.UserViews
var isUsingCollectionStrip = IsUsingCollectionStrip(view);
var recursive = isUsingCollectionStrip && !new[] { CollectionType.Channels, CollectionType.BoxSets, CollectionType.Playlists }.Contains(view.ViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
var result = await view.GetItems(new InternalItemsQuery
var result = view.GetItems(new InternalItemsQuery
{
User = view.UserId.HasValue ? _userManager.GetUserById(view.UserId.Value) : null,
CollapseBoxSetItems = false,
Recursive = recursive,
ExcludeItemTypes = new[] { "UserView", "CollectionFolder", "Person" },
}).ConfigureAwait(false);
DtoOptions = new DtoOptions(false)
});
var items = result.Items.Select(i =>
{
@@ -159,7 +163,7 @@ namespace Emby.Server.Implementations.UserViews
return collectionStripViewTypes.Contains(view.ViewType ?? string.Empty);
}
protected override async Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
protected override string CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
{
if (itemsWithImages.Count == 0)
{
@@ -168,7 +172,7 @@ namespace Emby.Server.Implementations.UserViews
var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png");
return await CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540).ConfigureAwait(false);
return CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540);
}
}
}