added dashboard tour

This commit is contained in:
Luke Pulverenti 2014-10-04 14:05:24 -04:00
parent e83a3e710b
commit 7a084a589e
13 changed files with 190 additions and 62 deletions

View File

@ -157,7 +157,12 @@ namespace MediaBrowser.Api
private Task MarkRead(string idList, string userId, bool read) 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); return _notificationsRepo.MarkRead(ids, userId, read, CancellationToken.None);
} }

View File

@ -635,8 +635,7 @@ namespace MediaBrowser.Api.Playback.Hls
// See if we can save come cpu cycles by avoiding encoding // See if we can save come cpu cycles by avoiding encoding
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
{ {
// TOOD: Switch to -bsf dump_extra? return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy";
return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf h264_mp4toannexb" : "-codec:v:0 copy";
} }
var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})",

View File

@ -139,8 +139,7 @@ namespace MediaBrowser.Api.Playback.Hls
// See if we can save come cpu cycles by avoiding encoding // See if we can save come cpu cycles by avoiding encoding
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
{ {
// TOOD: Switch to -bsf dump_extra? return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy";
return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf h264_mp4toannexb" : "-codec:v:0 copy";
} }
var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})",

View File

@ -132,8 +132,7 @@ namespace MediaBrowser.Api.Playback.Progressive
// See if we can save come cpu cycles by avoiding encoding // See if we can save come cpu cycles by avoiding encoding
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
{ {
// TODO: Switch to -bsf dump_extra ? return state.VideoStream != null && IsH264(state.VideoStream) ? args + " -bsf:v h264_mp4toannexb" : args;
return state.VideoStream != null && IsH264(state.VideoStream) ? args + " -bsf h264_mp4toannexb" : args;
} }
var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})",

View File

@ -33,6 +33,12 @@ namespace MediaBrowser.Controller.Entities
/// <value>The type of the location.</value> /// <value>The type of the location.</value>
LocationType LocationType { get; } LocationType LocationType { get; }
/// <summary>
/// Gets the locked fields.
/// </summary>
/// <value>The locked fields.</value>
List<MetadataFields> LockedFields { get; }
/// <summary> /// <summary>
/// Gets the images. /// Gets the images.
/// </summary> /// </summary>

View File

@ -1,6 +1,5 @@
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
@ -25,12 +24,6 @@ namespace MediaBrowser.Controller.Entities
/// <value>The date modified.</value> /// <value>The date modified.</value>
DateTime DateModified { get; } DateTime DateModified { get; }
/// <summary>
/// Gets the locked fields.
/// </summary>
/// <value>The locked fields.</value>
List<MetadataFields> LockedFields { get; }
/// <summary> /// <summary>
/// Gets or sets the date last saved. /// Gets or sets the date last saved.
/// </summary> /// </summary>

View File

@ -51,6 +51,15 @@ namespace MediaBrowser.Controller.Notifications
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task MarkRead(IEnumerable<string> notificationIdList, string userId, bool isRead, CancellationToken cancellationToken); Task MarkRead(IEnumerable<string> notificationIdList, string userId, bool isRead, CancellationToken cancellationToken);
/// <summary>
/// Marks all read.
/// </summary>
/// <param name="userId">The user identifier.</param>
/// <param name="isRead">if set to <c>true</c> [is read].</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task MarkAllRead(string userId, bool isRead, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the notifications summary. /// Gets the notifications summary.
/// </summary> /// </summary>

View File

@ -787,13 +787,6 @@ namespace MediaBrowser.Model.ApiClient
/// <value>The server address.</value> /// <value>The server address.</value>
string ServerAddress { get; } string ServerAddress { get; }
/// <summary>
/// Changes the server location.
/// </summary>
/// <param name="address">The address.</param>
/// <param name="keepExistingAuth">Don't clear any existing authentication</param>
void ChangeServerLocation(string address, bool keepExistingAuth = false);
/// <summary> /// <summary>
/// Gets or sets the type of the client. /// Gets or sets the type of the client.
/// </summary> /// </summary>

