Merge pull request #2767 from MediaBrowser/beta

Beta
pull/1154/head
Luke 7 years ago committed by GitHub
commit 5ec9d4e9fe

@ -736,10 +736,10 @@ namespace Emby.Common.Implementations.HttpClientManager
{ {
if (options.LogErrors) 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; var response = webException.Response as HttpWebResponse;
if (response != null) 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; return exception;
} }

@ -26,6 +26,7 @@ using System.Xml;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Xml; using MediaBrowser.Model.Xml;
@ -482,6 +483,12 @@ namespace Emby.Dlna.ContentDirectory
return GetMusicArtistItems(item, null, user, sort, startIndex, limit); 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.HasValue)
{ {
if (stubType.Value == StubType.People) if (stubType.Value == StubType.People)
@ -518,7 +525,7 @@ namespace Emby.Dlna.ContentDirectory
StartIndex = startIndex, StartIndex = startIndex,
User = user, User = user,
IsMissing = false, IsMissing = false,
PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Music }, PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows },
ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name }, ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name },
IsPlaceHolder = false, IsPlaceHolder = false,
DtoOptions = GetDtoOptions() DtoOptions = GetDtoOptions()
@ -531,6 +538,278 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(queryResult); 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) private QueryResult<ServerItem> GetMusicArtistItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit)
{ {
var query = new InternalItemsQuery(user) var query = new InternalItemsQuery(user)
@ -571,6 +850,19 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result); 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) private QueryResult<ServerItem> ToResult(QueryResult<BaseItem> result)
{ {
var serverItems = result var serverItems = result
@ -660,6 +952,56 @@ namespace Emby.Dlna.ContentDirectory
stubType = StubType.People; stubType = StubType.People;
id = id.Split(new[] { '_' }, 2)[1]; 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)) if (Guid.TryParse(id, out itemId))
{ {
@ -696,6 +1038,16 @@ namespace Emby.Dlna.ContentDirectory
public enum StubType public enum StubType
{ {
Folder = 0, 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
} }
} }

@ -399,6 +399,46 @@ namespace Emby.Dlna.Didl
} }
return _localization.GetLocalizedString("HeaderPeople"); 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 episode = item as Episode;
var season = context as Season; var season = context as Season;

@ -26,32 +26,34 @@ namespace Emby.Dlna.Eventing
_logger = logger; _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); var subscription = GetSubscription(subscriptionId, true);
_logger.Debug("Renewing event subscription for {0} with timeout of {1} to {2}", // Remove logging for now because some devices are sending this very frequently
subscription.NotificationType, // TODO re-enable with dlna debug logging setting
timeout, //_logger.Debug("Renewing event subscription for {0} with timeout of {1} to {2}",
subscription.CallbackUrl); // subscription.NotificationType,
// timeout,
// subscription.CallbackUrl);
subscription.TimeoutSeconds = timeout; subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300;
subscription.SubscriptionTime = DateTime.UtcNow; 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"); var id = "uuid:" + Guid.NewGuid().ToString("N");
_logger.Debug("Creating event subscription for {0} with timeout of {1} to {2}", // Remove logging for now because some devices are sending this very frequently
notificationType, // TODO re-enable with dlna debug logging setting
timeout, //_logger.Debug("Creating event subscription for {0} with timeout of {1} to {2}",
callbackUrl); // notificationType,
// timeout,
// callbackUrl);
_subscriptions.TryAdd(id, new EventSubscription _subscriptions.TryAdd(id, new EventSubscription
{ {
@ -61,7 +63,25 @@ namespace Emby.Dlna.Eventing
TimeoutSeconds = timeout 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) public EventSubscriptionResponse CancelEventSubscription(string subscriptionId)
@ -73,22 +93,22 @@ namespace Emby.Dlna.Eventing
return new EventSubscriptionResponse return new EventSubscriptionResponse
{ {
Content = "\r\n", Content = string.Empty,
ContentType = "text/plain" ContentType = "text/plain"
}; };
} }
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); 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 var response = new EventSubscriptionResponse
{ {
Content = "\r\n", Content = string.Empty,
ContentType = "text/plain" ContentType = "text/plain"
}; };
response.Headers["SID"] = subscriptionId; response.Headers["SID"] = subscriptionId;
response.Headers["TIMEOUT"] = "SECOND-" + timeoutSeconds.ToString(_usCulture); response.Headers["TIMEOUT"] = string.IsNullOrWhiteSpace(requestedTimeoutString) ? ("SECOND-" + timeoutSeconds.ToString(_usCulture)) : requestedTimeoutString;
return response; return response;
} }

@ -56,8 +56,9 @@ namespace Emby.Dlna.PlayTo
if (profile.Container.Length > 0) if (profile.Container.Length > 0)
{ {
// Check container type // Check container type
var mediaContainer = Path.GetExtension(mediaPath); var mediaContainer = (Path.GetExtension(mediaPath) ?? string.Empty).TrimStart('.');
if (!profile.GetContainers().Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
if (!profile.SupportsContainer(mediaContainer))
{ {
return false; return false;
} }

@ -53,14 +53,7 @@ namespace Emby.Dlna.Profiles
{ {
new DirectPlayProfile new DirectPlayProfile
{ {
Container = "ts", Container = "ts,mpegts,avi,mkv",
VideoCodec = "h264",
AudioCodec = "aac,ac3,mp3,dca,dts",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mkv",
VideoCodec = "h264", VideoCodec = "h264",
AudioCodec = "aac,ac3,mp3,dca,dts", AudioCodec = "aac,ac3,mp3,dca,dts",
Type = DlnaProfileType.Video Type = DlnaProfileType.Video

@ -35,8 +35,7 @@
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests> <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<XmlRootAttributes /> <XmlRootAttributes />
<DirectPlayProfiles> <DirectPlayProfiles>
<DirectPlayProfile container="ts" 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="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="mp4,m4v" audioCodec="aac,ac3,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" />
<DirectPlayProfile container="mp3" type="Audio" /> <DirectPlayProfile container="mp3" type="Audio" />
<DirectPlayProfile container="jpeg" type="Photo" /> <DirectPlayProfile container="jpeg" type="Photo" />

@ -24,14 +24,14 @@ namespace Emby.Dlna.Service
return EventManager.CancelEventSubscription(subscriptionId); 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);
} }
} }
} }

@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Channels
return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>()); 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(); throw new NotImplementedException();
} }

@ -17,7 +17,7 @@ using MediaBrowser.Model.Tasks;
namespace Emby.Server.Implementations.Data namespace Emby.Server.Implementations.Data
{ {
public class CleanDatabaseScheduledTask : IScheduledTask public class CleanDatabaseScheduledTask : ILibraryPostScanTask
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo; private readonly IItemRepository _itemRepo;
@ -49,7 +49,7 @@ namespace Emby.Server.Implementations.Data
get { return "Library"; } 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. // Ensure these objects are lazy loaded.
// Without this there is a deadlock that will need to be investigated // Without this there is a deadlock that will need to be investigated

@ -307,7 +307,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL"> <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> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
@ -418,6 +418,9 @@
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Localization\Ratings\uk.txt" /> <EmbeddedResource Include="Localization\Ratings\uk.txt" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Localization\Ratings\es.txt" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.

@ -109,7 +109,7 @@ namespace Emby.Server.Implementations.FFMpeg
} }
if (!string.IsNullOrWhiteSpace(customffProbePath)) if (!string.IsNullOrWhiteSpace(customffProbePath))
{ {
info.EncoderPath = customffProbePath; info.ProbePath = customffProbePath;
} }
return info; return info;

