aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller
diff options
context:
space:
mode:
authorLuke <luke.pulverenti@gmail.com>2015-01-04 09:27:54 -0500
committerLuke <luke.pulverenti@gmail.com>2015-01-04 09:27:54 -0500
commitc5ff30f66e368efc2ca7dea7813fba6d9f6a657c (patch)
treec5552b898f66b7d510e9257eb8bbeafd6a003676 /MediaBrowser.Controller
parent767590125b27c2498e3ad9544edbede30fb70f45 (diff)
parent59b6bc28c332701d5e383fbf99170bdc740fb6cc (diff)
Merge pull request #965 from MediaBrowser/dev
3.0.5482.0
Diffstat (limited to 'MediaBrowser.Controller')
-rw-r--r--MediaBrowser.Controller/Channels/Channel.cs2
-rw-r--r--MediaBrowser.Controller/Channels/ChannelAudioItem.cs5
-rw-r--r--MediaBrowser.Controller/Channels/ChannelFolderItem.cs3
-rw-r--r--MediaBrowser.Controller/Channels/ChannelVideoItem.cs3
-rw-r--r--MediaBrowser.Controller/Devices/IDeviceManager.cs8
-rw-r--r--MediaBrowser.Controller/Dto/DtoOptions.cs47
-rw-r--r--MediaBrowser.Controller/Entities/Audio/Audio.cs18
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs3
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs37
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs18
-rw-r--r--MediaBrowser.Controller/Entities/Book.cs3
-rw-r--r--MediaBrowser.Controller/Entities/CollectionFolder.cs16
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs8
-rw-r--r--MediaBrowser.Controller/Entities/Game.cs3
-rw-r--r--MediaBrowser.Controller/Entities/GameSystem.cs3
-rw-r--r--MediaBrowser.Controller/Entities/IHasMediaSources.cs4
-rw-r--r--MediaBrowser.Controller/Entities/Movies/BoxSet.cs14
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs12
-rw-r--r--MediaBrowser.Controller/Entities/MusicVideo.cs3
-rw-r--r--MediaBrowser.Controller/Entities/Photo.cs5
-rw-r--r--MediaBrowser.Controller/Entities/PhotoAlbum.cs5
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs61
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs79
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs15
-rw-r--r--MediaBrowser.Controller/Entities/Trailer.cs3
-rw-r--r--MediaBrowser.Controller/Entities/User.cs72
-rw-r--r--MediaBrowser.Controller/Entities/UserView.cs3
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs17
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs37
-rw-r--r--MediaBrowser.Controller/Library/IUserDataManager.cs4
-rw-r--r--MediaBrowser.Controller/Library/IUserManager.cs29
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs3
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvChannel.cs3
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvProgram.cs3
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs3
-rw-r--r--MediaBrowser.Controller/LiveTv/RecordingGroup.cs3
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj11
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs91
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingOptions.cs80
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingResult.cs13
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs22
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs12
-rw-r--r--MediaBrowser.Controller/MediaEncoding/VideoEncodingOptions.cs26
-rw-r--r--MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs327
-rw-r--r--MediaBrowser.Controller/Net/IWebSocket.cs54
-rw-r--r--MediaBrowser.Controller/Net/IWebSocketConnection.cs72
-rw-r--r--MediaBrowser.Controller/Net/IWebSocketListener.cs18
-rw-r--r--MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs21
-rw-r--r--MediaBrowser.Controller/Net/WebSocketMessageInfo.cs16
-rw-r--r--MediaBrowser.Controller/Providers/DirectoryService.cs5
-rw-r--r--MediaBrowser.Controller/Sync/ISyncManager.cs31
-rw-r--r--MediaBrowser.Controller/Sync/ISyncProvider.cs12
52 files changed, 1008 insertions, 358 deletions
diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs
index f618c8a25..5a9fc3322 100644
--- a/MediaBrowser.Controller/Channels/Channel.cs
+++ b/MediaBrowser.Controller/Channels/Channel.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Controller.Channels
public override bool IsVisible(User user)
{
- if (user.Configuration.BlockedChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
+ if (user.Policy.BlockedChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
{
return false;
}
diff --git a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs
index ede366dab..896d598bb 100644
--- a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs
+++ b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs
@@ -6,6 +6,7 @@ using MediaBrowser.Model.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels
{
@@ -25,8 +26,8 @@ namespace MediaBrowser.Controller.Channels
public string OriginalImageUrl { get; set; }
public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
-
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent);
}
diff --git a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs
index 5362cc195..8482e38df 100644
--- a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs
+++ b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs
@@ -5,6 +5,7 @@ using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Querying;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels
{
@@ -20,7 +21,7 @@ namespace MediaBrowser.Controller.Channels
public string OriginalImageUrl { get; set; }
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
// Don't block.
return false;
diff --git a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs
index 72e2b110a..f0eafcbdf 100644
--- a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs
+++ b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs
@@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels
{
@@ -51,7 +52,7 @@ namespace MediaBrowser.Controller.Channels
return ExternalId;
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent);
}
diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs
index efd24336a..f5010bb45 100644
--- a/MediaBrowser.Controller/Devices/IDeviceManager.cs
+++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs
@@ -85,5 +85,13 @@ namespace MediaBrowser.Controller.Devices
/// <param name="file">The file.</param>
/// <returns>Task.</returns>
Task AcceptCameraUpload(string deviceId, Stream stream, LocalFileInfo file);
+
+ /// <summary>
+ /// Determines whether this instance [can access device] the specified user identifier.
+ /// </summary>
+ /// <param name="userId">The user identifier.</param>
+ /// <param name="deviceId">The device identifier.</param>
+ /// <returns><c>true</c> if this instance [can access device] the specified user identifier; otherwise, <c>false</c>.</returns>
+ bool CanAccessDevice(string userId, string deviceId);
}
}
diff --git a/MediaBrowser.Controller/Dto/DtoOptions.cs b/MediaBrowser.Controller/Dto/DtoOptions.cs
new file mode 100644
index 000000000..eeb4fc114
--- /dev/null
+++ b/MediaBrowser.Controller/Dto/DtoOptions.cs
@@ -0,0 +1,47 @@
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Querying;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MediaBrowser.Controller.Dto
+{
+ public class DtoOptions
+ {
+ private static readonly List<ItemFields> DefaultExcludedFields = new List<ItemFields>
+ {
+ ItemFields.SeasonUserData
+ };
+
+ public List<ItemFields> Fields { get; set; }
+ public List<ImageType> ImageTypes { get; set; }
+ public int ImageTypeLimit { get; set; }
+ public bool EnableImages { get; set; }
+
+ public DtoOptions()
+ {
+ Fields = new List<ItemFields>();
+ ImageTypeLimit = int.MaxValue;
+ EnableImages = true;
+
+ Fields = Enum.GetNames(typeof (ItemFields))
+ .Select(i => (ItemFields) Enum.Parse(typeof (ItemFields), i, true))
+ .Except(DefaultExcludedFields)
+ .ToList();
+
+ ImageTypes = Enum.GetNames(typeof(ImageType))
+ .Select(i => (ImageType)Enum.Parse(typeof(ImageType), i, true))
+ .ToList();
+ }
+
+ public int GetImageLimit(ImageType type)
+ {
+ if (EnableImages && ImageTypes.Contains(type))
+ {
+ return ImageTypeLimit;
+ }
+
+ return 0;
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index 447328ea1..c5ed09016 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -8,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Audio
{
@@ -88,6 +89,21 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
+ [IgnoreDataMember]
+ public bool IsArchive
+ {
+ get
+ {
+ if (string.IsNullOrWhiteSpace(Path))
+ {
+ return false;
+ }
+ var ext = System.IO.Path.GetExtension(Path) ?? string.Empty;
+
+ return new[] { ".zip", ".rar", ".7z" }.Contains(ext, StringComparer.OrdinalIgnoreCase);
+ }
+ }
+
/// <summary>
/// Gets or sets the artist.
/// </summary>
@@ -173,7 +189,7 @@ namespace MediaBrowser.Controller.Entities.Audio
return base.GetUserDataKey();
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Music);
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index 1f7c62de0..90edfcce7 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Audio
{
@@ -154,7 +155,7 @@ namespace MediaBrowser.Controller.Entities.Audio
return base.GetUserDataKey();
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Music);
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 2d9e052b1..a60258d1a 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Audio
{
@@ -114,7 +115,7 @@ namespace MediaBrowser.Controller.Entities.Audio
return "Artist-" + item.Name;
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Music);
}
@@ -135,7 +136,7 @@ namespace MediaBrowser.Controller.Entities.Audio
// Refresh songs
foreach (var item in songs)
{
- if (tasks.Count >= 3)
+ if (tasks.Count >= 2)
{
await Task.WhenAll(tasks).ConfigureAwait(false);
tasks.Clear();
@@ -172,37 +173,23 @@ namespace MediaBrowser.Controller.Entities.Audio
// Refresh all non-songs
foreach (var item in others)
{
- if (tasks.Count >= 3)
- {
- await Task.WhenAll(tasks).ConfigureAwait(false);
- tasks.Clear();
- }
-
cancellationToken.ThrowIfCancellationRequested();
- var innerProgress = new ActionableProgress<double>();
// Avoid implicitly captured closure
var currentChild = item;
- innerProgress.RegisterAction(p =>
- {
- lock (percentages)
- {
- percentages[currentChild.Id] = p / 100;
- var percent = percentages.Values.Sum();
- percent /= totalItems;
- percent *= 100;
- progress.Report(percent);
- }
- });
+ await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+ lock (percentages)
+ {
+ percentages[currentChild.Id] = 1;
- // Avoid implicitly captured closure
- var taskChild = item;
- tasks.Add(Task.Run(async () => await RefreshItem(taskChild, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false), cancellationToken));
+ var percent = percentages.Values.Sum();
+ percent /= totalItems;
+ percent *= 100;
+ progress.Report(percent);
+ }
}
- await Task.WhenAll(tasks).ConfigureAwait(false);
-
progress.Report(100);
}
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index ed950b1c5..ee562d8b4 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -14,6 +14,7 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.IO;
@@ -45,6 +46,8 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".tbn" };
+ public static readonly List<string> SupportedImageExtensionsList = SupportedImageExtensions.ToList();
+
/// <summary>
/// The trailer folder name
/// </summary>
@@ -593,7 +596,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>PlayAccess.</returns>
public PlayAccess GetPlayAccess(User user)
{
- if (!user.Configuration.EnableMediaPlayback)
+ if (!user.Policy.EnableMediaPlayback)
{
return PlayAccess.None;
}
@@ -985,7 +988,7 @@ namespace MediaBrowser.Controller.Entities
return false;
}
- var maxAllowedRating = user.Configuration.MaxParentalRating;
+ var maxAllowedRating = user.Policy.MaxParentalRating;
if (maxAllowedRating == null)
{
@@ -1001,7 +1004,7 @@ namespace MediaBrowser.Controller.Entities
if (string.IsNullOrWhiteSpace(rating))
{
- return !GetBlockUnratedValue(user.Configuration);
+ return !GetBlockUnratedValue(user.Policy);
}
var value = LocalizationManager.GetRatingLevel(rating);
@@ -1021,7 +1024,7 @@ namespace MediaBrowser.Controller.Entities
if (hasTags != null)
{
- if (user.Configuration.BlockedTags.Any(i => hasTags.Tags.Contains(i, StringComparer.OrdinalIgnoreCase)))
+ if (user.Policy.BlockedTags.Any(i => hasTags.Tags.Contains(i, StringComparer.OrdinalIgnoreCase)))
{
return false;
}
@@ -1035,7 +1038,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <param name="config">The configuration.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- protected virtual bool GetBlockUnratedValue(UserConfiguration config)
+ protected virtual bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Other);
}
@@ -1574,6 +1577,11 @@ namespace MediaBrowser.Controller.Entities
foreach (var newImage in images)
{
+ if (newImage == null)
+ {
+ throw new ArgumentException("null image found in list");
+ }
+
var existing = existingImages
.FirstOrDefault(i => string.Equals(i.Path, newImage.FullName, StringComparison.OrdinalIgnoreCase));
diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs
index ea7ecfb4a..381b2101d 100644
--- a/MediaBrowser.Controller/Entities/Book.cs
+++ b/MediaBrowser.Controller/Entities/Book.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Model.Configuration;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
{
@@ -36,7 +37,7 @@ namespace MediaBrowser.Controller.Entities
Tags = new List<string>();
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Book);
}
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index a10742f01..f47a439a7 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -66,6 +66,22 @@ namespace MediaBrowser.Controller.Entities
return CreateResolveArgs(directoryService).FileSystemChildren;
}
+ internal override bool IsValidFromResolver(BaseItem newItem)
+ {
+ var newCollectionFolder = newItem as CollectionFolder;
+
+ if (newCollectionFolder != null)
+ {
+ if (!string.Equals(CollectionType, newCollectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ }
+
+
+ return base.IsValidFromResolver(newItem);
+ }
+
private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService)
{
var path = ContainingFolderPath;
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 87ad9c380..2761aa5d7 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -303,10 +303,10 @@ namespace MediaBrowser.Controller.Entities
{
if (this is ICollectionFolder)
{
- if (user.Configuration.BlockedMediaFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase) ||
+ if (user.Policy.BlockedMediaFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase) ||
// Backwards compatibility
- user.Configuration.BlockedMediaFolders.Contains(Name, StringComparer.OrdinalIgnoreCase))
+ user.Policy.BlockedMediaFolders.Contains(Name, StringComparer.OrdinalIgnoreCase))
{
return false;
}
@@ -545,7 +545,7 @@ namespace MediaBrowser.Controller.Entities
foreach (var child in children)
{
- if (tasks.Count >= 3)
+ if (tasks.Count >= 2)
{
await Task.WhenAll(tasks).ConfigureAwait(false);
tasks.Clear();
@@ -708,7 +708,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>IEnumerable{BaseItem}.</returns>
protected virtual IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
{
- var collectionType = LibraryManager.FindCollectionType(this);
+ var collectionType = LibraryManager.GetContentType(this);
return LibraryManager.ResolvePaths(GetFileSystemChildren(directoryService), directoryService, this, collectionType);
}
diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs
index e4d032359..bf32d3e63 100644
--- a/MediaBrowser.Controller/Entities/Game.cs
+++ b/MediaBrowser.Controller/Entities/Game.cs
@@ -4,6 +4,7 @@ using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
{
@@ -108,7 +109,7 @@ namespace MediaBrowser.Controller.Entities
return base.GetDeletePaths();
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Game);
}
diff --git a/MediaBrowser.Controller/Entities/GameSystem.cs b/MediaBrowser.Controller/Entities/GameSystem.cs
index f2fec4397..758498977 100644
--- a/MediaBrowser.Controller/Entities/GameSystem.cs
+++ b/MediaBrowser.Controller/Entities/GameSystem.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using System;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
{
@@ -43,7 +44,7 @@ namespace MediaBrowser.Controller.Entities
return base.GetUserDataKey();
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
// Don't block. Determine by game
return false;
diff --git a/MediaBrowser.Controller/Entities/IHasMediaSources.cs b/MediaBrowser.Controller/Entities/IHasMediaSources.cs
index d487362f5..98d268298 100644
--- a/MediaBrowser.Controller/Entities/IHasMediaSources.cs
+++ b/MediaBrowser.Controller/Entities/IHasMediaSources.cs
@@ -49,8 +49,8 @@ namespace MediaBrowser.Controller.Entities
: new[] { user.Configuration.AudioLanguagePreference };
var preferredSubs = string.IsNullOrEmpty(user.Configuration.SubtitleLanguagePreference)
- ? new string[] { }
- : new[] { user.Configuration.SubtitleLanguagePreference };
+ ? new List<string> { }
+ : new List<string> { user.Configuration.SubtitleLanguagePreference };
foreach (var source in sources)
{
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
index 9dc600675..4483c7b0f 100644
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
@@ -9,6 +9,7 @@ using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Movies
{
@@ -18,7 +19,7 @@ namespace MediaBrowser.Controller.Entities.Movies
public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer, IHasShares
{
public List<Share> Shares { get; set; }
-
+
public BoxSet()
{
RemoteTrailers = new List<MediaUrl>();
@@ -67,7 +68,7 @@ namespace MediaBrowser.Controller.Entities.Movies
/// <value>The display order.</value>
public string DisplayOrder { get; set; }
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Movie);
}
@@ -170,10 +171,13 @@ namespace MediaBrowser.Controller.Entities.Movies
{
var userId = user.Id.ToString("N");
- return Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)) ||
+ // Need to check Count > 0 for boxsets created prior to the introduction of Shares
+ if (Shares.Count > 0 && !Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)))
+ {
+ //return false;
+ }
- // Need to support this for boxsets created prior to the creation of Shares
- Shares.Count == 0;
+ return true;
}
return false;
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index e749d89e4..b3774cfe0 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.IO;
@@ -146,14 +147,21 @@ namespace MediaBrowser.Controller.Entities.Movies
return itemsChanged;
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Movie);
}
public MovieInfo GetLookupInfo()
{
- return GetItemLookupInfo<MovieInfo>();
+ var info = GetItemLookupInfo<MovieInfo>();
+
+ if (!IsInMixedFolder)
+ {
+ info.Name = System.IO.Path.GetFileName(ContainingFolderPath);
+ }
+
+ return info;
}
public override bool BeforeMetadataRefresh()
diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs
index d7cd62aa6..4ca8cf1c5 100644
--- a/MediaBrowser.Controller/Entities/MusicVideo.cs
+++ b/MediaBrowser.Controller/Entities/MusicVideo.cs
@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
{
@@ -80,7 +81,7 @@ namespace MediaBrowser.Controller.Entities
return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.GetUserDataKey();
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Music);
}
diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs
index 367db5dcb..a3d892181 100644
--- a/MediaBrowser.Controller/Entities/Photo.cs
+++ b/MediaBrowser.Controller/Entities/Photo.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Model.Drawing;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
{
@@ -69,8 +70,8 @@ namespace MediaBrowser.Controller.Entities
public double? Longitude { get; set; }
public double? Altitude { get; set; }
public int? IsoSpeedRating { get; set; }
-
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Other);
}
diff --git a/MediaBrowser.Controller/Entities/PhotoAlbum.cs b/MediaBrowser.Controller/Entities/PhotoAlbum.cs
index 982b1ef17..24ebf8815 100644
--- a/MediaBrowser.Controller/Entities/PhotoAlbum.cs
+++ b/MediaBrowser.Controller/Entities/PhotoAlbum.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Model.Configuration;
using System.Linq;
using System.Runtime.Serialization;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
{
@@ -22,8 +23,8 @@ namespace MediaBrowser.Controller.Entities
return true;
}
}
-
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Other);
}
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index cc0fc6812..6b67cebc8 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -1,7 +1,7 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -179,6 +179,15 @@ namespace MediaBrowser.Controller.Entities.TV
}
[IgnoreDataMember]
+ public bool IsInSeasonFolder
+ {
+ get
+ {
+ return FindParent<Season>() != null;
+ }
+ }
+
+ [IgnoreDataMember]
public string SeriesName
{
get
@@ -275,7 +284,7 @@ namespace MediaBrowser.Controller.Entities.TV
return new[] { Path };
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Series);
}
@@ -301,51 +310,9 @@ namespace MediaBrowser.Controller.Entities.TV
{
var hasChanges = base.BeforeMetadataRefresh();
- var locationType = LocationType;
- if (locationType == LocationType.FileSystem || locationType == LocationType.Offline)
- {
- if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path))
- {
- IndexNumber = LibraryManager.GetEpisodeNumberFromFile(Path, true);
-
- // If a change was made record it
- if (IndexNumber.HasValue)
- {
- hasChanges = true;
- }
- }
-
- if (!IndexNumberEnd.HasValue && !string.IsNullOrEmpty(Path))
- {
- IndexNumberEnd = LibraryManager.GetEndingEpisodeNumberFromFile(Path);
-
- // If a change was made record it
- if (IndexNumberEnd.HasValue)
- {
- hasChanges = true;
- }
- }
- }
-
- if (!ParentIndexNumber.HasValue)
+ if (LibraryManager.FillMissingEpisodeNumbersFromPath(this))
{
- var season = Season;
-
- if (season != null)
- {
- ParentIndexNumber = season.IndexNumber;
- }
-
- if (!ParentIndexNumber.HasValue && !string.IsNullOrEmpty(Path))
- {
- ParentIndexNumber = LibraryManager.GetSeasonNumberFromEpisodeFile(Path);
- }
-
- // If a change was made record it
- if (ParentIndexNumber.HasValue)
- {
- hasChanges = true;
- }
+ hasChanges = true;
}
return hasChanges;
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index 2df90244c..54db12b6f 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -1,9 +1,9 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
+using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Users;
+using MoreLinq;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
@@ -155,24 +155,6 @@ namespace MediaBrowser.Controller.Entities.TV
return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name;
}
- private IEnumerable<Episode> GetEpisodes()
- {
- var series = Series;
-
- if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
- {
- var seasonNumber = IndexNumber;
-
- if (seasonNumber.HasValue)
- {
- return series.RecursiveChildren.OfType<Episode>()
- .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value);
- }
- }
-
- return Children.OfType<Episode>();
- }
-
[IgnoreDataMember]
public bool IsMissingSeason
{
@@ -220,16 +202,32 @@ namespace MediaBrowser.Controller.Entities.TV
var episodes = GetRecursiveChildren(user)
.OfType<Episode>();
- if (IndexNumber.HasValue)
+ var series = Series;
+
+ if (IndexNumber.HasValue && series != null)
{
- var series = Series;
+ return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes);
+ }
- if (series != null)
+ if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
+ {
+ var seasonNumber = IndexNumber;
+ var list = episodes.ToList();
+
+ if (seasonNumber.HasValue)
{
- return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes);
+ list.AddRange(series.GetRecursiveChildren(user).OfType<Episode>()
+ .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value));
+ }
+ else
+ {
+ list.AddRange(series.GetRecursiveChildren(user).OfType<Episode>()
+ .Where(i => !i.ParentIndexNumber.HasValue));
}
- }
+ episodes = list.DistinctBy(i => i.Id);
+ }
+
if (!includeMissingEpisodes)
{
episodes = episodes.Where(i => !i.IsMissingEpisode);
@@ -244,12 +242,39 @@ namespace MediaBrowser.Controller.Entities.TV
.Cast<Episode>();
}
+ private IEnumerable<Episode> GetEpisodes()
+ {
+ var episodes = RecursiveChildren.OfType<Episode>();
+ var series = Series;
+
+ if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
+ {
+ var seasonNumber = IndexNumber;
+ var list = episodes.ToList();
+
+ if (seasonNumber.HasValue)
+ {
+ list.AddRange(series.RecursiveChildren.OfType<Episode>()
+ .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value));
+ }
+ else
+ {
+ list.AddRange(series.RecursiveChildren.OfType<Episode>()
+ .Where(i => !i.ParentIndexNumber.HasValue));
+ }
+
+ episodes = list.DistinctBy(i => i.Id);
+ }
+
+ return episodes;
+ }
+
public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
{
return GetEpisodes(user);
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
// Don't block. Let either the entire series rating or episode rating determine it
return false;
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 4c0d1fdfb..55cfffeb2 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -87,7 +88,17 @@ namespace MediaBrowser.Controller.Entities.TV
/// Gets or sets the date last episode added.
/// </summary>
/// <value>The date last episode added.</value>
- public DateTime DateLastEpisodeAdded { get; set; }
+ [IgnoreDataMember]
+ public DateTime DateLastEpisodeAdded
+ {
+ get
+ {
+ return RecursiveChildren.OfType<Episode>()
+ .Select(i => i.DateCreated)
+ .OrderByDescending(i => i)
+ .FirstOrDefault();
+ }
+ }
/// <summary>
/// Series aren't included directly in indices - Their Episodes will roll up to them
@@ -246,7 +257,7 @@ namespace MediaBrowser.Controller.Entities.TV
});
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Series);
}
diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs
index bb165d790..7a1eef8db 100644
--- a/MediaBrowser.Controller/Entities/Trailer.cs
+++ b/MediaBrowser.Controller/Entities/Trailer.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
{
@@ -98,7 +99,7 @@ namespace MediaBrowser.Controller.Entities
return base.GetUserDataKey();
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Trailer);
}
diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs
index 3dfc8cc7d..626afcfdf 100644
--- a/MediaBrowser.Controller/Entities/User.cs
+++ b/MediaBrowser.Controller/Entities/User.cs
@@ -1,5 +1,4 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Connect;
@@ -107,37 +106,27 @@ namespace MediaBrowser.Controller.Entities
/// <value>The last activity date.</value>
public DateTime? LastActivityDate { get; set; }
- /// <summary>
- /// The _configuration
- /// </summary>
- private UserConfiguration _configuration;
- /// <summary>
- /// The _configuration initialized
- /// </summary>
- private bool _configurationInitialized;
- /// <summary>
- /// The _configuration sync lock
- /// </summary>
- private object _configurationSyncLock = new object();
- /// <summary>
- /// Gets the user's configuration
- /// </summary>
- /// <value>The configuration.</value>
+ private UserConfiguration _config;
+ private readonly object _configSyncLock = new object();
[IgnoreDataMember]
public UserConfiguration Configuration
{
get
{
- // Lazy load
- LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationInitialized, ref _configurationSyncLock, () => (UserConfiguration)ConfigurationHelper.GetXmlConfiguration(typeof(UserConfiguration), ConfigurationFilePath, XmlSerializer));
- return _configuration;
- }
- private set
- {
- _configuration = value;
+ if (_config == null)
+ {
+ lock (_configSyncLock)
+ {
+ if (_config == null)
+ {
+ _config = UserManager.GetUserConfiguration(this);
+ }
+ }
+ }
- _configurationInitialized = value != null;
+ return _config;
}
+ set { _config = value; }
}
private UserPolicy _policy;
@@ -256,35 +245,6 @@ namespace MediaBrowser.Controller.Entities
return System.IO.Path.Combine(parentPath, Id.ToString("N"));
}
- /// <summary>
- /// Gets the path to the user's configuration file
- /// </summary>
- /// <value>The configuration file path.</value>
- [IgnoreDataMember]
- public string ConfigurationFilePath
- {
- get
- {
- return System.IO.Path.Combine(ConfigurationDirectoryPath, "config.xml");
- }
- }
-
- /// <summary>
- /// Updates the configuration.
- /// </summary>
- /// <param name="config">The config.</param>
- /// <exception cref="System.ArgumentNullException">config</exception>
- public void UpdateConfiguration(UserConfiguration config)
- {
- if (config == null)
- {
- throw new ArgumentNullException("config");
- }
-
- Configuration = config;
- UserManager.UpdateConfiguration(this, Configuration);
- }
-
public bool IsParentalScheduleAllowed()
{
return IsParentalScheduleAllowed(DateTime.UtcNow);
@@ -292,7 +252,7 @@ namespace MediaBrowser.Controller.Entities
public bool IsParentalScheduleAllowed(DateTime date)
{
- var schedules = Configuration.AccessSchedules;
+ var schedules = Policy.AccessSchedules;
if (schedules.Length == 0)
{
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index 926ffa19c..0364ff678 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -63,7 +63,8 @@ namespace MediaBrowser.Controller.Entities
{
CollectionType.Books,
CollectionType.HomeVideos,
- CollectionType.Photos
+ CollectionType.Photos,
+ string.Empty
};
var collectionFolder = folder as ICollectionFolder;
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index 0c6125dbe..3abaf095c 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -91,6 +91,21 @@ namespace MediaBrowser.Controller.Entities
get { return LocalAlternateVersions.Count > 0; }
}
+ [IgnoreDataMember]
+ public bool IsArchive
+ {
+ get
+ {
+ if (string.IsNullOrWhiteSpace(Path))
+ {
+ return false;
+ }
+ var ext = System.IO.Path.GetExtension(Path) ?? string.Empty;
+
+ return new[] { ".zip", ".rar", ".7z" }.Contains(ext, StringComparer.OrdinalIgnoreCase);
+ }
+ }
+
public IEnumerable<Guid> GetAdditionalPartIds()
{
return AdditionalParts.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
@@ -246,7 +261,7 @@ namespace MediaBrowser.Controller.Entities
{
return System.IO.Path.GetFileName(Path);
}
-
+
return System.IO.Path.GetFileNameWithoutExtension(Path);
}
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 33dea4dca..8573f32e0 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.Sorting;
@@ -22,11 +23,9 @@ namespace MediaBrowser.Controller.Library
/// </summary>
/// <param name="fileInfo">The file information.</param>
/// <param name="parent">The parent.</param>
- /// <param name="collectionType">Type of the collection.</param>
/// <returns>BaseItem.</returns>
BaseItem ResolvePath(FileSystemInfo fileInfo,
- Folder parent = null,
- string collectionType = null);
+ Folder parent = null);
/// <summary>
/// Resolves a set of files into a list of BaseItem
@@ -258,9 +257,16 @@ namespace MediaBrowser.Controller.Library
/// </summary>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
- string FindCollectionType(BaseItem item);
+ string GetContentType(BaseItem item);
/// <summary>
+ /// Gets the type of the inherited content.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <returns>System.String.</returns>
+ string GetInheritedContentType(BaseItem item);
+
+ /// <summary>
/// Normalizes the root path list.
/// </summary>
/// <param name="paths">The paths.</param>
@@ -340,26 +346,11 @@ namespace MediaBrowser.Controller.Library
int? GetSeasonNumberFromPath(string path);
/// <summary>
- /// Gets the season number from episode file.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>System.Nullable&lt;System.Int32&gt;.</returns>
- int? GetSeasonNumberFromEpisodeFile(string path);
-
- /// <summary>
- /// Gets the ending episode number from file.
+ /// Fills the missing episode numbers from path.
/// </summary>
- /// <param name="path">The path.</param>
- /// <returns>System.Nullable&lt;System.Int32&gt;.</returns>
- int? GetEndingEpisodeNumberFromFile(string path);
-
- /// <summary>
- /// Gets the episode number from file.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="considerSeasonless">if set to <c>true</c> [consider seasonless].</param>
- /// <returns>System.Nullable&lt;System.Int32&gt;.</returns>
- int? GetEpisodeNumberFromFile(string path, bool considerSeasonless);
+ /// <param name="episode">The episode.</param>
+ /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
+ bool FillMissingEpisodeNumbersFromPath(Episode episode);
/// <summary>
/// Parses the name.
diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs
index 226f77525..06e7d1763 100644
--- a/MediaBrowser.Controller/Library/IUserDataManager.cs
+++ b/MediaBrowser.Controller/Library/IUserDataManager.cs
@@ -61,5 +61,9 @@ namespace MediaBrowser.Controller.Library
/// <returns></returns>
Task SaveAllUserData(Guid userId, IEnumerable<UserItemData> userData, CancellationToken cancellationToken);
+ /// <summary>
+ /// Updates playstate for an item and returns true or false indicating if it was played to completion
+ /// </summary>
+ bool UpdatePlayState(BaseItem item, UserItemData data, long positionTicks);
}
}
diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs
index 9dc16ba4d..f5846973e 100644
--- a/MediaBrowser.Controller/Library/IUserManager.cs
+++ b/MediaBrowser.Controller/Library/IUserManager.cs
@@ -36,13 +36,6 @@ namespace MediaBrowser.Controller.Library
event EventHandler<GenericEventArgs<User>> UserPasswordChanged;
/// <summary>
- /// Updates the configuration.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="newConfiguration">The new configuration.</param>
- void UpdateConfiguration(User user, UserConfiguration newConfiguration);
-
- /// <summary>
/// Gets a User by Id
/// </summary>
/// <param name="id">The id.</param>
@@ -173,10 +166,32 @@ namespace MediaBrowser.Controller.Library
UserPolicy GetUserPolicy(User user);
/// <summary>
+ /// Gets the user configuration.
+ /// </summary>
+ /// <param name="user">The user.</param>
+ /// <returns>UserConfiguration.</returns>
+ UserConfiguration GetUserConfiguration(User user);
+
+ /// <summary>
+ /// Updates the configuration.
+ /// </summary>
+ /// <param name="userId">The user identifier.</param>
+ /// <param name="newConfiguration">The new configuration.</param>
+ /// <returns>Task.</returns>
+ Task UpdateConfiguration(string userId, UserConfiguration newConfiguration);
+
+ /// <summary>
/// Updates the user policy.
/// </summary>
/// <param name="userId">The user identifier.</param>
/// <param name="userPolicy">The user policy.</param>
Task UpdateUserPolicy(string userId, UserPolicy userPolicy);
+
+ /// <summary>
+ /// Makes the valid username.
+ /// </summary>
+ /// <param name="username">The username.</param>
+ /// <returns>System.String.</returns>
+ string MakeValidUsername(string username);
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
index 9f8d67a48..b95d67ad8 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System.Linq;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.LiveTv
{
@@ -78,7 +79,7 @@ namespace MediaBrowser.Controller.LiveTv
}
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
index df118b25f..de72accff 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
@@ -6,6 +6,7 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.MediaInfo;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.LiveTv
{
@@ -33,7 +34,7 @@ namespace MediaBrowser.Controller.LiveTv
}
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.LiveTvChannel);
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
index 266eaabee..29b23a551 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
@@ -6,6 +6,7 @@ using System;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.LiveTv
{
@@ -199,7 +200,7 @@ namespace MediaBrowser.Controller.LiveTv
return ItemRepository.SaveItem(this, cancellationToken);
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
index 66de81213..6fc985643 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System.Linq;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.LiveTv
{
@@ -78,7 +79,7 @@ namespace MediaBrowser.Controller.LiveTv
}
}
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);
}
diff --git a/MediaBrowser.Controller/LiveTv/RecordingGroup.cs b/MediaBrowser.Controller/LiveTv/RecordingGroup.cs
index 7bd810b8d..d7250d9d2 100644
--- a/MediaBrowser.Controller/LiveTv/RecordingGroup.cs
+++ b/MediaBrowser.Controller/LiveTv/RecordingGroup.cs
@@ -1,11 +1,12 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.LiveTv
{
public class RecordingGroup : Folder
{
- protected override bool GetBlockUnratedValue(UserConfiguration config)
+ protected override bool GetBlockUnratedValue(UserPolicy config)
{
// Don't block.
return false;
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index c198a58d4..0667730fd 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -115,6 +115,7 @@
<Compile Include="Drawing\ImageProcessingOptions.cs" />
<Compile Include="Drawing\ImageProcessorExtensions.cs" />
<Compile Include="Drawing\ImageStream.cs" />
+ <Compile Include="Dto\DtoOptions.cs" />
<Compile Include="Dto\IDtoService.cs" />
<Compile Include="Entities\AdultVideo.cs" />
<Compile Include="Entities\Audio\IHasAlbumArtist.cs" />
@@ -198,18 +199,17 @@
<Compile Include="LiveTv\SeriesTimerInfo.cs" />
<Compile Include="LiveTv\TimerInfo.cs" />
<Compile Include="Localization\ILocalizationManager.cs" />
- <Compile Include="MediaEncoding\EncodingOptions.cs" />
<Compile Include="MediaEncoding\ChapterImageRefreshOptions.cs" />
- <Compile Include="MediaEncoding\EncodingResult.cs" />
+ <Compile Include="MediaEncoding\EncodingJobOptions.cs" />
<Compile Include="MediaEncoding\IEncodingManager.cs" />
<Compile Include="MediaEncoding\ImageEncodingOptions.cs" />
<Compile Include="MediaEncoding\IMediaEncoder.cs" />
<Compile Include="MediaEncoding\InternalMediaInfoResult.cs" />
<Compile Include="MediaEncoding\ISubtitleEncoder.cs" />
<Compile Include="MediaEncoding\MediaStreamSelector.cs" />
- <Compile Include="MediaEncoding\VideoEncodingOptions.cs" />
<Compile Include="Net\AuthenticatedAttribute.cs" />
<Compile Include="Net\AuthorizationInfo.cs" />
+ <Compile Include="Net\BasePeriodicWebSocketListener.cs" />
<Compile Include="Net\IAuthorizationContext.cs" />
<Compile Include="Net\IAuthService.cs" />
<Compile Include="Net\IHasAuthorization.cs" />
@@ -221,10 +221,15 @@
<Compile Include="Net\IServerManager.cs" />
<Compile Include="Net\IServiceRequest.cs" />
<Compile Include="Net\ISessionContext.cs" />
+ <Compile Include="Net\IWebSocket.cs" />
+ <Compile Include="Net\IWebSocketConnection.cs" />
+ <Compile Include="Net\IWebSocketListener.cs" />
<Compile Include="Net\LoggedAttribute.cs" />
<Compile Include="Net\SecurityException.cs" />
<Compile Include="Net\ServiceStackServiceRequest.cs" />
<Compile Include="Net\StaticResultOptions.cs" />
+ <Compile Include="Net\WebSocketConnectEventArgs.cs" />
+ <Compile Include="Net\WebSocketMessageInfo.cs" />
<Compile Include="News\INewsService.cs" />
<Compile Include="Notifications\INotificationManager.cs" />
<Compile Include="Notifications\INotificationService.cs" />
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
new file mode 100644
index 000000000..a988c2f97
--- /dev/null
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
@@ -0,0 +1,91 @@
+using MediaBrowser.Model.Dlna;
+
+namespace MediaBrowser.Controller.MediaEncoding
+{
+ public class EncodingJobOptions
+ {
+ public string OutputContainer { get; set; }
+
+ public long? StartTimeTicks { get; set; }
+ public int? Width { get; set; }
+ public int? Height { get; set; }
+ public int? MaxWidth { get; set; }
+ public int? MaxHeight { get; set; }
+ public bool Static = false;
+ public float? Framerate { get; set; }
+ public float? MaxFramerate { get; set; }
+ public string Profile { get; set; }
+ public int? Level { get; set; }
+
+ public string DeviceId { get; set; }
+ public string ItemId { get; set; }
+ public string MediaSourceId { get; set; }
+ public string AudioCodec { get; set; }
+
+ public bool EnableAutoStreamCopy { get; set; }
+
+ public int? MaxAudioChannels { get; set; }
+ public int? AudioChannels { get; set; }
+ public int? AudioBitRate { get; set; }
+ public int? AudioSampleRate { get; set; }
+
+ public DeviceProfile DeviceProfile { get; set; }
+ public EncodingContext Context { get; set; }
+
+ public string VideoCodec { get; set; }
+
+ public int? VideoBitRate { get; set; }
+ public int? AudioStreamIndex { get; set; }
+ public int? VideoStreamIndex { get; set; }
+ public int? SubtitleStreamIndex { get; set; }
+ public int? MaxRefFrames { get; set; }
+ public int? MaxVideoBitDepth { get; set; }
+ public SubtitleDeliveryMethod SubtitleMethod { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance has fixed resolution.
+ /// </summary>
+ /// <value><c>true</c> if this instance has fixed resolution; otherwise, <c>false</c>.</value>
+ public bool HasFixedResolution
+ {
+ get
+ {
+ return Width.HasValue || Height.HasValue;
+ }
+ }
+
+ public bool? Cabac { get; set; }
+
+ public EncodingJobOptions()
+ {
+
+ }
+
+ public EncodingJobOptions(StreamInfo info, DeviceProfile deviceProfile)
+ {
+ OutputContainer = info.Container;
+ StartTimeTicks = info.StartPositionTicks;
+ MaxWidth = info.MaxWidth;
+ MaxHeight = info.MaxHeight;
+ MaxFramerate = info.MaxFramerate;
+ Profile = info.VideoProfile;
+ Level = info.VideoLevel;
+ ItemId = info.ItemId;
+ MediaSourceId = info.MediaSourceId;
+ AudioCodec = info.AudioCodec;
+ MaxAudioChannels = info.MaxAudioChannels;
+ AudioBitRate = info.AudioBitrate;
+ AudioSampleRate = info.TargetAudioSampleRate;
+ DeviceProfile = deviceProfile;
+ VideoCodec = info.VideoCodec;
+ VideoBitRate = info.VideoBitrate;
+ AudioStreamIndex = info.AudioStreamIndex;
+ SubtitleStreamIndex = info.SubtitleStreamIndex;
+ MaxRefFrames = info.MaxRefFrames;
+ MaxVideoBitDepth = info.MaxVideoBitDepth;
+ SubtitleMethod = info.SubtitleDeliveryMethod;
+ Cabac = info.Cabac;
+ Context = info.Context;
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingOptions.cs
deleted file mode 100644
index 26182ebc4..000000000
--- a/MediaBrowser.Controller/MediaEncoding/EncodingOptions.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Model.Dlna;
-
-namespace MediaBrowser.Controller.MediaEncoding
-{
- public class EncodingOptions
- {
- /// <summary>
- /// Gets or sets the item identifier.
- /// </summary>
- /// <value>The item identifier.</value>
- public string ItemId { get; set; }
-
- /// <summary>
- /// Gets or sets the media source identifier.
- /// </summary>
- /// <value>The media source identifier.</value>
- public string MediaSourceId { get; set; }
-
- /// <summary>
- /// Gets or sets the device profile.
- /// </summary>
- /// <value>The device profile.</value>
- public DeviceProfile DeviceProfile { get; set; }
-
- /// <summary>
- /// Gets or sets the output path.
- /// </summary>
- /// <value>The output path.</value>
- public string OutputPath { get; set; }
-
- /// <summary>
- /// Gets or sets the container.
- /// </summary>
- /// <value>The container.</value>
- public string Container { get; set; }
-
- /// <summary>
- /// Gets or sets the audio codec.
- /// </summary>
- /// <value>The audio codec.</value>
- public string AudioCodec { get; set; }
-
- /// <summary>
- /// Gets or sets the start time ticks.
- /// </summary>
- /// <value>The start time ticks.</value>
- public long? StartTimeTicks { get; set; }
-
- /// <summary>
- /// Gets or sets the maximum channels.
- /// </summary>
- /// <value>The maximum channels.</value>
- public int? MaxAudioChannels { get; set; }
-
- /// <summary>
- /// Gets or sets the channels.
- /// </summary>
- /// <value>The channels.</value>
- public int? AudioChannels { get; set; }
-
- /// <summary>
- /// Gets or sets the sample rate.
- /// </summary>
- /// <value>The sample rate.</value>
- public int? AudioSampleRate { get; set; }
-
- /// <summary>
- /// Gets or sets the bit rate.
- /// </summary>
- /// <value>The bit rate.</value>
- public int? AudioBitRate { get; set; }
-
- /// <summary>
- /// Gets or sets the maximum audio bit rate.
- /// </summary>
- /// <value>The maximum audio bit rate.</value>
- public int? MaxAudioBitRate { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingResult.cs b/MediaBrowser.Controller/MediaEncoding/EncodingResult.cs
deleted file mode 100644
index 75ee90e42..000000000
--- a/MediaBrowser.Controller/MediaEncoding/EncodingResult.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.MediaEncoding
-{
- public class EncodingResult
- {
- public string OutputPath { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index 38c2c83c4..47544f972 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -96,5 +96,27 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <param name="ticks">The ticks.</param>
/// <returns>System.String.</returns>
string GetTimeParameter(long ticks);
+
+ /// <summary>
+ /// Encodes the audio.
+ /// </summary>
+ /// <param name="options">The options.</param>
+ /// <param name="progress">The progress.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task<string> EncodeAudio(EncodingJobOptions options,
+ IProgress<double> progress,
+ CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Encodes the video.
+ /// </summary>
+ /// <param name="options">The options.</param>
+ /// <param name="progress">The progress.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task&lt;System.String&gt;.</returns>
+ Task<string> EncodeVideo(EncodingJobOptions options,
+ IProgress<double> progress,
+ CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs b/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs
index 58a68c257..4a807df7a 100644
--- a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs
+++ b/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs
@@ -34,15 +34,13 @@ namespace MediaBrowser.Controller.MediaEncoding
}
public static int? GetDefaultSubtitleStreamIndex(List<MediaStream> streams,
- IEnumerable<string> preferredLanguages,
+ List<string> preferredLanguages,
SubtitlePlaybackMode mode,
string audioTrackLanguage)
{
- var languages = preferredLanguages.ToList();
- streams = GetSortedStreams(streams, MediaStreamType.Subtitle, languages).ToList();
+ streams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages).ToList();
var full = streams.Where(s => !s.IsForced);
- var forced = streams.Where(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase));
MediaStream stream = null;
@@ -54,9 +52,9 @@ namespace MediaBrowser.Controller.MediaEncoding
if (mode == SubtitlePlaybackMode.Default)
{
// if the audio language is not understood by the user, load their preferred subs, if there are any
- if (!ContainsOrdinal(languages, audioTrackLanguage))
+ if (!ContainsOrdinal(preferredLanguages, audioTrackLanguage))
{
- stream = full.FirstOrDefault(s => ContainsOrdinal(languages, s.Language));
+ stream = full.FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language));
}
}
else if (mode == SubtitlePlaybackMode.Always)
@@ -66,7 +64,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// load forced subs if we have found no suitable full subtitles
- stream = stream ?? forced.FirstOrDefault();
+ stream = stream ?? streams.FirstOrDefault(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase));
if (stream != null)
{
diff --git a/MediaBrowser.Controller/MediaEncoding/VideoEncodingOptions.cs b/MediaBrowser.Controller/MediaEncoding/VideoEncodingOptions.cs
deleted file mode 100644
index 773f0ea46..000000000
--- a/MediaBrowser.Controller/MediaEncoding/VideoEncodingOptions.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-
-namespace MediaBrowser.Controller.MediaEncoding
-{
- public class VideoEncodingOptions : EncodingOptions
- {
- public string VideoCodec { get; set; }
-
- public string VideoProfile { get; set; }
-
- public double? VideoLevel { get; set; }
-
- public int? VideoStreamIndex { get; set; }
-
- public int? AudioStreamIndex { get; set; }
-
- public int? SubtitleStreamIndex { get; set; }
-
- public int? MaxWidth { get; set; }
-
- public int? MaxHeight { get; set; }
-
- public int? Height { get; set; }
-
- public int? Width { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
new file mode 100644
index 000000000..f1e371c1a
--- /dev/null
+++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
@@ -0,0 +1,327 @@
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Net
+{
+ /// <summary>
+ /// Starts sending data over a web socket periodically when a message is received, and then stops when a corresponding stop message is received
+ /// </summary>
+ /// <typeparam name="TReturnDataType">The type of the T return data type.</typeparam>
+ /// <typeparam name="TStateType">The type of the T state type.</typeparam>
+ public abstract class BasePeriodicWebSocketListener<TReturnDataType, TStateType> : IWebSocketListener, IDisposable
+ where TStateType : WebSocketListenerState, new()
+ where TReturnDataType : class
+ {
+ /// <summary>
+ /// The _active connections
+ /// </summary>
+ protected readonly List<Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>> ActiveConnections =
+ new List<Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>>();
+
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ protected abstract string Name { get; }
+
+ /// <summary>
+ /// Gets the data to send.
+ /// </summary>
+ /// <param name="state">The state.</param>
+ /// <returns>Task{`1}.</returns>
+ protected abstract Task<TReturnDataType> GetDataToSend(TStateType state);
+
+ /// <summary>
+ /// The logger
+ /// </summary>
+ protected ILogger Logger;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BasePeriodicWebSocketListener{TStateType}" /> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <exception cref="System.ArgumentNullException">logger</exception>
+ protected BasePeriodicWebSocketListener(ILogger logger)
+ {
+ if (logger == null)
+ {
+ throw new ArgumentNullException("logger");
+ }
+
+ Logger = logger;
+ }
+
+ /// <summary>
+ /// The null task result
+ /// </summary>
+ protected Task NullTaskResult = Task.FromResult(true);
+
+ /// <summary>
+ /// Processes the message.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>Task.</returns>
+ public Task ProcessMessage(WebSocketMessageInfo message)
+ {
+ if (message.MessageType.Equals(Name + "Start", StringComparison.OrdinalIgnoreCase))
+ {
+ Start(message);
+ }
+
+ if (message.MessageType.Equals(Name + "Stop", StringComparison.OrdinalIgnoreCase))
+ {
+ Stop(message);
+ }
+
+ return NullTaskResult;
+ }
+
+ protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
+
+ protected virtual bool SendOnTimer
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Starts sending messages over a web socket
+ /// </summary>
+ /// <param name="message">The message.</param>
+ private void Start(WebSocketMessageInfo message)
+ {
+ var vals = message.Data.Split(',');
+
+ var dueTimeMs = long.Parse(vals[0], UsCulture);
+ var periodMs = long.Parse(vals[1], UsCulture);
+
+ var cancellationTokenSource = new CancellationTokenSource();
+
+ Logger.Info("{1} Begin transmitting over websocket to {0}", message.Connection.RemoteEndPoint, GetType().Name);
+
+ var timer = SendOnTimer ?
+ new Timer(TimerCallback, message.Connection, Timeout.Infinite, Timeout.Infinite) :
+ null;
+
+ var state = new TStateType
+ {
+ IntervalMs = periodMs,
+ InitialDelayMs = dueTimeMs
+ };
+
+ var semaphore = new SemaphoreSlim(1, 1);
+
+ lock (ActiveConnections)
+ {
+ ActiveConnections.Add(new Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>(message.Connection, cancellationTokenSource, timer, state, semaphore));
+ }
+
+ if (timer != null)
+ {
+ timer.Change(TimeSpan.FromMilliseconds(dueTimeMs), TimeSpan.FromMilliseconds(periodMs));
+ }
+ }
+
+ /// <summary>
+ /// Timers the callback.
+ /// </summary>
+ /// <param name="state">The state.</param>
+ private void TimerCallback(object state)
+ {
+ var connection = (IWebSocketConnection)state;
+
+ Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> tuple;
+
+ lock (ActiveConnections)
+ {
+ tuple = ActiveConnections.FirstOrDefault(c => c.Item1 == connection);
+ }
+
+ if (tuple == null)
+ {
+ return;
+ }
+
+ if (connection.State != WebSocketState.Open || tuple.Item2.IsCancellationRequested)
+ {
+ DisposeConnection(tuple);
+ return;
+ }
+
+ SendData(tuple);
+ }
+
+ protected void SendData(bool force)
+ {
+ List<Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>> tuples;
+
+ lock (ActiveConnections)
+ {
+ tuples = ActiveConnections
+ .Where(c =>
+ {
+ if (c.Item1.State == WebSocketState.Open && !c.Item2.IsCancellationRequested)
+ {
+ var state = c.Item4;
+
+ if (force || (DateTime.UtcNow - state.DateLastSendUtc).TotalMilliseconds >= state.IntervalMs)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ })
+ .ToList();
+ }
+
+ foreach (var tuple in tuples)
+ {
+ SendData(tuple);
+ }
+ }
+
+ private async void SendData(Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> tuple)
+ {
+ var connection = tuple.Item1;
+
+ try
+ {
+ await tuple.Item5.WaitAsync(tuple.Item2.Token).ConfigureAwait(false);
+
+ var state = tuple.Item4;
+
+ var data = await GetDataToSend(state).ConfigureAwait(false);
+
+ if (data != null)
+ {
+ await connection.SendAsync(new WebSocketMessage<TReturnDataType>
+ {
+ MessageType = Name,
+ Data = data
+
+ }, tuple.Item2.Token).ConfigureAwait(false);
+
+ state.DateLastSendUtc = DateTime.UtcNow;
+ }
+
+ tuple.Item5.Release();
+ }
+ catch (OperationCanceledException)
+ {
+ if (tuple.Item2.IsCancellationRequested)
+ {
+ DisposeConnection(tuple);
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error sending web socket message {0}", ex, Name);
+ DisposeConnection(tuple);
+ }
+ }
+
+ /// <summary>
+ /// Stops sending messages over a web socket
+ /// </summary>
+ /// <param name="message">The message.</param>
+ private void Stop(WebSocketMessageInfo message)
+ {
+ lock (ActiveConnections)
+ {
+ var connection = ActiveConnections.FirstOrDefault(c => c.Item1 == message.Connection);
+
+ if (connection != null)
+ {
+ DisposeConnection(connection);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Disposes the connection.
+ /// </summary>
+ /// <param name="connection">The connection.</param>
+ private void DisposeConnection(Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> connection)
+ {
+ Logger.Info("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name);
+
+ var timer = connection.Item3;
+
+ if (timer != null)
+ {
+ try
+ {
+ timer.Dispose();
+ }
+ catch (ObjectDisposedException)
+ {
+
+ }
+ }
+
+ try
+ {
+ connection.Item2.Cancel();
+ connection.Item2.Dispose();
+ }
+ catch (ObjectDisposedException)
+ {
+
+ }
+
+ try
+ {
+ connection.Item5.Dispose();
+ }
+ catch (ObjectDisposedException)
+ {
+
+ }
+
+ ActiveConnections.Remove(connection);
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources.
+ /// </summary>
+ /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool dispose)
+ {
+ if (dispose)
+ {
+ lock (ActiveConnections)
+ {
+ foreach (var connection in ActiveConnections.ToList())
+ {
+ DisposeConnection(connection);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+ }
+
+ public class WebSocketListenerState
+ {
+ public DateTime DateLastSendUtc { get; set; }
+ public long InitialDelayMs { get; set; }
+ public long IntervalMs { get; set; }
+ }
+}
diff --git a/MediaBrowser.Controller/Net/IWebSocket.cs b/MediaBrowser.Controller/Net/IWebSocket.cs
new file mode 100644
index 000000000..b88f2c389
--- /dev/null
+++ b/MediaBrowser.Controller/Net/IWebSocket.cs
@@ -0,0 +1,54 @@
+using MediaBrowser.Model.Net;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Net
+{
+ /// <summary>
+ /// Interface IWebSocket
+ /// </summary>
+ public interface IWebSocket : IDisposable
+ {
+ /// <summary>
+ /// Occurs when [closed].
+ /// </summary>
+ event EventHandler<EventArgs> Closed;
+
+ /// <summary>
+ /// Gets or sets the state.
+ /// </summary>
+ /// <value>The state.</value>
+ WebSocketState State { get; }
+
+ /// <summary>
+ /// Gets or sets the receive action.
+ /// </summary>
+ /// <value>The receive action.</value>
+ Action<byte[]> OnReceiveBytes { get; set; }
+
+ /// <summary>
+ /// Gets or sets the on receive.
+ /// </summary>
+ /// <value>The on receive.</value>
+ Action<string> OnReceive { get; set; }
+
+ /// <summary>
+ /// Sends the async.
+ /// </summary>
+ /// <param name="bytes">The bytes.</param>
+ /// <param name="endOfMessage">if set to <c>true</c> [end of message].</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task SendAsync(byte[] bytes, bool endOfMessage, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends the asynchronous.
+ /// </summary>
+ /// <param name="text">The text.</param>
+ /// <param name="endOfMessage">if set to <c>true</c> [end of message].</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task SendAsync(string text, bool endOfMessage, CancellationToken cancellationToken);
+ }
+}
diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
new file mode 100644
index 000000000..37fd6708d
--- /dev/null
+++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
@@ -0,0 +1,72 @@
+using MediaBrowser.Model.Net;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Net
+{
+ public interface IWebSocketConnection : IDisposable
+ {
+ /// <summary>
+ /// Occurs when [closed].
+ /// </summary>
+ event EventHandler<EventArgs> Closed;
+
+ /// <summary>
+ /// Gets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ Guid Id { get; }
+
+ /// <summary>
+ /// Gets the last activity date.
+ /// </summary>
+ /// <value>The last activity date.</value>
+ DateTime LastActivityDate { get; }
+
+ /// <summary>
+ /// Gets or sets the receive action.
+ /// </summary>
+ /// <value>The receive action.</value>
+ Action<WebSocketMessageInfo> OnReceive { get; set; }
+
+ /// <summary>
+ /// Gets the state.
+ /// </summary>
+ /// <value>The state.</value>
+ WebSocketState State { get; }
+
+ /// <summary>
+ /// Gets the remote end point.
+ /// </summary>
+ /// <value>The remote end point.</value>
+ string RemoteEndPoint { get; }
+
+ /// <summary>
+ /// Sends a message asynchronously.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="message">The message.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ /// <exception cref="System.ArgumentNullException">message</exception>
+ Task SendAsync<T>(WebSocketMessage<T> message, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends a message asynchronously.
+ /// </summary>
+ /// <param name="buffer">The buffer.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task SendAsync(byte[] buffer, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends a message asynchronously.
+ /// </summary>
+ /// <param name="text">The text.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ /// <exception cref="System.ArgumentNullException">buffer</exception>
+ Task SendAsync(string text, CancellationToken cancellationToken);
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Net/IWebSocketListener.cs b/MediaBrowser.Controller/Net/IWebSocketListener.cs
new file mode 100644
index 000000000..2b4fc7676
--- /dev/null
+++ b/MediaBrowser.Controller/Net/IWebSocketListener.cs
@@ -0,0 +1,18 @@
+using MediaBrowser.Common.Net;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Net
+{
+ /// <summary>
+ ///This is an interface for listening to messages coming through a web socket connection
+ /// </summary>
+ public interface IWebSocketListener
+ {
+ /// <summary>
+ /// Processes the message.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>Task.</returns>
+ Task ProcessMessage(WebSocketMessageInfo message);
+ }
+}
diff --git a/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs b/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs
new file mode 100644
index 000000000..394fcd92f
--- /dev/null
+++ b/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace MediaBrowser.Controller.Net
+{
+ /// <summary>
+ /// Class WebSocketConnectEventArgs
+ /// </summary>
+ public class WebSocketConnectEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets or sets the web socket.
+ /// </summary>
+ /// <value>The web socket.</value>
+ public IWebSocket WebSocket { get; set; }
+ /// <summary>
+ /// Gets or sets the endpoint.
+ /// </summary>
+ /// <value>The endpoint.</value>
+ public string Endpoint { get; set; }
+ }
+}
diff --git a/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs b/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs
new file mode 100644
index 000000000..332f16420
--- /dev/null
+++ b/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs
@@ -0,0 +1,16 @@
+using MediaBrowser.Model.Net;
+
+namespace MediaBrowser.Controller.Net
+{
+ /// <summary>
+ /// Class WebSocketMessageInfo
+ /// </summary>
+ public class WebSocketMessageInfo : WebSocketMessage<string>
+ {
+ /// <summary>
+ /// Gets or sets the connection.
+ /// </summary>
+ /// <value>The connection.</value>
+ public IWebSocketConnection Connection { get; set; }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs
index 74a9c6606..06ea7be02 100644
--- a/MediaBrowser.Controller/Providers/DirectoryService.cs
+++ b/MediaBrowser.Controller/Providers/DirectoryService.cs
@@ -46,6 +46,11 @@ namespace MediaBrowser.Controller.Providers
private Dictionary<string, FileSystemInfo> GetFileSystemDictionary(string path, bool clearCache)
{
+ if (string.IsNullOrWhiteSpace(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
Dictionary<string, FileSystemInfo> entries;
if (clearCache)
diff --git a/MediaBrowser.Controller/Sync/ISyncManager.cs b/MediaBrowser.Controller/Sync/ISyncManager.cs
index 47339f677..59136c0e6 100644
--- a/MediaBrowser.Controller/Sync/ISyncManager.cs
+++ b/MediaBrowser.Controller/Sync/ISyncManager.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Sync;
+using MediaBrowser.Model.Users;
using System.Collections.Generic;
using System.Threading.Tasks;
@@ -20,7 +21,7 @@ namespace MediaBrowser.Controller.Sync
/// Gets the jobs.
/// </summary>
/// <returns>QueryResult&lt;SyncJob&gt;.</returns>
- QueryResult<SyncJob> GetJobs(SyncJobQuery query);
+ Task<QueryResult<SyncJob>> GetJobs(SyncJobQuery query);
/// <summary>
/// Gets the job items.
@@ -37,6 +38,13 @@ namespace MediaBrowser.Controller.Sync
SyncJob GetJob(string id);
/// <summary>
+ /// Updates the job.
+ /// </summary>
+ /// <param name="job">The job.</param>
+ /// <returns>Task.</returns>
+ Task UpdateJob(SyncJob job);
+
+ /// <summary>
/// Cancels the job.
/// </summary>
/// <param name="id">The identifier.</param>
@@ -80,5 +88,26 @@ namespace MediaBrowser.Controller.Sync
/// <param name="id">The identifier.</param>
/// <returns>SyncJobItem.</returns>
SyncJobItem GetJobItem(string id);
+
+ /// <summary>
+ /// Reports the offline action.
+ /// </summary>
+ /// <param name="action">The action.</param>
+ /// <returns>Task.</returns>
+ Task ReportOfflineAction(UserAction action);
+
+ /// <summary>
+ /// Gets the ready synchronize items.
+ /// </summary>
+ /// <param name="targetId">The target identifier.</param>
+ /// <returns>List&lt;SyncedItem&gt;.</returns>
+ List<SyncedItem> GetReadySyncItems(string targetId);
+
+ /// <summary>
+ /// Synchronizes the data.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>Task&lt;SyncDataResponse&gt;.</returns>
+ Task<SyncDataResponse> SyncData(SyncDataRequest request);
}
}
diff --git a/MediaBrowser.Controller/Sync/ISyncProvider.cs b/MediaBrowser.Controller/Sync/ISyncProvider.cs
index 89f61b80e..af08edb5e 100644
--- a/MediaBrowser.Controller/Sync/ISyncProvider.cs
+++ b/MediaBrowser.Controller/Sync/ISyncProvider.cs
@@ -19,10 +19,22 @@ namespace MediaBrowser.Controller.Sync
IEnumerable<SyncTarget> GetSyncTargets();
/// <summary>
+ /// Gets the synchronize targets.
+ /// </summary>
+ /// <param name="userId">The user identifier.</param>
+ /// <returns>IEnumerable&lt;SyncTarget&gt;.</returns>
+ IEnumerable<SyncTarget> GetSyncTargets(string userId);
+
+ /// <summary>
/// Gets the device profile.
/// </summary>
/// <param name="target">The target.</param>
/// <returns>DeviceProfile.</returns>
DeviceProfile GetDeviceProfile(SyncTarget target);
}
+
+ public interface IHasUniqueTargetIds
+ {
+
+ }
}