aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke <luke.pulverenti@gmail.com>2017-07-23 02:12:16 -0400
committerGitHub <noreply@github.com>2017-07-23 02:12:16 -0400
commit5ec9d4e9fe4b3e5109ca1abf6c1ffdb87fd2d4dd (patch)
treee49cc827583b6dffeabb142af5fae889b1a09581
parente99bc61d53f393dc475e265e3b5bc8c19b186594 (diff)
parent0d1b5ad733e6f1bbf6d730e723969495dda99016 (diff)
Merge pull request #2767 from MediaBrowser/beta
Beta
-rw-r--r--Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs13
-rw-r--r--Emby.Dlna/ContentDirectory/ControlHandler.cs356
-rw-r--r--Emby.Dlna/Didl/DidlBuilder.cs40
-rw-r--r--Emby.Dlna/Eventing/EventManager.cs60
-rw-r--r--Emby.Dlna/PlayTo/PlaylistItemFactory.cs5
-rw-r--r--Emby.Dlna/Profiles/LgTvProfile.cs9
-rw-r--r--Emby.Dlna/Profiles/Xml/LG Smart TV.xml3
-rw-r--r--Emby.Dlna/Service/BaseService.cs8
-rw-r--r--Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs2
-rw-r--r--Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs4
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj5
-rw-r--r--Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs2
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs28
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpResultFactory.cs9
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs47
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs34
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs11
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs15
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/es.txt5
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/nz.txt9
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs25
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs2
-rw-r--r--Emby.Server.Implementations/packages.config2
-rw-r--r--MediaBrowser.Api/Dlna/DlnaServerService.cs33
-rw-r--r--MediaBrowser.Api/EnvironmentService.cs58
-rw-r--r--MediaBrowser.Api/Images/ImageService.cs5
-rw-r--r--MediaBrowser.Api/Library/LibraryService.cs4
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs20
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs22
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs39
-rw-r--r--MediaBrowser.Api/Playback/Hls/VideoHlsService.cs19
-rw-r--r--MediaBrowser.Api/Playback/MediaInfoService.cs8
-rw-r--r--MediaBrowser.Api/Playback/Progressive/AudioService.cs5
-rw-r--r--MediaBrowser.Api/Playback/Progressive/VideoService.cs5
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs2
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs2
-rw-r--r--MediaBrowser.Common/Extensions/ResourceNotFoundException.cs16
-rw-r--r--MediaBrowser.Controller/Channels/ChannelMediaInfo.cs2
-rw-r--r--MediaBrowser.Controller/Dlna/IEventManager.cs11
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs16
-rw-r--r--MediaBrowser.Controller/Library/IMediaSourceProvider.cs5
-rw-r--r--MediaBrowser.Controller/Providers/DirectoryService.cs6
-rw-r--r--MediaBrowser.Controller/Session/SessionInfo.cs14
-rw-r--r--MediaBrowser.Controller/Sync/ISyncManager.cs2
-rw-r--r--MediaBrowser.Controller/Sync/ISyncProvider.cs4
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs2
-rw-r--r--MediaBrowser.Model/Dlna/DirectPlayProfile.cs18
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs134
-rw-r--r--MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs4
-rw-r--r--MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs4
-rw-r--r--MediaBrowser.Model/Session/TranscodingInfo.cs3
-rw-r--r--MediaBrowser.Model/Sync/SyncJob.cs5
-rw-r--r--MediaBrowser.Providers/Music/ArtistMetadataService.cs21
-rw-r--r--MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs32
-rw-r--r--MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs2
-rw-r--r--MediaBrowser.Server.Mac/Emby.Server.Mac.csproj118
-rw-r--r--MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj4
-rw-r--r--MediaBrowser.Server.Mono/packages.config4
-rw-r--r--MediaBrowser.ServerApplication/MainStartup.cs44
-rw-r--r--MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj8
-rw-r--r--MediaBrowser.ServerApplication/packages.config5
-rw-r--r--MediaBrowser.WebDashboard/Api/PackageCreator.cs5
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs19
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs5
-rw-r--r--Nuget/MediaBrowser.Common.nuspec2
-rw-r--r--Nuget/MediaBrowser.Server.Core.nuspec4
69 files changed, 1006 insertions, 439 deletions
diff --git a/Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs
index 5bd18cb80..700d04c4d 100644
--- a/Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs
@@ -736,10 +736,10 @@ namespace Emby.Common.Implementations.HttpClientManager
{
if (options.LogErrors)
{
- _logger.ErrorException("Error getting response from " + options.Url, ex);
+ _logger.ErrorException("Error " + webException.Status + " getting response from " + options.Url, webException);
}
- var exception = new HttpException(ex.Message, ex);
+ var exception = new HttpException(webException.Message, webException);
var response = webException.Response as HttpWebResponse;
if (response != null)
@@ -752,6 +752,15 @@ namespace Emby.Common.Implementations.HttpClientManager
}
}
+ if (!exception.StatusCode.HasValue)
+ {
+ if (webException.Status == WebExceptionStatus.NameResolutionFailure ||
+ webException.Status == WebExceptionStatus.ConnectFailure)
+ {
+ exception.IsTimedOut = true;
+ }
+ }
+
return exception;
}
diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
index e93ee5990..9345a1df7 100644
--- a/Emby.Dlna/ContentDirectory/ControlHandler.cs
+++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs
@@ -26,6 +26,7 @@ using System.Xml;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Xml;
@@ -482,6 +483,12 @@ namespace Emby.Dlna.ContentDirectory
return GetMusicArtistItems(item, null, user, sort, startIndex, limit);
}
+ var collectionFolder = item as ICollectionFolder;
+ if (collectionFolder != null && string.Equals(CollectionType.Music, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
+ {
+ return GetMusicFolders(item, user, stubType, sort, startIndex, limit);
+ }
+
if (stubType.HasValue)
{
if (stubType.Value == StubType.People)
@@ -518,7 +525,7 @@ namespace Emby.Dlna.ContentDirectory
StartIndex = startIndex,
User = user,
IsMissing = false,
- PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Music },
+ PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows },
ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name },
IsPlaceHolder = false,
DtoOptions = GetDtoOptions()
@@ -531,6 +538,278 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(queryResult);
}
+ private QueryResult<ServerItem> GetMusicFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
+ {
+ var query = new InternalItemsQuery(user)
+ {
+ StartIndex = startIndex,
+ Limit = limit
+ };
+ SetSorting(query, sort, false);
+
+ if (stubType.HasValue && stubType.Value == StubType.Latest)
+ {
+ return GetMusicLatest(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.Playlists)
+ {
+ return GetMusicPlaylists(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.Albums)
+ {
+ return GetMusicAlbums(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.Artists)
+ {
+ return GetMusicArtists(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.AlbumArtists)
+ {
+ return GetMusicAlbumArtists(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.FavoriteAlbums)
+ {
+ return GetFavoriteAlbums(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.FavoriteArtists)
+ {
+ return GetFavoriteArtists(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.FavoriteSongs)
+ {
+ return GetFavoriteSongs(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.Songs)
+ {
+ return GetMusicSongs(item, user, query);
+ }
+
+ if (stubType.HasValue && stubType.Value == StubType.Genres)
+ {
+ return GetMusicGenres(item, user, query);
+ }
+
+ var list = new List<ServerItem>();
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.Latest
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.Playlists
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.Albums
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.AlbumArtists
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.Artists
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.Songs
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.Genres
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.FavoriteArtists
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.FavoriteAlbums
+ });
+
+ list.Add(new ServerItem(item)
+ {
+ StubType = StubType.FavoriteSongs
+ });
+
+ return new QueryResult<ServerItem>
+ {
+ Items = list.ToArray(),
+ TotalRecordCount = list.Count
+ };
+ }
+
+ private QueryResult<ServerItem> GetMusicAlbums(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.Recursive = true;
+ query.Parent = parent;
+ query.SetUser(user);
+
+ query.IncludeItemTypes = new[] { typeof(MusicAlbum).Name };
+
+ var result = _libraryManager.GetItemsResult(query);
+
+ return ToResult(result);
+ }
+
+ private QueryResult<ServerItem> GetMusicSongs(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.Recursive = true;
+ query.Parent = parent;
+ query.SetUser(user);
+
+ query.IncludeItemTypes = new[] { typeof(Audio).Name };
+
+ var result = _libraryManager.GetItemsResult(query);
+
+ return ToResult(result);
+ }
+
+ private QueryResult<ServerItem> GetFavoriteSongs(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.Recursive = true;
+ query.Parent = parent;
+ query.SetUser(user);
+ query.IsFavorite = true;
+ query.IncludeItemTypes = new[] { typeof(Audio).Name };
+
+ var result = _libraryManager.GetItemsResult(query);
+
+ return ToResult(result);
+ }
+
+ private QueryResult<ServerItem> GetFavoriteAlbums(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.Recursive = true;
+ query.Parent = parent;
+ query.SetUser(user);
+ query.IsFavorite = true;
+ query.IncludeItemTypes = new[] { typeof(MusicAlbum).Name };
+
+ var result = _libraryManager.GetItemsResult(query);
+
+ return ToResult(result);
+ }
+
+ private QueryResult<ServerItem> GetMusicGenres(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ var genresResult = _libraryManager.GetMusicGenres(new InternalItemsQuery(user)
+ {
+ AncestorIds = new[] { parent.Id.ToString("N") },
+ StartIndex = query.StartIndex,
+ Limit = query.Limit
+ });
+
+ var result = new QueryResult<BaseItem>
+ {
+ TotalRecordCount = genresResult.TotalRecordCount,
+ Items = genresResult.Items.Select(i => i.Item1).ToArray()
+ };
+
+ return ToResult(result);
+ }
+
+ private QueryResult<ServerItem> GetMusicAlbumArtists(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ var artists = _libraryManager.GetAlbumArtists(new InternalItemsQuery(user)
+ {
+ AncestorIds = new[] { parent.Id.ToString("N") },
+ StartIndex = query.StartIndex,
+ Limit = query.Limit
+ });
+
+ var result = new QueryResult<BaseItem>
+ {
+ TotalRecordCount = artists.TotalRecordCount,
+ Items = artists.Items.Select(i => i.Item1).ToArray()
+ };
+
+ return ToResult(result);
+ }
+
+ private QueryResult<ServerItem> GetMusicArtists(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ var artists = _libraryManager.GetArtists(new InternalItemsQuery(user)
+ {
+ AncestorIds = new[] { parent.Id.ToString("N") },
+ StartIndex = query.StartIndex,
+ Limit = query.Limit
+ });
+
+ var result = new QueryResult<BaseItem>
+ {
+ TotalRecordCount = artists.TotalRecordCount,
+ Items = artists.Items.Select(i => i.Item1).ToArray()
+ };
+
+ return ToResult(result);
+ }
+
+ private QueryResult<ServerItem> GetFavoriteArtists(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ var artists = _libraryManager.GetArtists(new InternalItemsQuery(user)
+ {
+ AncestorIds = new[] { parent.Id.ToString("N") },
+ StartIndex = query.StartIndex,
+ Limit = query.Limit,
+ IsFavorite = true
+ });
+
+ var result = new QueryResult<BaseItem>
+ {
+ TotalRecordCount = artists.TotalRecordCount,
+ Items = artists.Items.Select(i => i.Item1).ToArray()
+ };
+
+ return ToResult(result);
+ }
+
+ private QueryResult<ServerItem> GetMusicPlaylists(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.Parent = null;
+ query.IncludeItemTypes = new[] { typeof(Playlist).Name };
+ query.SetUser(user);
+ query.Recursive = true;
+
+ var result = _libraryManager.GetItemsResult(query);
+
+ return ToResult(result);
+ }
+
+ private QueryResult<ServerItem> GetMusicLatest(BaseItem parent, User user, InternalItemsQuery query)
+ {
+ query.SortBy = new string[] { };
+
+ var items = _userViewManager.GetLatestItems(new LatestItemsQuery
+ {
+ UserId = user.Id.ToString("N"),
+ Limit = 50,
+ IncludeItemTypes = new[] { typeof(Audio).Name },
+ ParentId = parent == null ? null : parent.Id.ToString("N"),
+ GroupItems = true
+
+ }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToList();
+
+ return ToResult(items);
+ }
+
private QueryResult<ServerItem> GetMusicArtistItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit)
{
var query = new InternalItemsQuery(user)
@@ -571,6 +850,19 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
+ private QueryResult<ServerItem> ToResult(List<BaseItem> result)
+ {
+ var serverItems = result
+ .Select(i => new ServerItem(i))
+ .ToArray();
+
+ return new QueryResult<ServerItem>
+ {
+ TotalRecordCount = result.Count,
+ Items = serverItems
+ };
+ }
+
private QueryResult<ServerItem> ToResult(QueryResult<BaseItem> result)
{
var serverItems = result
@@ -660,6 +952,56 @@ namespace Emby.Dlna.ContentDirectory
stubType = StubType.People;
id = id.Split(new[] { '_' }, 2)[1];
}
+ else if (id.StartsWith("latest_", StringComparison.OrdinalIgnoreCase))
+ {
+ stubType = StubType.Latest;
+ id = id.Split(new[] { '_' }, 2)[1];
+ }
+ else if (id.StartsWith("playlists_", StringComparison.OrdinalIgnoreCase))
+ {
+ stubType = StubType.Playlists;
+ id = id.Split(new[] { '_' }, 2)[1];
+ }
+ else if (id.StartsWith("Albums_", StringComparison.OrdinalIgnoreCase))
+ {
+ stubType = StubType.Albums;
+ id = id.Split(new[] { '_' }, 2)[1];
+ }
+ else if (id.StartsWith("AlbumArtists_", StringComparison.OrdinalIgnoreCase))
+ {
+ stubType = StubType.AlbumArtists;
+ id = id.Split(new[] { '_' }, 2)[1];
+ }
+ else if (id.StartsWith("Artists_", StringComparison.OrdinalIgnoreCase))
+ {
+ stubType = StubType.Artists;
+ id = id.Split(new[] { '_' }, 2)[1];
+ }
+ else if (id.StartsWith("Genres_", StringComparison.OrdinalIgnoreCase))
+ {
+ stubType = StubType.Genres;
+ id = id.Split(new[] { '_' }, 2)[1];
+ }
+ else if (id.StartsWith("Songs_", StringComparison.OrdinalIgnoreCase))
+ {
+ stubType = StubType.Songs;
+ id = id.Split(new[] { '_' }, 2)[1];
+ }
+ else if (id.StartsWith("FavoriteAlbums_", StringComparison.OrdinalIgnoreCase))
+ {
+ stubType = StubType.FavoriteAlbums;
+ id = id.Split(new[] { '_' }, 2)[1];
+ }
+ else if (id.StartsWith("FavoriteArtists_", StringComparison.OrdinalIgnoreCase))
+ {
+ stubType = StubType.FavoriteArtists;
+ id = id.Split(new[] { '_' }, 2)[1];
+ }
+ else if (id.StartsWith("FavoriteSongs_", StringComparison.OrdinalIgnoreCase))
+ {
+ stubType = StubType.FavoriteSongs;
+ id = id.Split(new[] { '_' }, 2)[1];
+ }
if (Guid.TryParse(id, out itemId))
{
@@ -696,6 +1038,16 @@ namespace Emby.Dlna.ContentDirectory
public enum StubType
{
Folder = 0,
- People = 1
+ People = 1,
+ Latest = 2,
+ Playlists = 3,
+ Albums = 4,
+ AlbumArtists = 5,
+ Artists = 6,
+ Songs = 7,
+ Genres = 8,
+ FavoriteSongs = 9,
+ FavoriteArtists = 10,
+ FavoriteAlbums = 11
}
}
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index 3344bfcfe..d2a160cf7 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -399,6 +399,46 @@ namespace Emby.Dlna.Didl
}
return _localization.GetLocalizedString("HeaderPeople");
}
+ if (itemStubType.HasValue && itemStubType.Value == StubType.Latest)
+ {
+ return _localization.GetLocalizedString("ViewTypeMusicLatest");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.Playlists)
+ {
+ return _localization.GetLocalizedString("ViewTypeMusicPlaylists");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.AlbumArtists)
+ {
+ return _localization.GetLocalizedString("ViewTypeMusicAlbumArtists");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.Albums)
+ {
+ return _localization.GetLocalizedString("ViewTypeMusicAlbums");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.Artists)
+ {
+ return _localization.GetLocalizedString("ViewTypeMusicArtists");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.Songs)
+ {
+ return _localization.GetLocalizedString("ViewTypeMusicSongs");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.Genres)
+ {
+ return _localization.GetLocalizedString("ViewTypeTvGenres");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteAlbums)
+ {
+ return _localization.GetLocalizedString("ViewTypeMusicFavoriteAlbums");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteArtists)
+ {
+ return _localization.GetLocalizedString("ViewTypeMusicFavoriteArtists");
+ }
+ if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteSongs)
+ {
+ return _localization.GetLocalizedString("ViewTypeMusicFavoriteSongs");
+ }
var episode = item as Episode;
var season = context as Season;
diff --git a/Emby.Dlna/Eventing/EventManager.cs b/Emby.Dlna/Eventing/EventManager.cs
index cf2c8d995..0516585ae 100644
--- a/Emby.Dlna/Eventing/EventManager.cs
+++ b/Emby.Dlna/Eventing/EventManager.cs
@@ -26,32 +26,34 @@ namespace Emby.Dlna.Eventing
_logger = logger;
}
- public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, int? timeoutSeconds)
+ public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string requestedTimeoutString)
{
- var timeout = timeoutSeconds ?? 300;
-
var subscription = GetSubscription(subscriptionId, true);
- _logger.Debug("Renewing event subscription for {0} with timeout of {1} to {2}",
- subscription.NotificationType,
- timeout,
- subscription.CallbackUrl);
+ // Remove logging for now because some devices are sending this very frequently
+ // TODO re-enable with dlna debug logging setting
+ //_logger.Debug("Renewing event subscription for {0} with timeout of {1} to {2}",
+ // subscription.NotificationType,
+ // timeout,
+ // subscription.CallbackUrl);
- subscription.TimeoutSeconds = timeout;
+ subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300;
subscription.SubscriptionTime = DateTime.UtcNow;
- return GetEventSubscriptionResponse(subscriptionId, timeout);
+ return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, subscription.TimeoutSeconds);
}
- public EventSubscriptionResponse CreateEventSubscription(string notificationType, int? timeoutSeconds, string callbackUrl)
+ public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
{
- var timeout = timeoutSeconds ?? 300;
+ var timeout = ParseTimeout(requestedTimeoutString) ?? 300;
var id = "uuid:" + Guid.NewGuid().ToString("N");
- _logger.Debug("Creating event subscription for {0} with timeout of {1} to {2}",
- notificationType,
- timeout,
- callbackUrl);
+ // Remove logging for now because some devices are sending this very frequently
+ // TODO re-enable with dlna debug logging setting
+ //_logger.Debug("Creating event subscription for {0} with timeout of {1} to {2}",
+ // notificationType,
+ // timeout,
+ // callbackUrl);
_subscriptions.TryAdd(id, new EventSubscription
{
@@ -61,7 +63,25 @@ namespace Emby.Dlna.Eventing
TimeoutSeconds = timeout
});
- return GetEventSubscriptionResponse(id, timeout);
+ return GetEventSubscriptionResponse(id, requestedTimeoutString, timeout);
+ }
+
+ private int? ParseTimeout(string header)
+ {
+ if (!string.IsNullOrEmpty(header))
+ {
+ // Starts with SECOND-
+ header = header.Split('-').Last();
+
+ int val;
+
+ if (int.TryParse(header, NumberStyles.Any, _usCulture, out val))
+ {
+ return val;
+ }
+ }
+
+ return null;
}
public EventSubscriptionResponse CancelEventSubscription(string subscriptionId)
@@ -73,22 +93,22 @@ namespace Emby.Dlna.Eventing
return new EventSubscriptionResponse
{
- Content = "\r\n",
+ Content = string.Empty,
ContentType = "text/plain"
};
}
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- private EventSubscriptionResponse GetEventSubscriptionResponse(string subscriptionId, int timeoutSeconds)
+ private EventSubscriptionResponse GetEventSubscriptionResponse(string subscriptionId, string requestedTimeoutString, int timeoutSeconds)
{
var response = new EventSubscriptionResponse
{
- Content = "\r\n",
+ Content = string.Empty,
ContentType = "text/plain"
};
response.Headers["SID"] = subscriptionId;
- response.Headers["TIMEOUT"] = "SECOND-" + timeoutSeconds.ToString(_usCulture);
+ response.Headers["TIMEOUT"] = string.IsNullOrWhiteSpace(requestedTimeoutString) ? ("SECOND-" + timeoutSeconds.ToString(_usCulture)) : requestedTimeoutString;
return response;
}
diff --git a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
index 3eb2bc1d5..d31dc155e 100644
--- a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
+++ b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
@@ -56,8 +56,9 @@ namespace Emby.Dlna.PlayTo
if (profile.Container.Length > 0)
{
// Check container type
- var mediaContainer = Path.GetExtension(mediaPath);
- if (!profile.GetContainers().Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
+ var mediaContainer = (Path.GetExtension(mediaPath) ?? string.Empty).TrimStart('.');
+
+ if (!profile.SupportsContainer(mediaContainer))
{
return false;
}
diff --git a/Emby.Dlna/Profiles/LgTvProfile.cs b/Emby.Dlna/Profiles/LgTvProfile.cs
index 71f684ec4..f81fb1ee7 100644
--- a/Emby.Dlna/Profiles/LgTvProfile.cs
+++ b/Emby.Dlna/Profiles/LgTvProfile.cs
@@ -53,14 +53,7 @@ namespace Emby.Dlna.Profiles
{
new DirectPlayProfile
{
- Container = "ts",
- VideoCodec = "h264",
- AudioCodec = "aac,ac3,mp3,dca,dts",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mkv",
+ Container = "ts,mpegts,avi,mkv",
VideoCodec = "h264",
AudioCodec = "aac,ac3,mp3,dca,dts",
Type = DlnaProfileType.Video
diff --git a/Emby.Dlna/Profiles/Xml/LG Smart TV.xml b/Emby.Dlna/Profiles/Xml/LG Smart TV.xml
index a61fefcc8..92e02799e 100644
--- a/Emby.Dlna/Profiles/Xml/LG Smart TV.xml
+++ b/Emby.Dlna/Profiles/Xml/LG Smart TV.xml
@@ -35,8 +35,7 @@
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<XmlRootAttributes />
<DirectPlayProfiles>
- <DirectPlayProfile container="ts" audioCodec="aac,ac3,mp3,dca,dts" videoCodec="h264" type="Video" />
- <DirectPlayProfile container="mkv" audioCodec="aac,ac3,mp3,dca,dts" videoCodec="h264" type="Video" />
+ <DirectPlayProfile container="ts,mpegts,avi,mkv" audioCodec="aac,ac3,mp3,dca,dts" videoCodec="h264" type="Video" />
<DirectPlayProfile container="mp4,m4v" audioCodec="aac,ac3,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" />
<DirectPlayProfile container="mp3" type="Audio" />
<DirectPlayProfile container="jpeg" type="Photo" />
diff --git a/Emby.Dlna/Service/BaseService.cs b/Emby.Dlna/Service/BaseService.cs
index 574d74958..ddc37da09 100644
--- a/Emby.Dlna/Service/BaseService.cs
+++ b/Emby.Dlna/Service/BaseService.cs
@@ -24,14 +24,14 @@ namespace Emby.Dlna.Service
return EventManager.CancelEventSubscription(subscriptionId);
}
- public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, int? timeoutSeconds)
+ public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string timeoutString)
{
- return EventManager.RenewEventSubscription(subscriptionId, timeoutSeconds);
+ return EventManager.RenewEventSubscription(subscriptionId, timeoutString);
}
- public EventSubscriptionResponse CreateEventSubscription(string notificationType, int? timeoutSeconds, string callbackUrl)
+ public EventSubscriptionResponse CreateEventSubscription(string notificationType, string timeoutString, string callbackUrl)
{
- return EventManager.CreateEventSubscription(notificationType, timeoutSeconds, callbackUrl);
+ return EventManager.CreateEventSubscription(notificationType, timeoutString, callbackUrl);
}
}
}
diff --git a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
index 98011ddd4..7be4101c8 100644
--- a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
@@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Channels
return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>());
}
- public Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> OpenMediaSource(string openToken, CancellationToken cancellationToken)
+ public Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> OpenMediaSource(string openToken, bool allowLiveStreamProbe, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
index df8d1ac45..f43e45441 100644
--- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
+++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
@@ -17,7 +17,7 @@ using MediaBrowser.Model.Tasks;
namespace Emby.Server.Implementations.Data
{
- public class CleanDatabaseScheduledTask : IScheduledTask
+ public class CleanDatabaseScheduledTask : ILibraryPostScanTask
{
private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo;
@@ -49,7 +49,7 @@ namespace Emby.Server.Implementations.Data
get { return "Library"; }
}
- public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+ public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
// Ensure these objects are lazy loaded.
// Without this there is a deadlock that will need to be investigated
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 5486f404f..1b0cbb936 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -307,7 +307,7 @@
</ItemGroup>
<ItemGroup>
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
- <HintPath>..\packages\SQLitePCLRaw.core.1.1.6\lib\net45\SQLitePCLRaw.core.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.7\lib\net45\SQLitePCLRaw.core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
@@ -418,6 +418,9 @@
<ItemGroup>
<EmbeddedResource Include="Localization\Ratings\uk.txt" />
</ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Ratings\es.txt" />
+ </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
diff --git a/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs b/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
index 3c8ad55fe..e266ea21b 100644
--- a/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
+++ b/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
@@ -109,7 +109,7 @@ namespace Emby.Server.Implementations.FFMpeg
}
if (!string.IsNullOrWhiteSpace(customffProbePath))
{
- info.EncoderPath = customffProbePath;
+ info.ProbePath = customffProbePath;
}
return info;
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index 28c23b766..05f78eba9 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -104,6 +104,7 @@ namespace Emby.Server.Implementations.HttpServer
readonly Dictionary<Type, int> _mapExceptionToStatusCode = new Dictionary<Type, int>
{
{typeof (ResourceNotFoundException), 404},
+ {typeof (RemoteServiceUnavailableException), 502},
{typeof (FileNotFoundException), 404},
//{typeof (DirectoryNotFoundException), 404},
{typeof (SecurityException), 401},
@@ -268,6 +269,29 @@ namespace Emby.Server.Implementations.HttpServer
}
}
+ private Exception GetActualException(Exception ex)
+ {
+ var agg = ex as AggregateException;
+ if (agg != null)
+ {
+ var inner = agg.InnerException;
+ if (inner != null)
+ {
+ return GetActualException(inner);
+ }
+ else
+ {
+ var inners = agg.InnerExceptions;
+ if (inners != null && inners.Count > 0)
+ {
+ return GetActualException(inners[0]);
+ }
+ }
+ }
+
+ return ex;
+ }
+
private int GetStatusCode(Exception ex)
{
if (ex is ArgumentException)
@@ -280,7 +304,7 @@ namespace Emby.Server.Implementations.HttpServer
int statusCode;
if (!_mapExceptionToStatusCode.TryGetValue(exceptionType, out statusCode))
{
- if (string.Equals(exceptionType.Name, "DirectoryNotFoundException", StringComparison.OrdinalIgnoreCase))
+ if (ex is DirectoryNotFoundException)
{
statusCode = 404;
}
@@ -297,6 +321,8 @@ namespace Emby.Server.Implementations.HttpServer
{
try
{
+ ex = GetActualException(ex);
+
if (logException)
{
_logger.ErrorException("Error processing request", ex);
diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
index 6c37d5f7a..396bd8e88 100644
--- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -438,6 +438,15 @@ namespace Emby.Server.Implementations.HttpServer
options.CacheKey = cacheKey.GetMD5();
options.ContentFactory = () => Task.FromResult(GetFileStream(path, fileShare));
+ options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+ // Quotes are valid in linux. They'll possibly cause issues here
+ var filename = (Path.GetFileName(path) ?? string.Empty).Replace("\"", string.Empty);
+ if (!string.IsNullOrWhiteSpace(filename))
+ {
+ options.ResponseHeaders["Content-Disposition"] = "inline; filename=\"" + filename + "\"";
+ }
+
return GetStaticResult(requestContext, options);
}
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index 4857008f3..c1360c887 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -371,7 +371,7 @@ namespace Emby.Server.Implementations.Library
var tuple = GetProvider(request.OpenToken);
var provider = tuple.Item1;
- var mediaSourceTuple = await provider.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false);
+ var mediaSourceTuple = await provider.OpenMediaSource(tuple.Item2, request.EnableMediaProbe, cancellationToken).ConfigureAwait(false);
var mediaSource = mediaSourceTuple.Item1;
diff --git a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs
index 428b6202b..143350a8b 100644
--- a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs
@@ -16,8 +16,8 @@ namespace Emby.Server.Implementations.LiveTv
private readonly IMediaEncoder _mediaEncoder;
private readonly ILogger _logger;
- const int ProbeAnalyzeDurationMs = 2000;
- const int PlaybackAnalyzeDurationMs = 2000;
+ const int ProbeAnalyzeDurationMs = 3000;
+ const int PlaybackAnalyzeDurationMs = 3000;
public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger)
{
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 24afc03da..bf11b7d3a 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -1514,7 +1514,7 @@ namespace Emby.Server.Implementations.LiveTv
}
private DateTime _lastRecordingRefreshTime;
- private async Task RefreshRecordings(CancellationToken cancellationToken)
+ private async Task RefreshRecordings(Guid internalLiveTvFolderId, CancellationToken cancellationToken)
{
const int cacheMinutes = 2;
@@ -1542,10 +1542,8 @@ namespace Emby.Server.Implementations.LiveTv
});
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
- var folder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
- var parentFolderId = folder.Id;
- var recordingTasks = results.SelectMany(i => i.ToList()).Select(i => CreateRecordingRecord(i.Item1, i.Item2.Name, parentFolderId, cancellationToken));
+ var recordingTasks = results.SelectMany(i => i.ToList()).Select(i => CreateRecordingRecord(i.Item1, i.Item2.Name, internalLiveTvFolderId, cancellationToken));
var idList = await Task.WhenAll(recordingTasks).ConfigureAwait(false);
@@ -1559,7 +1557,7 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, User user)
+ private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, Guid internalLiveTvFolderId, User user)
{
if (user == null)
{
@@ -1571,21 +1569,31 @@ namespace Emby.Server.Implementations.LiveTv
return new QueryResult<BaseItem>();
}
- var folders = EmbyTV.EmbyTV.Current.GetRecordingFolders()
+ var folderIds = EmbyTV.EmbyTV.Current.GetRecordingFolders()
.SelectMany(i => i.Locations)
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(i => _libraryManager.FindByPath(i, true))
.Where(i => i != null)
.Where(i => i.IsVisibleStandalone(user))
+ .Select(i => i.Id)
.ToList();
- if (folders.Count == 0)
+ var excludeItemTypes = new List<string>();
+
+ if (!query.IsInProgress.HasValue)
+ {
+ folderIds.Add(internalLiveTvFolderId);
+
+ excludeItemTypes.Add(typeof(LiveTvChannel).Name);
+ excludeItemTypes.Add(typeof(LiveTvProgram).Name);
+ }
+
+ if (folderIds.Count == 0)
{
return new QueryResult<BaseItem>();
}
var includeItemTypes = new List<string>();
- var excludeItemTypes = new List<string>();
var genres = new List<string>();
if (query.IsMovie.HasValue)
@@ -1631,7 +1639,7 @@ namespace Emby.Server.Implementations.LiveTv
{
MediaTypes = new[] { MediaType.Video },
Recursive = true,
- AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(),
+ AncestorIds = folderIds.Select(i => i.ToString("N")).ToArray(),
IsFolder = false,
IsVirtualItem = false,
Limit = query.Limit,
@@ -1714,12 +1722,24 @@ namespace Emby.Server.Implementations.LiveTv
return new QueryResult<BaseItem>();
}
- if (_services.Count == 1 && !(query.IsInProgress ?? false) && (!query.IsLibraryItem.HasValue || query.IsLibraryItem.Value))
+ var folder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
+
+ if (_services.Count == 1 && (!query.IsInProgress.HasValue || !query.IsInProgress.Value) && (!query.IsLibraryItem.HasValue || query.IsLibraryItem.Value))
{
- return GetEmbyRecordings(query, options, user);
+ if (!query.IsInProgress.HasValue)
+ {
+ await RefreshRecordings(folder.Id, cancellationToken).ConfigureAwait(false);
+ }
+
+ return GetEmbyRecordings(query, options, folder.Id, user);
}
- await RefreshRecordings(cancellationToken).ConfigureAwait(false);
+ return await GetInternalRecordingsFromServices(query, user, options, folder.Id, cancellationToken).ConfigureAwait(false);
+ }
+
+ private async Task<QueryResult<BaseItem>> GetInternalRecordingsFromServices(RecordingQuery query, User user, DtoOptions options, Guid internalLiveTvFolderId, CancellationToken cancellationToken)
+ {
+ await RefreshRecordings(internalLiveTvFolderId, cancellationToken).ConfigureAwait(false);
var internalQuery = new InternalItemsQuery(user)
{
@@ -2620,7 +2640,8 @@ namespace Emby.Server.Implementations.LiveTv
}, new DtoOptions(), cancellationToken).ConfigureAwait(false);
- var recordings = recordingResult.Items.OfType<ILiveTvRecording>().ToList();
+ var embyServiceName = EmbyTV.EmbyTV.Current.Name;
+ var recordings = recordingResult.Items.Where(i => !string.Equals(i.ServiceName, embyServiceName, StringComparison.OrdinalIgnoreCase)).OfType<ILiveTvRecording>().ToList();
var groups = new List<BaseItemDto>();
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index 0329ea606..919c0f10d 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
@@ -118,7 +118,7 @@ namespace Emby.Server.Implementations.LiveTv
return list;
}
- public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> OpenMediaSource(string openToken, CancellationToken cancellationToken)
+ public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> OpenMediaSource(string openToken, bool allowLiveStreamProbe, CancellationToken cancellationToken)
{
MediaSourceInfo stream = null;
const bool isAudio = false;
@@ -140,7 +140,7 @@ namespace Emby.Server.Implementations.LiveTv
try
{
- if (!stream.SupportsProbing || stream.MediaStreams.Any(i => i.Index != -1))
+ if (!allowLiveStreamProbe || !stream.SupportsProbing || stream.MediaStreams.Any(i => i.Index != -1))
{
AddMediaInfo(stream, isAudio, cancellationToken);
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
index 10b7132a1..4b4f61d53 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -10,9 +10,11 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
namespace Emby.Server.Implementations.LiveTv.TunerHosts
@@ -23,16 +25,18 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
protected readonly ILogger Logger;
protected IJsonSerializer JsonSerializer;
protected readonly IMediaEncoder MediaEncoder;
+ protected readonly IFileSystem FileSystem;
private readonly ConcurrentDictionary<string, ChannelCache> _channelCache =
new ConcurrentDictionary<string, ChannelCache>(StringComparer.OrdinalIgnoreCase);
- protected BaseTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder)
+ protected BaseTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem)
{
Config = config;
Logger = logger;
JsonSerializer = jsonSerializer;
MediaEncoder = mediaEncoder;
+ FileSystem = fileSystem;
}
protected abstract Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken);
@@ -81,16 +85,44 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
foreach (var host in hosts)
{
+ var channelCacheFile = Path.Combine(Config.ApplicationPaths.CachePath, host.Id + "_channels");
+
try
{
var channels = await GetChannels(host, enableCache, cancellationToken).ConfigureAwait(false);
var newChannels = channels.Where(i => !list.Any(l => string.Equals(i.Id, l.Id, StringComparison.OrdinalIgnoreCase))).ToList();
list.AddRange(newChannels);
+
+ if (!enableCache)
+ {
+ try
+ {
+ FileSystem.CreateDirectory(FileSystem.GetDirectoryName(channelCacheFile));
+ JsonSerializer.SerializeToFile(channels, channelCacheFile);
+ }
+ catch (IOException)
+ {
+
+ }
+ }
}
catch (Exception ex)
{
Logger.ErrorException("Error getting channel list", ex);
+
+ if (enableCache)
+ {
+ try
+ {
+ var channels = JsonSerializer.DeserializeFromFile<List<ChannelInfo>>(channelCacheFile);
+ list.AddRange(channels);
+ }
+ catch (IOException)
+ {
+
+ }
+ }
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 153f86aed..b6ba8b45c 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -27,17 +27,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public class HdHomerunHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost
{
private readonly IHttpClient _httpClient;
- private readonly IFileSystem _fileSystem;
private readonly IServerApplicationHost _appHost;
private readonly ISocketFactory _socketFactory;
private readonly INetworkManager _networkManager;
private readonly IEnvironmentInfo _environment;
- public HdHomerunHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IHttpClient httpClient, IFileSystem fileSystem, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager, IEnvironmentInfo environment)
- : base(config, logger, jsonSerializer, mediaEncoder)
+ public HdHomerunHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager, IEnvironmentInfo environment) : base(config, logger, jsonSerializer, mediaEncoder, fileSystem)
{
_httpClient = httpClient;
- _fileSystem = fileSystem;
_appHost = appHost;
_socketFactory = socketFactory;
_networkManager = networkManager;
@@ -509,7 +506,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
if (hdhomerunChannel != null && hdhomerunChannel.IsLegacyTuner)
{
- return new HdHomerunUdpStream(mediaSource, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Url), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager, _environment);
+ return new HdHomerunUdpStream(mediaSource, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Url), modelInfo.TunerCount, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager, _environment);
}
// The UDP method is not working reliably on OSX, and on BSD it hasn't been tested yet
@@ -529,10 +526,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
mediaSource.Path = httpUrl;
- return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _environment);
+ return new HdHomerunHttpStream(mediaSource, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _environment);
}
- return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number, profile), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager, _environment);
+ return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number, profile), modelInfo.TunerCount, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager, _environment);
}
public async Task Validate(TunerHostInfo info)
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index 8bf7a052e..113cb33f4 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -25,15 +25,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
public class M3UTunerHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost
{
- private readonly IFileSystem _fileSystem;
private readonly IHttpClient _httpClient;
private readonly IServerApplicationHost _appHost;
private readonly IEnvironmentInfo _environment;
- public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, IEnvironmentInfo environment)
- : base(config, logger, jsonSerializer, mediaEncoder)
+ public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, IEnvironmentInfo environment) : base(config, logger, jsonSerializer, mediaEncoder, fileSystem)
{
- _fileSystem = fileSystem;
_httpClient = httpClient;
_appHost = appHost;
_environment = environment;
@@ -51,7 +48,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
protected override async Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
{
- var result = await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).Parse(info.Url, ChannelIdPrefix, info.Id, !info.EnableTvgId, cancellationToken).ConfigureAwait(false);
+ var result = await new M3uParser(Logger, FileSystem, _httpClient, _appHost).Parse(info.Url, ChannelIdPrefix, info.Id, !info.EnableTvgId, cancellationToken).ConfigureAwait(false);
return result.Cast<ChannelInfo>().ToList();
}
@@ -76,13 +73,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
var sources = await GetChannelStreamMediaSources(info, channelId, cancellationToken).ConfigureAwait(false);
- var liveStream = new LiveStream(sources.First(), _environment, _fileSystem);
+ var liveStream = new LiveStream(sources.First(), _environment, FileSystem);
return liveStream;
}
public async Task Validate(TunerHostInfo info)
{
- using (var stream = await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
+ using (var stream = await new M3uParser(Logger, FileSystem, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
{
}
@@ -154,7 +151,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
Id = channel.Path.GetMD5().ToString("N"),
IsInfiniteStream = true,
- IsRemote = true
+ IsRemote = true,
+
+ IgnoreDts = true
};
mediaSource.InferTotalBitrate();
diff --git a/Emby.Server.Implementations/Localization/Ratings/es.txt b/Emby.Server.Implementations/Localization/Ratings/es.txt
new file mode 100644
index 000000000..1ba24fb99
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Ratings/es.txt
@@ -0,0 +1,5 @@
+ES-A,1
+ES-7,3
+ES-12,6
+ES-16,8
+ES-18,11 \ No newline at end of file
diff --git a/Emby.Server.Implementations/Localization/Ratings/nz.txt b/Emby.Server.Implementations/Localization/Ratings/nz.txt
index bc761dcab..46e4067ba 100644
--- a/Emby.Server.Implementations/Localization/Ratings/nz.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/nz.txt
@@ -1,10 +1,11 @@
NZ-G,1
NZ-PG,5
-NZ-M,9
+NZ-M,6
NZ-R13,7
+NZ-RP13,7
NZ-R15,8
+NZ-RP16,9
NZ-R16,9
NZ-R18,10
-NZ-RP13,7
-NZ-RP16,9
-NZ-R,10 \ No newline at end of file
+NZ-R,10
+NZ-MA,10 \ No newline at end of file
diff --git a/Emby.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs
index a41e21a4b..0c9eb0f1b 100644
--- a/Emby.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs
@@ -68,23 +68,12 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <returns>Task.</returns>
public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{
- EventHandler<double> innerProgressHandler = (sender, e) => progress.Report(e * .1);
-
// Create a progress object for the update check
- var innerProgress = new SimpleProgress<double>();
- innerProgress.ProgressChanged += innerProgressHandler;
-
- var updateInfo = await _appHost.CheckForApplicationUpdate(cancellationToken, innerProgress).ConfigureAwait(false);
-
- // Release the event handler
- innerProgress.ProgressChanged -= innerProgressHandler;
-
- progress.Report(10);
+ var updateInfo = await _appHost.CheckForApplicationUpdate(cancellationToken, new SimpleProgress<double>()).ConfigureAwait(false);
if (!updateInfo.IsUpdateAvailable)
{
Logger.Debug("No application update available.");
- progress.Report(100);
return;
}
@@ -96,22 +85,12 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
Logger.Info("Update Revision {0} available. Updating...", updateInfo.AvailableVersion);
- innerProgressHandler = (sender, e) => progress.Report(e * .9 + .1);
-
- innerProgress = new SimpleProgress<double>();
- innerProgress.ProgressChanged += innerProgressHandler;
-
- await _appHost.UpdateApplication(updateInfo.Package, cancellationToken, innerProgress).ConfigureAwait(false);
-
- // Release the event handler
- innerProgress.ProgressChanged -= innerProgressHandler;
+ await _appHost.UpdateApplication(updateInfo.Package, cancellationToken, progress).ConfigureAwait(false);
}
else
{
Logger.Info("A new version of " + _appHost.Name + " is available.");
}
-
- progress.Report(100);
}
/// <summary>
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 717416da1..6e37c1dc1 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -513,8 +513,6 @@ namespace Emby.Server.Implementations.Updates
CurrentInstallations.Remove(tuple);
}
- progress.Report(100);
-
CompletedInstallationsInternal.Add(installationInfo);
EventHelper.FireEventIfNotNull(PackageInstallationCompleted, this, installationEventArgs, _logger);
diff --git a/Emby.Server.Implementations/packages.config b/Emby.Server.Implementations/packages.config
index 468bf925f..03336c936 100644
--- a/Emby.Server.Implementations/packages.config
+++ b/Emby.Server.Implementations/packages.config
@@ -3,5 +3,5 @@
<package id="Emby.XmlTv" version="1.0.9" targetFramework="net46" />
<package id="MediaBrowser.Naming" version="1.0.5" targetFramework="portable45-net45+win8" />
<package id="SQLitePCL.pretty" version="1.1.0" targetFramework="portable45-net45+win8" />
- <package id="SQLitePCLRaw.core" version="1.1.6" targetFramework="net46" />
+ <package id="SQLitePCLRaw.core" version="1.1.7" targetFramework="net46" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.Api/Dlna/DlnaServerService.cs b/MediaBrowser.Api/Dlna/DlnaServerService.cs
index 6e59cceec..fc8c0edf6 100644
--- a/MediaBrowser.Api/Dlna/DlnaServerService.cs
+++ b/MediaBrowser.Api/Dlna/DlnaServerService.cs
@@ -219,20 +219,20 @@ namespace MediaBrowser.Api.Dlna
private object ProcessEventRequest(IEventManager eventManager)
{
var subscriptionId = GetHeader("SID");
- var notificationType = GetHeader("NT");
- var callback = GetHeader("CALLBACK");
- var timeoutString = GetHeader("TIMEOUT");
-
- var timeout = ParseTimeout(timeoutString);
if (string.Equals(Request.Verb, "SUBSCRIBE", StringComparison.OrdinalIgnoreCase))
{
+ var notificationType = GetHeader("NT");
+
+ var callback = GetHeader("CALLBACK");
+ var timeoutString = GetHeader("TIMEOUT");
+
if (string.IsNullOrEmpty(notificationType))
{
- return GetSubscriptionResponse(eventManager.RenewEventSubscription(subscriptionId, timeout));
+ return GetSubscriptionResponse(eventManager.RenewEventSubscription(subscriptionId, timeoutString));
}
- return GetSubscriptionResponse(eventManager.CreateEventSubscription(notificationType, timeout, callback));
+ return GetSubscriptionResponse(eventManager.CreateEventSubscription(notificationType, timeoutString, callback));
}
return GetSubscriptionResponse(eventManager.CancelEventSubscription(subscriptionId));
@@ -242,24 +242,5 @@ namespace MediaBrowser.Api.Dlna
{
return ResultFactory.GetResult(response.Content, response.ContentType, response.Headers);
}
-
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- private int? ParseTimeout(string header)
- {
- if (!string.IsNullOrEmpty(header))
- {
- // Starts with SECOND-
- header = header.Split('-').Last();
-
- int val;
-
- if (int.TryParse(header, NumberStyles.Any, _usCulture, out val))
- {
- return val;
- }
- }
-
- return null;
- }
}
}
diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs
index c4cb0cb1d..9764a71db 100644
--- a/MediaBrowser.Api/EnvironmentService.cs
+++ b/MediaBrowser.Api/EnvironmentService.cs
@@ -50,6 +50,20 @@ namespace MediaBrowser.Api
}
}
+ [Route("/Environment/ValidatePath", "POST", Summary = "Gets the contents of a given directory in the file system")]
+ public class ValidatePath
+ {
+ /// <summary>
+ /// Gets or sets the path.
+ /// </summary>
+ /// <value>The path.</value>
+ [ApiMember(Name = "Path", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string Path { get; set; }
+
+ public bool ValidateWriteable { get; set; }
+ public bool? IsFile { get; set; }
+ }
+
[Route("/Environment/NetworkShares", "GET", Summary = "Gets shares from a network device")]
public class GetNetworkShares : IReturn<List<FileSystemEntryInfo>>
{
@@ -112,7 +126,7 @@ namespace MediaBrowser.Api
/// The _network manager
/// </summary>
private readonly INetworkManager _networkManager;
- private IFileSystem _fileSystem;
+ private readonly IFileSystem _fileSystem;
/// <summary>
/// Initializes a new instance of the <see cref="EnvironmentService" /> class.
@@ -129,6 +143,48 @@ namespace MediaBrowser.Api
_fileSystem = fileSystem;
}
+ public void Post(ValidatePath request)
+ {
+ if (request.IsFile.HasValue)
+ {
+ if (request.IsFile.Value)
+ {
+ if (!_fileSystem.FileExists(request.Path))
+ {
+ throw new FileNotFoundException("File not found", request.Path);
+ }
+ }
+ else
+ {
+ if (!_fileSystem.DirectoryExists(request.Path))
+ {
+ throw new FileNotFoundException("File not found", request.Path);
+ }
+ }
+ }
+
+ else
+ {
+ if (!_fileSystem.FileExists(request.Path) && !_fileSystem.DirectoryExists(request.Path))
+ {
+ throw new FileNotFoundException("Path not found", request.Path);
+ }
+
+ if (request.ValidateWriteable)
+ {
+ EnsureWriteAccess(request.Path);
+ }
+ }
+ }
+
+ protected void EnsureWriteAccess(string path)
+ {
+ var file = Path.Combine(path, Guid.NewGuid().ToString());
+
+ _fileSystem.WriteAllText(file, string.Empty);
+ _fileSystem.DeleteFile(file);
+ }
+
public object Get(GetDefaultDirectoryBrowser request)
{
var result = new DefaultDirectoryBrowserInfo();
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs
index 221755916..f6c97e091 100644
--- a/MediaBrowser.Api/Images/ImageService.cs
+++ b/MediaBrowser.Api/Images/ImageService.cs
@@ -17,6 +17,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.IO;
+using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services;
@@ -567,7 +568,9 @@ namespace MediaBrowser.Api.Images
}).ToList() : new List<IImageEnhancer>();
- var cropwhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art;
+ var cropwhitespace = request.Type == ImageType.Logo ||
+ request.Type == ImageType.Art
+ || (request.Type == ImageType.Primary && item is LiveTvChannel);
if (request.CropWhitespace.HasValue)
{
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index 80d8c072e..3bb119cba 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -512,10 +512,6 @@ namespace MediaBrowser.Api.Library
var headers = new Dictionary<string, string>();
- // Quotes are valid in linux. They'll possibly cause issues here
- var filename = Path.GetFileName(item.Path).Replace("\"", string.Empty);
- headers["Content-Disposition"] = string.Format("attachment; filename=\"{0}\"", filename);
-
if (user != null)
{
LogDownload(item, user, auth);
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 8d01e4021..c300fcce3 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -22,6 +22,7 @@ using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Diagnostics;
namespace MediaBrowser.Api.Playback
@@ -100,11 +101,7 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Gets the command line arguments.
/// </summary>
- /// <param name="outputPath">The output path.</param>
- /// <param name="state">The state.</param>
- /// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
- /// <returns>System.String.</returns>
- protected abstract string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding);
+ protected abstract string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding);
/// <summary>
/// Gets the type of the transcoding job.
@@ -125,11 +122,11 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Gets the output file path.
/// </summary>
- private string GetOutputFilePath(StreamState state, string outputFileExtension)
+ private string GetOutputFilePath(StreamState state, EncodingOptions encodingOptions, string outputFileExtension)
{
var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath;
- var data = GetCommandLineArguments("dummy\\dummy", state, false);
+ var data = GetCommandLineArguments("dummy\\dummy", encodingOptions, state, false);
data += "-" + (state.Request.DeviceId ?? string.Empty);
data += "-" + (state.Request.PlaySessionId ?? string.Empty);
@@ -217,8 +214,10 @@ namespace MediaBrowser.Api.Playback
}
}
+ var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
+
var transcodingId = Guid.NewGuid().ToString("N");
- var commandLineArgs = GetCommandLineArguments(outputPath, state, true);
+ var commandLineArgs = GetCommandLineArguments(outputPath, encodingOptions, state, true);
var process = ApiEntryPoint.Instance.ProcessFactory.Create(new ProcessOptions
{
@@ -826,7 +825,10 @@ namespace MediaBrowser.Api.Playback
var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
? GetOutputFileExtension(state)
: ("." + state.OutputContainer);
- state.OutputFilePath = GetOutputFilePath(state, ext);
+
+ var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
+
+ state.OutputFilePath = GetOutputFilePath(state, encodingOptions, ext);
return state;
}
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index 63d2cd9eb..83157c703 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -14,6 +14,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Api.Playback.Hls
{
@@ -25,15 +26,12 @@ namespace MediaBrowser.Api.Playback.Hls
/// <summary>
/// Gets the audio arguments.
/// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- protected abstract string GetAudioArguments(StreamState state);
+ protected abstract string GetAudioArguments(StreamState state, EncodingOptions encodingOptions);
+
/// <summary>
/// Gets the video arguments.
/// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- protected abstract string GetVideoArguments(StreamState state);
+ protected abstract string GetVideoArguments(StreamState state, EncodingOptions encodingOptions);
/// <summary>
/// Gets the segment file extension.
@@ -242,10 +240,8 @@ namespace MediaBrowser.Api.Playback.Hls
}
}
- protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
+ protected override string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding)
{
- var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
-
var itsOffsetMs = 0;
var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture));
@@ -285,8 +281,8 @@ namespace MediaBrowser.Api.Playback.Hls
EncodingHelper.GetInputArgument(state, encodingOptions),
threads,
EncodingHelper.GetMapArgs(state),
- GetVideoArguments(state),
- GetAudioArguments(state),
+ GetVideoArguments(state, encodingOptions),
+ GetAudioArguments(state, encodingOptions),
state.SegmentLength.ToString(UsCulture),
startNumberParam,
outputPath,
@@ -306,8 +302,8 @@ namespace MediaBrowser.Api.Playback.Hls
EncodingHelper.GetInputArgument(state, encodingOptions),
threads,
EncodingHelper.GetMapArgs(state),
- GetVideoArguments(state),
- GetAudioArguments(state),
+ GetVideoArguments(state, encodingOptions),
+ GetAudioArguments(state, encodingOptions),
state.SegmentLength.ToString(UsCulture),
startNumberParam,
state.HlsListSize.ToString(UsCulture),
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index c6282bbad..6744fbd92 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -18,6 +18,7 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Services;
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
@@ -777,20 +778,20 @@ namespace MediaBrowser.Api.Playback.Hls
return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
}
- protected override string GetAudioArguments(StreamState state)
+ protected override string GetAudioArguments(StreamState state, EncodingOptions encodingOptions)
{
- var codec = EncodingHelper.GetAudioEncoder(state);
+ var audioCodec = EncodingHelper.GetAudioEncoder(state);
if (!state.IsOutputVideo)
{
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
return "-acodec copy";
}
var audioTranscodeParams = new List<string>();
- audioTranscodeParams.Add("-acodec " + codec);
+ audioTranscodeParams.Add("-acodec " + audioCodec);
if (state.OutputAudioBitrate.HasValue)
{
@@ -811,12 +812,19 @@ namespace MediaBrowser.Api.Playback.Hls
return string.Join(" ", audioTranscodeParams.ToArray());
}
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- return "-codec:a:0 copy -copypriorss:a:0 0";
+ var videoCodec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
+
+ if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.EnableBreakOnNonKeyFrames(videoCodec))
+ {
+ return "-codec:a:0 copy -copypriorss:a:0 0";
+ }
+
+ return "-codec:a:0 copy";
}
- var args = "-codec:a:0 " + codec;
+ var args = "-codec:a:0 " + audioCodec;
var channels = state.OutputAudioChannels;
@@ -837,19 +845,19 @@ namespace MediaBrowser.Api.Playback.Hls
args += " -ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture);
}
- args += " " + EncodingHelper.GetAudioFilterParam(state, ApiEntryPoint.Instance.GetEncodingOptions(), true);
+ args += " " + EncodingHelper.GetAudioFilterParam(state, encodingOptions, true);
return args;
}
- protected override string GetVideoArguments(StreamState state)
+ protected override string GetVideoArguments(StreamState state, EncodingOptions encodingOptions)
{
if (!state.IsOutputVideo)
{
return string.Empty;
}
- var codec = EncodingHelper.GetVideoEncoder(state, ApiEntryPoint.Instance.GetEncodingOptions());
+ var codec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
var args = "-codec:v:0 " + codec;
@@ -875,8 +883,6 @@ namespace MediaBrowser.Api.Playback.Hls
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
- var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
-
args += " " + EncodingHelper.GetVideoQualityParam(state, codec, encodingOptions, GetDefaultH264Preset()) + keyFrameArg;
//args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0";
@@ -911,9 +917,8 @@ namespace MediaBrowser.Api.Playback.Hls
return args;
}
- protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
+ protected override string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding)
{
- var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
var threads = EncodingHelper.GetNumberOfThreads(state, encodingOptions, false);
var inputModifier = EncodingHelper.GetInputModifier(state, encodingOptions);
@@ -940,7 +945,7 @@ namespace MediaBrowser.Api.Playback.Hls
segmentFormat = "mpegts";
}
- var videoCodec = EncodingHelper.GetVideoEncoder(state, ApiEntryPoint.Instance.GetEncodingOptions());
+ var videoCodec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
var breakOnNonKeyFrames = state.EnableBreakOnNonKeyFrames(videoCodec);
var breakOnNonKeyFramesArg = breakOnNonKeyFrames ? " -break_non_keyframes 1" : "";
@@ -950,8 +955,8 @@ namespace MediaBrowser.Api.Playback.Hls
EncodingHelper.GetInputArgument(state, encodingOptions),
threads,
mapArgs,
- GetVideoArguments(state),
- GetAudioArguments(state),
+ GetVideoArguments(state, encodingOptions),
+ GetAudioArguments(state, encodingOptions),
state.SegmentLength.ToString(UsCulture),
startNumberParam,
outputPath,
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index e32970be5..9b3c8a08f 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -7,6 +7,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using System;
using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Services;
@@ -31,9 +32,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// <summary>
/// Gets the audio arguments.
/// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- protected override string GetAudioArguments(StreamState state)
+ protected override string GetAudioArguments(StreamState state, EncodingOptions encodingOptions)
{
var codec = EncodingHelper.GetAudioEncoder(state);
@@ -63,7 +62,7 @@ namespace MediaBrowser.Api.Playback.Hls
args += " -ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture);
}
- args += " " + EncodingHelper.GetAudioFilterParam(state, ApiEntryPoint.Instance.GetEncodingOptions(), true);
+ args += " " + EncodingHelper.GetAudioFilterParam(state, encodingOptions, true);
return args;
}
@@ -71,11 +70,14 @@ namespace MediaBrowser.Api.Playback.Hls
/// <summary>
/// Gets the video arguments.
/// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- protected override string GetVideoArguments(StreamState state)
+ protected override string GetVideoArguments(StreamState state, EncodingOptions encodingOptions)
{
- var codec = EncodingHelper.GetVideoEncoder(state, ApiEntryPoint.Instance.GetEncodingOptions());
+ if (!state.IsOutputVideo)
+ {
+ return string.Empty;
+ }
+
+ var codec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
var args = "-codec:v:0 " + codec;
@@ -101,7 +103,6 @@ namespace MediaBrowser.Api.Playback.Hls
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
- var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
args += " " + EncodingHelper.GetVideoQualityParam(state, codec, encodingOptions, GetDefaultH264Preset()) + keyFrameArg;
// Add resolution params, if specified
diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs
index 6853ddbeb..536236f5f 100644
--- a/MediaBrowser.Api/Playback/MediaInfoService.cs
+++ b/MediaBrowser.Api/Playback/MediaInfoService.cs
@@ -134,7 +134,7 @@ namespace MediaBrowser.Api.Playback
SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate,
request.StartTimeTicks ?? 0, result.MediaSource.Id, request.AudioStreamIndex,
- request.SubtitleStreamIndex, request.MaxAudioChannels, request.PlaySessionId, request.UserId, request.EnableDirectPlay, request.ForceDirectPlayRemoteMediaSource, request.EnableDirectStream, true, true, true);
+ request.SubtitleStreamIndex, request.MaxAudioChannels, request.PlaySessionId, request.UserId, request.EnableDirectPlay, true, request.EnableDirectStream, true, true, true);
}
else
{
@@ -176,7 +176,7 @@ namespace MediaBrowser.Api.Playback
{
var mediaSourceId = request.MediaSourceId;
- SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex, request.MaxAudioChannels, request.UserId, request.EnableDirectPlay, request.ForceDirectPlayRemoteMediaSource, request.EnableDirectStream, request.EnableTranscoding, request.AllowVideoStreamCopy, request.AllowAudioStreamCopy);
+ SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex, request.MaxAudioChannels, request.UserId, request.EnableDirectPlay, true, request.EnableDirectStream, request.EnableTranscoding, request.AllowVideoStreamCopy, request.AllowAudioStreamCopy);
}
if (request.AutoOpenLiveStream)
@@ -191,7 +191,6 @@ namespace MediaBrowser.Api.Playback
DeviceProfile = request.DeviceProfile,
EnableDirectPlay = request.EnableDirectPlay,
EnableDirectStream = request.EnableDirectStream,
- ForceDirectPlayRemoteMediaSource = request.ForceDirectPlayRemoteMediaSource,
ItemId = request.Id,
MaxAudioChannels = request.MaxAudioChannels,
MaxStreamingBitrate = request.MaxStreamingBitrate,
@@ -199,7 +198,8 @@ namespace MediaBrowser.Api.Playback
StartTimeTicks = request.StartTimeTicks,
SubtitleStreamIndex = request.SubtitleStreamIndex,
UserId = request.UserId,
- OpenToken = mediaSource.OpenToken
+ OpenToken = mediaSource.OpenToken,
+ EnableMediaProbe = request.EnableMediaProbe
}).ConfigureAwait(false);
diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
index af8670eb1..44e096dd7 100644
--- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
@@ -10,6 +10,7 @@ using MediaBrowser.Model.Serialization;
using System.Collections.Generic;
using System.Threading.Tasks;
using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.System;
@@ -58,10 +59,8 @@ namespace MediaBrowser.Api.Playback.Progressive
return ProcessRequest(request, true);
}
- protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
+ protected override string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding)
{
- var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
-
return EncodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath);
}
}
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
index 5e21f6a84..a41b4cbf5 100644
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
@@ -8,6 +8,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using System.Threading.Tasks;
using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.System;
@@ -91,10 +92,8 @@ namespace MediaBrowser.Api.Playback.Progressive
return ProcessRequest(request, true);
}
- protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
+ protected override string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding)
{
- var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
-
return EncodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, outputPath, GetDefaultH264Preset());
}
}
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index 2cf7d81af..eecc12432 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -80,7 +80,7 @@ namespace MediaBrowser.Api.Playback
return 6;
}
- return 10;
+ return 6;
}
if (IsSegmentedLiveStream)
diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
index 3b638208b..4bb3de882 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -224,7 +224,7 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ParentId { get; set; }
- [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string Fields { get; set; }
[ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
diff --git a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs
index 86a974229..89e20b1b4 100644
--- a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs
+++ b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs
@@ -12,7 +12,7 @@ namespace MediaBrowser.Common.Extensions
/// </summary>
public ResourceNotFoundException()
{
-
+
}
/// <summary>
@@ -26,6 +26,20 @@ namespace MediaBrowser.Common.Extensions
}
}
+ public class RemoteServiceUnavailableException : Exception
+ {
+ public RemoteServiceUnavailableException()
+ {
+
+ }
+
+ public RemoteServiceUnavailableException(string message)
+ : base(message)
+ {
+
+ }
+ }
+
public class RateLimitExceededException : Exception
{
/// <summary>
diff --git a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs
index ba20395d1..cf7b6ba6a 100644
--- a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs
+++ b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs
@@ -67,7 +67,7 @@ namespace MediaBrowser.Controller.Channels
Name = id,
Id = id,
ReadAtNativeFramerate = ReadAtNativeFramerate,
- SupportsDirectStream = false,
+ SupportsDirectStream = Protocol == MediaProtocol.Http && !string.IsNullOrWhiteSpace(Container) && !string.Equals(Container, "hls", StringComparison.OrdinalIgnoreCase),
SupportsDirectPlay = SupportsDirectPlay,
IsRemote = true
};
diff --git a/MediaBrowser.Controller/Dlna/IEventManager.cs b/MediaBrowser.Controller/Dlna/IEventManager.cs
index 54e2a02dd..8c91bd889 100644
--- a/MediaBrowser.Controller/Dlna/IEventManager.cs
+++ b/MediaBrowser.Controller/Dlna/IEventManager.cs
@@ -12,18 +12,11 @@ namespace MediaBrowser.Controller.Dlna
/// <summary>
/// Renews the event subscription.
/// </summary>
- /// <param name="subscriptionId">The subscription identifier.</param>
- /// <param name="timeoutSeconds">The timeout seconds.</param>
- /// <returns>EventSubscriptionResponse.</returns>
- EventSubscriptionResponse RenewEventSubscription(string subscriptionId, int? timeoutSeconds);
+ EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string requestedTimeoutString);
/// <summary>
/// Creates the event subscription.
/// </summary>
- /// <param name="notificationType">Type of the notification.</param>
- /// <param name="timeoutSeconds">The timeout seconds.</param>
- /// <param name="callbackUrl">The callback URL.</param>
- /// <returns>EventSubscriptionResponse.</returns>
- EventSubscriptionResponse CreateEventSubscription(string notificationType, int? timeoutSeconds, string callbackUrl);
+ EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl);
}
}
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index 7978f4761..c5d9b9203 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -57,6 +57,22 @@ namespace MediaBrowser.Controller.Entities
{
get
{
+ var extraType = ExtraType;
+ if (extraType.HasValue)
+ {
+ if (extraType.Value == Model.Entities.ExtraType.Sample)
+ {
+ return false;
+ }
+ if (extraType.Value == Model.Entities.ExtraType.ThemeVideo)
+ {
+ return false;
+ }
+ if (extraType.Value == Model.Entities.ExtraType.Trailer)
+ {
+ return false;
+ }
+ }
return true;
}
}
diff --git a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs
index b0881ba7c..4ec9adf4e 100644
--- a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs
+++ b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs
@@ -20,10 +20,7 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Opens the media source.
/// </summary>
- /// <param name="openToken">The open token.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
- Task<Tuple<MediaSourceInfo,IDirectStreamProvider>> OpenMediaSource(string openToken, CancellationToken cancellationToken);
+ Task<Tuple<MediaSourceInfo,IDirectStreamProvider>> OpenMediaSource(string openToken, bool allowLiveStreamProbe, CancellationToken cancellationToken);
/// <summary>
/// Closes the media source.
diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs
index 3d1af2afa..6d220f3a3 100644
--- a/MediaBrowser.Controller/Providers/DirectoryService.cs
+++ b/MediaBrowser.Controller/Providers/DirectoryService.cs
@@ -120,10 +120,14 @@ namespace MediaBrowser.Controller.Providers
{
file = _fileSystem.GetFileInfo(path);
- if (file != null)
+ if (file != null && file.Exists)
{
_fileCache.TryAdd(path, file);
}
+ else
+ {
+ return null;
+ }
}
return file;
diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs
index f590d9aec..11a9ceac4 100644
--- a/MediaBrowser.Controller/Session/SessionInfo.cs
+++ b/MediaBrowser.Controller/Session/SessionInfo.cs
@@ -203,6 +203,11 @@ namespace MediaBrowser.Controller.Session
public void StartAutomaticProgress(ITimerFactory timerFactory, PlaybackProgressInfo progressInfo)
{
+ if (_disposed)
+ {
+ return;
+ }
+
lock (_progressLock)
{
_lastProgressInfo = progressInfo;
@@ -223,6 +228,11 @@ namespace MediaBrowser.Controller.Session
private async void OnProgressTimerCallback(object state)
{
+ if (_disposed)
+ {
+ return;
+ }
+
var progressInfo = _lastProgressInfo;
if (progressInfo == null)
{
@@ -274,8 +284,12 @@ namespace MediaBrowser.Controller.Session
}
}
+ private bool _disposed = false;
+
public void Dispose()
{
+ _disposed = true;
+
StopAutomaticProgress();
_sessionManager = null;
}
diff --git a/MediaBrowser.Controller/Sync/ISyncManager.cs b/MediaBrowser.Controller/Sync/ISyncManager.cs
index fbcb549ad..291632ea5 100644
--- a/MediaBrowser.Controller/Sync/ISyncManager.cs
+++ b/MediaBrowser.Controller/Sync/ISyncManager.cs
@@ -90,6 +90,8 @@ namespace MediaBrowser.Controller.Sync
/// </summary>
IEnumerable<SyncTarget> GetSyncTargets(string userId);
+ IEnumerable<SyncTarget> GetSyncTargets(string userId, bool? supportsRemoteSync);
+
/// <summary>
/// Supportses the synchronize.
/// </summary>
diff --git a/MediaBrowser.Controller/Sync/ISyncProvider.cs b/MediaBrowser.Controller/Sync/ISyncProvider.cs
index ef34bfe69..aa4b36427 100644
--- a/MediaBrowser.Controller/Sync/ISyncProvider.cs
+++ b/MediaBrowser.Controller/Sync/ISyncProvider.cs
@@ -11,6 +11,8 @@ namespace MediaBrowser.Controller.Sync
/// <value>The name.</value>
string Name { get; }
+ bool SupportsRemoteSync { get; }
+
/// <summary>
/// Gets the synchronize targets.
/// </summary>
@@ -27,6 +29,6 @@ namespace MediaBrowser.Controller.Sync
public interface IHasUniqueTargetIds
{
-
+
}
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index faf5b0667..f1bf29d92 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -733,9 +733,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
var mapArg = imageStreamIndex.HasValue ? (" -map 0:v:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty;
var enableThumbnail = !new List<string> { "wtv" }.Contains(container ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+ // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
var thumbnail = enableThumbnail ? ",thumbnail=24" : string.Empty;
- // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
var args = useIFrame ? string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail) :
string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg);
diff --git a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
index 0b6ecf385..df511b0da 100644
--- a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
+++ b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
@@ -1,6 +1,7 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Xml.Serialization;
-using MediaBrowser.Model.Dlna;
namespace MediaBrowser.Model.Dlna
{
@@ -28,6 +29,19 @@ namespace MediaBrowser.Model.Dlna
return list;
}
+ public bool SupportsContainer(string container)
+ {
+ var all = GetContainers();
+
+ // Only allow unknown container if the profile is all inclusive
+ if (string.IsNullOrWhiteSpace(container))
+ {
+ return all.Count == 0;
+ }
+
+ return all.Count == 0 || all.Contains(container, StringComparer.OrdinalIgnoreCase);
+ }
+
public List<string> GetAudioCodecs()
{
List<string> list = new List<string>();
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index de6cbf4bb..48f9a4212 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -492,52 +492,45 @@ namespace MediaBrowser.Model.Dlna
foreach (var profile in directPlayProfiles)
{
- if (profile.Container.Length > 0)
+ // Check container type
+ if (profile.SupportsContainer(item.Container))
{
- // Check container type
- string mediaContainer = item.Container ?? string.Empty;
- foreach (string i in profile.GetContainers())
+ containerSupported = true;
+
+ if (videoStream != null)
{
- if (StringHelper.EqualsIgnoreCase(i, mediaContainer))
+ // Check video codec
+ List<string> videoCodecs = profile.GetVideoCodecs();
+ if (videoCodecs.Count > 0)
{
- containerSupported = true;
-
- if (videoStream != null)
+ string videoCodec = videoStream.Codec;
+ if (!string.IsNullOrEmpty(videoCodec) && ListHelper.ContainsIgnoreCase(videoCodecs, videoCodec))
{
- // Check video codec
- List<string> videoCodecs = profile.GetVideoCodecs();
- if (videoCodecs.Count > 0)
- {
- string videoCodec = videoStream.Codec;
- if (!string.IsNullOrEmpty(videoCodec) && ListHelper.ContainsIgnoreCase(videoCodecs, videoCodec))
- {
- videoSupported = true;
- }
- }
- else
- {
- videoSupported = true;
- }
+ videoSupported = true;
}
+ }
+ else
+ {
+ videoSupported = true;
+ }
+ }
- if (audioStream != null)
+ if (audioStream != null)
+ {
+ // Check audio codec
+ List<string> audioCodecs = profile.GetAudioCodecs();
+ if (audioCodecs.Count > 0)
+ {
+ string audioCodec = audioStream.Codec;
+ if (!string.IsNullOrEmpty(audioCodec) && ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec))
{
- // Check audio codec
- List<string> audioCodecs = profile.GetAudioCodecs();
- if (audioCodecs.Count > 0)
- {
- string audioCodec = audioStream.Codec;
- if (!string.IsNullOrEmpty(audioCodec) && ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec))
- {
- audioSupported = true;
- }
- }
- else
- {
- audioSupported = true;
- }
+ audioSupported = true;
}
}
+ else
+ {
+ audioSupported = true;
+ }
}
}
}
@@ -635,8 +628,10 @@ namespace MediaBrowser.Model.Dlna
MediaStream videoStream = item.VideoStream;
// TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough
- bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true), subtitleStream, options, PlayMethod.DirectPlay));
- bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || IsEligibleForDirectPlay(item, options.GetMaxBitrate(false), subtitleStream, options, PlayMethod.DirectStream));
+ var directPlayEligibilityResult = IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true), subtitleStream, options, PlayMethod.DirectPlay);
+ var directStreamEligibilityResult = IsEligibleForDirectPlay(item, options.GetMaxBitrate(false), subtitleStream, options, PlayMethod.DirectStream);
+ bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayEligibilityResult.Item1);
+ bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directStreamEligibilityResult.Item1);
_logger.Info("Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}",
options.Profile.Name ?? "Unknown Profile",
@@ -669,6 +664,16 @@ namespace MediaBrowser.Model.Dlna
transcodeReasons.AddRange(directPlayInfo.Item2);
}
+ if (directPlayEligibilityResult.Item2.HasValue)
+ {
+ transcodeReasons.Add(directPlayEligibilityResult.Item2.Value);
+ }
+
+ if (directStreamEligibilityResult.Item2.HasValue)
+ {
+ transcodeReasons.Add(directStreamEligibilityResult.Item2.Value);
+ }
+
// Can't direct play, find the transcoding profile
TranscodingProfile transcodingProfile = null;
foreach (TranscodingProfile i in options.Profile.TranscodingProfiles)
@@ -1136,7 +1141,7 @@ namespace MediaBrowser.Model.Dlna
mediaSource.Path ?? "Unknown path");
}
- private bool IsEligibleForDirectPlay(MediaSourceInfo item,
+ private Tuple<bool, TranscodeReason?> IsEligibleForDirectPlay(MediaSourceInfo item,
long? maxBitrate,
MediaStream subtitleStream,
VideoOptions options,
@@ -1149,11 +1154,18 @@ namespace MediaBrowser.Model.Dlna
if (subtitleProfile.Method != SubtitleDeliveryMethod.External && subtitleProfile.Method != SubtitleDeliveryMethod.Embed)
{
_logger.Info("Not eligible for {0} due to unsupported subtitles", playMethod);
- return false;
+ return new Tuple<bool, TranscodeReason?>(false, TranscodeReason.SubtitleCodecNotSupported);
}
}
- return IsAudioEligibleForDirectPlay(item, maxBitrate, playMethod);
+ var result = IsAudioEligibleForDirectPlay(item, maxBitrate, playMethod);
+
+ if (result)
+ {
+ return new Tuple<bool, TranscodeReason?>(result, null);
+ }
+
+ return new Tuple<bool, TranscodeReason?>(result, TranscodeReason.ContainerBitrateExceedsLimit);
}
public static SubtitleProfile GetSubtitleProfile(MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, string transcodingSubProtocol, string transcodingContainer)
@@ -1519,23 +1531,10 @@ namespace MediaBrowser.Model.Dlna
private bool IsAudioDirectPlaySupported(DirectPlayProfile profile, MediaSourceInfo item, MediaStream audioStream)
{
- if (profile.Container.Length > 0)
+ // Check container type
+ if (!profile.SupportsContainer(item.Container))
{
- // Check container type
- string mediaContainer = item.Container ?? string.Empty;
- bool any = false;
- foreach (string i in profile.GetContainers())
- {
- if (StringHelper.EqualsIgnoreCase(i, mediaContainer))
- {
- any = true;
- break;
- }
- }
- if (!any)
- {
- return false;
- }
+ return false;
}
// Check audio codec
@@ -1555,23 +1554,10 @@ namespace MediaBrowser.Model.Dlna
private bool IsVideoDirectPlaySupported(DirectPlayProfile profile, MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream)
{
- if (profile.Container.Length > 0)
+ // Check container type
+ if (!profile.SupportsContainer(item.Container))
{
- // Check container type
- string mediaContainer = item.Container ?? string.Empty;
- bool any = false;
- foreach (string i in profile.GetContainers())
- {
- if (StringHelper.EqualsIgnoreCase(i, mediaContainer))
- {
- any = true;
- break;
- }
- }
- if (!any)
- {
- return false;
- }
+ return false;
}
// Check video codec
diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
index 025f1bc7a..dedbb428d 100644
--- a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
+++ b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
@@ -17,13 +17,13 @@ namespace MediaBrowser.Model.MediaInfo
public bool EnableDirectPlay { get; set; }
public bool EnableDirectStream { get; set; }
- public bool ForceDirectPlayRemoteMediaSource { get; set; }
+ public bool EnableMediaProbe { get; set; }
public LiveStreamRequest()
{
- ForceDirectPlayRemoteMediaSource = true;
EnableDirectPlay = true;
EnableDirectStream = true;
+ EnableMediaProbe = true;
}
public LiveStreamRequest(AudioOptions options)
diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
index 1843fb69a..df39f0556 100644
--- a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
+++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
@@ -27,19 +27,19 @@ namespace MediaBrowser.Model.MediaInfo
public bool EnableDirectPlay { get; set; }
public bool EnableDirectStream { get; set; }
public bool EnableTranscoding { get; set; }
- public bool ForceDirectPlayRemoteMediaSource { get; set; }
public bool AllowVideoStreamCopy { get; set; }
public bool AllowAudioStreamCopy { get; set; }
public bool AutoOpenLiveStream { get; set; }
+ public bool EnableMediaProbe { get; set; }
public PlaybackInfoRequest()
{
- ForceDirectPlayRemoteMediaSource = true;
EnableDirectPlay = true;
EnableDirectStream = true;
EnableTranscoding = true;
AllowVideoStreamCopy = true;
AllowAudioStreamCopy = true;
+ EnableMediaProbe = true;
}
}
}
diff --git a/MediaBrowser.Model/Session/TranscodingInfo.cs b/MediaBrowser.Model/Session/TranscodingInfo.cs
index f1cbacd90..f58e605b2 100644
--- a/MediaBrowser.Model/Session/TranscodingInfo.cs
+++ b/MediaBrowser.Model/Session/TranscodingInfo.cs
@@ -48,6 +48,7 @@ namespace MediaBrowser.Model.Session
VideoFramerateNotSupported = 17,
VideoLevelNotSupported = 18,
VideoProfileNotSupported = 19,
- AudioBitDepthNotSupported = 20
+ AudioBitDepthNotSupported = 20,
+ SubtitleCodecNotSupported = 21
}
} \ No newline at end of file
diff --git a/MediaBrowser.Model/Sync/SyncJob.cs b/MediaBrowser.Model/Sync/SyncJob.cs
index 6709426b8..f0e262c50 100644
--- a/MediaBrowser.Model/Sync/SyncJob.cs
+++ b/MediaBrowser.Model/Sync/SyncJob.cs
@@ -59,7 +59,7 @@ namespace MediaBrowser.Model.Sync
/// Gets or sets the status.
/// </summary>
/// <value>The status.</value>
- public SyncJobStatus Status { get; set; }
+ public SyncJobStatus Status { get; set; }
/// <summary>
/// Gets or sets the user identifier.
/// </summary>
@@ -105,9 +105,12 @@ namespace MediaBrowser.Model.Sync
public string PrimaryImageItemId { get; set; }
public string PrimaryImageTag { get; set; }
+ public bool EnableAutomaticResync { get; set; }
+
public SyncJob()
{
RequestedItemIds = new List<string>();
+ EnableAutomaticResync = true;
}
}
}
diff --git a/MediaBrowser.Providers/Music/ArtistMetadataService.cs b/MediaBrowser.Providers/Music/ArtistMetadataService.cs
index d4e580871..98fe766e0 100644
--- a/MediaBrowser.Providers/Music/ArtistMetadataService.cs
+++ b/MediaBrowser.Providers/Music/ArtistMetadataService.cs
@@ -21,7 +21,7 @@ namespace MediaBrowser.Providers.Music
if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
{
- if (!item.IsLocked)
+ if (!item.IsLocked && !item.LockedFields.Contains(MetadataFields.Genres))
{
var taggedItems = item.IsAccessedByName ?
item.GetTaggedItems(new Controller.Entities.InternalItemsQuery()
@@ -31,22 +31,19 @@ namespace MediaBrowser.Providers.Music
}) :
item.GetRecursiveChildren(i => i is IHasArtist && !i.IsFolder).ToList();
- if (!item.LockedFields.Contains(MetadataFields.Genres))
- {
- var currentList = item.Genres.ToList();
+ var currentList = item.Genres.ToList();
- item.Genres = taggedItems.SelectMany(i => i.Genres)
- .DistinctNames()
- .ToList();
+ item.Genres = taggedItems.SelectMany(i => i.Genres)
+ .DistinctNames()
+ .ToList();
- if (currentList.Count != item.Genres.Count || !currentList.OrderBy(i => i).SequenceEqual(item.Genres.OrderBy(i => i), StringComparer.OrdinalIgnoreCase))
- {
- updateType = updateType | ItemUpdateType.MetadataEdit;
- }
+ if (currentList.Count != item.Genres.Count || !currentList.OrderBy(i => i).SequenceEqual(item.Genres.OrderBy(i => i), StringComparer.OrdinalIgnoreCase))
+ {
+ updateType = updateType | ItemUpdateType.MetadataEdit;
}
}
}
-
+
return updateType;
}
diff --git a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
index 235fd8c8a..c4ea106fb 100644
--- a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
+++ b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Configuration;
+using System;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
@@ -6,7 +7,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-
+using System.Linq;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
@@ -33,6 +34,33 @@ namespace MediaBrowser.Providers.Playlists
}
}
+ protected override ItemUpdateType BeforeSave(Playlist item, bool isFullRefresh, ItemUpdateType currentUpdateType)
+ {
+ var updateType = base.BeforeSave(item, isFullRefresh, currentUpdateType);
+
+ if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
+ {
+ if (!item.IsLocked && !item.LockedFields.Contains(MetadataFields.Genres))
+ {
+ var items = item.GetLinkedChildren()
+ .ToList();
+
+ var currentList = item.Genres.ToList();
+
+ item.Genres = items.SelectMany(i => i.Genres)
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToList();
+
+ if (currentList.Count != item.Genres.Count || !currentList.OrderBy(i => i).SequenceEqual(item.Genres.OrderBy(i => i), StringComparer.OrdinalIgnoreCase))
+ {
+ updateType = updateType | ItemUpdateType.MetadataEdit;
+ }
+ }
+ }
+
+ return updateType;
+ }
+
public PlaylistMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
}
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs
index 2e549f610..618a4df45 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs
@@ -196,8 +196,6 @@ namespace MediaBrowser.Providers.TV
}
catch (HttpException ex)
{
- Logger.Error("No metadata found for {0}", seasonNumber.Value);
-
if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
{
return result;
diff --git a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
index 3e69e9f12..196bdd096 100644
--- a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
+++ b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
@@ -116,10 +116,10 @@
<HintPath>..\packages\SkiaSharp.1.58.0\lib\net45\SkiaSharp.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.core">
- <HintPath>..\packages\SQLitePCLRaw.core.1.1.6\lib\net45\SQLitePCLRaw.core.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.7\lib\net45\SQLitePCLRaw.core.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.provider.sqlite3">
- <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.6\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.7\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
</Reference>
<Reference Include="Emby.Server.Connect">
<HintPath>..\ThirdParty\emby\Emby.Server.Connect.dll</HintPath>
@@ -411,6 +411,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\mypreferencesmenu.html">
<Link>Resources\dashboard-ui\mypreferencesmenu.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\mypreferencessubtitles.html">
+ <Link>Resources\dashboard-ui\mypreferencessubtitles.html</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\myprofile.html">
<Link>Resources\dashboard-ui\myprofile.html</Link>
</BundleResource>
@@ -918,6 +921,12 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\emby-radio\emby-radio.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\emby-radio\emby-radio.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\emby-scrollbuttons\emby-scrollbuttons.css">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\emby-scrollbuttons\emby-scrollbuttons.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\emby-scrollbuttons\emby-scrollbuttons.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\emby-scrollbuttons\emby-scrollbuttons.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\emby-scroller\emby-scroller.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\emby-scroller\emby-scroller.js</Link>
</BundleResource>
@@ -1476,6 +1485,15 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\subtitleeditor\subtitleeditor.template.html">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\subtitleeditor\subtitleeditor.template.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\subtitlesettings\subtitleappearancehelper.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\subtitlesettings\subtitleappearancehelper.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\subtitlesettings\subtitlesettings.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\subtitlesettings\subtitlesettings.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\subtitlesettings\subtitlesettings.template.html">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\subtitlesettings\subtitlesettings.template.html</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\sync\emby-downloadbutton.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\sync\emby-downloadbutton.js</Link>
</BundleResource>
@@ -1488,15 +1506,18 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\sync\syncjoblist.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\sync\syncjoblist.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\sync\synctoggle.js">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\sync\synctoggle.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\toast\toast.css">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\toast\toast.css</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\toast\toast.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\toast\toast.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\upnextdialog\upnextdialog.css">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\upnextdialog\upnextdialog.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\upnextdialog\upnextdialog.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\upnextdialog\upnextdialog.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\userdatabuttons\emby-playstatebutton.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\userdatabuttons\emby-playstatebutton.js</Link>
</BundleResource>
@@ -1788,9 +1809,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\librarybrowser.css">
<Link>Resources\dashboard-ui\css\librarybrowser.css</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\librarymenu.css">
- <Link>Resources\dashboard-ui\css\librarymenu.css</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\livetv.css">
<Link>Resources\dashboard-ui\css\livetv.css</Link>
</BundleResource>
@@ -2082,84 +2100,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\devices\ios\ios.css">
<Link>Resources\dashboard-ui\devices\ios\ios.css</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\-l14jk06m6puhb-5mxqqnrjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\-l14jk06m6puhb-5mxqqnrjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\0ec6fl06luxeywpbsjvxcbjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\0ec6fl06luxeywpbsjvxcbjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\2tsd397wlxj96qwhynikxpeszw2xoq-xsnqo47m55da.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\2tsd397wlxj96qwhynikxpeszw2xoq-xsnqo47m55da.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\97uahxiqzroncbacei3awxjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\97uahxiqzroncbacei3awxjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\azmswpodyevhtrvuabjwvbtbgvql8ndjpwnre27mub0.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\azmswpodyevhtrvuabjwvbtbgvql8ndjpwnre27mub0.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\cwb0xya8bzo0ksthx0utua.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\cwb0xya8bzo0ksthx0utua.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\d-6iyplofoccackzxwxsoftxra8tvwticgirnjhmvjw.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\d-6iyplofoccackzxwxsoftxra8tvwticgirnjhmvjw.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\e7mevayvogmqfwwl61pkhbtbgvql8ndjpwnre27mub0.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\e7mevayvogmqfwwl61pkhbtbgvql8ndjpwnre27mub0.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\fcx7wwv8ozt71a3e1xoajveszw2xoq-xsnqo47m55da.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\fcx7wwv8ozt71a3e1xoajveszw2xoq-xsnqo47m55da.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\fl4y0qdoxyythegmxx8kcrjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\fl4y0qdoxyythegmxx8kcrjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\frnv30oaydlfrth2vnzzdhtbgvql8ndjpwnre27mub0.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\frnv30oaydlfrth2vnzzdhtbgvql8ndjpwnre27mub0.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\gwvjdern2amz39wrsoz7fxtbgvql8ndjpwnre27mub0.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\gwvjdern2amz39wrsoz7fxtbgvql8ndjpwnre27mub0.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\hgo13k-tfspn0qi1sfdufvtxra8tvwticgirnjhmvjw.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\hgo13k-tfspn0qi1sfdufvtxra8tvwticgirnjhmvjw.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\i3s1wsgsg9ycurv6puktorjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\i3s1wsgsg9ycurv6puktorjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\nydwbdd4giq26g5xybhsfbjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\nydwbdd4giq26g5xybhsfbjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\ooefwznlrtefzlymlvv1ubjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\ooefwznlrtefzlymlvv1ubjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\pru33qjshpzsmg3z6vywnrjtnkitppoi_ivcxxdnrsc.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\pru33qjshpzsmg3z6vywnrjtnkitppoi_ivcxxdnrsc.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\robotobold.woff">
- <Link>Resources\dashboard-ui\fonts\roboto\robotobold.woff</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\robotolight.woff">
- <Link>Resources\dashboard-ui\fonts\roboto\robotolight.woff</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\robotomedium.woff">
- <Link>Resources\dashboard-ui\fonts\roboto\robotomedium.woff</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\robotoregular.woff">
- <Link>Resources\dashboard-ui\fonts\roboto\robotoregular.woff</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\robotothin.woff">
- <Link>Resources\dashboard-ui\fonts\roboto\robotothin.woff</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\rxzjdnzeo3r5zsexge8uuvtxra8tvwticgirnjhmvjw.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\rxzjdnzeo3r5zsexge8uuvtxra8tvwticgirnjhmvjw.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\style.css">
- <Link>Resources\dashboard-ui\fonts\roboto\style.css</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\ty9dfvlaziwdqq2dhoyjphtbgvql8ndjpwnre27mub0.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\ty9dfvlaziwdqq2dhoyjphtbgvql8ndjpwnre27mub0.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\fonts\roboto\vvxugkzxbhtx_s_vctlpghtbgvql8ndjpwnre27mub0.woff2">
- <Link>Resources\dashboard-ui\fonts\roboto\vvxugkzxbhtx_s_vctlpghtbgvql8ndjpwnre27mub0.woff2</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\legacy\buttonenabled.js">
<Link>Resources\dashboard-ui\legacy\buttonenabled.js</Link>
</BundleResource>
@@ -2358,6 +2298,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mypreferenceslanguages.js">
<Link>Resources\dashboard-ui\scripts\mypreferenceslanguages.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mypreferencessubtitles.js">
+ <Link>Resources\dashboard-ui\scripts\mypreferencessubtitles.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\myprofile.js">
<Link>Resources\dashboard-ui\scripts\myprofile.js</Link>
</BundleResource>
@@ -2658,9 +2601,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\themes\holiday\theme.js">
<Link>Resources\dashboard-ui\themes\holiday\theme.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\paper-button-style.css">
- <Link>Resources\dashboard-ui\thirdparty\paper-button-style.css</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.listview.css">
<Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.listview.css</Link>
</BundleResource>
diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
index be928319f..df98c6584 100644
--- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
+++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
@@ -71,11 +71,11 @@
<Private>True</Private>
</Reference>
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
- <HintPath>..\packages\SQLitePCLRaw.core.1.1.6\lib\net45\SQLitePCLRaw.core.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.7\lib\net45\SQLitePCLRaw.core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLitePCLRaw.provider.sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=62684c7b4f184e3f, processorArchitecture=MSIL">
- <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.6\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.7\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
diff --git a/MediaBrowser.Server.Mono/packages.config b/MediaBrowser.Server.Mono/packages.config
index f1de00589..dcc477e9a 100644
--- a/MediaBrowser.Server.Mono/packages.config
+++ b/MediaBrowser.Server.Mono/packages.config
@@ -6,6 +6,6 @@
<package id="SharpCompress" version="0.14.0" targetFramework="net46" />
<package id="SimpleInjector" version="4.0.8" targetFramework="net46" />
<package id="SkiaSharp" version="1.58.0" targetFramework="net46" />
- <package id="SQLitePCLRaw.core" version="1.1.6" targetFramework="net46" />
- <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.6" targetFramework="net46" />
+ <package id="SQLitePCLRaw.core" version="1.1.7" targetFramework="net46" />
+ <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.7" targetFramework="net46" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs
index 6b100b86d..bc38476ca 100644
--- a/MediaBrowser.ServerApplication/MainStartup.cs
+++ b/MediaBrowser.ServerApplication/MainStartup.cs
@@ -678,7 +678,7 @@ namespace MediaBrowser.ServerApplication
_logger.Info("Calling Application.Exit");
//Application.Exit();
-
+
Environment.Exit(0);
}
@@ -770,19 +770,43 @@ namespace MediaBrowser.ServerApplication
try
{
- var subkey = Environment.Is64BitProcess
- ? "SOFTWARE\\WOW6432Node\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64"
- : "SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x86";
+ RegistryKey key;
- using (RegistryKey ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default)
- .OpenSubKey(subkey))
+ if (Environment.Is64BitProcess)
{
- if (ndpKey != null && ndpKey.GetValue("Version") != null)
+ key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default)
+ .OpenSubKey("SOFTWARE\\Classes\\Installer\\Dependencies\\{d992c12e-cab2-426f-bde3-fb8c53950b0d}");
+
+ if (key == null)
{
- var installedVersion = ((string)ndpKey.GetValue("Version")).TrimStart('v');
- if (installedVersion.StartsWith("14", StringComparison.OrdinalIgnoreCase))
+ key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default)
+ .OpenSubKey("SOFTWARE\\WOW6432Node\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64");
+ }
+ }
+ else
+ {
+ key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default)
+ .OpenSubKey("SOFTWARE\\Classes\\Installer\\Dependencies\\{e2803110-78b3-4664-a479-3611a381656a}");
+
+ if (key == null)
+ {
+ key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default)
+ .OpenSubKey("SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x86");
+ }
+ }
+
+ if (key != null)
+ {
+ using (key)
+ {
+ var version = key.GetValue("Version");
+ if (version != null)
{
- return;
+ var installedVersion = ((string)version).TrimStart('v');
+ if (installedVersion.StartsWith("14", StringComparison.OrdinalIgnoreCase))
+ {
+ return;
+ }
}
}
}
diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
index 19f4921d3..591ac0fba 100644
--- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
+++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
@@ -73,10 +73,6 @@
<Reference Include="Emby.Server.Sync">
<HintPath>..\ThirdParty\emby\Emby.Server.Sync.dll</HintPath>
</Reference>
- <Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\ImageMagickSharp.1.0.0.18\lib\net45\ImageMagickSharp.dll</HintPath>
- </Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.4.11\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
@@ -96,11 +92,11 @@
<HintPath>..\packages\SkiaSharp.1.58.0\lib\net45\SkiaSharp.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
- <HintPath>..\packages\SQLitePCLRaw.core.1.1.6\lib\net45\SQLitePCLRaw.core.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.7\lib\net45\SQLitePCLRaw.core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLitePCLRaw.provider.sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=62684c7b4f184e3f, processorArchitecture=MSIL">
- <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.6\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.7\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config
index ca382ffb3..0fa93db82 100644
--- a/MediaBrowser.ServerApplication/packages.config
+++ b/MediaBrowser.ServerApplication/packages.config
@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="ImageMagickSharp" version="1.0.0.18" targetFramework="net45" />
<package id="NLog" version="4.4.11" targetFramework="net462" />
<package id="ServiceStack.Text" version="4.5.8" targetFramework="net462" />
<package id="SharpCompress" version="0.14.0" targetFramework="net462" />
<package id="SimpleInjector" version="4.0.8" targetFramework="net462" />
<package id="SkiaSharp" version="1.58.0" targetFramework="net462" />
- <package id="SQLitePCLRaw.core" version="1.1.6" targetFramework="net462" />
- <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.6" targetFramework="net462" />
+ <package id="SQLitePCLRaw.core" version="1.1.7" targetFramework="net462" />
+ <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.7" targetFramework="net462" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
index 7d825875d..da43dd63a 100644
--- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs
+++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
@@ -242,10 +242,7 @@ namespace MediaBrowser.WebDashboard.Api
var files = new[]
{
- "css/site.css" + versionString,
- "css/librarymenu.css" + versionString,
- "css/librarybrowser.css" + versionString,
- "thirdparty/paper-button-style.css" + versionString
+ "css/site.css" + versionString
};
var tags = files.Select(s => string.Format("<link rel=\"stylesheet\" href=\"{0}\" async />", s)).ToArray();
diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
index cf7c25584..5015dfbf9 100644
--- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
@@ -16,9 +16,10 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
-
+using MediaBrowser.Controller.Extensions;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Xml;
@@ -353,7 +354,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (!string.IsNullOrEmpty(stream.Language))
{
- writer.WriteElementString("language", stream.Language);
+ // https://emby.media/community/index.php?/topic/49071-nfo-not-generated-on-actualize-or-rescan-or-identify
+ writer.WriteElementString("language", RemoveInvalidXMLChars(stream.Language));
}
var scanType = stream.IsInterlaced ? "interlaced" : "progressive";
@@ -422,6 +424,19 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteEndElement();
}
+ // filters control characters but allows only properly-formed surrogate sequences
+ private static Regex _invalidXMLChars = new Regex(
+ @"(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]");
+
+ /// <summary>
+ /// removes any unusual unicode characters that can't be encoded into XML
+ /// </summary>
+ public static string RemoveInvalidXMLChars(string text)
+ {
+ if (string.IsNullOrEmpty(text)) return string.Empty;
+ return _invalidXMLChars.Replace(text, string.Empty);
+ }
+
public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
/// <summary>
diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
index f32f89bc2..aea3f2c70 100644
--- a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
@@ -51,6 +51,11 @@ namespace MediaBrowser.XbmcMetadata.Savers
//}
list.Add(Path.ChangeExtension(item.Path, ".nfo"));
+
+ if (!item.IsInMixedFolder)
+ {
+ list.Add(Path.Combine(item.ContainingFolderPath, "movie.nfo"));
+ }
}
return list;
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index ff58c13ac..26c19c79a 100644
--- a/Nuget/MediaBrowser.Common.nuspec
+++ b/Nuget/MediaBrowser.Common.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
- <version>3.0.704</version>
+ <version>3.0.708</version>
<title>Emby.Common</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index a5707a265..641db709a 100644
--- a/Nuget/MediaBrowser.Server.Core.nuspec
+++ b/Nuget/MediaBrowser.Server.Core.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
- <version>3.0.704</version>
+ <version>3.0.708</version>
<title>Emby.Server.Core</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Emby Server.</description>
<copyright>Copyright © Emby 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.704" />
+ <dependency id="MediaBrowser.Common" version="3.0.708" />
</dependencies>
</metadata>
<files>