@ -104,6 +104,7 @@ namespace Emby.Server.Implementations.HttpServer
readonly Dictionary<Type, int> _mapExceptionToStatusCode = new Dictionary<Type, int> readonly Dictionary<Type, int> _mapExceptionToStatusCode = new Dictionary<Type, int>
{ {
{typeof (ResourceNotFoundException), 404}, {typeof (ResourceNotFoundException), 404},
{typeof (RemoteServiceUnavailableException), 502},
{typeof (FileNotFoundException), 404}, {typeof (FileNotFoundException), 404},
//{typeof (DirectoryNotFoundException), 404}, //{typeof (DirectoryNotFoundException), 404},
{typeof (SecurityException), 401}, {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) private int GetStatusCode(Exception ex)
{ {
if (ex is ArgumentException) if (ex is ArgumentException)
@ -280,7 +304,7 @@ namespace Emby.Server.Implementations.HttpServer
int statusCode; int statusCode;
if (!_mapExceptionToStatusCode.TryGetValue(exceptionType, out statusCode)) if (!_mapExceptionToStatusCode.TryGetValue(exceptionType, out statusCode))
{ {
if (string.Equals(exceptionType.Name, "DirectoryNotFoundException", StringComparison.OrdinalIgnoreCase)) if (ex is DirectoryNotFoundException)
{ {
statusCode = 404; statusCode = 404;
} }
@ -297,6 +321,8 @@ namespace Emby.Server.Implementations.HttpServer
{ {
try try
{ {
ex = GetActualException(ex);
if (logException) if (logException)
{ {
_logger.ErrorException("Error processing request", ex); _logger.ErrorException("Error processing request", ex);

@ -438,6 +438,15 @@ namespace Emby.Server.Implementations.HttpServer
options.CacheKey = cacheKey.GetMD5(); options.CacheKey = cacheKey.GetMD5();
options.ContentFactory = () => Task.FromResult(GetFileStream(path, fileShare)); 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); return GetStaticResult(requestContext, options);
} }

@ -371,7 +371,7 @@ namespace Emby.Server.Implementations.Library
var tuple = GetProvider(request.OpenToken); var tuple = GetProvider(request.OpenToken);
var provider = tuple.Item1; 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; var mediaSource = mediaSourceTuple.Item1;

@ -16,8 +16,8 @@ namespace Emby.Server.Implementations.LiveTv
private readonly IMediaEncoder _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
private readonly ILogger _logger; private readonly ILogger _logger;
const int ProbeAnalyzeDurationMs = 2000; const int ProbeAnalyzeDurationMs = 3000;
const int PlaybackAnalyzeDurationMs = 2000; const int PlaybackAnalyzeDurationMs = 3000;
public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger) public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger)
{ {

@ -1514,7 +1514,7 @@ namespace Emby.Server.Implementations.LiveTv
} }
private DateTime _lastRecordingRefreshTime; private DateTime _lastRecordingRefreshTime;
private async Task RefreshRecordings(CancellationToken cancellationToken) private async Task RefreshRecordings(Guid internalLiveTvFolderId, CancellationToken cancellationToken)
{ {
const int cacheMinutes = 2; const int cacheMinutes = 2;
@ -1542,10 +1542,8 @@ namespace Emby.Server.Implementations.LiveTv
}); });
var results = await Task.WhenAll(tasks).ConfigureAwait(false); 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); 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) if (user == null)
{ {
@ -1571,21 +1569,31 @@ namespace Emby.Server.Implementations.LiveTv
return new QueryResult<BaseItem>(); return new QueryResult<BaseItem>();
} }
var folders = EmbyTV.EmbyTV.Current.GetRecordingFolders() var folderIds = EmbyTV.EmbyTV.Current.GetRecordingFolders()
.SelectMany(i => i.Locations) .SelectMany(i => i.Locations)
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.Select(i => _libraryManager.FindByPath(i, true)) .Select(i => _libraryManager.FindByPath(i, true))
.Where(i => i != null) .Where(i => i != null)
.Where(i => i.IsVisibleStandalone(user)) .Where(i => i.IsVisibleStandalone(user))
.Select(i => i.Id)
.ToList(); .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>(); return new QueryResult<BaseItem>();
} }
var includeItemTypes = new List<string>(); var includeItemTypes = new List<string>();
var excludeItemTypes = new List<string>();
var genres = new List<string>(); var genres = new List<string>();
if (query.IsMovie.HasValue) if (query.IsMovie.HasValue)
@ -1631,7 +1639,7 @@ namespace Emby.Server.Implementations.LiveTv
{ {
MediaTypes = new[] { MediaType.Video }, MediaTypes = new[] { MediaType.Video },
Recursive = true, Recursive = true,
AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(), AncestorIds = folderIds.Select(i => i.ToString("N")).ToArray(),
IsFolder = false, IsFolder = false,
IsVirtualItem = false, IsVirtualItem = false,
Limit = query.Limit, Limit = query.Limit,
@ -1714,12 +1722,24 @@ namespace Emby.Server.Implementations.LiveTv
return new QueryResult<BaseItem>(); 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) var internalQuery = new InternalItemsQuery(user)
{ {
@ -2620,7 +2640,8 @@ namespace Emby.Server.Implementations.LiveTv
}, new DtoOptions(), cancellationToken).ConfigureAwait(false); }, 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>(); var groups = new List<BaseItemDto>();

@ -118,7 +118,7 @@ namespace Emby.Server.Implementations.LiveTv
return list; 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; MediaSourceInfo stream = null;
const bool isAudio = false; const bool isAudio = false;
@ -140,7 +140,7 @@ namespace Emby.Server.Implementations.LiveTv
try 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); AddMediaInfo(stream, isAudio, cancellationToken);
} }

@ -10,9 +10,11 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
namespace Emby.Server.Implementations.LiveTv.TunerHosts namespace Emby.Server.Implementations.LiveTv.TunerHosts
@ -23,16 +25,18 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
protected readonly ILogger Logger; protected readonly ILogger Logger;
protected IJsonSerializer JsonSerializer; protected IJsonSerializer JsonSerializer;
protected readonly IMediaEncoder MediaEncoder; protected readonly IMediaEncoder MediaEncoder;
protected readonly IFileSystem FileSystem;
private readonly ConcurrentDictionary<string, ChannelCache> _channelCache = private readonly ConcurrentDictionary<string, ChannelCache> _channelCache =
new ConcurrentDictionary<string, ChannelCache>(StringComparer.OrdinalIgnoreCase); 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; Config = config;
Logger = logger; Logger = logger;
JsonSerializer = jsonSerializer; JsonSerializer = jsonSerializer;
MediaEncoder = mediaEncoder; MediaEncoder = mediaEncoder;
FileSystem = fileSystem;
} }
protected abstract Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken); 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) foreach (var host in hosts)
{ {
var channelCacheFile = Path.Combine(Config.ApplicationPaths.CachePath, host.Id + "_channels");
try try
{ {
var channels = await GetChannels(host, enableCache, cancellationToken).ConfigureAwait(false); 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(); var newChannels = channels.Where(i => !list.Any(l => string.Equals(i.Id, l.Id, StringComparison.OrdinalIgnoreCase))).ToList();
list.AddRange(newChannels); list.AddRange(newChannels);
if (!enableCache)
{
try
{
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(channelCacheFile));
JsonSerializer.SerializeToFile(channels, channelCacheFile);
}
catch (IOException)
{
}
}
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.ErrorException("Error getting channel list", ex); Logger.ErrorException("Error getting channel list", ex);
if (enableCache)
{
try
{
var channels = JsonSerializer.DeserializeFromFile<List<ChannelInfo>>(channelCacheFile);
list.AddRange(channels);
}
catch (IOException)
{
}
}
} }
} }