View File

@ -123,7 +123,7 @@ namespace MediaBrowser.Providers.Manager
foreach (var imageType in images) 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))) if (!item.HasImage(imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
{ {
@ -224,14 +224,14 @@ namespace MediaBrowser.Providers.Manager
/// <param name="result">The result.</param> /// <param name="result">The result.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
private async Task RefreshFromProvider(IHasImages item, private async Task RefreshFromProvider(IHasImages item,
IRemoteImageProvider provider, IRemoteImageProvider provider,
ImageRefreshOptions refreshOptions, ImageRefreshOptions refreshOptions,
MetadataOptions savedOptions, MetadataOptions savedOptions,
int backdropLimit, int backdropLimit,
int screenshotLimit, int screenshotLimit,
ICollection<ImageType> downloadedImages, ICollection<ImageType> downloadedImages,
RefreshResult result, RefreshResult result,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
try try
@ -241,8 +241,8 @@ namespace MediaBrowser.Providers.Manager
return; return;
} }
if (!refreshOptions.ReplaceAllImages && if (!refreshOptions.ReplaceAllImages &&
refreshOptions.ReplaceImages.Count == 0 && refreshOptions.ReplaceImages.Count == 0 &&
ContainsImages(item, provider.GetSupportedImages(item).ToList(), savedOptions, backdropLimit, screenshotLimit)) ContainsImages(item, provider.GetSupportedImages(item).ToList(), savedOptions, backdropLimit, screenshotLimit))
{ {
return; return;
@ -263,7 +263,7 @@ namespace MediaBrowser.Providers.Manager
foreach (var imageType in _singularImages) 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))) if (!item.HasImage(imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
{ {
@ -277,14 +277,20 @@ namespace MediaBrowser.Providers.Manager
} }
} }
minWidth = savedOptions.GetMinWidth(ImageType.Backdrop); if (!item.LockedFields.Contains(MetadataFields.Backdrops))
await DownloadBackdrops(item, ImageType.Backdrop, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
var hasScreenshots = item as IHasScreenshots;
if (hasScreenshots != null)
{ {
minWidth = savedOptions.GetMinWidth(ImageType.Screenshot); minWidth = savedOptions.GetMinWidth(ImageType.Backdrop);
await DownloadBackdrops(item, ImageType.Screenshot, screenshotLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false); 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) 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) private void ClearImages(IHasImages item, ImageType type)
{ {
var deleted = false; var deleted = false;
@ -397,12 +430,12 @@ namespace MediaBrowser.Providers.Manager
return changed; return changed;
} }
private async Task<bool> DownloadImage(IHasImages item, private async Task<bool> DownloadImage(IHasImages item,
IRemoteImageProvider provider, IRemoteImageProvider provider,
RefreshResult result, RefreshResult result,
IEnumerable<RemoteImageInfo> images, IEnumerable<RemoteImageInfo> images,
int minWidth, int minWidth,
ImageType type, ImageType type,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
foreach (var image in images.Where(i => i.Type == type)) foreach (var image in images.Where(i => i.Type == type))

View File

@ -579,18 +579,26 @@
"LabelFullReview": "Full review:", "LabelFullReview": "Full review:",
"LabelShortRatingDescription": "Short rating summary:", "LabelShortRatingDescription": "Short rating summary:",
"OptionIRecommendThisItem": "I recommend this item", "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.", "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", "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", "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", "WebClientTourTapHold": "Tap and hold or right click any poster for a context menu",
"WebClientTourMetadataManager": "Click edit to open the metadata manager", "WebClientTourMetadataManager": "Click edit to open the metadata manager",
"WebClientTourPlaylists": "Easily create playlists and instant mixes, and play them on any device", "WebClientTourPlaylists": "Easily create playlists and instant mixes, and play them on any device",
"WebClientTourCollections": "Create movie collections to group box sets together", "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", "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", "WebClientTourUserPreferences2": "Configure your audio and subtitle language settings once, for every Media Browser app",
"WebClientTourUserPreferences3": "Design the web client home page to your liking", "WebClientTourUserPreferences3": "Design the web client home page to your liking",
"WebClientTourUserPreferences4": "Configure backdrops, theme songs and external players", "WebClientTourUserPreferences4": "Configure backdrops, theme songs and external players",
"WebClientTourMobile1": "The web client works great on smartphones and tablets...", "WebClientTourMobile1": "The web client works great on smartphones and tablets...",
"WebClientTourMobile2": "and easily controls other devices and Media Browser apps", "WebClientTourMobile2": "and easily controls other devices and Media Browser apps",
"MessageEnjoyYourStay": "Enjoy your stay" "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."
} }

View File

@ -1206,5 +1206,6 @@
"LabelDisplayTrailersWithinMovieSuggestionsHelp": "Requires installation of the Trailer channel.", "LabelDisplayTrailersWithinMovieSuggestionsHelp": "Requires installation of the Trailer channel.",
"CinemaModeConfigurationHelp2": "Individual users will have the ability to disable cinema mode within their own preferences.", "CinemaModeConfigurationHelp2": "Individual users will have the ability to disable cinema mode within their own preferences.",
"LabelEnableCinemaMode": "Enable cinema mode", "LabelEnableCinemaMode": "Enable cinema mode",
"HeaderCinemaMode": "Cinema Mode" "HeaderCinemaMode": "Cinema Mode",
"HeaderWelcomeToMediaBrowserServerDashboard": "Welcome to the Media Browser Server Dashboard"
} }

View File

@ -15,7 +15,7 @@ namespace MediaBrowser.Server.Implementations.Notifications
{ {
public class SqliteNotificationsRepository : INotificationsRepository public class SqliteNotificationsRepository : INotificationsRepository
{ {
private IDbConnection _connection; private IDbConnection _connection;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IServerApplicationPaths _appPaths; private readonly IServerApplicationPaths _appPaths;
@ -33,13 +33,14 @@ namespace MediaBrowser.Server.Implementations.Notifications
private IDbCommand _replaceNotificationCommand; private IDbCommand _replaceNotificationCommand;
private IDbCommand _markReadCommand; private IDbCommand _markReadCommand;
private IDbCommand _markAllReadCommand;
public async Task Initialize() public async Task Initialize()
{ {
var dbFile = Path.Combine(_appPaths.DataPath, "notifications.db"); var dbFile = Path.Combine(_appPaths.DataPath, "notifications.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false); _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false);
string[] queries = { 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))", "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, "@UserId");
_markReadCommand.Parameters.Add(_replaceNotificationCommand, "@IsRead"); _markReadCommand.Parameters.Add(_replaceNotificationCommand, "@IsRead");
_markReadCommand.Parameters.Add(_replaceNotificationCommand, "@Id"); _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");
} }
/// <summary> /// <summary>
@ -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<Guid> notificationIdList, string userId, bool isRead, CancellationToken cancellationToken) private async Task MarkReadInternal(IEnumerable<Guid> notificationIdList, string userId, bool isRead, CancellationToken cancellationToken)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();

View File

@ -275,6 +275,30 @@
<Content Include="dashboard-ui\css\images\media\play.png"> <Content Include="dashboard-ui\css\images\media\play.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="dashboard-ui\css\images\tour\dashboard\chapters.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\css\images\tour\dashboard\cinemamode.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\css\images\tour\dashboard\dashboard.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\css\images\tour\dashboard\notifications.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\css\images\tour\dashboard\plugins.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\css\images\tour\dashboard\scheduledtasks.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\css\images\tour\dashboard\subtitles.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\css\images\tour\dashboard\users.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\css\images\tour\enjoy.jpg"> <Content Include="dashboard-ui\css\images\tour\enjoy.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>