diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 094a9034da..5d772527c1 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Extensions; +using MediaBrowser.Api.Playback.Hls; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; @@ -317,7 +318,7 @@ namespace MediaBrowser.Api.Playback switch (qualitySetting) { case EncodingQuality.HighSpeed: - param = "-preset ultrafast"; + param = "-preset superfast"; break; case EncodingQuality.HighQuality: param = "-preset superfast"; @@ -945,7 +946,8 @@ namespace MediaBrowser.Api.Playback } // Allow a small amount of time to buffer a little - if (state.IsInputVideo) + // But not with HLS because it already has it's own wait + if (state.IsInputVideo && TranscodingJobType != TranscodingJobType.Hls) { await Task.Delay(500, cancellationTokenSource.Token).ConfigureAwait(false); } diff --git a/MediaBrowser.Api/SessionsService.cs b/MediaBrowser.Api/SessionsService.cs index ed7db626f9..8017f35231 100644 --- a/MediaBrowser.Api/SessionsService.cs +++ b/MediaBrowser.Api/SessionsService.cs @@ -1,5 +1,6 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Session; using ServiceStack; @@ -241,6 +242,25 @@ namespace MediaBrowser.Api { } + [Route("/Auth/Keys", "GET")] + public class GetApiKeys + { + } + + [Route("/Auth/Keys/{Key}", "DELETE")] + public class RevokeKey + { + [ApiMember(Name = "Key", Description = "Auth Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string Key { get; set; } + } + + [Route("/Auth/Keys", "POST")] + public class CreateKey + { + [ApiMember(Name = "App", Description = "App", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public string App { get; set; } + } + /// /// Class SessionsService /// @@ -253,19 +273,43 @@ namespace MediaBrowser.Api private readonly IUserManager _userManager; private readonly IAuthorizationContext _authContext; + private readonly IAuthenticationRepository _authRepo; /// /// Initializes a new instance of the class. /// /// The session manager. /// The user manager. - public SessionsService(ISessionManager sessionManager, IUserManager userManager, IAuthorizationContext authContext) + /// The authentication context. + /// The authentication repo. + public SessionsService(ISessionManager sessionManager, IUserManager userManager, IAuthorizationContext authContext, IAuthenticationRepository authRepo) { _sessionManager = sessionManager; _userManager = userManager; _authContext = authContext; + _authRepo = authRepo; } + public void Delete(RevokeKey request) + { + var task = _sessionManager.RevokeToken(request.Key); + + Task.WaitAll(task); + } + + public void Post(CreateKey request) + { + var task = _authRepo.Create(new AuthenticationInfo + { + AppName = request.App, + IsActive = true, + AccessToken = Guid.NewGuid().ToString("N"), + DateCreated = DateTime.UtcNow + + }, CancellationToken.None); + + Task.WaitAll(task); + } public void Post(ReportSessionEnded request) { @@ -274,6 +318,16 @@ namespace MediaBrowser.Api _sessionManager.Logout(auth.Token); } + public object Get(GetApiKeys request) + { + var result = _authRepo.Get(new AuthenticationInfoQuery + { + IsActive = true + }); + + return ToOptimizedResult(result); + } + /// /// Gets the specified request. /// diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index 88d0e9c1a1..c8ae0e6a75 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -276,6 +276,13 @@ namespace MediaBrowser.Controller.Session /// Task. Task RevokeUserTokens(string userId); + /// + /// Revokes the token. + /// + /// The identifier. + /// Task. + Task RevokeToken(string id); + /// /// Determines whether the specified remote endpoint is local. /// diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index d46ddf7dcc..c0313561ae 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -255,6 +255,8 @@ namespace MediaBrowser.Providers.MediaInfo AddDummyChapters(video, chapters); } + NormalizeChapterNames(chapters); + await _encodingManager.RefreshChapterImages(new ChapterImageRefreshOptions { Chapters = chapters, @@ -268,6 +270,25 @@ namespace MediaBrowser.Providers.MediaInfo } } + private void NormalizeChapterNames(List chapters) + { + var index = 1; + + foreach (var chapter in chapters) + { + TimeSpan time; + + // Check if the name is empty and/or if the name is a time + // Some ripping programs do that. + if (string.IsNullOrWhiteSpace(chapter.Name) || + TimeSpan.TryParse(chapter.Name, out time)) + { + chapter.Name = string.Format(_localization.GetLocalizedString("LabelChapterName"), index.ToString(CultureInfo.InvariantCulture)); + } + index++; + } + } + private ChapterInfo GetChapterInfo(MediaChapter chapter) { var info = new ChapterInfo(); @@ -570,7 +591,6 @@ namespace MediaBrowser.Providers.MediaInfo { chapters.Add(new ChapterInfo { - Name = "Chapter " + index, StartPositionTicks = currentChapterTicks }); diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json index 85cc51f538..a69a7b124d 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json @@ -233,5 +233,8 @@ "OptionBlockGames": "Games", "OptionBlockLiveTvPrograms": "Live TV Programs", "OptionBlockLiveTvChannels": "Live TV Channels", - "OptionBlockChannelContent": "Internet Channel Content" + "OptionBlockChannelContent": "Internet Channel Content", + "ButtonRevoke": "Revoke", + "MessageConfirmRevokeApiKey": "Are you sure you wish to revoke this api key? The application's connection to Media Browser will be abruptly terminated.", + "HeaderConfirmRevokeApiKey": "Revoke Api Key" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index bc350bb38c..96b80d1435 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -849,7 +849,7 @@ "LabelXbmcMetadataEnablePathSubstitutionHelp2": "See path substitution.", "LabelGroupChannelsIntoViews": "Display the following channels directly within my views:", "LabelGroupChannelsIntoViewsHelp": "If enabled, these channels will be displayed directly alongside other views. If disabled, they'll be displayed within a separate Channels view.", - "LabelDisplayCollectionsView": "Display a Collections view to show movie collections", + "LabelDisplayCollectionsView": "Display a collections view to show movie collections", "LabelXbmcMetadataEnableExtraThumbs": "Copy extrafanart into extrathumbs", "LabelXbmcMetadataEnableExtraThumbsHelp": "When downloading images they can be saved into both extrafanart and extrathumbs for maximum Xbmc skin compatibility.", "TabServices": "Services", @@ -870,5 +870,17 @@ "LabelImagesByName": "Images by name:", "LabelTranscodingTemporaryFiles": "Transcoding temporary files:", "HeaderLatestMusic": "Latest Music", - "HeaderBranding": "Branding" + "HeaderBranding": "Branding", + "HeaderApiKeys": "Api Keys", + "HeaderApiKeysHelp": "External applications are required to have an Api key in order to communicate with Media Browser. Keys are issued by logging in with a Media Browser account, or by manually granting the application a key.", + "HeaderApiKey": "Api Key", + "HeaderApp": "App", + "HeaderDevice": "Device", + "HeaderUser": "User", + "HeaderDateIssued": "Date Issued", + "LabelChapterName": "Chapter {0}", + "HeaderNewApiKey": "New Api Key", + "LabelAppName": "App name", + "LabelAppNameExample": "Example: Sickbeard, NzbDrone", + "HeaderNewApiKeyHelp": "Grant an application permission to communicate with Media Browser." } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Security/AuthenticationRepository.cs b/MediaBrowser.Server.Implementations/Security/AuthenticationRepository.cs index 5f225ddd47..43a960ea29 100644 --- a/MediaBrowser.Server.Implementations/Security/AuthenticationRepository.cs +++ b/MediaBrowser.Server.Implementations/Security/AuthenticationRepository.cs @@ -283,11 +283,11 @@ namespace MediaBrowser.Server.Implementations.Security } info.IsActive = reader.GetBoolean(6); - info.DateCreated = reader.GetDateTime(7); + info.DateCreated = reader.GetDateTime(7).ToUniversalTime(); if (!reader.IsDBNull(8)) { - info.DateRevoked = reader.GetDateTime(8); + info.DateRevoked = reader.GetDateTime(8).ToUniversalTime(); } return info; diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 338bf89598..1d1910e40e 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -1210,15 +1210,15 @@ namespace MediaBrowser.Server.Implementations.Session /// Task{SessionInfo}. /// Invalid user or password entered. /// - public async Task AuthenticateNewSession(string username, - string password, - string clientType, - string appVersion, - string deviceId, - string deviceName, + public async Task AuthenticateNewSession(string username, + string password, + string clientType, + string appVersion, + string deviceId, + string deviceName, string remoteEndPoint) { - var result = (IsLocalhost(remoteEndPoint) && string.Equals(clientType, "Dashboard", StringComparison.OrdinalIgnoreCase)) || + var result = (IsLocalhost(remoteEndPoint) && string.Equals(clientType, "Dashboard", StringComparison.OrdinalIgnoreCase)) || await _userManager.AuthenticateUser(username, password).ConfigureAwait(false); if (!result) @@ -1332,6 +1332,11 @@ namespace MediaBrowser.Server.Implementations.Session } } + public Task RevokeToken(string token) + { + return Logout(token); + } + private bool IsLocalhost(string remoteEndpoint) { if (string.IsNullOrWhiteSpace(remoteEndpoint)) diff --git a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs index 5fc28e81b0..e1d77c2682 100644 --- a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs +++ b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs @@ -62,14 +62,28 @@ namespace MediaBrowser.Server.Implementations.Session void connection_Closed(object sender, EventArgs e) { - var capabilities = new SessionCapabilities + if (!GetActiveSockets().Any()) { - PlayableMediaTypes = Session.PlayableMediaTypes, - SupportedCommands = Session.SupportedCommands, - SupportsMediaControl = SupportsMediaControl - }; + try + { + _sessionManager.ReportSessionEnded(Session.Id); + } + catch (Exception ex) + { + _logger.ErrorException("Error reporting session ended.", ex); + } + } + else + { + var capabilities = new SessionCapabilities + { + PlayableMediaTypes = Session.PlayableMediaTypes, + SupportedCommands = Session.SupportedCommands, + SupportsMediaControl = SupportsMediaControl + }; - _sessionManager.ReportCapabilities(Session.Id, capabilities); + _sessionManager.ReportCapabilities(Session.Id, capabilities); + } } private IWebSocketConnection GetActiveSocket() diff --git a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs index 800cb8e108..79b262fc31 100644 --- a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; using System.Globalization; @@ -14,8 +15,7 @@ namespace MediaBrowser.XbmcMetadata.Savers { public class AlbumNfoSaver : BaseNfoSaver { - public AlbumNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager) - : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager) + public AlbumNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger) { } diff --git a/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs index a76eaef839..f4ffc35fbd 100644 --- a/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Logging; using MediaBrowser.XbmcMetadata.Configuration; using System.Collections.Generic; using System.Globalization; @@ -14,8 +15,7 @@ namespace MediaBrowser.XbmcMetadata.Savers { public class ArtistNfoSaver : BaseNfoSaver { - public ArtistNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager) - : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager) + public ArtistNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger) { } diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index cb9137ad09..f795eb8b3b 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -9,6 +9,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; using MediaBrowser.XbmcMetadata.Configuration; using System; using System.Collections.Generic; @@ -98,8 +99,9 @@ namespace MediaBrowser.XbmcMetadata.Savers }.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); - protected BaseNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager) + protected BaseNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) { + Logger = logger; UserDataManager = userDataManager; UserManager = userManager; LibraryManager = libraryManager; @@ -112,6 +114,7 @@ namespace MediaBrowser.XbmcMetadata.Savers protected ILibraryManager LibraryManager { get; private set; } protected IUserManager UserManager { get; private set; } protected IUserDataManager UserDataManager { get; private set; } + protected ILogger Logger { get; private set; } public string Name { @@ -232,11 +235,11 @@ namespace MediaBrowser.XbmcMetadata.Savers try { - AddCustomTags(xmlPath, tagsUsed, writer); + AddCustomTags(xmlPath, tagsUsed, writer, Logger); } catch (FileNotFoundException) { - + } writer.WriteEndElement(); @@ -950,7 +953,7 @@ namespace MediaBrowser.XbmcMetadata.Savers return string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase); } - private static void AddCustomTags(string path, List xmlTagsUsed, XmlWriter writer) + private static void AddCustomTags(string path, List xmlTagsUsed, XmlWriter writer, ILogger logger) { var settings = new XmlReaderSettings { @@ -965,7 +968,15 @@ namespace MediaBrowser.XbmcMetadata.Savers // Use XmlReader for best performance using (var reader = XmlReader.Create(streamReader, settings)) { - reader.MoveToContent(); + try + { + reader.MoveToContent(); + } + catch (Exception ex) + { + logger.ErrorException("Error reading existing xml tags from {0}.", ex, path); + return; + } // Loop through each element while (reader.Read()) diff --git a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs index 381faedf4a..f5dc0558bd 100644 --- a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Logging; using MediaBrowser.XbmcMetadata.Configuration; using System.Collections.Generic; using System.Globalization; @@ -13,8 +14,7 @@ namespace MediaBrowser.XbmcMetadata.Savers { public class EpisodeNfoSaver : BaseNfoSaver { - public EpisodeNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager) - : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager) + public EpisodeNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger) { } diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs index 9c8442e167..b23473295c 100644 --- a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs @@ -5,6 +5,7 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -14,8 +15,7 @@ namespace MediaBrowser.XbmcMetadata.Savers { public class MovieNfoSaver : BaseNfoSaver { - public MovieNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager) - : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager) + public MovieNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger) { } diff --git a/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs index fd5819b3d2..715337a015 100644 --- a/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Logging; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -12,7 +13,7 @@ namespace MediaBrowser.XbmcMetadata.Savers { public class SeasonNfoSaver : BaseNfoSaver { - public SeasonNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager) + public SeasonNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger) { } diff --git a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs index 473b5f46bb..8a67b04182 100644 --- a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs @@ -4,6 +4,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -13,8 +14,7 @@ namespace MediaBrowser.XbmcMetadata.Savers { public class SeriesNfoSaver : BaseNfoSaver { - public SeriesNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager) - : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager) + public SeriesNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger) { }