@ -27,17 +27,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public class HdHomerunHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost public class HdHomerunHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost
{ {
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly ISocketFactory _socketFactory; private readonly ISocketFactory _socketFactory;
private readonly INetworkManager _networkManager; private readonly INetworkManager _networkManager;
private readonly IEnvironmentInfo _environment; 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) 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)
: base(config, logger, jsonSerializer, mediaEncoder)
{ {
_httpClient = httpClient; _httpClient = httpClient;
_fileSystem = fileSystem;
_appHost = appHost; _appHost = appHost;
_socketFactory = socketFactory; _socketFactory = socketFactory;
_networkManager = networkManager; _networkManager = networkManager;
@ -509,7 +506,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
if (hdhomerunChannel != null && hdhomerunChannel.IsLegacyTuner) 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 // 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; 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) public async Task Validate(TunerHostInfo info)

@ -25,15 +25,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{ {
public class M3UTunerHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost public class M3UTunerHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost
{ {
private readonly IFileSystem _fileSystem;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly IEnvironmentInfo _environment; private readonly IEnvironmentInfo _environment;
public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, 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, fileSystem)
: base(config, logger, jsonSerializer, mediaEncoder)
{ {
_fileSystem = fileSystem;
_httpClient = httpClient; _httpClient = httpClient;
_appHost = appHost; _appHost = appHost;
_environment = environment; _environment = environment;
@ -51,7 +48,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
protected override async Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken) 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(); 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 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; return liveStream;
} }
public async Task Validate(TunerHostInfo info) 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"), Id = channel.Path.GetMD5().ToString("N"),
IsInfiniteStream = true, IsInfiniteStream = true,
IsRemote = true IsRemote = true,
IgnoreDts = true
}; };
mediaSource.InferTotalBitrate(); mediaSource.InferTotalBitrate();

@ -0,0 +1,5 @@
ES-A,1
ES-7,3
ES-12,6
ES-16,8
ES-18,11

@ -1,10 +1,11 @@
NZ-G,1 NZ-G,1
NZ-PG,5 NZ-PG,5
NZ-M,9 NZ-M,6
NZ-R13,7 NZ-R13,7
NZ-RP13,7
NZ-R15,8 NZ-R15,8
NZ-RP16,9
NZ-R16,9 NZ-R16,9
NZ-R18,10 NZ-R18,10
NZ-RP13,7 NZ-R,10
NZ-RP16,9 NZ-MA,10
NZ-R,10

@ -68,23 +68,12 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <returns>Task.</returns> /// <returns>Task.</returns>
public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress) 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 // Create a progress object for the update check
var innerProgress = new SimpleProgress<double>(); var updateInfo = await _appHost.CheckForApplicationUpdate(cancellationToken, new SimpleProgress<double>()).ConfigureAwait(false);
innerProgress.ProgressChanged += innerProgressHandler;
var updateInfo = await _appHost.CheckForApplicationUpdate(cancellationToken, innerProgress).ConfigureAwait(false);
// Release the event handler
innerProgress.ProgressChanged -= innerProgressHandler;
progress.Report(10);
if (!updateInfo.IsUpdateAvailable) if (!updateInfo.IsUpdateAvailable)
{ {
Logger.Debug("No application update available."); Logger.Debug("No application update available.");
progress.Report(100);
return; return;
} }
@ -96,22 +85,12 @@ namespace Emby.Server.Implementations.ScheduledTasks
{ {
Logger.Info("Update Revision {0} available. Updating...", updateInfo.AvailableVersion); Logger.Info("Update Revision {0} available. Updating...", updateInfo.AvailableVersion);
innerProgressHandler = (sender, e) => progress.Report(e * .9 + .1); await _appHost.UpdateApplication(updateInfo.Package, cancellationToken, progress).ConfigureAwait(false);
innerProgress = new SimpleProgress<double>();
innerProgress.ProgressChanged += innerProgressHandler;
await _appHost.UpdateApplication(updateInfo.Package, cancellationToken, innerProgress).ConfigureAwait(false);
// Release the event handler
innerProgress.ProgressChanged -= innerProgressHandler;
} }
else else
{ {
Logger.Info("A new version of " + _appHost.Name + " is available."); Logger.Info("A new version of " + _appHost.Name + " is available.");
} }
progress.Report(100);
} }
/// <summary> /// <summary>

@ -513,8 +513,6 @@ namespace Emby.Server.Implementations.Updates
CurrentInstallations.Remove(tuple); CurrentInstallations.Remove(tuple);
} }
progress.Report(100);
CompletedInstallationsInternal.Add(installationInfo); CompletedInstallationsInternal.Add(installationInfo);
EventHelper.FireEventIfNotNull(PackageInstallationCompleted, this, installationEventArgs, _logger); EventHelper.FireEventIfNotNull(PackageInstallationCompleted, this, installationEventArgs, _logger);

@ -3,5 +3,5 @@
<package id="Emby.XmlTv" version="1.0.9" targetFramework="net46" /> <package id="Emby.XmlTv" version="1.0.9" targetFramework="net46" />
<package id="MediaBrowser.Naming" version="1.0.5" targetFramework="portable45-net45+win8" /> <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="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> </packages>

@ -219,20 +219,20 @@ namespace MediaBrowser.Api.Dlna
private object ProcessEventRequest(IEventManager eventManager) private object ProcessEventRequest(IEventManager eventManager)
{ {
var subscriptionId = GetHeader("SID"); 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)) if (string.Equals(Request.Verb, "SUBSCRIBE", StringComparison.OrdinalIgnoreCase))
{ {
var notificationType = GetHeader("NT");
var callback = GetHeader("CALLBACK");
var timeoutString = GetHeader("TIMEOUT");
if (string.IsNullOrEmpty(notificationType)) 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)); return GetSubscriptionResponse(eventManager.CancelEventSubscription(subscriptionId));
@ -242,24 +242,5 @@ namespace MediaBrowser.Api.Dlna
{ {
return ResultFactory.GetResult(response.Content, response.ContentType, response.Headers); 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;
}
} }
} }

