diff --git a/MediaBrowser.Api/NotificationsService.cs b/MediaBrowser.Api/NotificationsService.cs
index 51a0801060..69f1f34891 100644
--- a/MediaBrowser.Api/NotificationsService.cs
+++ b/MediaBrowser.Api/NotificationsService.cs
@@ -157,7 +157,12 @@ namespace MediaBrowser.Api
private Task MarkRead(string idList, string userId, bool read)
{
- var ids = idList.Split(',');
+ var ids = (idList ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+
+ if (ids.Length == 0)
+ {
+ return _notificationsRepo.MarkAllRead(userId, read, CancellationToken.None);
+ }
return _notificationsRepo.MarkRead(ids, userId, read, CancellationToken.None);
}
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index fb8168198d..2035aff164 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -635,8 +635,7 @@ namespace MediaBrowser.Api.Playback.Hls
// See if we can save come cpu cycles by avoiding encoding
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
{
- // TOOD: Switch to -bsf dump_extra?
- return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf h264_mp4toannexb" : "-codec:v:0 copy";
+ return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy";
}
var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})",
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index 28d534c4f8..02acf7e718 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -139,8 +139,7 @@ namespace MediaBrowser.Api.Playback.Hls
// See if we can save come cpu cycles by avoiding encoding
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
{
- // TOOD: Switch to -bsf dump_extra?
- return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf h264_mp4toannexb" : "-codec:v:0 copy";
+ return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy";
}
var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})",
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
index f82de5a6ac..af3cb25eef 100644
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
@@ -132,8 +132,7 @@ namespace MediaBrowser.Api.Playback.Progressive
// See if we can save come cpu cycles by avoiding encoding
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
{
- // TODO: Switch to -bsf dump_extra ?
- return state.VideoStream != null && IsH264(state.VideoStream) ? args + " -bsf h264_mp4toannexb" : args;
+ return state.VideoStream != null && IsH264(state.VideoStream) ? args + " -bsf:v h264_mp4toannexb" : args;
}
var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})",
diff --git a/MediaBrowser.Controller/Entities/IHasImages.cs b/MediaBrowser.Controller/Entities/IHasImages.cs
index 66d3fbb2ef..3f8cd9e824 100644
--- a/MediaBrowser.Controller/Entities/IHasImages.cs
+++ b/MediaBrowser.Controller/Entities/IHasImages.cs
@@ -33,6 +33,12 @@ namespace MediaBrowser.Controller.Entities
/// The type of the location.
LocationType LocationType { get; }
+ ///
+ /// Gets the locked fields.
+ ///
+ /// The locked fields.
+ List LockedFields { get; }
+
///
/// Gets the images.
///
diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs
index 4e90fb1558..10ddaa474a 100644
--- a/MediaBrowser.Controller/Entities/IHasMetadata.cs
+++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs
@@ -1,6 +1,5 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.Threading;
@@ -25,12 +24,6 @@ namespace MediaBrowser.Controller.Entities
/// The date modified.
DateTime DateModified { get; }
- ///
- /// Gets the locked fields.
- ///
- /// The locked fields.
- List LockedFields { get; }
-
///
/// Gets or sets the date last saved.
///
diff --git a/MediaBrowser.Controller/Notifications/INotificationsRepository.cs b/MediaBrowser.Controller/Notifications/INotificationsRepository.cs
index 254e56e059..6ad4a5377e 100644
--- a/MediaBrowser.Controller/Notifications/INotificationsRepository.cs
+++ b/MediaBrowser.Controller/Notifications/INotificationsRepository.cs
@@ -51,6 +51,15 @@ namespace MediaBrowser.Controller.Notifications
/// Task.
Task MarkRead(IEnumerable notificationIdList, string userId, bool isRead, CancellationToken cancellationToken);
+ ///
+ /// Marks all read.
+ ///
+ /// The user identifier.
+ /// if set to true [is read].
+ /// The cancellation token.
+ /// Task.
+ Task MarkAllRead(string userId, bool isRead, CancellationToken cancellationToken);
+
///
/// Gets the notifications summary.
///
diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs
index 6fb72d507f..254a7e7e0b 100644
--- a/MediaBrowser.Model/ApiClient/IApiClient.cs
+++ b/MediaBrowser.Model/ApiClient/IApiClient.cs
@@ -787,13 +787,6 @@ namespace MediaBrowser.Model.ApiClient
/// The server address.
string ServerAddress { get; }
- ///
- /// Changes the server location.
- ///
- /// The address.
- /// Don't clear any existing authentication
- void ChangeServerLocation(string address, bool keepExistingAuth = false);
-
///
/// Gets or sets the type of the client.
///
diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
index ef5c30975b..0d318c3773 100644
--- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs
+++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
@@ -123,7 +123,7 @@ namespace MediaBrowser.Providers.Manager
foreach (var imageType in images)
{
- if (!savedOptions.IsEnabled(imageType)) continue;
+ if (!IsEnabled(savedOptions, imageType, item)) continue;
if (!item.HasImage(imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
{
@@ -224,14 +224,14 @@ namespace MediaBrowser.Providers.Manager
/// The result.
/// The cancellation token.
/// Task.
- private async Task RefreshFromProvider(IHasImages item,
- IRemoteImageProvider provider,
- ImageRefreshOptions refreshOptions,
- MetadataOptions savedOptions,
- int backdropLimit,
- int screenshotLimit,
+ private async Task RefreshFromProvider(IHasImages item,
+ IRemoteImageProvider provider,
+ ImageRefreshOptions refreshOptions,
+ MetadataOptions savedOptions,
+ int backdropLimit,
+ int screenshotLimit,
ICollection downloadedImages,
- RefreshResult result,
+ RefreshResult result,
CancellationToken cancellationToken)
{
try
@@ -241,8 +241,8 @@ namespace MediaBrowser.Providers.Manager
return;
}
- if (!refreshOptions.ReplaceAllImages &&
- refreshOptions.ReplaceImages.Count == 0 &&
+ if (!refreshOptions.ReplaceAllImages &&
+ refreshOptions.ReplaceImages.Count == 0 &&
ContainsImages(item, provider.GetSupportedImages(item).ToList(), savedOptions, backdropLimit, screenshotLimit))
{
return;
@@ -263,7 +263,7 @@ namespace MediaBrowser.Providers.Manager
foreach (var imageType in _singularImages)
{
- if (!savedOptions.IsEnabled(imageType)) continue;
+ if (!IsEnabled(savedOptions, imageType, item)) continue;
if (!item.HasImage(imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
{
@@ -277,14 +277,20 @@ namespace MediaBrowser.Providers.Manager
}
}
- minWidth = savedOptions.GetMinWidth(ImageType.Backdrop);
- await DownloadBackdrops(item, ImageType.Backdrop, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
-
- var hasScreenshots = item as IHasScreenshots;
- if (hasScreenshots != null)
+ if (!item.LockedFields.Contains(MetadataFields.Backdrops))
{
- minWidth = savedOptions.GetMinWidth(ImageType.Screenshot);
- await DownloadBackdrops(item, ImageType.Screenshot, screenshotLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
+ minWidth = savedOptions.GetMinWidth(ImageType.Backdrop);
+ await DownloadBackdrops(item, ImageType.Backdrop, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
+ }
+
+ if (!item.LockedFields.Contains(MetadataFields.Screenshots))
+ {
+ var hasScreenshots = item as IHasScreenshots;
+ if (hasScreenshots != null)
+ {
+ minWidth = savedOptions.GetMinWidth(ImageType.Screenshot);
+ await DownloadBackdrops(item, ImageType.Screenshot, screenshotLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
+ }
}
}
catch (OperationCanceledException)
@@ -299,6 +305,33 @@ namespace MediaBrowser.Providers.Manager
}
}
+ private bool IsEnabled(MetadataOptions options, ImageType type, IHasImages item)
+ {
+ if (type == ImageType.Backdrop)
+ {
+ if (item.LockedFields.Contains(MetadataFields.Backdrops))
+ {
+ return false;
+ }
+ }
+ else if (type == ImageType.Screenshot)
+ {
+ if (item.LockedFields.Contains(MetadataFields.Screenshots))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (item.LockedFields.Contains(MetadataFields.Images))
+ {
+ return false;
+ }
+ }
+
+ return options.IsEnabled(type);
+ }
+
private void ClearImages(IHasImages item, ImageType type)
{
var deleted = false;
@@ -397,12 +430,12 @@ namespace MediaBrowser.Providers.Manager
return changed;
}
- private async Task DownloadImage(IHasImages item,
- IRemoteImageProvider provider,
- RefreshResult result,
- IEnumerable images,
- int minWidth,
- ImageType type,
+ private async Task DownloadImage(IHasImages item,
+ IRemoteImageProvider provider,
+ RefreshResult result,
+ IEnumerable images,
+ int minWidth,
+ ImageType type,
CancellationToken cancellationToken)
{
foreach (var image in images.Where(i => i.Type == type))
diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
index 995bfe6f66..b31c841280 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
@@ -579,18 +579,26 @@
"LabelFullReview": "Full review:",
"LabelShortRatingDescription": "Short rating summary:",
"OptionIRecommendThisItem": "I recommend this item",
- "WebClientTourContent": "View your recently added media, next episodes, and more. The green circles indicate how many unplayed items you have.",
- "WebClientTourMovies": "Play movies, trailers and more from any device with a web browser",
- "WebClientTourMouseOver": "Hold the mouse over any poster for quick access to important information",
- "WebClientTourTapHold": "Tap and hold or right click any poster for a context menu",
- "WebClientTourMetadataManager": "Click edit to open the metadata manager",
- "WebClientTourPlaylists": "Easily create playlists and instant mixes, and play them on any device",
- "WebClientTourCollections": "Create movie collections to group box sets together",
- "WebClientTourUserPreferences1": "User preferences allow you to customize the way your library is presented in all of your Media Browser apps",
- "WebClientTourUserPreferences2": "Configure your audio and subtitle language settings once, for every Media Browser app",
- "WebClientTourUserPreferences3": "Design the web client home page to your liking",
- "WebClientTourUserPreferences4": "Configure backdrops, theme songs and external players",
- "WebClientTourMobile1": "The web client works great on smartphones and tablets...",
- "WebClientTourMobile2": "and easily controls other devices and Media Browser apps",
- "MessageEnjoyYourStay": "Enjoy your stay"
+ "WebClientTourContent": "View your recently added media, next episodes, and more. The green circles indicate how many unplayed items you have.",
+ "WebClientTourMovies": "Play movies, trailers and more from any device with a web browser",
+ "WebClientTourMouseOver": "Hold the mouse over any poster for quick access to important information",
+ "WebClientTourTapHold": "Tap and hold or right click any poster for a context menu",
+ "WebClientTourMetadataManager": "Click edit to open the metadata manager",
+ "WebClientTourPlaylists": "Easily create playlists and instant mixes, and play them on any device",
+ "WebClientTourCollections": "Create movie collections to group box sets together",
+ "WebClientTourUserPreferences1": "User preferences allow you to customize the way your library is presented in all of your Media Browser apps",
+ "WebClientTourUserPreferences2": "Configure your audio and subtitle language settings once, for every Media Browser app",
+ "WebClientTourUserPreferences3": "Design the web client home page to your liking",
+ "WebClientTourUserPreferences4": "Configure backdrops, theme songs and external players",
+ "WebClientTourMobile1": "The web client works great on smartphones and tablets...",
+ "WebClientTourMobile2": "and easily controls other devices and Media Browser apps",
+ "MessageEnjoyYourStay": "Enjoy your stay",
+ "DashboardTourDashboard": "The server dashboard allows you to monitor your server and your users. You'll always know who is doing what and where they are.",
+ "DashboardTourUsers": "Easily create user accounts for your friends and family, each with their own permissions, library access, parental controls and more.",
+ "DashboardTourCinemaMode": "Cinema mode brings the theater experience straight to your living room with the ability to play trailers and custom intros before the main feature.",
+ "DashboardTourChapters": "Enable chapter image generation for your videos for a more pleasing presentation while viewing.",
+ "DashboardTourSubtitles": "Automatically download subtitles for your videos in any language.",
+ "DashboardTourPlugins": "Install plugins such as internet video channels, live tv, metadata scanners, and more.",
+ "DashboardTourNotifications": "Automatically send notifications of server events to your mobile device, email and more.",
+ "DashboardTourScheduledTasks": "Easily manage long running operations with scheduled tasks. Decide when they run, and how often."
}
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json
index 4162818743..af2228ad57 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/server.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json
@@ -1206,5 +1206,6 @@
"LabelDisplayTrailersWithinMovieSuggestionsHelp": "Requires installation of the Trailer channel.",
"CinemaModeConfigurationHelp2": "Individual users will have the ability to disable cinema mode within their own preferences.",
"LabelEnableCinemaMode": "Enable cinema mode",
- "HeaderCinemaMode": "Cinema Mode"
+ "HeaderCinemaMode": "Cinema Mode",
+ "HeaderWelcomeToMediaBrowserServerDashboard": "Welcome to the Media Browser Server Dashboard"
}
diff --git a/MediaBrowser.Server.Implementations/Notifications/SqliteNotificationsRepository.cs b/MediaBrowser.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
index d34d5a293d..f883e6dbce 100644
--- a/MediaBrowser.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
+++ b/MediaBrowser.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
@@ -15,7 +15,7 @@ namespace MediaBrowser.Server.Implementations.Notifications
{
public class SqliteNotificationsRepository : INotificationsRepository
{
- private IDbConnection _connection;
+ private IDbConnection _connection;
private readonly ILogger _logger;
private readonly IServerApplicationPaths _appPaths;
@@ -33,13 +33,14 @@ namespace MediaBrowser.Server.Implementations.Notifications
private IDbCommand _replaceNotificationCommand;
private IDbCommand _markReadCommand;
+ private IDbCommand _markAllReadCommand;
public async Task Initialize()
{
var dbFile = Path.Combine(_appPaths.DataPath, "notifications.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false);
-
+
string[] queries = {
"create table if not exists Notifications (Id GUID NOT NULL, UserId GUID NOT NULL, Date DATETIME NOT NULL, Name TEXT NOT NULL, Description TEXT, Url TEXT, Level TEXT NOT NULL, IsRead BOOLEAN NOT NULL, Category TEXT NOT NULL, RelatedId TEXT, PRIMARY KEY (Id, UserId))",
@@ -78,6 +79,12 @@ namespace MediaBrowser.Server.Implementations.Notifications
_markReadCommand.Parameters.Add(_replaceNotificationCommand, "@UserId");
_markReadCommand.Parameters.Add(_replaceNotificationCommand, "@IsRead");
_markReadCommand.Parameters.Add(_replaceNotificationCommand, "@Id");
+
+ _markAllReadCommand = _connection.CreateCommand();
+ _markAllReadCommand.CommandText = "update Notifications set IsRead=@IsRead where UserId=@UserId";
+
+ _markAllReadCommand.Parameters.Add(_replaceNotificationCommand, "@UserId");
+ _markAllReadCommand.Parameters.Add(_replaceNotificationCommand, "@IsRead");
}
///
@@ -357,6 +364,58 @@ namespace MediaBrowser.Server.Implementations.Notifications
}
}
+ public async Task MarkAllRead(string userId, bool isRead, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+ IDbTransaction transaction = null;
+
+ try
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ transaction = _connection.BeginTransaction();
+
+ _markAllReadCommand.GetParameter(0).Value = new Guid(userId);
+ _markAllReadCommand.GetParameter(1).Value = isRead;
+
+ _markAllReadCommand.ExecuteNonQuery();
+
+ transaction.Commit();
+ }
+ catch (OperationCanceledException)
+ {
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
+ }
+ catch (Exception e)
+ {
+ _logger.ErrorException("Failed to save notification:", e);
+
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
+ }
+ finally
+ {
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ }
+
+ _writeLock.Release();
+ }
+ }
+
private async Task MarkReadInternal(IEnumerable notificationIdList, string userId, bool isRead, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index 330df0a6f0..383b1ba468 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -275,6 +275,30 @@
PreserveNewest
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
PreserveNewest