commit
0c4f78be3a
|
@ -863,7 +863,7 @@ namespace Emby.Server.Core
|
|||
/// </summary>
|
||||
private void ConfigureNotificationsRepository()
|
||||
{
|
||||
var repo = new SqliteNotificationsRepository(LogManager.GetLogger("SqliteNotificationsRepository"), ServerConfigurationManager.ApplicationPaths);
|
||||
var repo = new SqliteNotificationsRepository(LogManager.GetLogger("SqliteNotificationsRepository"), ServerConfigurationManager.ApplicationPaths, FileSystemManager);
|
||||
|
||||
repo.Initialize();
|
||||
|
||||
|
|
|
@ -3874,6 +3874,25 @@ namespace Emby.Server.Implementations.Data
|
|||
whereClauses.Add(clause);
|
||||
}
|
||||
|
||||
if (query.AlbumIds.Length > 0)
|
||||
{
|
||||
var clauses = new List<string>();
|
||||
var index = 0;
|
||||
foreach (var albumId in query.AlbumIds)
|
||||
{
|
||||
var paramName = "@AlbumIds" + index;
|
||||
|
||||
clauses.Add("Album in (select Name from typedbaseitems where guid=" + paramName + ")");
|
||||
if (statement != null)
|
||||
{
|
||||
statement.TryBind(paramName, albumId.ToGuidParamValue());
|
||||
}
|
||||
index++;
|
||||
}
|
||||
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
|
||||
whereClauses.Add(clause);
|
||||
}
|
||||
|
||||
if (query.ExcludeArtistIds.Length > 0)
|
||||
{
|
||||
var clauses = new List<string>();
|
||||
|
@ -4227,30 +4246,6 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
whereClauses.Add("ProviderIds like '%tvdb=%'");
|
||||
}
|
||||
|
||||
if (query.AlbumNames.Length > 0)
|
||||
{
|
||||
var clause = "(";
|
||||
|
||||
var index = 0;
|
||||
foreach (var name in query.AlbumNames)
|
||||
{
|
||||
if (index > 0)
|
||||
{
|
||||
clause += " OR ";
|
||||
}
|
||||
clause += "Album=@AlbumName" + index;
|
||||
|
||||
if (statement != null)
|
||||
{
|
||||
statement.TryBind("@AlbumName" + index, name);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
clause += ")";
|
||||
whereClauses.Add(clause);
|
||||
}
|
||||
if (query.HasThemeSong.HasValue)
|
||||
{
|
||||
if (query.HasThemeSong.Value)
|
||||
|
|
|
@ -492,7 +492,10 @@ namespace Emby.Server.Implementations.Dto
|
|||
}
|
||||
}
|
||||
|
||||
//if (!(item is LiveTvProgram))
|
||||
{
|
||||
dto.PlayAccess = item.GetPlayAccess(user);
|
||||
}
|
||||
|
||||
if (fields.Contains(ItemFields.BasicSyncInfo) || fields.Contains(ItemFields.SyncInfo))
|
||||
{
|
||||
|
@ -994,7 +997,12 @@ namespace Emby.Server.Implementations.Dto
|
|||
}
|
||||
|
||||
dto.MediaType = item.MediaType;
|
||||
|
||||
if (!(item is LiveTvProgram))
|
||||
{
|
||||
dto.LocationType = item.LocationType;
|
||||
}
|
||||
|
||||
if (item.IsHD.HasValue && item.IsHD.Value)
|
||||
{
|
||||
dto.IsHD = item.IsHD;
|
||||
|
@ -1102,7 +1110,10 @@ namespace Emby.Server.Implementations.Dto
|
|||
}
|
||||
|
||||
dto.Type = item.GetClientTypeName();
|
||||
if ((item.CommunityRating ?? 0) > 0)
|
||||
{
|
||||
dto.CommunityRating = item.CommunityRating;
|
||||
}
|
||||
|
||||
if (fields.Contains(ItemFields.VoteCount))
|
||||
{
|
||||
|
@ -1410,8 +1421,6 @@ namespace Emby.Server.Implementations.Dto
|
|||
dto.AirDays = series.AirDays;
|
||||
dto.AirTime = series.AirTime;
|
||||
dto.SeriesStatus = series.Status;
|
||||
|
||||
dto.AnimeSeriesIndex = series.AnimeSeriesIndex;
|
||||
}
|
||||
|
||||
// Add SeasonInfo
|
||||
|
@ -1473,10 +1482,13 @@ namespace Emby.Server.Implementations.Dto
|
|||
SetBookProperties(dto, book);
|
||||
}
|
||||
|
||||
if (fields.Contains(ItemFields.ProductionLocations))
|
||||
{
|
||||
if (item.ProductionLocations.Count > 0 || item is Movie)
|
||||
{
|
||||
dto.ProductionLocations = item.ProductionLocations.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
var photo = item as Photo;
|
||||
if (photo != null)
|
||||
|
|
|
@ -409,17 +409,17 @@ namespace Emby.Server.Implementations.Library
|
|||
|
||||
if (options.DeleteFileLocation && locationType != LocationType.Remote && locationType != LocationType.Virtual)
|
||||
{
|
||||
foreach (var path in item.GetDeletePaths().ToList())
|
||||
foreach (var fileSystemInfo in item.GetDeletePaths().ToList())
|
||||
{
|
||||
if (_fileSystem.DirectoryExists(path))
|
||||
if (fileSystemInfo.IsDirectory)
|
||||
{
|
||||
_logger.Debug("Deleting path {0}", path);
|
||||
_fileSystem.DeleteDirectory(path, true);
|
||||
_logger.Debug("Deleting path {0}", fileSystemInfo.FullName);
|
||||
_fileSystem.DeleteDirectory(fileSystemInfo.FullName, true);
|
||||
}
|
||||
else if (_fileSystem.FileExists(path))
|
||||
else
|
||||
{
|
||||
_logger.Debug("Deleting path {0}", path);
|
||||
_fileSystem.DeleteFile(path);
|
||||
_logger.Debug("Deleting path {0}", fileSystemInfo.FullName);
|
||||
_fileSystem.DeleteFile(fileSystemInfo.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -818,30 +818,6 @@ namespace Emby.Server.Implementations.Library
|
|||
return _userRootFolder;
|
||||
}
|
||||
|
||||
public Guid? FindIdByPath(string path, bool? isFolder)
|
||||
{
|
||||
// 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
|
||||
|
||||
var query = new InternalItemsQuery
|
||||
{
|
||||
Path = path,
|
||||
IsFolder = isFolder,
|
||||
SortBy = new[] { ItemSortBy.DateCreated },
|
||||
SortOrder = SortOrder.Descending,
|
||||
Limit = 1
|
||||
};
|
||||
|
||||
var id = GetItemIds(query);
|
||||
|
||||
if (id.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return id[0];
|
||||
}
|
||||
|
||||
public BaseItem FindByPath(string path, bool? isFolder)
|
||||
{
|
||||
// If this returns multiple items it could be tricky figuring out which one is correct.
|
||||
|
|
|
@ -248,6 +248,13 @@ namespace Emby.Server.Implementations.Library
|
|||
}
|
||||
}
|
||||
|
||||
var isPlayed = request.IsPlayed;
|
||||
|
||||
if (parents.OfType<ICollectionFolder>().Any(i => string.Equals(i.CollectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
isPlayed = null;
|
||||
}
|
||||
|
||||
if (parents.Count == 0)
|
||||
{
|
||||
parents = user.RootFolder.GetChildren(user, true)
|
||||
|
@ -282,7 +289,7 @@ namespace Emby.Server.Implementations.Library
|
|||
IsVirtualItem = false,
|
||||
Limit = limit * 5,
|
||||
SourceTypes = parents.Count == 0 ? new[] { SourceType.Library } : new SourceType[] { },
|
||||
IsPlayed = request.IsPlayed
|
||||
IsPlayed = isPlayed
|
||||
|
||||
}, parents);
|
||||
}
|
||||
|
|
|
@ -1172,7 +1172,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
};
|
||||
|
||||
var isAudio = false;
|
||||
await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, false, cancellationToken).ConfigureAwait(false);
|
||||
await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return new List<MediaSourceInfo>
|
||||
{
|
||||
|
|
|
@ -260,7 +260,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||
{
|
||||
_logger.Info("Calling recording process.WaitForExit for {0}", _targetPath);
|
||||
|
||||
if (_process.WaitForExit(5000))
|
||||
if (_process.WaitForExit(10000))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, bool assumeInterlaced, CancellationToken cancellationToken)
|
||||
public async Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
|
||||
{
|
||||
var originalRuntime = mediaSource.RunTimeTicks;
|
||||
|
||||
|
@ -96,17 +96,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
videoStream.IsAVC = null;
|
||||
}
|
||||
|
||||
if (assumeInterlaced)
|
||||
{
|
||||
foreach (var mediaStream in mediaSource.MediaStreams)
|
||||
{
|
||||
if (mediaStream.Type == MediaStreamType.Video)
|
||||
{
|
||||
mediaStream.IsInterlaced = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to estimate this
|
||||
mediaSource.InferTotalBitrate(true);
|
||||
}
|
||||
|
|
|
@ -607,6 +607,10 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
item.Audio = info.Audio;
|
||||
item.ChannelId = channel.Id.ToString("N");
|
||||
item.CommunityRating = item.CommunityRating ?? info.CommunityRating;
|
||||
if ((item.CommunityRating ?? 0).Equals(0))
|
||||
{
|
||||
item.CommunityRating = null;
|
||||
}
|
||||
|
||||
item.EpisodeTitle = info.EpisodeTitle;
|
||||
item.ExternalId = info.Id;
|
||||
|
|
|
@ -126,14 +126,12 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
var keys = openToken.Split(new[] { StreamIdDelimeter }, 3);
|
||||
var mediaSourceId = keys.Length >= 3 ? keys[2] : null;
|
||||
IDirectStreamProvider directStreamProvider = null;
|
||||
var assumeInterlaced = false;
|
||||
|
||||
if (string.Equals(keys[0], typeof(LiveTvChannel).Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var info = await _liveTvManager.GetChannelStream(keys[1], mediaSourceId, cancellationToken).ConfigureAwait(false);
|
||||
stream = info.Item1;
|
||||
directStreamProvider = info.Item2;
|
||||
assumeInterlaced = info.Item3;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -148,7 +146,7 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
}
|
||||
else
|
||||
{
|
||||
await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, assumeInterlaced, cancellationToken).ConfigureAwait(false);
|
||||
await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
|||
using Emby.Server.Implementations.Data;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Notifications;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Notifications;
|
||||
using SQLitePCL.pretty;
|
||||
|
@ -16,8 +17,11 @@ namespace Emby.Server.Implementations.Notifications
|
|||
{
|
||||
public class SqliteNotificationsRepository : BaseSqliteRepository, INotificationsRepository
|
||||
{
|
||||
public SqliteNotificationsRepository(ILogger logger, IServerApplicationPaths appPaths) : base(logger)
|
||||
protected IFileSystem FileSystem { get; private set; }
|
||||
|
||||
public SqliteNotificationsRepository(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem) : base(logger)
|
||||
{
|
||||
FileSystem = fileSystem;
|
||||
DbFilePath = Path.Combine(appPaths.DataPath, "notifications.db");
|
||||
}
|
||||
|
||||
|
@ -26,6 +30,22 @@ namespace Emby.Server.Implementations.Notifications
|
|||
////public event EventHandler<NotificationUpdateEventArgs> NotificationUpdated;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
InitializeInternal();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error loading notifications database file. Will reset and retry.", ex);
|
||||
|
||||
FileSystem.DeleteFile(DbFilePath);
|
||||
|
||||
InitializeInternal();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeInternal()
|
||||
{
|
||||
using (var connection = CreateConnection())
|
||||
{
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
<Compile Include="IHasDtoOptions.cs" />
|
||||
<Compile Include="Playback\MediaInfoService.cs" />
|
||||
<Compile Include="Playback\TranscodingThrottler.cs" />
|
||||
<Compile Include="Playback\UniversalAudioService.cs" />
|
||||
<Compile Include="PlaylistService.cs" />
|
||||
<Compile Include="Reports\Activities\ReportActivitiesBuilder.cs" />
|
||||
<Compile Include="Reports\Common\HeaderActivitiesMetadata.cs" />
|
||||
|
|
|
@ -126,14 +126,10 @@ namespace MediaBrowser.Api.Playback
|
|||
/// <summary>
|
||||
/// Gets the output file path.
|
||||
/// </summary>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetOutputFilePath(StreamState state)
|
||||
private string GetOutputFilePath(StreamState state, string outputFileExtension)
|
||||
{
|
||||
var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath;
|
||||
|
||||
var outputFileExtension = GetOutputFileExtension(state);
|
||||
|
||||
var data = GetCommandLineArguments("dummy\\dummy", state, false);
|
||||
|
||||
data += "-" + (state.Request.DeviceId ?? string.Empty);
|
||||
|
@ -876,11 +872,16 @@ namespace MediaBrowser.Api.Playback
|
|||
|
||||
var container = Path.GetExtension(state.RequestedUrl);
|
||||
|
||||
if (string.IsNullOrEmpty(container))
|
||||
{
|
||||
container = request.Container;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(container))
|
||||
{
|
||||
container = request.Static ?
|
||||
state.InputContainer :
|
||||
(Path.GetExtension(GetOutputFilePath(state)) ?? string.Empty).TrimStart('.');
|
||||
GetOutputFileExtension(state);
|
||||
}
|
||||
|
||||
state.OutputContainer = (container ?? string.Empty).TrimStart('.');
|
||||
|
@ -923,7 +924,10 @@ namespace MediaBrowser.Api.Playback
|
|||
ApplyDeviceProfileSettings(state);
|
||||
}
|
||||
|
||||
state.OutputFilePath = GetOutputFilePath(state);
|
||||
var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
|
||||
? GetOutputFileExtension(state)
|
||||
: ("." + state.OutputContainer);
|
||||
state.OutputFilePath = GetOutputFilePath(state, ext);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -18,9 +18,6 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Controller.IO;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Services;
|
||||
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ namespace MediaBrowser.Api.Playback
|
|||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
public async Task<object> Post(GetPostedPlaybackInfo request)
|
||||
public async Task<PlaybackInfoResponse> GetPlaybackInfo(GetPostedPlaybackInfo request)
|
||||
{
|
||||
var authInfo = _authContext.GetAuthorizationInfo(Request);
|
||||
|
||||
|
@ -172,7 +172,14 @@ namespace MediaBrowser.Api.Playback
|
|||
SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex, request.MaxAudioChannels, request.UserId);
|
||||
}
|
||||
|
||||
return ToOptimizedResult(info);
|
||||
return info;
|
||||
}
|
||||
|
||||
public async Task<object> Post(GetPostedPlaybackInfo request)
|
||||
{
|
||||
var result = await GetPlaybackInfo(request).ConfigureAwait(false);
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
private T Clone<T>(T obj)
|
||||
|
|
|
@ -26,8 +26,6 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
[Route("/Audio/{Id}/stream", "HEAD", Summary = "Gets an audio stream")]
|
||||
public class GetAudioStream : StreamRequest
|
||||
{
|
||||
[ApiMember(Name = "Container", Description = "Container", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Container { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -22,6 +22,9 @@ namespace MediaBrowser.Api.Playback
|
|||
[ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string DeviceId { get; set; }
|
||||
|
||||
[ApiMember(Name = "Container", Description = "Container", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Container { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the audio codec.
|
||||
/// </summary>
|
||||
|
|
248
MediaBrowser.Api/Playback/UniversalAudioService.cs
Normal file
248
MediaBrowser.Api/Playback/UniversalAudioService.cs
Normal file
|
@ -0,0 +1,248 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Api.Playback.Hls;
|
||||
using MediaBrowser.Api.Playback.Progressive;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Devices;
|
||||
using MediaBrowser.Controller.Dlna;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Services;
|
||||
|
||||
namespace MediaBrowser.Api.Playback
|
||||
{
|
||||
public class BaseUniversalRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string MediaSourceId { get; set; }
|
||||
|
||||
[ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string DeviceId { get; set; }
|
||||
|
||||
public string Token { get; set; }
|
||||
|
||||
public string UserId { get; set; }
|
||||
public string AudioCodec { get; set; }
|
||||
public string Container { get; set; }
|
||||
|
||||
public int? MaxAudioChannels { get; set; }
|
||||
|
||||
public long? MaxStreamingBitrate { get; set; }
|
||||
|
||||
[ApiMember(Name = "StartTimeTicks", Description = "Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public long? StartTimeTicks { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Audio/{Id}/universal.{Container}", "GET", Summary = "Gets an audio stream")]
|
||||
[Route("/Audio/{Id}/universal", "GET", Summary = "Gets an audio stream")]
|
||||
[Route("/Audio/{Id}/universal.{Container}", "HEAD", Summary = "Gets an audio stream")]
|
||||
[Route("/Audio/{Id}/universal", "HEAD", Summary = "Gets an audio stream")]
|
||||
public class GetUniversalAudioStream : BaseUniversalRequest
|
||||
{
|
||||
}
|
||||
|
||||
//[Authenticated]
|
||||
public class UniversalAudioService : BaseApiService
|
||||
{
|
||||
public UniversalAudioService(IServerConfigurationManager serverConfigurationManager, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, IDeviceManager deviceManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, INetworkManager networkManager)
|
||||
{
|
||||
ServerConfigurationManager = serverConfigurationManager;
|
||||
UserManager = userManager;
|
||||
LibraryManager = libraryManager;
|
||||
IsoManager = isoManager;
|
||||
MediaEncoder = mediaEncoder;
|
||||
FileSystem = fileSystem;
|
||||
DlnaManager = dlnaManager;
|
||||
DeviceManager = deviceManager;
|
||||
SubtitleEncoder = subtitleEncoder;
|
||||
MediaSourceManager = mediaSourceManager;
|
||||
ZipClient = zipClient;
|
||||
JsonSerializer = jsonSerializer;
|
||||
AuthorizationContext = authorizationContext;
|
||||
ImageProcessor = imageProcessor;
|
||||
NetworkManager = networkManager;
|
||||
}
|
||||
|
||||
protected IServerConfigurationManager ServerConfigurationManager { get; private set; }
|
||||
protected IUserManager UserManager { get; private set; }
|
||||
protected ILibraryManager LibraryManager { get; private set; }
|
||||
protected IIsoManager IsoManager { get; private set; }
|
||||
protected IMediaEncoder MediaEncoder { get; private set; }
|
||||
protected IFileSystem FileSystem { get; private set; }
|
||||
protected IDlnaManager DlnaManager { get; private set; }
|
||||
protected IDeviceManager DeviceManager { get; private set; }
|
||||
protected ISubtitleEncoder SubtitleEncoder { get; private set; }
|
||||
protected IMediaSourceManager MediaSourceManager { get; private set; }
|
||||
protected IZipClient ZipClient { get; private set; }
|
||||
protected IJsonSerializer JsonSerializer { get; private set; }
|
||||
protected IAuthorizationContext AuthorizationContext { get; private set; }
|
||||
protected IImageProcessor ImageProcessor { get; private set; }
|
||||
protected INetworkManager NetworkManager { get; private set; }
|
||||
|
||||
public Task<object> Get(GetUniversalAudioStream request)
|
||||
{
|
||||
return GetUniversalStream(request, false);
|
||||
}
|
||||
|
||||
public Task<object> Head(GetUniversalAudioStream request)
|
||||
{
|
||||
return GetUniversalStream(request, true);
|
||||
}
|
||||
|
||||
private DeviceProfile GetDeviceProfile(GetUniversalAudioStream request)
|
||||
{
|
||||
var deviceProfile = new DeviceProfile();
|
||||
|
||||
var directPlayProfiles = new List<DirectPlayProfile>();
|
||||
|
||||
directPlayProfiles.Add(new DirectPlayProfile
|
||||
{
|
||||
Type = DlnaProfileType.Audio,
|
||||
Container = request.Container
|
||||
});
|
||||
|
||||
deviceProfile.DirectPlayProfiles = directPlayProfiles.ToArray();
|
||||
|
||||
deviceProfile.TranscodingProfiles = new[]
|
||||
{
|
||||
new TranscodingProfile
|
||||
{
|
||||
Type = DlnaProfileType.Audio,
|
||||
Context = EncodingContext.Streaming,
|
||||
Container = "ts",
|
||||
AudioCodec = "aac",
|
||||
Protocol = "hls"
|
||||
}
|
||||
};
|
||||
|
||||
return deviceProfile;
|
||||
}
|
||||
|
||||
private async Task<object> GetUniversalStream(GetUniversalAudioStream request, bool isHeadRequest)
|
||||
{
|
||||
var deviceProfile = GetDeviceProfile(request);
|
||||
|
||||
AuthorizationContext.GetAuthorizationInfo(Request).DeviceId = request.DeviceId;
|
||||
|
||||
var mediaInfoService = new MediaInfoService(MediaSourceManager, DeviceManager, LibraryManager, ServerConfigurationManager, NetworkManager, MediaEncoder, UserManager, JsonSerializer, AuthorizationContext)
|
||||
{
|
||||
Request = Request
|
||||
};
|
||||
|
||||
var playbackInfoResult = await mediaInfoService.GetPlaybackInfo(new GetPostedPlaybackInfo
|
||||
{
|
||||
Id = request.Id,
|
||||
MaxAudioChannels = request.MaxAudioChannels,
|
||||
MaxStreamingBitrate = request.MaxStreamingBitrate,
|
||||
StartTimeTicks = request.StartTimeTicks,
|
||||
UserId = request.UserId,
|
||||
DeviceProfile = deviceProfile,
|
||||
MediaSourceId = request.MediaSourceId
|
||||
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
var mediaSource = playbackInfoResult.MediaSources[0];
|
||||
|
||||
var isStatic = mediaSource.SupportsDirectStream;
|
||||
|
||||
if (!isStatic && string.Equals(mediaSource.TranscodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var service = new DynamicHlsService(ServerConfigurationManager,
|
||||
UserManager,
|
||||
LibraryManager,
|
||||
IsoManager,
|
||||
MediaEncoder,
|
||||
FileSystem,
|
||||
DlnaManager,
|
||||
SubtitleEncoder,
|
||||
DeviceManager,
|
||||
MediaSourceManager,
|
||||
ZipClient,
|
||||
JsonSerializer,
|
||||
AuthorizationContext,
|
||||
NetworkManager)
|
||||
{
|
||||
Request = Request
|
||||
};
|
||||
|
||||
var transcodingProfile = deviceProfile.TranscodingProfiles[0];
|
||||
|
||||
var newRequest = new GetMasterHlsAudioPlaylist
|
||||
{
|
||||
AudioBitRate = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
|
||||
AudioCodec = transcodingProfile.AudioCodec,
|
||||
Container = ".m3u8",
|
||||
DeviceId = request.DeviceId,
|
||||
Id = request.Id,
|
||||
MaxAudioChannels = request.MaxAudioChannels,
|
||||
MediaSourceId = mediaSource.Id,
|
||||
PlaySessionId = playbackInfoResult.PlaySessionId,
|
||||
StartTimeTicks = request.StartTimeTicks,
|
||||
Static = isStatic
|
||||
};
|
||||
|
||||
if (isHeadRequest)
|
||||
{
|
||||
return service.Head(newRequest);
|
||||
}
|
||||
return service.Get(newRequest);
|
||||
}
|
||||
else
|
||||
{
|
||||
var service = new AudioService(ServerConfigurationManager,
|
||||
UserManager,
|
||||
LibraryManager,
|
||||
IsoManager,
|
||||
MediaEncoder,
|
||||
FileSystem,
|
||||
DlnaManager,
|
||||
SubtitleEncoder,
|
||||
DeviceManager,
|
||||
MediaSourceManager,
|
||||
ZipClient,
|
||||
JsonSerializer,
|
||||
AuthorizationContext,
|
||||
ImageProcessor)
|
||||
{
|
||||
Request = Request
|
||||
};
|
||||
|
||||
var newRequest = new GetAudioStream
|
||||
{
|
||||
AudioBitRate = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
|
||||
//AudioCodec = request.AudioCodec,
|
||||
Container = isStatic ? null : ("." + mediaSource.TranscodingContainer),
|
||||
DeviceId = request.DeviceId,
|
||||
Id = request.Id,
|
||||
MaxAudioChannels = request.MaxAudioChannels,
|
||||
MediaSourceId = mediaSource.Id,
|
||||
PlaySessionId = playbackInfoResult.PlaySessionId,
|
||||
StartTimeTicks = request.StartTimeTicks,
|
||||
Static = isStatic
|
||||
};
|
||||
|
||||
if (isHeadRequest)
|
||||
{
|
||||
return service.Head(newRequest);
|
||||
}
|
||||
return service.Get(newRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -317,12 +317,6 @@ namespace MediaBrowser.Api.Reports
|
|||
query.MaxParentalRating = _localization.GetRatingLevel(request.MaxOfficialRating);
|
||||
}
|
||||
|
||||
// Albums
|
||||
if (!string.IsNullOrEmpty(request.Albums))
|
||||
{
|
||||
query.AlbumNames = request.Albums.Split('|');
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
|
|
|
@ -245,25 +245,15 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
|
||||
User user = null;
|
||||
BaseItem parentItem;
|
||||
List<BaseItem> libraryItems = null;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.UserId))
|
||||
{
|
||||
user = UserManager.GetUserById(request.UserId);
|
||||
parentItem = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId);
|
||||
|
||||
if (RequiresLibraryItems(request, dtoOptions))
|
||||
{
|
||||
libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : LibraryManager.GetItemById(request.ParentId);
|
||||
if (RequiresLibraryItems(request, dtoOptions))
|
||||
{
|
||||
libraryItems = LibraryManager.RootFolder.GetRecursiveChildren().ToList();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<BaseItem> items;
|
||||
|
@ -307,8 +297,6 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
|
||||
var filteredItems = FilterItems(request, extractedItems, user);
|
||||
|
||||
filteredItems = FilterByLibraryItems(request, filteredItems.Cast<IItemByName>(), user, libraryItems).Cast<BaseItem>();
|
||||
|
||||
filteredItems = LibraryManager.Sort(filteredItems, user, request.GetOrderBy(), request.SortOrder ?? SortOrder.Ascending);
|
||||
|
||||
var ibnItemsArray = filteredItems.ToList();
|
||||
|
@ -334,15 +322,7 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
|
||||
}
|
||||
|
||||
IEnumerable<Tuple<BaseItem, List<BaseItem>>> tuples;
|
||||
if (dtoOptions.Fields.Contains(ItemFields.ItemCounts))
|
||||
{
|
||||
tuples = ibnItems.Select(i => new Tuple<BaseItem, List<BaseItem>>(i, ((IItemByName)i).GetTaggedItems(libraryItems).ToList()));
|
||||
}
|
||||
else
|
||||
{
|
||||
tuples = ibnItems.Select(i => new Tuple<BaseItem, List<BaseItem>>(i, new List<BaseItem>()));
|
||||
}
|
||||
var tuples = ibnItems.Select(i => new Tuple<BaseItem, List<BaseItem>>(i, new List<BaseItem>()));
|
||||
|
||||
var syncProgess = DtoService.GetSyncedItemProgress(dtoOptions);
|
||||
var dtos = tuples.Select(i => DtoService.GetItemByNameDto(i.Item1, dtoOptions, i.Item2, syncProgess, user));
|
||||
|
@ -352,52 +332,6 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
return result;
|
||||
}
|
||||
|
||||
private bool RequiresLibraryItems(GetItemsByName request, DtoOptions options)
|
||||
{
|
||||
var filters = request.GetFilters().ToList();
|
||||
|
||||
if (filters.Contains(ItemFilter.IsPlayed))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (filters.Contains(ItemFilter.IsUnplayed))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (request.IsPlayed.HasValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return options.Fields.Contains(ItemFields.ItemCounts);
|
||||
}
|
||||
|
||||
private IEnumerable<IItemByName> FilterByLibraryItems(GetItemsByName request, IEnumerable<IItemByName> items, User user, IEnumerable<BaseItem> libraryItems)
|
||||
{
|
||||
var filters = request.GetFilters().ToList();
|
||||
|
||||
if (filters.Contains(ItemFilter.IsPlayed))
|
||||
{
|
||||
items = items.Where(i => i.GetTaggedItems(libraryItems).All(l => l.IsPlayed(user)));
|
||||
}
|
||||
|
||||
if (filters.Contains(ItemFilter.IsUnplayed))
|
||||
{
|
||||
items = items.Where(i => i.GetTaggedItems(libraryItems).All(l => l.IsUnplayed(user)));
|
||||
}
|
||||
|
||||
if (request.IsPlayed.HasValue)
|
||||
{
|
||||
var val = request.IsPlayed.Value;
|
||||
|
||||
items = items.Where(i => i.GetTaggedItems(libraryItems).All(l => l.IsPlayed(user)) == val);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters the items.
|
||||
/// </summary>
|
||||
|
|
|
@ -277,6 +277,8 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
[ApiMember(Name = "Albums", Description = "Optional. If specified, results will be filtered based on album. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
|
||||
public string Albums { get; set; }
|
||||
|
||||
public string AlbumIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the item ids.
|
||||
/// </summary>
|
||||
|
|
|
@ -9,6 +9,7 @@ using System.Collections.Generic;
|
|||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.Services;
|
||||
|
||||
|
@ -359,15 +360,30 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
}
|
||||
|
||||
// ExcludeArtistIds
|
||||
if (!string.IsNullOrEmpty(request.ExcludeArtistIds))
|
||||
if (!string.IsNullOrWhiteSpace(request.ExcludeArtistIds))
|
||||
{
|
||||
query.ExcludeArtistIds = request.ExcludeArtistIds.Split('|');
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.AlbumIds))
|
||||
{
|
||||
query.AlbumIds = request.AlbumIds.Split('|');
|
||||
}
|
||||
|
||||
// Albums
|
||||
if (!string.IsNullOrEmpty(request.Albums))
|
||||
{
|
||||
query.AlbumNames = request.Albums.Split('|');
|
||||
query.AlbumIds = request.Albums.Split('|').Select(i =>
|
||||
{
|
||||
return _libraryManager.GetItemList(new InternalItemsQuery
|
||||
{
|
||||
IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
|
||||
Name = i,
|
||||
Limit = 1
|
||||
|
||||
}).Select(album => album.Id.ToString("N")).FirstOrDefault();
|
||||
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
// Studios
|
||||
|
|
|
@ -360,7 +360,8 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
var currentUser = user;
|
||||
|
||||
var dtos = series
|
||||
.GetRecursiveChildren(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
|
||||
.GetEpisodes(user)
|
||||
.Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
|
||||
.OrderBy(i =>
|
||||
{
|
||||
if (i.PremiereDate.HasValue)
|
||||
|
|
|
@ -2078,9 +2078,31 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// Gets the file system path to delete when the item is to be deleted
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual IEnumerable<string> GetDeletePaths()
|
||||
public virtual IEnumerable<FileSystemMetadata> GetDeletePaths()
|
||||
{
|
||||
return new[] { Path };
|
||||
return new[] {
|
||||
new FileSystemMetadata
|
||||
{
|
||||
FullName = Path,
|
||||
IsDirectory = IsFolder
|
||||
}
|
||||
}.Concat(GetLocalMetadataFilesToDelete());
|
||||
}
|
||||
|
||||
protected List<FileSystemMetadata> GetLocalMetadataFilesToDelete()
|
||||
{
|
||||
if (IsFolder || !IsInMixedFolder)
|
||||
{
|
||||
return new List<FileSystemMetadata>();
|
||||
}
|
||||
|
||||
var filename = System.IO.Path.GetFileNameWithoutExtension(Path);
|
||||
var extensions = new[] { ".nfo", ".xml", ".srt" }.ToList();
|
||||
extensions.AddRange(SupportedImageExtensionsList);
|
||||
|
||||
return FileSystem.GetFiles(System.IO.Path.GetDirectoryName(Path))
|
||||
.Where(i => extensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase) && System.IO.Path.GetFileNameWithoutExtension(i.FullName).StartsWith(filename, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public bool AllowsMultipleImages(ImageType type)
|
||||
|
|
|
@ -4,6 +4,7 @@ using MediaBrowser.Model.Entities;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
|
@ -97,11 +98,17 @@ namespace MediaBrowser.Controller.Entities
|
|||
return list;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> GetDeletePaths()
|
||||
public override IEnumerable<FileSystemMetadata> GetDeletePaths()
|
||||
{
|
||||
if (!DetectIsInMixedFolder())
|
||||
{
|
||||
return new[] { System.IO.Path.GetDirectoryName(Path) };
|
||||
return new[] {
|
||||
new FileSystemMetadata
|
||||
{
|
||||
FullName = System.IO.Path.GetDirectoryName(Path),
|
||||
IsDirectory = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return base.GetDeletePaths();
|
||||
|
|
|
@ -141,7 +141,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
public string ExternalSeriesId { get; set; }
|
||||
public string ExternalId { get; set; }
|
||||
|
||||
public string[] AlbumNames { get; set; }
|
||||
public string[] AlbumIds { get; set; }
|
||||
public string[] ArtistIds { get; set; }
|
||||
public string[] ExcludeArtistIds { get; set; }
|
||||
public string AncestorWithPresentationUniqueKey { get; set; }
|
||||
|
@ -202,7 +202,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
EnableTotalRecordCount = true;
|
||||
|
||||
DtoOptions = new DtoOptions();
|
||||
AlbumNames = new string[] { };
|
||||
AlbumIds = new string[] { };
|
||||
ArtistIds = new string[] { };
|
||||
ExcludeArtistIds = new string[] { };
|
||||
ExcludeProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities.TV
|
||||
|
@ -319,9 +320,15 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||
return list;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> GetDeletePaths()
|
||||
public override IEnumerable<FileSystemMetadata> GetDeletePaths()
|
||||
{
|
||||
return new[] { Path };
|
||||
return new[] {
|
||||
new FileSystemMetadata
|
||||
{
|
||||
FullName = Path,
|
||||
IsDirectory = IsFolder
|
||||
}
|
||||
}.Concat(GetLocalMetadataFilesToDelete());
|
||||
}
|
||||
|
||||
public override UnratedItem GetBlockUnratedType()
|
||||
|
@ -338,7 +345,6 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||
if (series != null)
|
||||
{
|
||||
id.SeriesProviderIds = series.ProviderIds;
|
||||
id.AnimeSeriesIndex = series.AnimeSeriesIndex;
|
||||
}
|
||||
|
||||
id.IsMissingEpisode = IsMissingEpisode;
|
||||
|
|
|
@ -251,7 +251,6 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||
if (series != null)
|
||||
{
|
||||
id.SeriesProviderIds = series.ProviderIds;
|
||||
id.AnimeSeriesIndex = series.AnimeSeriesIndex;
|
||||
}
|
||||
|
||||
return id;
|
||||
|
|
|
@ -19,8 +19,6 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||
/// </summary>
|
||||
public class Series : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IMetadataContainer
|
||||
{
|
||||
public int? AnimeSeriesIndex { get; set; }
|
||||
|
||||
public Series()
|
||||
{
|
||||
AirDays = new List<DayOfWeek>();
|
||||
|
@ -554,8 +552,6 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||
{
|
||||
var info = GetItemLookupInfo<SeriesInfo>();
|
||||
|
||||
info.AnimeSeriesIndex = AnimeSeriesIndex;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
|
|
@ -1719,53 +1719,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
}
|
||||
}
|
||||
|
||||
// Artists
|
||||
if (query.ArtistIds.Length > 0)
|
||||
{
|
||||
var audio = item as IHasArtist;
|
||||
|
||||
//if (!(audio != null && query.ArtistNames.Any(audio.HasAnyArtist)))
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
}
|
||||
|
||||
// Albums
|
||||
if (query.AlbumNames.Length > 0)
|
||||
{
|
||||
var audio = item as Audio.Audio;
|
||||
|
||||
if (audio != null)
|
||||
{
|
||||
if (!query.AlbumNames.Any(a => string.Equals(a, audio.Album, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var album = item as MusicAlbum;
|
||||
|
||||
if (album != null)
|
||||
{
|
||||
if (!query.AlbumNames.Any(a => string.Equals(a, album.Name, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var musicVideo = item as MusicVideo;
|
||||
|
||||
if (musicVideo != null)
|
||||
{
|
||||
if (!query.AlbumNames.Any(a => string.Equals(a, musicVideo.Album, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -477,11 +477,17 @@ namespace MediaBrowser.Controller.Entities
|
|||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<string> GetDeletePaths()
|
||||
public override IEnumerable<FileSystemMetadata> GetDeletePaths()
|
||||
{
|
||||
if (!DetectIsInMixedFolder())
|
||||
{
|
||||
return new[] { ContainingFolderPath };
|
||||
return new[] {
|
||||
new FileSystemMetadata
|
||||
{
|
||||
FullName = System.IO.Path.GetDirectoryName(Path),
|
||||
IsDirectory = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return base.GetDeletePaths();
|
||||
|
|
|
@ -62,8 +62,6 @@ namespace MediaBrowser.Controller.Library
|
|||
/// <returns>BaseItem.</returns>
|
||||
BaseItem FindByPath(string path, bool? isFolder);
|
||||
|
||||
Guid? FindIdByPath(string path, bool? isFolder);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the artist.
|
||||
/// </summary>
|
||||
|
|
|
@ -8,7 +8,6 @@ namespace MediaBrowser.Controller.Providers
|
|||
public Dictionary<string, string> SeriesProviderIds { get; set; }
|
||||
|
||||
public int? IndexNumberEnd { get; set; }
|
||||
public int? AnimeSeriesIndex { get; set; }
|
||||
|
||||
public bool IsMissingEpisode { get; set; }
|
||||
public bool IsVirtualUnaired { get; set; }
|
||||
|
|
|
@ -6,7 +6,6 @@ namespace MediaBrowser.Controller.Providers
|
|||
public class SeasonInfo : ItemLookupInfo
|
||||
{
|
||||
public Dictionary<string, string> SeriesProviderIds { get; set; }
|
||||
public int? AnimeSeriesIndex { get; set; }
|
||||
|
||||
public SeasonInfo()
|
||||
{
|
||||
|
|
|
@ -2,6 +2,5 @@ namespace MediaBrowser.Controller.Providers
|
|||
{
|
||||
public class SeriesInfo : ItemLookupInfo
|
||||
{
|
||||
public int? AnimeSeriesIndex { get; set; }
|
||||
}
|
||||
}
|
|
@ -69,21 +69,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|||
break;
|
||||
}
|
||||
|
||||
case "AnimeSeriesIndex":
|
||||
{
|
||||
var number = reader.ReadElementContentAsString();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(number))
|
||||
{
|
||||
int num;
|
||||
|
||||
if (int.TryParse(number, out num))
|
||||
{
|
||||
item.AnimeSeriesIndex = num;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Status":
|
||||
{
|
||||
var status = reader.ReadElementContentAsString();
|
||||
|
|
|
@ -606,9 +606,20 @@ namespace MediaBrowser.Model.Dlna
|
|||
return playlistItem;
|
||||
}
|
||||
|
||||
private int GetDefaultAudioBitrateIfUnknown(MediaStream audioStream)
|
||||
{
|
||||
if ((audioStream.Channels ?? 0) >= 6)
|
||||
{
|
||||
return 384000;
|
||||
}
|
||||
|
||||
return 192000;
|
||||
}
|
||||
|
||||
private int GetAudioBitrate(string subProtocol, long? maxTotalBitrate, int? targetAudioChannels, string targetAudioCodec, MediaStream audioStream)
|
||||
{
|
||||
int defaultBitrate = audioStream == null ? 192000 : audioStream.BitRate ?? 192000;
|
||||
int defaultBitrate = audioStream == null ? 192000 : audioStream.BitRate ?? GetDefaultAudioBitrateIfUnknown(audioStream);
|
||||
|
||||
// Reduce the bitrate if we're downmixing
|
||||
if (targetAudioChannels.HasValue && audioStream != null && audioStream.Channels.HasValue && targetAudioChannels.Value < audioStream.Channels.Value)
|
||||
{
|
||||
|
|
|
@ -85,8 +85,6 @@ namespace MediaBrowser.Model.Dto
|
|||
public float? Metascore { get; set; }
|
||||
public bool? HasDynamicCategories { get; set; }
|
||||
|
||||
public int? AnimeSeriesIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether [supports synchronize].
|
||||
/// </summary>
|
||||
|
@ -263,7 +261,7 @@ namespace MediaBrowser.Model.Dto
|
|||
/// Gets or sets the play access.
|
||||
/// </summary>
|
||||
/// <value>The play access.</value>
|
||||
public PlayAccess PlayAccess { get; set; }
|
||||
public PlayAccess? PlayAccess { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the aspect ratio.
|
||||
|
@ -759,7 +757,7 @@ namespace MediaBrowser.Model.Dto
|
|||
/// Gets or sets the type of the location.
|
||||
/// </summary>
|
||||
/// <value>The type of the location.</value>
|
||||
public LocationType LocationType { get; set; }
|
||||
public LocationType? LocationType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the iso.
|
||||
|
|
|
@ -96,8 +96,7 @@ namespace MediaBrowser.Providers.TV
|
|||
|
||||
try
|
||||
{
|
||||
int seasonNumber = AdjustForSeriesOffset(series, season.IndexNumber.Value);
|
||||
AddImages(list, seasonNumber, path, cancellationToken);
|
||||
AddImages(list, season.IndexNumber.Value, path, cancellationToken);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
|
@ -139,15 +138,6 @@ namespace MediaBrowser.Providers.TV
|
|||
.ThenByDescending(i => i.VoteCount ?? 0);
|
||||
}
|
||||
|
||||
private int AdjustForSeriesOffset(Series series, int seasonNumber)
|
||||
{
|
||||
var offset = TvdbSeriesProvider.GetSeriesOffset(series.ProviderIds);
|
||||
if (offset != null)
|
||||
return (int)(seasonNumber + offset);
|
||||
|
||||
return seasonNumber;
|
||||
}
|
||||
|
||||
private void AddImages(List<RemoteImageInfo> list, int seasonNumber, string path, CancellationToken cancellationToken)
|
||||
{
|
||||
var root = _json.DeserializeFromFile<FanartSeriesProvider.RootObject>(path);
|
||||
|
|
|
@ -203,9 +203,8 @@ namespace MediaBrowser.Providers.TV
|
|||
CancellationToken cancellationToken)
|
||||
{
|
||||
var existingEpisodes = (from s in series
|
||||
let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
|
||||
from c in s.GetRecursiveChildren().OfType<Episode>()
|
||||
select new Tuple<int, Episode>((c.ParentIndexNumber ?? 0) + seasonOffset, c))
|
||||
select new Tuple<int, Episode>((c.ParentIndexNumber ?? 0) , c))
|
||||
.ToList();
|
||||
|
||||
var lookup = episodeLookup as IList<Tuple<int, int>> ?? episodeLookup.ToList();
|
||||
|
@ -248,7 +247,6 @@ namespace MediaBrowser.Providers.TV
|
|||
var now = DateTime.UtcNow;
|
||||
|
||||
var targetSeries = DetermineAppropriateSeries(series, tuple.Item1);
|
||||
var seasonOffset = TvdbSeriesProvider.GetSeriesOffset(targetSeries.ProviderIds) ?? ((targetSeries.AnimeSeriesIndex ?? 1) - 1);
|
||||
|
||||
var unairedThresholdDays = 2;
|
||||
now = now.AddDays(0 - unairedThresholdDays);
|
||||
|
@ -259,7 +257,7 @@ namespace MediaBrowser.Providers.TV
|
|||
{
|
||||
// tvdb has a lot of nearly blank episodes
|
||||
_logger.Info("Creating virtual missing episode {0} {1}x{2}", targetSeries.Name, tuple.Item1, tuple.Item2);
|
||||
await AddEpisode(targetSeries, tuple.Item1 - seasonOffset, tuple.Item2, cancellationToken).ConfigureAwait(false);
|
||||
await AddEpisode(targetSeries, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
hasChanges = true;
|
||||
}
|
||||
|
@ -268,7 +266,7 @@ namespace MediaBrowser.Providers.TV
|
|||
{
|
||||
// tvdb has a lot of nearly blank episodes
|
||||
_logger.Info("Creating virtual unaired episode {0} {1}x{2}", targetSeries.Name, tuple.Item1, tuple.Item2);
|
||||
await AddEpisode(targetSeries, tuple.Item1 - seasonOffset, tuple.Item2, cancellationToken).ConfigureAwait(false);
|
||||
await AddEpisode(targetSeries, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
hasChanges = true;
|
||||
}
|
||||
|
@ -279,13 +277,11 @@ namespace MediaBrowser.Providers.TV
|
|||
|
||||
private Series DetermineAppropriateSeries(IEnumerable<Series> series, int seasonNumber)
|
||||
{
|
||||
var seriesAndOffsets = series.Select(s => new { Series = s, SeasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1) }).ToList();
|
||||
var seriesAndOffsets = series.ToList();
|
||||
|
||||
var bestMatch = seriesAndOffsets.FirstOrDefault(s => s.Series.GetRecursiveChildren().OfType<Season>().Any(season => (season.IndexNumber + s.SeasonOffset) == seasonNumber)) ??
|
||||
seriesAndOffsets.FirstOrDefault(s => s.Series.GetRecursiveChildren().OfType<Season>().Any(season => (season.IndexNumber + s.SeasonOffset) == 1)) ??
|
||||
seriesAndOffsets.OrderBy(s => s.Series.GetRecursiveChildren().OfType<Season>().Select(season => season.IndexNumber + s.SeasonOffset).Min()).First();
|
||||
|
||||
return bestMatch.Series;
|
||||
return seriesAndOffsets.FirstOrDefault(s => s.GetRecursiveChildren().OfType<Season>().Any(season => (season.IndexNumber) == seasonNumber)) ??
|
||||
seriesAndOffsets.FirstOrDefault(s => s.GetRecursiveChildren().OfType<Season>().Any(season => (season.IndexNumber) == 1)) ??
|
||||
seriesAndOffsets.OrderBy(s => s.GetRecursiveChildren().OfType<Season>().Select(season => season.IndexNumber).Min()).First();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -296,9 +292,8 @@ namespace MediaBrowser.Providers.TV
|
|||
bool allowMissingEpisodes)
|
||||
{
|
||||
var existingEpisodes = (from s in series
|
||||
let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
|
||||
from c in s.GetRecursiveChildren().OfType<Episode>()
|
||||
select new { SeasonOffset = seasonOffset, Episode = c })
|
||||
select new { Episode = c })
|
||||
.ToList();
|
||||
|
||||
var physicalEpisodes = existingEpisodes
|
||||
|
@ -314,12 +309,12 @@ namespace MediaBrowser.Providers.TV
|
|||
{
|
||||
if (i.Episode.IndexNumber.HasValue && i.Episode.ParentIndexNumber.HasValue)
|
||||
{
|
||||
var seasonNumber = i.Episode.ParentIndexNumber.Value + i.SeasonOffset;
|
||||
var seasonNumber = i.Episode.ParentIndexNumber.Value;
|
||||
var episodeNumber = i.Episode.IndexNumber.Value;
|
||||
|
||||
// If there's a physical episode with the same season and episode number, delete it
|
||||
if (physicalEpisodes.Any(p =>
|
||||
p.Episode.ParentIndexNumber.HasValue && (p.Episode.ParentIndexNumber.Value + p.SeasonOffset) == seasonNumber &&
|
||||
p.Episode.ParentIndexNumber.HasValue && (p.Episode.ParentIndexNumber.Value) == seasonNumber &&
|
||||
p.Episode.ContainsEpisodeNumber(episodeNumber)))
|
||||
{
|
||||
return true;
|
||||
|
@ -371,28 +366,27 @@ namespace MediaBrowser.Providers.TV
|
|||
IEnumerable<Tuple<int, int>> episodeLookup)
|
||||
{
|
||||
var existingSeasons = (from s in series
|
||||
let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
|
||||
from c in s.Children.OfType<Season>()
|
||||
select new { SeasonOffset = seasonOffset, Season = c })
|
||||
select c)
|
||||
.ToList();
|
||||
|
||||
var physicalSeasons = existingSeasons
|
||||
.Where(i => i.Season.LocationType != LocationType.Virtual)
|
||||
.Where(i => i.LocationType != LocationType.Virtual)
|
||||
.ToList();
|
||||
|
||||
var virtualSeasons = existingSeasons
|
||||
.Where(i => i.Season.LocationType == LocationType.Virtual)
|
||||
.Where(i => i.LocationType == LocationType.Virtual)
|
||||
.ToList();
|
||||
|
||||
var seasonsToRemove = virtualSeasons
|
||||
.Where(i =>
|
||||
{
|
||||
if (i.Season.IndexNumber.HasValue)
|
||||
if (i.IndexNumber.HasValue)
|
||||
{
|
||||
var seasonNumber = i.Season.IndexNumber.Value + i.SeasonOffset;
|
||||
var seasonNumber = i.IndexNumber.Value;
|
||||
|
||||
// If there's a physical season with the same number, delete it
|
||||
if (physicalSeasons.Any(p => p.Season.IndexNumber.HasValue && (p.Season.IndexNumber.Value + p.SeasonOffset) == seasonNumber && string.Equals(p.Season.Series.PresentationUniqueKey, i.Season.Series.PresentationUniqueKey, StringComparison.Ordinal)))
|
||||
if (physicalSeasons.Any(p => p.IndexNumber.HasValue && (p.IndexNumber.Value) == seasonNumber && string.Equals(p.Series.PresentationUniqueKey, i.Series.PresentationUniqueKey, StringComparison.Ordinal)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -408,13 +402,13 @@ namespace MediaBrowser.Providers.TV
|
|||
|
||||
// Season does not have a number
|
||||
// Remove if there are no episodes directly in series without a season number
|
||||
return i.Season.Series.GetRecursiveChildren().OfType<Episode>().All(s => s.ParentIndexNumber.HasValue || s.IsInSeasonFolder);
|
||||
return i.Series.GetRecursiveChildren().OfType<Episode>().All(s => s.ParentIndexNumber.HasValue || s.IsInSeasonFolder);
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var hasChanges = false;
|
||||
|
||||
foreach (var seasonToRemove in seasonsToRemove.Select(s => s.Season))
|
||||
foreach (var seasonToRemove in seasonsToRemove)
|
||||
{
|
||||
_logger.Info("Removing virtual season {0} {1}", seasonToRemove.Series.Name, seasonToRemove.IndexNumber);
|
||||
|
||||
|
|
|
@ -59,7 +59,6 @@ namespace MediaBrowser.Providers.TV
|
|||
{
|
||||
// Process images
|
||||
var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.ProviderIds);
|
||||
var indexOffset = TvdbSeriesProvider.GetSeriesOffset(series.ProviderIds) ?? 0;
|
||||
|
||||
var nodes = TvdbEpisodeProvider.Current.GetEpisodeXmlNodes(seriesDataPath, episode.GetLookupInfo());
|
||||
|
||||
|
|
|
@ -99,15 +99,6 @@ namespace MediaBrowser.Providers.TV
|
|||
return new RemoteImageInfo[] { };
|
||||
}
|
||||
|
||||
private int AdjustForSeriesOffset(Series series, int seasonNumber)
|
||||
{
|
||||
var offset = TvdbSeriesProvider.GetSeriesOffset(series.ProviderIds);
|
||||
if (offset != null)
|
||||
return (seasonNumber + offset.Value);
|
||||
|
||||
return seasonNumber;
|
||||
}
|
||||
|
||||
internal static IEnumerable<RemoteImageInfo> GetImages(string xmlPath, string preferredLanguage, int seasonNumber, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem, CancellationToken cancellationToken)
|
||||
{
|
||||
var settings = xmlReaderSettingsFactory.Create(false);
|
||||
|
|
|
@ -76,10 +76,6 @@ namespace MediaBrowser.Providers.TV
|
|||
|
||||
try
|
||||
{
|
||||
var seriesOffset = TvdbSeriesProvider.GetSeriesOffset(item.ProviderIds);
|
||||
if (seriesOffset != null && seriesOffset.Value != 0)
|
||||
return TvdbSeasonImageProvider.GetImages(path, language, seriesOffset.Value + 1, _xmlReaderSettingsFactory, _fileSystem, cancellationToken);
|
||||
|
||||
return GetImages(path, language, cancellationToken);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
|
|
|
@ -27,9 +27,6 @@ namespace MediaBrowser.Providers.TV
|
|||
{
|
||||
public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder
|
||||
{
|
||||
private const string TvdbSeriesOffset = "TvdbSeriesOffset";
|
||||
private const string TvdbSeriesOffsetFormat = "{0}-{1}";
|
||||
|
||||
internal readonly SemaphoreSlim TvDbResourcePool = new SemaphoreSlim(2, 2);
|
||||
internal static TvdbSeriesProvider Current { get; private set; }
|
||||
private readonly IZipClient _zipClient;
|
||||
|
@ -123,23 +120,6 @@ namespace MediaBrowser.Providers.TV
|
|||
return result;
|
||||
}
|
||||
|
||||
internal static int? GetSeriesOffset(Dictionary<string, string> seriesProviderIds)
|
||||
{
|
||||
string idString;
|
||||
if (!seriesProviderIds.TryGetValue(TvdbSeriesOffset, out idString))
|
||||
return null;
|
||||
|
||||
var parts = idString.Split('-');
|
||||
if (parts.Length < 2)
|
||||
return null;
|
||||
|
||||
int offset;
|
||||
if (int.TryParse(parts[1], out offset))
|
||||
return offset;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the series data.
|
||||
/// </summary>
|
||||
|
|
|
@ -65,22 +65,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
|
|||
break;
|
||||
}
|
||||
|
||||
case "animeseriesindex":
|
||||
{
|
||||
var number = reader.ReadElementContentAsString();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(number))
|
||||
{
|
||||
int num;
|
||||
|
||||
if (int.TryParse(number, out num))
|
||||
{
|
||||
item.AnimeSeriesIndex = num;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "status":
|
||||
{
|
||||
var status = reader.ReadElementContentAsString();
|
||||
|
|
|
@ -83,11 +83,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
|
|||
{
|
||||
writer.WriteElementString("airs_dayofweek", series.AirDays[0].ToString());
|
||||
}
|
||||
|
||||
if (series.AnimeSeriesIndex.HasValue)
|
||||
{
|
||||
writer.WriteElementString("animeseriesindex", series.AnimeSeriesIndex.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
|
||||
protected override List<string> GetTagsUsed(IHasMetadata item)
|
||||
|
@ -101,8 +96,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
|
|||
"episode",
|
||||
"status",
|
||||
"airs_time",
|
||||
"airs_dayofweek",
|
||||
"animeseriesindex"
|
||||
"airs_dayofweek"
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion("3.2.1.108")]
|
||||
[assembly: AssemblyVersion("3.2.1.109")]
|
||||
|
|
Loading…
Reference in New Issue
Block a user