@ -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")] [Route("/Environment/NetworkShares", "GET", Summary = "Gets shares from a network device")]
public class GetNetworkShares : IReturn<List<FileSystemEntryInfo>> public class GetNetworkShares : IReturn<List<FileSystemEntryInfo>>
{ {
@ -112,7 +126,7 @@ namespace MediaBrowser.Api
/// The _network manager /// The _network manager
/// </summary> /// </summary>
private readonly INetworkManager _networkManager; private readonly INetworkManager _networkManager;
private IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="EnvironmentService" /> class. /// Initializes a new instance of the <see cref="EnvironmentService" /> class.
@ -129,6 +143,48 @@ namespace MediaBrowser.Api
_fileSystem = fileSystem; _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) public object Get(GetDefaultDirectoryBrowser request)
{ {
var result = new DefaultDirectoryBrowserInfo(); var result = new DefaultDirectoryBrowserInfo();

@ -17,6 +17,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.IO; using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
@ -567,7 +568,9 @@ namespace MediaBrowser.Api.Images
}).ToList() : new List<IImageEnhancer>(); }).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) if (request.CropWhitespace.HasValue)
{ {

@ -512,10 +512,6 @@ namespace MediaBrowser.Api.Library
var headers = new Dictionary<string, string>(); 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) if (user != null)
{ {
LogDownload(item, user, auth); LogDownload(item, user, auth);

@ -22,6 +22,7 @@ using System.Threading.Tasks;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.Diagnostics;
namespace MediaBrowser.Api.Playback namespace MediaBrowser.Api.Playback
@ -100,11 +101,7 @@ namespace MediaBrowser.Api.Playback
/// <summary> /// <summary>
/// Gets the command line arguments. /// Gets the command line arguments.
/// </summary> /// </summary>
/// <param name="outputPath">The output path.</param> protected abstract string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding);
/// <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);
/// <summary> /// <summary>
/// Gets the type of the transcoding job. /// Gets the type of the transcoding job.
@ -125,11 +122,11 @@ namespace MediaBrowser.Api.Playback
/// <summary> /// <summary>
/// Gets the output file path. /// Gets the output file path.
/// </summary> /// </summary>
private string GetOutputFilePath(StreamState state, string outputFileExtension) private string GetOutputFilePath(StreamState state, EncodingOptions encodingOptions, string outputFileExtension)
{ {
var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath; 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.DeviceId ?? string.Empty);
data += "-" + (state.Request.PlaySessionId ?? 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 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 var process = ApiEntryPoint.Instance.ProcessFactory.Create(new ProcessOptions
{ {
@ -826,7 +825,10 @@ namespace MediaBrowser.Api.Playback
var ext = string.IsNullOrWhiteSpace(state.OutputContainer) var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
? GetOutputFileExtension(state) ? GetOutputFileExtension(state)
: ("." + state.OutputContainer); : ("." + state.OutputContainer);
state.OutputFilePath = GetOutputFilePath(state, ext);
var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
state.OutputFilePath = GetOutputFilePath(state, encodingOptions, ext);
return state; return state;
} }

@ -14,6 +14,7 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Api.Playback.Hls namespace MediaBrowser.Api.Playback.Hls
{ {
@ -25,15 +26,12 @@ namespace MediaBrowser.Api.Playback.Hls
/// <summary> /// <summary>
/// Gets the audio arguments. /// Gets the audio arguments.
/// </summary> /// </summary>
/// <param name="state">The state.</param> protected abstract string GetAudioArguments(StreamState state, EncodingOptions encodingOptions);
/// <returns>System.String.</returns>
protected abstract string GetAudioArguments(StreamState state);
/// <summary> /// <summary>
/// Gets the video arguments. /// Gets the video arguments.
/// </summary> /// </summary>
/// <param name="state">The state.</param> protected abstract string GetVideoArguments(StreamState state, EncodingOptions encodingOptions);
/// <returns>System.String.</returns>
protected abstract string GetVideoArguments(StreamState state);
/// <summary> /// <summary>
/// Gets the segment file extension. /// 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 itsOffsetMs = 0;
var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture)); 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), EncodingHelper.GetInputArgument(state, encodingOptions),
threads, threads,
EncodingHelper.GetMapArgs(state), EncodingHelper.GetMapArgs(state),
GetVideoArguments(state), GetVideoArguments(state, encodingOptions),
GetAudioArguments(state), GetAudioArguments(state, encodingOptions),
state.SegmentLength.ToString(UsCulture), state.SegmentLength.ToString(UsCulture),
startNumberParam, startNumberParam,
outputPath, outputPath,
@ -306,8 +302,8 @@ namespace MediaBrowser.Api.Playback.Hls
EncodingHelper.GetInputArgument(state, encodingOptions), EncodingHelper.GetInputArgument(state, encodingOptions),
threads, threads,
EncodingHelper.GetMapArgs(state), EncodingHelper.GetMapArgs(state),
GetVideoArguments(state), GetVideoArguments(state, encodingOptions),
GetAudioArguments(state), GetAudioArguments(state, encodingOptions),
state.SegmentLength.ToString(UsCulture), state.SegmentLength.ToString(UsCulture),
startNumberParam, startNumberParam,
state.HlsListSize.ToString(UsCulture), state.HlsListSize.ToString(UsCulture),

@ -18,6 +18,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
using MimeTypes = MediaBrowser.Model.Net.MimeTypes; 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>()); 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 (!state.IsOutputVideo)
{ {
if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase)) if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase))
{ {
return "-acodec copy"; return "-acodec copy";
} }
var audioTranscodeParams = new List<string>(); var audioTranscodeParams = new List<string>();
audioTranscodeParams.Add("-acodec " + codec); audioTranscodeParams.Add("-acodec " + audioCodec);
if (state.OutputAudioBitrate.HasValue) if (state.OutputAudioBitrate.HasValue)
{ {
@ -811,12 +812,19 @@ namespace MediaBrowser.Api.Playback.Hls
return string.Join(" ", audioTranscodeParams.ToArray()); 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; var channels = state.OutputAudioChannels;
@ -837,19 +845,19 @@ namespace MediaBrowser.Api.Playback.Hls
args += " -ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture); args += " -ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture);
} }
args += " " + EncodingHelper.GetAudioFilterParam(state, ApiEntryPoint.Instance.GetEncodingOptions(), true); args += " " + EncodingHelper.GetAudioFilterParam(state, encodingOptions, true);
return args; return args;
} }
protected override string GetVideoArguments(StreamState state) protected override string GetVideoArguments(StreamState state, EncodingOptions encodingOptions)
{ {
if (!state.IsOutputVideo) if (!state.IsOutputVideo)
{ {
return string.Empty; return string.Empty;
} }
var codec = EncodingHelper.GetVideoEncoder(state, ApiEntryPoint.Instance.GetEncodingOptions()); var codec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
var args = "-codec:v:0 " + codec; 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 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 += " " + EncodingHelper.GetVideoQualityParam(state, codec, encodingOptions, GetDefaultH264Preset()) + keyFrameArg;
//args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; //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; 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 threads = EncodingHelper.GetNumberOfThreads(state, encodingOptions, false);
var inputModifier = EncodingHelper.GetInputModifier(state, encodingOptions); var inputModifier = EncodingHelper.GetInputModifier(state, encodingOptions);
@ -940,7 +945,7 @@ namespace MediaBrowser.Api.Playback.Hls
segmentFormat = "mpegts"; segmentFormat = "mpegts";
} }
var videoCodec = EncodingHelper.GetVideoEncoder(state, ApiEntryPoint.Instance.GetEncodingOptions()); var videoCodec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
var breakOnNonKeyFrames = state.EnableBreakOnNonKeyFrames(videoCodec); var breakOnNonKeyFrames = state.EnableBreakOnNonKeyFrames(videoCodec);
var breakOnNonKeyFramesArg = breakOnNonKeyFrames ? " -break_non_keyframes 1" : ""; var breakOnNonKeyFramesArg = breakOnNonKeyFrames ? " -break_non_keyframes 1" : "";
@ -950,8 +955,8 @@ namespace MediaBrowser.Api.Playback.Hls
EncodingHelper.GetInputArgument(state, encodingOptions), EncodingHelper.GetInputArgument(state, encodingOptions),
threads, threads,
mapArgs, mapArgs,
GetVideoArguments(state), GetVideoArguments(state, encodingOptions),
GetAudioArguments(state), GetAudioArguments(state, encodingOptions),
state.SegmentLength.ToString(UsCulture), state.SegmentLength.ToString(UsCulture),
startNumberParam, startNumberParam,
outputPath, outputPath,

@ -7,6 +7,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using System; using System;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
@ -31,9 +32,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// <summary> /// <summary>
/// Gets the audio arguments. /// Gets the audio arguments.
/// </summary> /// </summary>
/// <param name="state">The state.</param> protected override string GetAudioArguments(StreamState state, EncodingOptions encodingOptions)
/// <returns>System.String.</returns>
protected override string GetAudioArguments(StreamState state)
{ {
var codec = EncodingHelper.GetAudioEncoder(state); var codec = EncodingHelper.GetAudioEncoder(state);
@ -63,7 +62,7 @@ namespace MediaBrowser.Api.Playback.Hls
args += " -ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture); args += " -ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture);
} }
args += " " + EncodingHelper.GetAudioFilterParam(state, ApiEntryPoint.Instance.GetEncodingOptions(), true); args += " " + EncodingHelper.GetAudioFilterParam(state, encodingOptions, true);
return args; return args;
} }
@ -71,11 +70,14 @@ namespace MediaBrowser.Api.Playback.Hls
/// <summary> /// <summary>
/// Gets the video arguments. /// Gets the video arguments.
/// </summary> /// </summary>
/// <param name="state">The state.</param> protected override string GetVideoArguments(StreamState state, EncodingOptions encodingOptions)
/// <returns>System.String.</returns>
protected override string GetVideoArguments(StreamState state)
{ {
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; 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 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 += " " + EncodingHelper.GetVideoQualityParam(state, codec, encodingOptions, GetDefaultH264Preset()) + keyFrameArg;
// Add resolution params, if specified // Add resolution params, if specified

@ -134,7 +134,7 @@ namespace MediaBrowser.Api.Playback
SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate, SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate,
request.StartTimeTicks ?? 0, result.MediaSource.Id, request.AudioStreamIndex, 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 else
{ {
@ -176,7 +176,7 @@ namespace MediaBrowser.Api.Playback
{ {
var mediaSourceId = request.MediaSourceId; 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) if (request.AutoOpenLiveStream)
@ -191,7 +191,6 @@ namespace MediaBrowser.Api.Playback
DeviceProfile = request.DeviceProfile, DeviceProfile = request.DeviceProfile,
EnableDirectPlay = request.EnableDirectPlay, EnableDirectPlay = request.EnableDirectPlay,
EnableDirectStream = request.EnableDirectStream, EnableDirectStream = request.EnableDirectStream,
ForceDirectPlayRemoteMediaSource = request.ForceDirectPlayRemoteMediaSource,
ItemId = request.Id, ItemId = request.Id,
MaxAudioChannels = request.MaxAudioChannels, MaxAudioChannels = request.MaxAudioChannels,
MaxStreamingBitrate = request.MaxStreamingBitrate, MaxStreamingBitrate = request.MaxStreamingBitrate,
@ -199,7 +198,8 @@ namespace MediaBrowser.Api.Playback
StartTimeTicks = request.StartTimeTicks, StartTimeTicks = request.StartTimeTicks,
SubtitleStreamIndex = request.SubtitleStreamIndex, SubtitleStreamIndex = request.SubtitleStreamIndex,
UserId = request.UserId, UserId = request.UserId,
OpenToken = mediaSource.OpenToken OpenToken = mediaSource.OpenToken,
EnableMediaProbe = request.EnableMediaProbe
}).ConfigureAwait(false); }).ConfigureAwait(false);

@ -10,6 +10,7 @@ using MediaBrowser.Model.Serialization;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
@ -58,10 +59,8 @@ namespace MediaBrowser.Api.Playback.Progressive
return ProcessRequest(request, true); 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); return EncodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath);
} }
} }

@ -8,6 +8,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
@ -91,10 +92,8 @@ namespace MediaBrowser.Api.Playback.Progressive
return ProcessRequest(request, true); 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()); return EncodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, outputPath, GetDefaultH264Preset());
} }
} }

@ -80,7 +80,7 @@ namespace MediaBrowser.Api.Playback
return 6; return 6;
} }
return 10; return 6;
} }
if (IsSegmentedLiveStream) if (IsSegmentedLiveStream)

@ -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")] [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; } 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; } 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)] [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)]

@ -12,7 +12,7 @@ namespace MediaBrowser.Common.Extensions
/// </summary> /// </summary>
public ResourceNotFoundException() public ResourceNotFoundException()
{ {
} }
/// <summary> /// <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 public class RateLimitExceededException : Exception
{ {
/// <summary> /// <summary>

@ -67,7 +67,7 @@ namespace MediaBrowser.Controller.Channels
Name = id, Name = id,
Id = id, Id = id,
ReadAtNativeFramerate = ReadAtNativeFramerate, ReadAtNativeFramerate = ReadAtNativeFramerate,
SupportsDirectStream = false, SupportsDirectStream = Protocol == MediaProtocol.Http && !string.IsNullOrWhiteSpace(Container) && !string.Equals(Container, "hls", StringComparison.OrdinalIgnoreCase),
SupportsDirectPlay = SupportsDirectPlay, SupportsDirectPlay = SupportsDirectPlay,
IsRemote = true IsRemote = true
}; };

@ -12,18 +12,11 @@ namespace MediaBrowser.Controller.Dlna
/// <summary> /// <summary>
/// Renews the event subscription. /// Renews the event subscription.
/// </summary> /// </summary>
/// <param name="subscriptionId">The subscription identifier.</param> EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string requestedTimeoutString);
/// <param name="timeoutSeconds">The timeout seconds.</param>
/// <returns>EventSubscriptionResponse.</returns>
EventSubscriptionResponse RenewEventSubscription(string subscriptionId, int? timeoutSeconds);
/// <summary> /// <summary>
/// Creates the event subscription. /// Creates the event subscription.
/// </summary> /// </summary>
/// <param name="notificationType">Type of the notification.</param> EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl);
/// <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);
} }
} }

@ -57,6 +57,22 @@ namespace MediaBrowser.Controller.Entities
{ {
get 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; return true;
} }
} }

@ -20,10 +20,7 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Opens the media source. /// Opens the media source.
/// </summary> /// </summary>
/// <param name="openToken">The open token.</param> Task<Tuple<MediaSourceInfo,IDirectStreamProvider>> OpenMediaSource(string openToken, bool allowLiveStreamProbe, CancellationToken cancellationToken);
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
Task<Tuple<MediaSourceInfo,IDirectStreamProvider>> OpenMediaSource(string openToken, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Closes the media source. /// Closes the media source.

@ -120,10 +120,14 @@ namespace MediaBrowser.Controller.Providers
{ {
file = _fileSystem.GetFileInfo(path); file = _fileSystem.GetFileInfo(path);
if (file != null) if (file != null && file.Exists)
{ {
_fileCache.TryAdd(path, file); _fileCache.TryAdd(path, file);
} }
else
{
return null;
}
} }
return file; return file;

@ -203,6 +203,11 @@ namespace MediaBrowser.Controller.Session
public void StartAutomaticProgress(ITimerFactory timerFactory, PlaybackProgressInfo progressInfo) public void StartAutomaticProgress(ITimerFactory timerFactory, PlaybackProgressInfo progressInfo)
{ {
if (_disposed)
{
return;
}
lock (_progressLock) lock (_progressLock)
{ {
_lastProgressInfo = progressInfo; _lastProgressInfo = progressInfo;
@ -223,6 +228,11 @@ namespace MediaBrowser.Controller.Session
private async void OnProgressTimerCallback(object state) private async void OnProgressTimerCallback(object state)
{ {
if (_disposed)
{
return;
}
var progressInfo = _lastProgressInfo; var progressInfo = _lastProgressInfo;
if (progressInfo == null) if (progressInfo == null)
{ {
@ -274,8 +284,12 @@ namespace MediaBrowser.Controller.Session
} }
} }
private bool _disposed = false;
public void Dispose() public void Dispose()
{ {
_disposed = true;
StopAutomaticProgress(); StopAutomaticProgress();
_sessionManager = null; _sessionManager = null;
} }

@ -90,6 +90,8 @@ namespace MediaBrowser.Controller.Sync
/// </summary> /// </summary>
IEnumerable<SyncTarget> GetSyncTargets(string userId); IEnumerable<SyncTarget> GetSyncTargets(string userId);
IEnumerable<SyncTarget> GetSyncTargets(string userId, bool? supportsRemoteSync);
/// <summary> /// <summary>
/// Supportses the synchronize. /// Supportses the synchronize.
/// </summary> /// </summary>

@ -11,6 +11,8 @@ namespace MediaBrowser.Controller.Sync
/// <value>The name.</value> /// <value>The name.</value>
string Name { get; } string Name { get; }
bool SupportsRemoteSync { get; }
/// <summary> /// <summary>
/// Gets the synchronize targets. /// Gets the synchronize targets.
/// </summary> /// </summary>
@ -27,6 +29,6 @@ namespace MediaBrowser.Controller.Sync
public interface IHasUniqueTargetIds public interface IHasUniqueTargetIds
{ {
} }
} }

@ -733,9 +733,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
var mapArg = imageStreamIndex.HasValue ? (" -map 0:v:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty; 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); 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; 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) : 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); string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg);

@ -1,6 +1,7 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Serialization; using System.Xml.Serialization;
using MediaBrowser.Model.Dlna;
namespace MediaBrowser.Model.Dlna namespace MediaBrowser.Model.Dlna
{ {
@ -28,6 +29,19 @@ namespace MediaBrowser.Model.Dlna
return list; 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() public List<string> GetAudioCodecs()
{ {
List<string> list = new List<string>(); List<string> list = new List<string>();

@ -492,52 +492,45 @@ namespace MediaBrowser.Model.Dlna
foreach (var profile in directPlayProfiles) foreach (var profile in directPlayProfiles)
{ {
if (profile.Container.Length > 0) // Check container type
if (profile.SupportsContainer(item.Container))
{ {
// Check container type containerSupported = true;
string mediaContainer = item.Container ?? string.Empty;
foreach (string i in profile.GetContainers()) if (videoStream != null)
{ {
if (StringHelper.EqualsIgnoreCase(i, mediaContainer)) // Check video codec
List<string> videoCodecs = profile.GetVideoCodecs();
if (videoCodecs.Count > 0)
{ {
containerSupported = true; string videoCodec = videoStream.Codec;
if (!string.IsNullOrEmpty(videoCodec) && ListHelper.ContainsIgnoreCase(videoCodecs, videoCodec))
if (videoStream != null)
{ {
// Check video codec videoSupported = true;
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;
}
} }
}
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 audioSupported = true;
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;
}
} }
} }
else
{
audioSupported = true;
}
} }
} }
} }
@ -635,8 +628,10 @@ namespace MediaBrowser.Model.Dlna
MediaStream videoStream = item.VideoStream; 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 // 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)); var directPlayEligibilityResult = 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 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}", _logger.Info("Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}",
options.Profile.Name ?? "Unknown Profile", options.Profile.Name ?? "Unknown Profile",
@ -669,6 +664,16 @@ namespace MediaBrowser.Model.Dlna
transcodeReasons.AddRange(directPlayInfo.Item2); 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 // Can't direct play, find the transcoding profile
TranscodingProfile transcodingProfile = null; TranscodingProfile transcodingProfile = null;
foreach (TranscodingProfile i in options.Profile.TranscodingProfiles) foreach (TranscodingProfile i in options.Profile.TranscodingProfiles)
@ -1136,7 +1141,7 @@ namespace MediaBrowser.Model.Dlna
mediaSource.Path ?? "Unknown path"); mediaSource.Path ?? "Unknown path");
} }
private bool IsEligibleForDirectPlay(MediaSourceInfo item, private Tuple<bool, TranscodeReason?> IsEligibleForDirectPlay(MediaSourceInfo item,
long? maxBitrate, long? maxBitrate,
MediaStream subtitleStream, MediaStream subtitleStream,
VideoOptions options, VideoOptions options,
@ -1149,11 +1154,18 @@ namespace MediaBrowser.Model.Dlna
if (subtitleProfile.Method != SubtitleDeliveryMethod.External && subtitleProfile.Method != SubtitleDeliveryMethod.Embed) if (subtitleProfile.Method != SubtitleDeliveryMethod.External && subtitleProfile.Method != SubtitleDeliveryMethod.Embed)
{ {
_logger.Info("Not eligible for {0} due to unsupported subtitles", playMethod); _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) 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) 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 return false;
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;
}
} }
// Check audio codec // Check audio codec
@ -1555,23 +1554,10 @@ namespace MediaBrowser.Model.Dlna
private bool IsVideoDirectPlaySupported(DirectPlayProfile profile, MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream) 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 return false;
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;
}
} }
// Check video codec // Check video codec

@ -17,13 +17,13 @@ namespace MediaBrowser.Model.MediaInfo
public bool EnableDirectPlay { get; set; } public bool EnableDirectPlay { get; set; }
public bool EnableDirectStream { get; set; } public bool EnableDirectStream { get; set; }
public bool ForceDirectPlayRemoteMediaSource { get; set; } public bool EnableMediaProbe { get; set; }
public LiveStreamRequest() public LiveStreamRequest()
{ {
ForceDirectPlayRemoteMediaSource = true;
EnableDirectPlay = true; EnableDirectPlay = true;
EnableDirectStream = true; EnableDirectStream = true;
EnableMediaProbe = true;
} }
public LiveStreamRequest(AudioOptions options) public LiveStreamRequest(AudioOptions options)

