diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index b950625673..e588068d0d 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -187,7 +187,7 @@ namespace MediaBrowser.Api.Playback if (!state.HasMediaStreams) { - return state.IsInputVideo ? "-map -0:s" : string.Empty; + return state.IsInputVideo ? "-sn" : string.Empty; } if (state.VideoStream != null) @@ -1493,7 +1493,7 @@ namespace MediaBrowser.Api.Playback if (videoRequest != null) { - if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream, state.VideoType)) + if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream)) { videoRequest.VideoCodec = "copy"; } @@ -1507,19 +1507,13 @@ namespace MediaBrowser.Api.Playback return state; } - private bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream, VideoType videoType) + private bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream) { if (videoStream.IsInterlaced) { return false; } - // Not going to attempt this with folder rips - if (videoType != VideoType.VideoFile) - { - return false; - } - // Source and target codecs must match if (!string.Equals(request.VideoCodec, videoStream.Codec, StringComparison.OrdinalIgnoreCase)) { diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 6e71e503f3..b5cd1bd409 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -24,7 +24,8 @@ namespace MediaBrowser.Api.Playback.Hls /// public abstract class BaseHlsService : BaseStreamingService { - protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager) + protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager) + : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager) { } @@ -77,6 +78,7 @@ namespace MediaBrowser.Api.Playback.Hls return ProcessRequestAsync(request).Result; } + private static readonly SemaphoreSlim FfmpegStartLock = new SemaphoreSlim(1, 1); /// /// Processes the request async. /// @@ -103,31 +105,40 @@ namespace MediaBrowser.Api.Playback.Hls } var playlist = GetOutputFilePath(state); - var isPlaylistNewlyCreated = false; - // If the playlist doesn't already exist, startup ffmpeg - if (!File.Exists(playlist)) - { - isPlaylistNewlyCreated = true; - - try - { - await StartFfMpeg(state, playlist).ConfigureAwait(false); - } - catch - { - state.Dispose(); - throw; - } - } - else + if (File.Exists(playlist)) { ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls); } - - if (isPlaylistNewlyCreated) + else { - await WaitForMinimumSegmentCount(playlist, GetSegmentWait()).ConfigureAwait(false); + await FfmpegStartLock.WaitAsync().ConfigureAwait(false); + try + { + if (File.Exists(playlist)) + { + ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls); + } + else + { + // If the playlist doesn't already exist, startup ffmpeg + try + { + await StartFfMpeg(state, playlist).ConfigureAwait(false); + } + catch + { + state.Dispose(); + throw; + } + } + + await WaitForMinimumSegmentCount(playlist, GetSegmentWait()).ConfigureAwait(false); + } + finally + { + FfmpegStartLock.Release(); + } } int audioBitrate; @@ -295,7 +306,7 @@ namespace MediaBrowser.Api.Playback.Hls // If performSubtitleConversions is true we're actually starting ffmpeg var startNumberParam = performSubtitleConversions ? GetStartNumber(state).ToString(UsCulture) : "0"; - + var args = string.Format("{0} {1} -i {2}{3} -map_metadata -1 -threads {4} {5} {6} -sc_threshold 0 {7} -hls_time {8} -start_number {9} -hls_list_size {10} \"{11}\"", itsOffset, inputModifier, diff --git a/MediaBrowser.Dlna/PlayTo/Device.cs b/MediaBrowser.Dlna/PlayTo/Device.cs index 2d0b3955bd..fc62f9780a 100644 --- a/MediaBrowser.Dlna/PlayTo/Device.cs +++ b/MediaBrowser.Dlna/PlayTo/Device.cs @@ -210,6 +210,9 @@ namespace MediaBrowser.Dlna.PlayTo return SetVolume(tmp); } + /// + /// Sets volume on a scale of 0-100 + /// public async Task SetVolume(int value) { var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetVolume"); diff --git a/MediaBrowser.Dlna/PlayTo/DlnaController.cs b/MediaBrowser.Dlna/PlayTo/DlnaController.cs index 96c92b830b..830d478297 100644 --- a/MediaBrowser.Dlna/PlayTo/DlnaController.cs +++ b/MediaBrowser.Dlna/PlayTo/DlnaController.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Net; +using System.Globalization; +using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Entities; @@ -206,7 +207,8 @@ namespace MediaBrowser.Dlna.PlayTo IsPaused = _device.IsPaused, MediaSourceId = playlistItem.MediaSourceId, AudioStreamIndex = playlistItem.AudioStreamIndex, - SubtitleStreamIndex = playlistItem.SubtitleStreamIndex + SubtitleStreamIndex = playlistItem.SubtitleStreamIndex, + VolumeLevel = _device.Volume }).ConfigureAwait(false); } @@ -614,6 +616,8 @@ namespace MediaBrowser.Dlna.PlayTo } } + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken) { GeneralCommandType commandType; @@ -632,6 +636,24 @@ namespace MediaBrowser.Dlna.PlayTo return _device.VolumeUp(true); case GeneralCommandType.ToggleMute: return _device.ToggleMute(); + case GeneralCommandType.SetVolume: + { + string volumeArg; + + if (command.Arguments.TryGetValue("Volume", out volumeArg)) + { + int volume; + + if (int.TryParse(volumeArg, NumberStyles.Any, _usCulture, out volume)) + { + return _device.SetVolume(volume); + } + + throw new ArgumentException("Unsupported volume value supplied."); + } + + throw new ArgumentException("Volume argument cannot be null"); + } default: return Task.FromResult(true); } diff --git a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs index 1730245be7..5cc3f13e54 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs @@ -265,7 +265,8 @@ namespace MediaBrowser.Dlna.PlayTo GeneralCommandType.VolumeUp.ToString(), GeneralCommandType.Mute.ToString(), GeneralCommandType.Unmute.ToString(), - GeneralCommandType.ToggleMute.ToString() + GeneralCommandType.ToggleMute.ToString(), + GeneralCommandType.SetVolume.ToString() } }); diff --git a/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs b/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs index 2e8d9044d3..2c0b3a2c94 100644 --- a/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs +++ b/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs @@ -57,8 +57,17 @@ namespace MediaBrowser.Dlna.Server { builder.Append("" + SecurityElement.Escape(_serverUdn) + ""); builder.Append("" + SecurityElement.Escape(_profile.XDlnaCap ?? string.Empty) + ""); - builder.Append("M-DMS-1.50"); - builder.Append("" + SecurityElement.Escape(_profile.XDlnaDoc ?? string.Empty) + ""); + + if (!string.IsNullOrWhiteSpace(_profile.XDlnaDoc)) + { + builder.Append("" + + SecurityElement.Escape(_profile.XDlnaDoc) + ""); + } + else + { + builder.Append("DMS-1.50"); + } + builder.Append("" + SecurityElement.Escape(_profile.FriendlyName ?? string.Empty) + ""); builder.Append("urn:schemas-upnp-org:device:MediaServer:1"); builder.Append("" + SecurityElement.Escape(_profile.Manufacturer ?? string.Empty) + ""); @@ -99,7 +108,7 @@ namespace MediaBrowser.Dlna.Server foreach (var service in GetServices()) { - builder.Append(""); + builder.Append(""); builder.Append("" + SecurityElement.Escape(service.ServiceType ?? string.Empty) + ""); builder.Append("" + SecurityElement.Escape(service.ServiceId ?? string.Empty) + ""); @@ -107,7 +116,7 @@ namespace MediaBrowser.Dlna.Server builder.Append("" + SecurityElement.Escape(service.ControlUrl ?? string.Empty) + ""); builder.Append("" + SecurityElement.Escape(service.EventSubUrl ?? string.Empty) + ""); - builder.Append(""); + builder.Append(""); } builder.Append(""); diff --git a/MediaBrowser.Dlna/Server/SsdpHandler.cs b/MediaBrowser.Dlna/Server/SsdpHandler.cs index 10f0101efa..c908eb4a8e 100644 --- a/MediaBrowser.Dlna/Server/SsdpHandler.cs +++ b/MediaBrowser.Dlna/Server/SsdpHandler.cs @@ -147,7 +147,7 @@ namespace MediaBrowser.Dlna.Server foreach (var d in Devices) { - if (!string.IsNullOrEmpty(req) && req != d.Type) + if (!string.IsNullOrEmpty(req) && !string.Equals(req, d.Type, StringComparison.OrdinalIgnoreCase)) { continue; } @@ -263,13 +263,19 @@ namespace MediaBrowser.Dlna.Server } } - foreach (var t in new[] { "upnp:rootdevice", "urn:schemas-upnp-org:device:MediaServer:1", "urn:schemas-upnp-org:service:ContentDirectory:1", "uuid:" + uuid }) + foreach (var t in new[] + { + "upnp:rootdevice", + "urn:schemas-upnp-org:device:MediaServer:1", + "urn:schemas-upnp-org:service:ContentDirectory:1", + "uuid:" + uuid + }) { list.Add(new UpnpDevice(uuid, t, descriptor, address)); } NotifyAll(); - _logger.Debug("Registered mount {0}", uuid); + _logger.Debug("Registered mount {0} at {1}", uuid, descriptor); } private void UnregisterNotification(Guid uuid) diff --git a/MediaBrowser.Model/Dto/UserDto.cs b/MediaBrowser.Model/Dto/UserDto.cs index dcf0843fec..efbd64343d 100644 --- a/MediaBrowser.Model/Dto/UserDto.cs +++ b/MediaBrowser.Model/Dto/UserDto.cs @@ -1,7 +1,7 @@ -using System.ComponentModel; -using System.Diagnostics; -using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Configuration; using System; +using System.ComponentModel; +using System.Diagnostics; using System.Runtime.Serialization; namespace MediaBrowser.Model.Dto diff --git a/MediaBrowser.Model/Session/SessionInfoDto.cs b/MediaBrowser.Model/Session/SessionInfoDto.cs index f235cebb30..df7bdd7ecb 100644 --- a/MediaBrowser.Model/Session/SessionInfoDto.cs +++ b/MediaBrowser.Model/Session/SessionInfoDto.cs @@ -51,6 +51,12 @@ namespace MediaBrowser.Model.Session /// The user id. public string UserId { get; set; } + /// + /// Gets or sets the user primary image tag. + /// + /// The user primary image tag. + public Guid? UserPrimaryImageTag { get; set; } + /// /// Gets or sets the name of the user. /// diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index b92e823859..372e4addf4 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -34,7 +34,10 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio { var collectionType = args.GetCollectionType(); - if (string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase)) + var isStandalone = args.Parent == null; + + if (isStandalone || + string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase)) { return new Controller.Entities.Audio.Audio(); } diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index fe120737f3..d16e0be991 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -450,5 +450,70 @@ "LabelMinResumeDuration": "Min resume duration (seconds):", "LabelMinResumePercentageHelp": "Titles are assumed unplayed if stopped before this time", "LabelMaxResumePercentageHelp": "Titles are assumed fully played if stopped after this time", - "LabelMinResumeDurationHelp": "Titles shorter than this will not be resumable" + "LabelMinResumeDurationHelp": "Titles shorter than this will not be resumable", + "TitleAutoOrganize": "Auto-Organize", + "TabActivityLog": "Activity Log", + "HeaderName": "Name", + "HeaderDate": "Date", + "HeaderSource": "Source", + "HeaderStatus": "Status", + "HeaderDestination": "Destination", + "HeaderProgram": "Program", + "HeaderClients": "Clients", + "LabelCompleted": "Completed", + "LabelFailed": "Failed", + "LabelSkipped": "Skipped", + "HeaderEpisodeOrganization": "Episode Organization", + "LabelSeries": "Series:", + "LabelSeasonNumber": "Season number:", + "LabelEpisodeNumber": "Episode number:", + "LabelEndingEpisodeNumber": "Ending episode number:", + "LabelEndingEpisodeNumberHelp": "Only required for multi-episode files", + "HeaderSupportTheTeam": "Support the Media Browser Team", + "LabelSupportAmount": "Amount (USD)", + "HeaderSupportTheTeamHelp": "Help ensure the continued development of this project by donating. A portion of all donations will be contributed to other free tools we depend on.", + "ButtonEnterSupporterKey": "Enter supporter key", + "DonationNextStep": "Once complete, please return and enter your supporter key, which you will receive by email.", + "AutoOrganizeHelp": "Auto-organize monitors your download folders for new files and moves them to your media directories.", + "AutoOrganizeTvHelp": "TV file organizing will only add episodes to existing series. It will not create new series folders.", + "OptionEnableEpisodeOrganization": "Enable new episode organization", + "LabelWatchFolder": "Watch folder:", + "LabelWatchFolderHelp": "The server will poll this folder during the 'Organize new media files' scheduled task.", + "ButtonViewScheduledTasks": "View scheduled tasks", + "LabelMinFileSizeForOrganize": "Minimum file size (MB):", + "LabelMinFileSizeForOrganizeHelp": "Files under this size will be ignored.", + "LabelSeasonFolderPattern": "Season folder pattern:", + "LabelSeasonZeroFolderName": "Season zero folder name:", + "HeaderEpisodeFilePattern": "Episode file pattern", + "LabelEpisodePattern": "Episode pattern:", + "LabelMultiEpisodePattern": "Multi-Episode pattern:", + "HeaderSupportedPatterns": "Supported Patterns", + "HeaderTerm": "Term", + "HeaderPattern": "Pattern", + "HeaderResult": "Result", + "LabelDeleteEmptyFolders": "Delete empty folders after organizing", + "LabelDeleteEmptyFoldersHelp": "Enable this to keep the download directory clean.", + "LabelDeleteLeftOverFiles": "Delete left over files with the following extensions:", + "LabelDeleteLeftOverFilesHelp": "Separate with ;. For example: .nfo;.txt", + "OptionOverwriteExistingEpisodes": "Overwrite existing episodes", + "LabelTransferMethod": "Transfer method", + "OptionCopy": "Copy", + "OptionMove": "Move", + "LabelTransferMethodHelp": "Copy or move files from the watch folder", + "HeaderLatestNews": "Latest News", + "HeaderHelpImproveMediaBrowser": "Help Improve Media Browser", + "HeaderRunningTasks": "Running Tasks", + "HeaderActiveDevices": "Active Devices", + "HeaderPendingInstallations": "Pending Installations", + "HeaerServerInformation": "Server Information", + "ButtonRestartNow": "Restart Now", + "ButtonRestart": "Restart", + "ButtonShutdown": "Shutdown", + "ButtonUpdateNow": "Update Now", + "PleaseUpdateManually": "Please shutdown the server and update manually.", + "NewServerVersionAvailable": "A new version of Media Browser Server is available!", + "ServerUpToDate": "Media Browser Server is up to date", + "ErrorConnectingToMediaBrowserRepository": "There was an error connecting to the remote Media Browser repository.", + "LabelComponentsUpdated": "The following components have been installed or updated:", + "MessagePleaseRestartServerToFinishUpdating": "Please restart the server to finish applying updates." } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 3e741f41b0..6f0a71c8cc 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -1182,6 +1182,13 @@ namespace MediaBrowser.Server.Implementations.Session if (session.UserId.HasValue) { dto.UserId = session.UserId.Value.ToString("N"); + + var user = _userManager.GetUserById(session.UserId.Value); + + if (user != null) + { + dto.UserPrimaryImageTag = GetImageCacheTag(user, ImageType.Primary); + } } return dto; @@ -1311,6 +1318,11 @@ namespace MediaBrowser.Server.Implementations.Session } } + if (backropItem == null) + { + backropItem = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Backdrop)); + } + if (thumbItem == null) { thumbItem = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Thumb)); @@ -1322,7 +1334,7 @@ namespace MediaBrowser.Server.Implementations.Session info.ThumbItemId = GetDtoId(thumbItem); } - if (thumbItem != null) + if (backropItem != null) { info.BackdropImageTag = GetImageCacheTag(backropItem, ImageType.Backdrop); info.BackdropItemId = GetDtoId(backropItem); diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 8df247ac04..931bb6a102 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -608,6 +608,7 @@ namespace MediaBrowser.WebDashboard.Api "supporterkeypage.js", "supporterpage.js", "episodes.js", + "thememediaplayer.js", "tvgenres.js", "tvlatest.js", "tvpeople.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 10ed2b7f4d..b1cfab37a3 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -220,6 +220,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -505,6 +508,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -637,6 +643,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest