diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 6462301600..31166ae2aa 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -269,7 +269,7 @@ namespace MediaBrowser.Api.Playback // If fixed dimensions were supplied if (request.Width.HasValue && request.Height.HasValue) { - return string.Format(" -vf \"scale={0}:{1}{2}\"", request.Width.Value, request.Height.Value, assSubtitleParam); + return string.Format(" -vf \"scale=trunc({0}/2)*2:trunc({1}/2)*2{2}\"", request.Width.Value, request.Height.Value, assSubtitleParam); } var isH264Output = outputVideoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase); diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 67221a9ac9..8174dded26 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1337,6 +1337,13 @@ namespace MediaBrowser.Controller.Entities var data = userManager.GetUserData(user.Id, key); + if (datePlayed.HasValue) + { + // Incremenet + data.PlayCount++; + } + + // Ensure it's at least one data.PlayCount = Math.Max(data.PlayCount, 1); data.LastPlayedDate = datePlayed ?? data.LastPlayedDate; diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 1176fca52b..96b120b8fe 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -203,7 +203,7 @@ namespace MediaBrowser.Controller.Entities.TV public bool IsUnaired { - get { return PremiereDate.HasValue && PremiereDate.Value >= DateTime.UtcNow; } + get { return PremiereDate.HasValue && PremiereDate.Value.ToLocalTime().Date >= DateTime.Now.Date; } } public bool IsVirtualUnaired diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index e1b38bc714..e9bb7f66d9 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -552,32 +552,6 @@ namespace MediaBrowser.Controller.Providers } break; - case "GamesDbId": - var gamesdbId = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(gamesdbId)) - { - item.SetProviderId(MetadataProviders.Gamesdb, gamesdbId); - } - break; - - case "Players": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - int num; - - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out num)) - { - var game = item as Game; - if (game != null) - { - game.PlayersSupported = num; - } - } - } - break; - } case "VoteCount": { var val = reader.ReadElementContentAsString(); @@ -592,19 +566,6 @@ namespace MediaBrowser.Controller.Providers } break; } - case "GameSystem": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - var game = item as Game; - if (game != null) - { - game.GameSystem = val; - } - } - break; - } case "MusicbrainzId": { var mbz = reader.ReadElementContentAsString(); diff --git a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs index 1b3aba1026..dbac826b34 100644 --- a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs +++ b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs @@ -44,7 +44,8 @@ namespace MediaBrowser.Controller.Resolvers ".f4v", ".3gp", ".webm", - ".mts" + ".mts", + ".rec" }; private static readonly Dictionary VideoFileExtensionsDictionary = VideoFileExtensions.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); diff --git a/MediaBrowser.Providers/Games/GameProviderFromXml.cs b/MediaBrowser.Providers/Games/GameProviderFromXml.cs index 6292cec465..44680b8328 100644 --- a/MediaBrowser.Providers/Games/GameProviderFromXml.cs +++ b/MediaBrowser.Providers/Games/GameProviderFromXml.cs @@ -3,11 +3,11 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; +using MediaBrowser.Providers.Savers; using System; using System.IO; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Providers.Savers; namespace MediaBrowser.Providers.Games { @@ -78,7 +78,7 @@ namespace MediaBrowser.Providers.Games try { - new BaseItemXmlParser(Logger).Fetch(game, metaFile, cancellationToken); + new GameXmlParser(Logger).Fetch(game, metaFile, cancellationToken); } finally { diff --git a/MediaBrowser.Providers/Games/GameXmlParser.cs b/MediaBrowser.Providers/Games/GameXmlParser.cs new file mode 100644 index 0000000000..53cc123884 --- /dev/null +++ b/MediaBrowser.Providers/Games/GameXmlParser.cs @@ -0,0 +1,110 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using System.Globalization; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +namespace MediaBrowser.Providers.Games +{ + /// + /// Class EpisodeXmlParser + /// + public class GameXmlParser : BaseItemXmlParser + { + private Task _chaptersTask = null; + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + + public GameXmlParser(ILogger logger) + : base(logger) + { + } + + public async Task FetchAsync(Game item, string metadataFile, CancellationToken cancellationToken) + { + _chaptersTask = null; + + Fetch(item, metadataFile, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + + if (_chaptersTask != null) + { + await _chaptersTask.ConfigureAwait(false); + } + } + + /// + /// Fetches the data from XML node. + /// + /// The reader. + /// The item. + protected override void FetchDataFromXmlNode(XmlReader reader, Game item) + { + switch (reader.Name) + { + case "GameSystem": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.GameSystem = val; + } + break; + } + + case "GamesDbId": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.Gamesdb, val); + } + break; + } + + case "NesBox": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.NesBox, val); + } + break; + } + + case "NesBoxRom": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.NesBoxRom, val); + } + break; + } + + case "Players": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + int num; + + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out num)) + { + item.PlayersSupported = num; + } + } + break; + } + + + default: + base.FetchDataFromXmlNode(reader, item); + break; + } + } + } +} diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index e687e14d9d..63bd8c9534 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -49,6 +49,7 @@ + diff --git a/MediaBrowser.Providers/Savers/GameXmlSaver.cs b/MediaBrowser.Providers/Savers/GameXmlSaver.cs index 5564b71cef..f35e4d1319 100644 --- a/MediaBrowser.Providers/Savers/GameXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/GameXmlSaver.cs @@ -1,6 +1,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; using MediaBrowser.Providers.Movies; using System; using System.Collections.Generic; @@ -70,6 +71,20 @@ namespace MediaBrowser.Providers.Savers builder.Append("" + SecurityElement.Escape(game.GameSystem) + ""); } + var val = game.GetProviderId(MetadataProviders.NesBox); + + if (!string.IsNullOrEmpty(val)) + { + builder.Append("" + SecurityElement.Escape(val) + ""); + } + + val = game.GetProviderId(MetadataProviders.NesBoxRom); + + if (!string.IsNullOrEmpty(val)) + { + builder.Append("" + SecurityElement.Escape(val) + ""); + } + XmlSaverHelpers.AddCommonNodes(item, builder); builder.Append(""); @@ -79,7 +94,9 @@ namespace MediaBrowser.Providers.Savers XmlSaverHelpers.Save(builder, xmlFilePath, new List { "Players", - "GameSystem" + "GameSystem", + "NesBox", + "NesBoxRom" }); // Set last refreshed so that the provider doesn't trigger after the file save diff --git a/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs b/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs index a5408c8dee..4eba7dbb4e 100644 --- a/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs @@ -275,6 +275,7 @@ namespace MediaBrowser.Providers.TV string url = null; int? bannerSeason = null; string resolution = null; + string language = null; while (reader.Read()) { @@ -282,6 +283,12 @@ namespace MediaBrowser.Providers.TV { switch (reader.Name) { + case "Language": + { + language = reader.ReadElementContentAsString() ?? string.Empty; + break; + } + case "BannerType": { bannerType = reader.ReadElementContentAsString() ?? string.Empty; @@ -333,10 +340,21 @@ namespace MediaBrowser.Providers.TV } else if (string.Equals(bannerType2, "seasonwide", StringComparison.OrdinalIgnoreCase)) { - // Just grab the first - if (string.IsNullOrWhiteSpace(data.Banner)) + if (string.IsNullOrWhiteSpace(language) || string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) { - data.Banner = url; + // Just grab the first + if (string.IsNullOrWhiteSpace(data.Banner)) + { + data.Banner = url; + } + } + else if (string.Equals(language, ConfigurationManager.Configuration.PreferredMetadataLanguage, StringComparison.OrdinalIgnoreCase)) + { + // Just grab the first + if (string.IsNullOrWhiteSpace(data.LanguageBanner)) + { + data.LanguageBanner = url; + } } } } diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 65a161821c..19e19f8ea4 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -373,6 +373,11 @@ namespace MediaBrowser.Server.Implementations.Dto dto.GameSystem = item.GameSystem; } + private void SetGameSystemProperties(BaseItemDto dto, GameSystem item) + { + dto.GameSystem = item.GameSystemName; + } + /// /// Gets the backdrop image tags. /// @@ -1064,6 +1069,13 @@ namespace MediaBrowser.Server.Implementations.Dto SetGameProperties(dto, game); } + var gameSystem = item as GameSystem; + + if (gameSystem != null) + { + SetGameSystemProperties(dto, gameSystem); + } + var musicVideo = item as MusicVideo; if (musicVideo != null) diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index 3d45f143bc..d3eed5c484 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -214,8 +214,6 @@ namespace MediaBrowser.ServerApplication SystemEvents.SessionEnding += SystemEvents_SessionEnding; SystemEvents.SessionSwitch += SystemEvents_SessionSwitch; - MigrateShortcuts(appPaths.RootFolderPath); - _appHost = new ApplicationHost(appPaths, logManager); _app = new App(_appHost, _appHost.LogManager.GetLogger("App"), runService); @@ -537,34 +535,5 @@ namespace MediaBrowser.ServerApplication /// SEM_NOOPENFILEERRORBOX = 0x8000 } - - private static void MigrateShortcuts(string directory) - { - Directory.CreateDirectory(directory); - - foreach (var file in Directory.EnumerateFiles(directory, "*.lnk", SearchOption.AllDirectories).ToList()) - { - MigrateShortcut(file); - } - } - - private static void MigrateShortcut(string file) - { - var newFile = Path.ChangeExtension(file, ".mblink"); - - try - { - var resolvedPath = FileSystem.ResolveShortcut(file); - - if (!string.IsNullOrEmpty(resolvedPath)) - { - FileSystem.CreateShortcut(newFile, resolvedPath); - } - } - finally - { - File.Delete(file); - } - } } } diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js index 97a443e844..8e64e4300d 100644 --- a/MediaBrowser.WebDashboard/ApiClient.js +++ b/MediaBrowser.WebDashboard/ApiClient.js @@ -2731,16 +2731,42 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi }); }; - /** - * Marks an item as played or unplayed - * This should not be used to update playstate following playback. - * There are separate playstate check-in methods for that. This should be used for a - * separate option to reset playstate. - * @param {String} userId - * @param {String} itemId - * @param {Boolean} wasPlayed - */ - self.updatePlayedStatus = function (userId, itemId, wasPlayed) { + self.getDateParamValue = function (date) { + function formatDigit(i) { + return i < 10 ? "0" + i : i; + } + + var d = date; + + return "" + d.getFullYear() + formatDigit(d.getMonth() + 1) + formatDigit(d.getDate()) + formatDigit(d.getHours()) + formatDigit(d.getMinutes()) + formatDigit(d.getSeconds()); + }; + + self.markPlayed = function (userId, itemId, date) { + + if (!userId) { + throw new Error("null userId"); + } + + if (!itemId) { + throw new Error("null itemId"); + } + + var options = {}; + + if (date) { + options.DatePlayed = self.getDateParamValue(date); + } + + var url = self.getUrl("Users/" + userId + "/PlayedItems/" + itemId, options); + + return self.ajax({ + type: "POST", + url: url, + dataType: "json" + }); + }; + + self.markUnplayed = function (userId, itemId) { if (!userId) { throw new Error("null userId"); @@ -2752,10 +2778,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi var url = self.getUrl("Users/" + userId + "/PlayedItems/" + itemId); - var method = wasPlayed ? "POST" : "DELETE"; - return self.ajax({ - type: method, + type: "DELETE", url: url, dataType: "json" }); diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config index 9c48b38095..02daa2cae4 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file