@ -27,19 +27,19 @@ namespace MediaBrowser.Model.MediaInfo
public bool EnableDirectPlay { get; set; } public bool EnableDirectPlay { get; set; }
public bool EnableDirectStream { get; set; } public bool EnableDirectStream { get; set; }
public bool EnableTranscoding { get; set; } public bool EnableTranscoding { get; set; }
public bool ForceDirectPlayRemoteMediaSource { get; set; }
public bool AllowVideoStreamCopy { get; set; } public bool AllowVideoStreamCopy { get; set; }
public bool AllowAudioStreamCopy { get; set; } public bool AllowAudioStreamCopy { get; set; }
public bool AutoOpenLiveStream { get; set; } public bool AutoOpenLiveStream { get; set; }
public bool EnableMediaProbe { get; set; }
public PlaybackInfoRequest() public PlaybackInfoRequest()
{ {
ForceDirectPlayRemoteMediaSource = true;
EnableDirectPlay = true; EnableDirectPlay = true;
EnableDirectStream = true; EnableDirectStream = true;
EnableTranscoding = true; EnableTranscoding = true;
AllowVideoStreamCopy = true; AllowVideoStreamCopy = true;
AllowAudioStreamCopy = true; AllowAudioStreamCopy = true;
EnableMediaProbe = true;
} }
} }
} }

@ -48,6 +48,7 @@ namespace MediaBrowser.Model.Session
VideoFramerateNotSupported = 17, VideoFramerateNotSupported = 17,
VideoLevelNotSupported = 18, VideoLevelNotSupported = 18,
VideoProfileNotSupported = 19, VideoProfileNotSupported = 19,
AudioBitDepthNotSupported = 20 AudioBitDepthNotSupported = 20,
SubtitleCodecNotSupported = 21
} }
} }

@ -59,7 +59,7 @@ namespace MediaBrowser.Model.Sync
/// Gets or sets the status. /// Gets or sets the status.
/// </summary> /// </summary>
/// <value>The status.</value> /// <value>The status.</value>
public SyncJobStatus Status { get; set; } public SyncJobStatus Status { get; set; }
/// <summary> /// <summary>
/// Gets or sets the user identifier. /// Gets or sets the user identifier.
/// </summary> /// </summary>
@ -105,9 +105,12 @@ namespace MediaBrowser.Model.Sync
public string PrimaryImageItemId { get; set; } public string PrimaryImageItemId { get; set; }
public string PrimaryImageTag { get; set; } public string PrimaryImageTag { get; set; }
public bool EnableAutomaticResync { get; set; }
public SyncJob() public SyncJob()
{ {
RequestedItemIds = new List<string>(); RequestedItemIds = new List<string>();
EnableAutomaticResync = true;
} }
} }
} }

@ -21,7 +21,7 @@ namespace MediaBrowser.Providers.Music
if (isFullRefresh || currentUpdateType > ItemUpdateType.None) if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
{ {
if (!item.IsLocked) if (!item.IsLocked && !item.LockedFields.Contains(MetadataFields.Genres))
{ {
var taggedItems = item.IsAccessedByName ? var taggedItems = item.IsAccessedByName ?
item.GetTaggedItems(new Controller.Entities.InternalItemsQuery() item.GetTaggedItems(new Controller.Entities.InternalItemsQuery()
@ -31,22 +31,19 @@ namespace MediaBrowser.Providers.Music
}) : }) :
item.GetRecursiveChildren(i => i is IHasArtist && !i.IsFolder).ToList(); 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) item.Genres = taggedItems.SelectMany(i => i.Genres)
.DistinctNames() .DistinctNames()
.ToList(); .ToList();
if (currentList.Count != item.Genres.Count || !currentList.OrderBy(i => i).SequenceEqual(item.Genres.OrderBy(i => i), StringComparer.OrdinalIgnoreCase)) if (currentList.Count != item.Genres.Count || !currentList.OrderBy(i => i).SequenceEqual(item.Genres.OrderBy(i => i), StringComparer.OrdinalIgnoreCase))
{ {
updateType = updateType | ItemUpdateType.MetadataEdit; updateType = updateType | ItemUpdateType.MetadataEdit;
}
} }
} }
} }
return updateType; return updateType;
} }

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration; using System;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
@ -6,7 +7,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Manager;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Controller.IO; using MediaBrowser.Controller.IO;
using MediaBrowser.Model.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) public PlaylistMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{ {
} }

@ -196,8 +196,6 @@ namespace MediaBrowser.Providers.TV
} }
catch (HttpException ex) catch (HttpException ex)
{ {
Logger.Error("No metadata found for {0}", seasonNumber.Value);
if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
{ {
return result; return result;

@ -116,10 +116,10 @@
<HintPath>..\packages\SkiaSharp.1.58.0\lib\net45\SkiaSharp.dll</HintPath> <HintPath>..\packages\SkiaSharp.1.58.0\lib\net45\SkiaSharp.dll</HintPath>
</Reference> </Reference>
<Reference Include="SQLitePCLRaw.core"> <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>
<Reference Include="SQLitePCLRaw.provider.sqlite3"> <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>
<Reference Include="Emby.Server.Connect"> <Reference Include="Emby.Server.Connect">
<HintPath>..\ThirdParty\emby\Emby.Server.Connect.dll</HintPath> <HintPath>..\ThirdParty\emby\Emby.Server.Connect.dll</HintPath>
@ -411,6 +411,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\mypreferencesmenu.html"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\mypreferencesmenu.html">
<Link>Resources\dashboard-ui\mypreferencesmenu.html</Link> <Link>Resources\dashboard-ui\mypreferencesmenu.html</Link>
</BundleResource> </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"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\myprofile.html">
<Link>Resources\dashboard-ui\myprofile.html</Link> <Link>Resources\dashboard-ui\myprofile.html</Link>
</BundleResource> </BundleResource>
@ -918,6 +921,12 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\emby-radio\emby-radio.js"> <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> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\emby-radio\emby-radio.js</Link>
</BundleResource> </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"> <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> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\emby-scroller\emby-scroller.js</Link>
</BundleResource> </BundleResource>
@ -1476,6 +1485,15 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\subtitleeditor\subtitleeditor.template.html"> <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> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\subtitleeditor\subtitleeditor.template.html</Link>
</BundleResource> </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"> <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> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\sync\emby-downloadbutton.js</Link>
</BundleResource> </BundleResource>
@ -1488,15 +1506,18 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\sync\syncjoblist.js"> <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> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\sync\syncjoblist.js</Link>
</BundleResource> </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"> <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> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\toast\toast.css</Link>
</BundleResource> </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\toast\toast.js"> <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> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\toast\toast.js</Link>
</BundleResource> </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"> <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> <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\userdatabuttons\emby-playstatebutton.js</Link>
</BundleResource> </BundleResource>
@ -1788,9 +1809,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\librarybrowser.css"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\librarybrowser.css">
<Link>Resources\dashboard-ui\css\librarybrowser.css</Link> <Link>Resources\dashboard-ui\css\librarybrowser.css</Link>
</BundleResource> </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"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\livetv.css">
<Link>Resources\dashboard-ui\css\livetv.css</Link> <Link>Resources\dashboard-ui\css\livetv.css</Link>
</BundleResource> </BundleResource>
@ -2082,84 +2100,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\devices\ios\ios.css"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\devices\ios\ios.css">
<Link>Resources\dashboard-ui\devices\ios\ios.css</Link> <Link>Resources\dashboard-ui\devices\ios\ios.css</Link>
</BundleResource> </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"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\legacy\buttonenabled.js">
<Link>Resources\dashboard-ui\legacy\buttonenabled.js</Link> <Link>Resources\dashboard-ui\legacy\buttonenabled.js</Link>
</BundleResource> </BundleResource>
@ -2358,6 +2298,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mypreferenceslanguages.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mypreferenceslanguages.js">
<Link>Resources\dashboard-ui\scripts\mypreferenceslanguages.js</Link> <Link>Resources\dashboard-ui\scripts\mypreferenceslanguages.js</Link>
</BundleResource> </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"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\myprofile.js">
<Link>Resources\dashboard-ui\scripts\myprofile.js</Link> <Link>Resources\dashboard-ui\scripts\myprofile.js</Link>
</BundleResource> </BundleResource>
@ -2658,9 +2601,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\themes\holiday\theme.js"> <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\themes\holiday\theme.js">
<Link>Resources\dashboard-ui\themes\holiday\theme.js</Link> <Link>Resources\dashboard-ui\themes\holiday\theme.js</Link>
</BundleResource> </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"> <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> <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.listview.css</Link>
</BundleResource> </BundleResource>

@ -71,11 +71,11 @@
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL"> <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> <Private>True</Private>
</Reference> </Reference>
<Reference Include="SQLitePCLRaw.provider.sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=62684c7b4f184e3f, processorArchitecture=MSIL"> <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> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />

@ -6,6 +6,6 @@
<package id="SharpCompress" version="0.14.0" targetFramework="net46" /> <package id="SharpCompress" version="0.14.0" targetFramework="net46" />
<package id="SimpleInjector" version="4.0.8" targetFramework="net46" /> <package id="SimpleInjector" version="4.0.8" targetFramework="net46" />
<package id="SkiaSharp" version="1.58.0" targetFramework="net46" /> <package id="SkiaSharp" version="1.58.0" targetFramework="net46" />
<package id="SQLitePCLRaw.core" 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.6" targetFramework="net46" /> <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.7" targetFramework="net46" />
</packages> </packages>

@ -678,7 +678,7 @@ namespace MediaBrowser.ServerApplication
_logger.Info("Calling Application.Exit"); _logger.Info("Calling Application.Exit");
//Application.Exit(); //Application.Exit();
Environment.Exit(0); Environment.Exit(0);
} }
@ -770,19 +770,43 @@ namespace MediaBrowser.ServerApplication
try try
{ {
var subkey = Environment.Is64BitProcess RegistryKey key;
? "SOFTWARE\\WOW6432Node\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64"
: "SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x86";
using (RegistryKey ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default) if (Environment.Is64BitProcess)
.OpenSubKey(subkey))
{ {
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'); key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default)
if (installedVersion.StartsWith("14", StringComparison.OrdinalIgnoreCase)) .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;
}
} }
} }
} }

