diff --git a/Emby.Server.Core/ApplicationHost.cs b/Emby.Server.Core/ApplicationHost.cs index a3c228a58..2163c4e47 100644 --- a/Emby.Server.Core/ApplicationHost.cs +++ b/Emby.Server.Core/ApplicationHost.cs @@ -863,7 +863,7 @@ namespace Emby.Server.Core /// private void ConfigureNotificationsRepository() { - var repo = new SqliteNotificationsRepository(LogManager.GetLogger("SqliteNotificationsRepository"), ServerConfigurationManager.ApplicationPaths); + var repo = new SqliteNotificationsRepository(LogManager.GetLogger("SqliteNotificationsRepository"), ServerConfigurationManager.ApplicationPaths, FileSystemManager); repo.Initialize(); diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 4a4a1a6bf..30fa68d95 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -3874,6 +3874,25 @@ namespace Emby.Server.Implementations.Data whereClauses.Add(clause); } + if (query.AlbumIds.Length > 0) + { + var clauses = new List(); + 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(); @@ -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) diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index c2cefe754..5b0bd8bbc 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -492,7 +492,10 @@ namespace Emby.Server.Implementations.Dto } } - dto.PlayAccess = item.GetPlayAccess(user); + //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; - dto.LocationType = item.LocationType; + + 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(); - dto.CommunityRating = item.CommunityRating; + 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,9 +1482,12 @@ namespace Emby.Server.Implementations.Dto SetBookProperties(dto, book); } - if (item.ProductionLocations.Count > 0 || item is Movie) + if (fields.Contains(ItemFields.ProductionLocations)) { - dto.ProductionLocations = item.ProductionLocations.ToArray(); + if (item.ProductionLocations.Count > 0 || item is Movie) + { + dto.ProductionLocations = item.ProductionLocations.ToArray(); + } } var photo = item as Photo; diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 616c6c1a2..56bffc233 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -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. diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index f11cbd498..9e1291847 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -248,6 +248,13 @@ namespace Emby.Server.Implementations.Library } } + var isPlayed = request.IsPlayed; + + if (parents.OfType().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); } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index bbb060203..7aae0d68a 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -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 { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 68126f926..5adb0b3c6 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -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; } diff --git a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs index 2ee6869f6..e2f973699 100644 --- a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs @@ -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); } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index b139c68f4..d5ea0d493 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -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; diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index e25e28484..747e0fdd3 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -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) diff --git a/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs b/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs index f18278cb2..76c7a7d77 100644 --- a/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs +++ b/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs @@ -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 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()) { diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 7be04d892..db5914f81 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -59,6 +59,7 @@ + diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index d1c3de427..e561b3f46 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -126,14 +126,10 @@ namespace MediaBrowser.Api.Playback /// /// Gets the output file path. /// - /// The state. - /// System.String. - 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); @@ -802,7 +798,7 @@ namespace MediaBrowser.Api.Playback { state.User = UserManager.GetUserById(auth.UserId); } - + //if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 || // (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 || // (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1) @@ -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; } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index cfd7471c4..1074a8bc1 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -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; diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index fcb8c34f3..80885271c 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -146,7 +146,7 @@ namespace MediaBrowser.Api.Playback Task.WaitAll(task); } - public async Task Post(GetPostedPlaybackInfo request) + public async Task 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 Post(GetPostedPlaybackInfo request) + { + var result = await GetPlaybackInfo(request).ConfigureAwait(false); + + return ToOptimizedResult(result); } private T Clone(T obj) diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index a0ab90664..04825c7a5 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -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; } } /// diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs index 6bdb30890..f223c99ef 100644 --- a/MediaBrowser.Api/Playback/StreamRequest.cs +++ b/MediaBrowser.Api/Playback/StreamRequest.cs @@ -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; } + /// /// Gets or sets the audio codec. /// diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs new file mode 100644 index 000000000..a52ae1df4 --- /dev/null +++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs @@ -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 + { + /// + /// Gets or sets the id. + /// + /// The id. + [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 Get(GetUniversalAudioStream request) + { + return GetUniversalStream(request, false); + } + + public Task Head(GetUniversalAudioStream request) + { + return GetUniversalStream(request, true); + } + + private DeviceProfile GetDeviceProfile(GetUniversalAudioStream request) + { + var deviceProfile = new DeviceProfile(); + + var directPlayProfiles = new List(); + + 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 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); + } + } + } +} diff --git a/MediaBrowser.Api/Reports/ReportsService.cs b/MediaBrowser.Api/Reports/ReportsService.cs index cb275fa29..5e13c1653 100644 --- a/MediaBrowser.Api/Reports/ReportsService.cs +++ b/MediaBrowser.Api/Reports/ReportsService.cs @@ -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; } diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index 5b939244e..c1cc1555d 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -245,25 +245,15 @@ namespace MediaBrowser.Api.UserLibrary User user = null; BaseItem parentItem; - List 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 items; @@ -307,8 +297,6 @@ namespace MediaBrowser.Api.UserLibrary var filteredItems = FilterItems(request, extractedItems, user); - filteredItems = FilterByLibraryItems(request, filteredItems.Cast(), user, libraryItems).Cast(); - filteredItems = LibraryManager.Sort(filteredItems, user, request.GetOrderBy(), request.SortOrder ?? SortOrder.Ascending); var ibnItemsArray = filteredItems.ToList(); @@ -334,15 +322,7 @@ namespace MediaBrowser.Api.UserLibrary } - IEnumerable>> tuples; - if (dtoOptions.Fields.Contains(ItemFields.ItemCounts)) - { - tuples = ibnItems.Select(i => new Tuple>(i, ((IItemByName)i).GetTaggedItems(libraryItems).ToList())); - } - else - { - tuples = ibnItems.Select(i => new Tuple>(i, new List())); - } + var tuples = ibnItems.Select(i => new Tuple>(i, new List())); 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 FilterByLibraryItems(GetItemsByName request, IEnumerable items, User user, IEnumerable 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; - } - /// /// Filters the items. /// diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs index 0594691a2..1acbce6db 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs @@ -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; } + /// /// Gets or sets the item ids. /// diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 8300beac3..5d267d059 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -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 diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index 08aba90d2..32f3a1f00 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -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) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 0efb7ade8..52e150aa4 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -156,7 +156,7 @@ namespace MediaBrowser.Controller.Entities { if (SupportsIsInMixedFolderDetection) { - + } return IsInMixedFolder; @@ -2078,9 +2078,31 @@ namespace MediaBrowser.Controller.Entities /// Gets the file system path to delete when the item is to be deleted /// /// - public virtual IEnumerable GetDeletePaths() + public virtual IEnumerable GetDeletePaths() { - return new[] { Path }; + return new[] { + new FileSystemMetadata + { + FullName = Path, + IsDirectory = IsFolder + } + }.Concat(GetLocalMetadataFilesToDelete()); + } + + protected List GetLocalMetadataFilesToDelete() + { + if (IsFolder || !IsInMixedFolder) + { + return new List(); + } + + 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) diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs index 8bfb8be99..d19552c07 100644 --- a/MediaBrowser.Controller/Entities/Game.cs +++ b/MediaBrowser.Controller/Entities/Game.cs @@ -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 GetDeletePaths() + public override IEnumerable 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(); diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index f03531270..ea4d60a44 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -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(StringComparer.OrdinalIgnoreCase); diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index d2ea8d315..31bf8d28b 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -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,10 +320,16 @@ namespace MediaBrowser.Controller.Entities.TV return list; } - public override IEnumerable GetDeletePaths() - { - return new[] { Path }; - } + public override IEnumerable GetDeletePaths() + { + 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; diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 2ff7e4ce5..be268782d 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -251,7 +251,6 @@ namespace MediaBrowser.Controller.Entities.TV if (series != null) { id.SeriesProviderIds = series.ProviderIds; - id.AnimeSeriesIndex = series.AnimeSeriesIndex; } return id; diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 9388160bd..7641c9523 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -19,8 +19,6 @@ namespace MediaBrowser.Controller.Entities.TV /// public class Series : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo, IMetadataContainer { - public int? AnimeSeriesIndex { get; set; } - public Series() { AirDays = new List(); @@ -554,8 +552,6 @@ namespace MediaBrowser.Controller.Entities.TV { var info = GetItemLookupInfo(); - info.AnimeSeriesIndex = AnimeSeriesIndex; - return info; } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 6e0f4ada9..f879d0fd8 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -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; } diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index fb9c3d213..78f907d61 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -477,11 +477,17 @@ namespace MediaBrowser.Controller.Entities } } - public override IEnumerable GetDeletePaths() + public override IEnumerable GetDeletePaths() { if (!DetectIsInMixedFolder()) { - return new[] { ContainingFolderPath }; + return new[] { + new FileSystemMetadata + { + FullName = System.IO.Path.GetDirectoryName(Path), + IsDirectory = true + } + }; } return base.GetDeletePaths(); diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 33cd4f3d1..ebebe71a3 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -62,8 +62,6 @@ namespace MediaBrowser.Controller.Library /// BaseItem. BaseItem FindByPath(string path, bool? isFolder); - Guid? FindIdByPath(string path, bool? isFolder); - /// /// Gets the artist. /// diff --git a/MediaBrowser.Controller/Providers/EpisodeInfo.cs b/MediaBrowser.Controller/Providers/EpisodeInfo.cs index b879040f8..b8e88ea53 100644 --- a/MediaBrowser.Controller/Providers/EpisodeInfo.cs +++ b/MediaBrowser.Controller/Providers/EpisodeInfo.cs @@ -8,7 +8,6 @@ namespace MediaBrowser.Controller.Providers public Dictionary SeriesProviderIds { get; set; } public int? IndexNumberEnd { get; set; } - public int? AnimeSeriesIndex { get; set; } public bool IsMissingEpisode { get; set; } public bool IsVirtualUnaired { get; set; } diff --git a/MediaBrowser.Controller/Providers/SeasonInfo.cs b/MediaBrowser.Controller/Providers/SeasonInfo.cs index 2c785d7d7..31af268b8 100644 --- a/MediaBrowser.Controller/Providers/SeasonInfo.cs +++ b/MediaBrowser.Controller/Providers/SeasonInfo.cs @@ -6,7 +6,6 @@ namespace MediaBrowser.Controller.Providers public class SeasonInfo : ItemLookupInfo { public Dictionary SeriesProviderIds { get; set; } - public int? AnimeSeriesIndex { get; set; } public SeasonInfo() { diff --git a/MediaBrowser.Controller/Providers/SeriesInfo.cs b/MediaBrowser.Controller/Providers/SeriesInfo.cs index 387865de2..0b1361757 100644 --- a/MediaBrowser.Controller/Providers/SeriesInfo.cs +++ b/MediaBrowser.Controller/Providers/SeriesInfo.cs @@ -2,6 +2,5 @@ namespace MediaBrowser.Controller.Providers { public class SeriesInfo : ItemLookupInfo { - public int? AnimeSeriesIndex { get; set; } } } \ No newline at end of file diff --git a/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs index 2ddd84378..f5352add7 100644 --- a/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs @@ -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(); diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 129b49cf6..480eb23a5 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -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) { diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index c69bbb581..ff3f0be59 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -85,8 +85,6 @@ namespace MediaBrowser.Model.Dto public float? Metascore { get; set; } public bool? HasDynamicCategories { get; set; } - public int? AnimeSeriesIndex { get; set; } - /// /// Gets or sets a value indicating whether [supports synchronize]. /// @@ -263,7 +261,7 @@ namespace MediaBrowser.Model.Dto /// Gets or sets the play access. /// /// The play access. - public PlayAccess PlayAccess { get; set; } + public PlayAccess? PlayAccess { get; set; } /// /// Gets or sets the aspect ratio. @@ -759,7 +757,7 @@ namespace MediaBrowser.Model.Dto /// Gets or sets the type of the location. /// /// The type of the location. - public LocationType LocationType { get; set; } + public LocationType? LocationType { get; set; } /// /// Gets or sets the type of the iso. diff --git a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs index 884fa297f..f1fc2c9f3 100644 --- a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs @@ -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 list, int seasonNumber, string path, CancellationToken cancellationToken) { var root = _json.DeserializeFromFile(path); diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 1fd04070b..d5154b1d3 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -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() - select new Tuple((c.ParentIndexNumber ?? 0) + seasonOffset, c)) + select new Tuple((c.ParentIndexNumber ?? 0) , c)) .ToList(); var lookup = episodeLookup as IList> ?? 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, 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().Any(season => (season.IndexNumber + s.SeasonOffset) == seasonNumber)) ?? - seriesAndOffsets.FirstOrDefault(s => s.Series.GetRecursiveChildren().OfType().Any(season => (season.IndexNumber + s.SeasonOffset) == 1)) ?? - seriesAndOffsets.OrderBy(s => s.Series.GetRecursiveChildren().OfType().Select(season => season.IndexNumber + s.SeasonOffset).Min()).First(); - - return bestMatch.Series; + return seriesAndOffsets.FirstOrDefault(s => s.GetRecursiveChildren().OfType().Any(season => (season.IndexNumber) == seasonNumber)) ?? + seriesAndOffsets.FirstOrDefault(s => s.GetRecursiveChildren().OfType().Any(season => (season.IndexNumber) == 1)) ?? + seriesAndOffsets.OrderBy(s => s.GetRecursiveChildren().OfType().Select(season => season.IndexNumber).Min()).First(); } /// @@ -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() - 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> episodeLookup) { var existingSeasons = (from s in series - let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1) from c in s.Children.OfType() - 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().All(s => s.ParentIndexNumber.HasValue || s.IsInSeasonFolder); + return i.Series.GetRecursiveChildren().OfType().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); diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index 791f56beb..989748846 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -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()); diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index e189c292c..2c4ccddd7 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -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 GetImages(string xmlPath, string preferredLanguage, int seasonNumber, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem, CancellationToken cancellationToken) { var settings = xmlReaderSettingsFactory.Create(false); diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index 2595ad585..97eedfa92 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -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) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 1798299e8..8769dc801 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -27,9 +27,6 @@ namespace MediaBrowser.Providers.TV { public class TvdbSeriesProvider : IRemoteMetadataProvider, 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 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; - } - /// /// Fetches the series data. /// diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs index 57238ef87..98016f4f7 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs @@ -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(); diff --git a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs index 9e48b0c8b..e9a5d4d60 100644 --- a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs @@ -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 GetTagsUsed(IHasMetadata item) @@ -101,8 +96,7 @@ namespace MediaBrowser.XbmcMetadata.Savers "episode", "status", "airs_time", - "airs_dayofweek", - "animeseriesindex" + "airs_dayofweek" }); return list; } diff --git a/SharedVersion.cs b/SharedVersion.cs index b31032cba..833542510 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.1.108")] +[assembly: AssemblyVersion("3.2.1.109")]