@ -73,10 +73,6 @@
<Reference Include="Emby.Server.Sync"> <Reference Include="Emby.Server.Sync">
<HintPath>..\ThirdParty\emby\Emby.Server.Sync.dll</HintPath> <HintPath>..\ThirdParty\emby\Emby.Server.Sync.dll</HintPath>
</Reference> </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"> <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> <HintPath>..\packages\NLog.4.4.11\lib\net45\NLog.dll</HintPath>
<Private>True</Private> <Private>True</Private>
@ -96,11 +92,11 @@
<HintPath>..\packages\SkiaSharp.1.58.0\lib\net45\SkiaSharp.dll</HintPath> <HintPath>..\packages\SkiaSharp.1.58.0\lib\net45\SkiaSharp.dll</HintPath>
</Reference> </Reference>
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL"> <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> <Private>True</Private>
</Reference> </Reference>
<Reference Include="SQLitePCLRaw.provider.sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=62684c7b4f184e3f, processorArchitecture=MSIL"> <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> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />

@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="ImageMagickSharp" version="1.0.0.18" targetFramework="net45" />
<package id="NLog" version="4.4.11" targetFramework="net462" /> <package id="NLog" version="4.4.11" targetFramework="net462" />
<package id="ServiceStack.Text" version="4.5.8" targetFramework="net462" /> <package id="ServiceStack.Text" version="4.5.8" targetFramework="net462" />
<package id="SharpCompress" version="0.14.0" targetFramework="net462" /> <package id="SharpCompress" version="0.14.0" targetFramework="net462" />
<package id="SimpleInjector" version="4.0.8" targetFramework="net462" /> <package id="SimpleInjector" version="4.0.8" targetFramework="net462" />
<package id="SkiaSharp" version="1.58.0" targetFramework="net462" /> <package id="SkiaSharp" version="1.58.0" targetFramework="net462" />
<package id="SQLitePCLRaw.core" 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.6" targetFramework="net462" /> <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.7" targetFramework="net462" />
</packages> </packages>

@ -242,10 +242,7 @@ namespace MediaBrowser.WebDashboard.Api
var files = new[] var files = new[]
{ {
"css/site.css" + versionString, "css/site.css" + versionString
"css/librarymenu.css" + versionString,
"css/librarybrowser.css" + versionString,
"thirdparty/paper-button-style.css" + versionString
}; };
var tags = files.Select(s => string.Format("<link rel=\"stylesheet\" href=\"{0}\" async />", s)).ToArray(); var tags = files.Select(s => string.Format("<link rel=\"stylesheet\" href=\"{0}\" async />", s)).ToArray();

@ -16,9 +16,10 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Xml; using System.Xml;
using MediaBrowser.Controller.Extensions;
using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Xml; using MediaBrowser.Model.Xml;
@ -353,7 +354,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (!string.IsNullOrEmpty(stream.Language)) 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"; var scanType = stream.IsInterlaced ? "interlaced" : "progressive";
@ -422,6 +424,19 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteEndElement(); 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"; public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
/// <summary> /// <summary>

@ -51,6 +51,11 @@ namespace MediaBrowser.XbmcMetadata.Savers
//} //}
list.Add(Path.ChangeExtension(item.Path, ".nfo")); list.Add(Path.ChangeExtension(item.Path, ".nfo"));
if (!item.IsInMixedFolder)
{
list.Add(Path.Combine(item.ContainingFolderPath, "movie.nfo"));
}
} }
return list; return list;

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>MediaBrowser.Common</id> <id>MediaBrowser.Common</id>
<version>3.0.704</version> <version>3.0.708</version>
<title>Emby.Common</title> <title>Emby.Common</title>
<authors>Emby Team</authors> <authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners> <owners>ebr,Luke,scottisafool</owners>

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>MediaBrowser.Server.Core</id> <id>MediaBrowser.Server.Core</id>
<version>3.0.704</version> <version>3.0.708</version>
<title>Emby.Server.Core</title> <title>Emby.Server.Core</title>
<authors>Emby Team</authors> <authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners> <owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Emby Server.</description> <description>Contains core components required to build plugins for Emby Server.</description>
<copyright>Copyright © Emby 2013</copyright> <copyright>Copyright © Emby 2013</copyright>
<dependencies> <dependencies>
<dependency id="MediaBrowser.Common" version="3.0.704" /> <dependency id="MediaBrowser.Common" version="3.0.708" />
</dependencies> </dependencies>
</metadata> </metadata>
<files> <files>

Loading…
Cancel
Save