aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md0
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs6
-rw-r--r--MediaBrowser.Api/ConfigurationService.cs3
-rw-r--r--MediaBrowser.Api/Library/LibraryService.cs193
-rw-r--r--MediaBrowser.Api/LiveTv/LiveTvService.cs40
-rw-r--r--MediaBrowser.Api/Movies/MoviesService.cs29
-rw-r--r--MediaBrowser.Api/Movies/TrailersService.cs39
-rw-r--r--MediaBrowser.Api/Music/AlbumsService.cs50
-rw-r--r--MediaBrowser.Api/Music/InstantMixService.cs19
-rw-r--r--MediaBrowser.Api/PackageService.cs2
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs25
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs33
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs164
-rw-r--r--MediaBrowser.Api/StartupWizardService.cs77
-rw-r--r--MediaBrowser.Api/System/SystemService.cs3
-rw-r--r--MediaBrowser.Api/UserLibrary/GenresService.cs2
-rw-r--r--MediaBrowser.Api/UserLibrary/ItemsService.cs11
-rw-r--r--MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs22
-rw-r--r--MediaBrowser.Common.Implementations/Updates/InstallationManager.cs19
-rw-r--r--MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs13
-rw-r--r--MediaBrowser.Common/Updates/IInstallationManager.cs2
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs21
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs4
-rw-r--r--MediaBrowser.Controller/Entities/IHasProgramAttributes.cs1
-rw-r--r--MediaBrowser.Controller/Entities/InternalItemsQuery.cs3
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs13
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs7
-rw-r--r--MediaBrowser.Controller/Library/IMusicManager.cs4
-rw-r--r--MediaBrowser.Controller/LiveTv/IHasRegistrationInfo.cs15
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvManager.cs19
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs4
-rw-r--r--MediaBrowser.Controller/LiveTv/ITunerHost.cs12
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvProgram.cs16
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs11
-rw-r--r--MediaBrowser.Controller/LiveTv/ProgramInfo.cs22
-rw-r--r--MediaBrowser.Controller/LiveTv/RecordingInfo.cs6
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj1
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs7
-rw-r--r--MediaBrowser.Controller/Session/SessionInfo.cs6
-rw-r--r--MediaBrowser.Controller/Sync/IServerSyncProvider.cs11
-rw-r--r--MediaBrowser.Dlna/Didl/DidlBuilder.cs13
-rw-r--r--MediaBrowser.Dlna/DlnaManager.cs4
-rw-r--r--MediaBrowser.Dlna/MediaBrowser.Dlna.csproj20
-rw-r--r--MediaBrowser.Dlna/Profiles/KodiProfile.cs97
-rw-r--r--MediaBrowser.Dlna/Profiles/SonyBravia2014Profile.cs309
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Kodi.xml55
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml108
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs4
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs91
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs2
-rw-r--r--MediaBrowser.Model/ApiClient/IApiClient.cs83
-rw-r--r--MediaBrowser.Model/Configuration/ChannelOptions.cs13
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs13
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs15
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs7
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs6
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvOptions.cs6
-rw-r--r--MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs3
-rw-r--r--MediaBrowser.Model/Session/SessionInfoDto.cs6
-rw-r--r--MediaBrowser.Model/System/PublicSystemInfo.cs6
-rw-r--r--MediaBrowser.Model/System/SystemInfo.cs11
-rw-r--r--MediaBrowser.Model/Updates/PackageInfo.cs6
-rw-r--r--MediaBrowser.Providers/Folders/DefaultImageProvider.cs166
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs2
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj1
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs16
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs2
-rw-r--r--MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs33
-rw-r--r--MediaBrowser.Providers/Omdb/OmdbItemProvider.cs4
-rw-r--r--MediaBrowser.Providers/Omdb/OmdbProvider.cs12
-rw-r--r--MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs409
-rw-r--r--MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs71
-rw-r--r--MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs6
-rw-r--r--MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Connect/ConnectManager.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Dto/DtoService.cs22
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs11
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs15
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs56
-rw-r--r--MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs20
-rw-r--r--MediaBrowser.Server.Implementations/Library/LibraryManager.cs86
-rw-r--r--MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs10
-rw-r--r--MediaBrowser.Server.Implementations/Library/MusicManager.cs24
-rw-r--r--MediaBrowser.Server.Implementations/Library/SearchEngine.cs272
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserViewManager.cs4
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs29
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs29
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs30
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs42
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs30
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs363
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs33
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs57
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs26
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs14
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs180
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs168
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs5
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs218
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs88
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs197
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Core/kk.json2
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Core/ru.json2
-rw-r--r--MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj11
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs164
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs147
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs58
-rw-r--r--MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs22
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionManager.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs27
-rw-r--r--MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs7
-rw-r--r--MediaBrowser.Server.Implementations/Sync/MediaSync.cs9
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs36
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncManager.cs20
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs5
-rw-r--r--MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs50
-rw-r--r--MediaBrowser.Server.Implementations/UserViews/livetv/1.jpgbin62382 -> 0 bytes
-rw-r--r--MediaBrowser.Server.Implementations/UserViews/livetv/2.jpgbin20550 -> 0 bytes
-rw-r--r--MediaBrowser.Server.Implementations/UserViews/livetv/3.jpgbin27983 -> 0 bytes
-rw-r--r--MediaBrowser.Server.Implementations/UserViews/livetv/4.jpgbin75468 -> 0 bytes
-rw-r--r--MediaBrowser.Server.Implementations/UserViews/livetv/5.jpgbin50933 -> 0 bytes
-rw-r--r--MediaBrowser.Server.Implementations/UserViews/livetv/6.jpgbin15931 -> 0 bytes
-rw-r--r--MediaBrowser.Server.Implementations/UserViews/livetv/7.jpgbin19916 -> 0 bytes
-rw-r--r--MediaBrowser.Server.Implementations/UserViews/livetv/8.jpgbin67721 -> 0 bytes
-rw-r--r--MediaBrowser.Server.Mac/Emby.Server.Mac.csproj3446
-rw-r--r--MediaBrowser.Server.Startup.Common/ApplicationHost.cs2
-rw-r--r--MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs8
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardService.cs6
-rw-r--r--MediaBrowser.WebDashboard/Api/PackageCreator.cs18
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj117
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs51
-rw-r--r--Nuget/MediaBrowser.Common.Internal.nuspec4
-rw-r--r--Nuget/MediaBrowser.Common.nuspec2
-rw-r--r--Nuget/MediaBrowser.Model.Signed.nuspec2
-rw-r--r--Nuget/MediaBrowser.Server.Core.nuspec4
-rw-r--r--SharedVersion.cs4
136 files changed, 6651 insertions, 2143 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/CONTRIBUTING.md
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 54c28d390..c2b406190 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -336,12 +336,6 @@ namespace MediaBrowser.Api
if (job.Type != TranscodingJobType.Progressive)
{
timerDuration = 1800000;
-
- // We can really reduce the timeout for apps that are using the newer api
- if (!string.IsNullOrWhiteSpace(job.PlaySessionId))
- {
- timerDuration = 300000;
- }
}
job.PingTimeout = timerDuration;
diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs
index 9f6c07dd2..f266180a4 100644
--- a/MediaBrowser.Api/ConfigurationService.cs
+++ b/MediaBrowser.Api/ConfigurationService.cs
@@ -7,7 +7,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Serialization;
using ServiceStack;
-using ServiceStack.Text.Controller;
using ServiceStack.Web;
using System.Collections.Generic;
using System.IO;
@@ -26,7 +25,7 @@ namespace MediaBrowser.Api
}
[Route("/System/Configuration/{Key}", "GET", Summary = "Gets a named configuration")]
- [Authenticated]
+ [Authenticated(AllowBeforeStartupWizard = true)]
public class GetNamedConfiguration
{
[ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index 50c4ad14f..49dd121ba 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -1,4 +1,7 @@
-using MediaBrowser.Controller.Activity;
+using MediaBrowser.Api.Movies;
+using MediaBrowser.Api.Music;
+using MediaBrowser.Controller.Activity;
+using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -9,7 +12,9 @@ using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
@@ -226,6 +231,17 @@ namespace MediaBrowser.Api.Library
public string TvdbId { get; set; }
}
+ [Route("/Library/Movies/Added", "POST", Summary = "Reports that new movies have been added by an external source")]
+ [Route("/Library/Movies/Updated", "POST", Summary = "Reports that new movies have been added by an external source")]
+ [Authenticated]
+ public class PostUpdatedMovies : IReturnVoid
+ {
+ [ApiMember(Name = "TmdbId", Description = "Tmdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string TmdbId { get; set; }
+ [ApiMember(Name = "ImdbId", Description = "Imdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string ImdbId { get; set; }
+ }
+
[Route("/Items/{Id}/Download", "GET", Summary = "Downloads item media")]
[Authenticated(Roles = "download")]
public class GetDownload
@@ -238,6 +254,12 @@ namespace MediaBrowser.Api.Library
public string Id { get; set; }
}
+ [Route("/Items/{Id}/Similar", "GET", Summary = "Gets similar items")]
+ [Authenticated]
+ public class GetSimilarItems : BaseGetSimilarItemsFromItem
+ {
+ }
+
/// <summary>
/// Class LibraryService
/// </summary>
@@ -257,12 +279,15 @@ namespace MediaBrowser.Api.Library
private readonly IActivityManager _activityManager;
private readonly ILocalizationManager _localization;
private readonly ILiveTvManager _liveTv;
+ private readonly IChannelManager _channelManager;
+ private readonly ITVSeriesManager _tvManager;
+ private readonly ILibraryMonitor _libraryMonitor;
/// <summary>
/// Initializes a new instance of the <see cref="LibraryService" /> class.
/// </summary>
public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager,
- IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv)
+ IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, IChannelManager channelManager, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor)
{
_itemRepo = itemRepo;
_libraryManager = libraryManager;
@@ -273,6 +298,116 @@ namespace MediaBrowser.Api.Library
_activityManager = activityManager;
_localization = localization;
_liveTv = liveTv;
+ _channelManager = channelManager;
+ _tvManager = tvManager;
+ _libraryMonitor = libraryMonitor;
+ }
+
+ public object Get(GetSimilarItems request)
+ {
+ var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+
+ var item = string.IsNullOrEmpty(request.Id) ?
+ (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
+ _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
+
+ if (item is Game)
+ {
+ return new GamesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService)
+ {
+ AuthorizationContext = AuthorizationContext,
+ Logger = Logger,
+ Request = Request,
+ SessionContext = SessionContext,
+ ResultFactory = ResultFactory
+
+ }.Get(new GetSimilarGames
+ {
+ Fields = request.Fields,
+ Id = request.Id,
+ Limit = request.Limit,
+ UserId = request.UserId
+ });
+ }
+ if (item is MusicAlbum)
+ {
+ return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService)
+ {
+ AuthorizationContext = AuthorizationContext,
+ Logger = Logger,
+ Request = Request,
+ SessionContext = SessionContext,
+ ResultFactory = ResultFactory
+
+ }.Get(new GetSimilarAlbums
+ {
+ Fields = request.Fields,
+ Id = request.Id,
+ Limit = request.Limit,
+ UserId = request.UserId
+ });
+ }
+ if (item is MusicArtist)
+ {
+ return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService)
+ {
+ AuthorizationContext = AuthorizationContext,
+ Logger = Logger,
+ Request = Request,
+ SessionContext = SessionContext,
+ ResultFactory = ResultFactory
+
+ }.Get(new GetSimilarArtists
+ {
+ Fields = request.Fields,
+ Id = request.Id,
+ Limit = request.Limit,
+ UserId = request.UserId
+ });
+ }
+
+ var program = item as IHasProgramAttributes;
+ var channelItem = item as ChannelVideoItem;
+
+ if (item is Movie || (program != null && program.IsMovie) || (channelItem != null && channelItem.ContentType == ChannelMediaContentType.Movie) || (channelItem != null && channelItem.ContentType == ChannelMediaContentType.MovieExtra))
+ {
+ return new MoviesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _channelManager)
+ {
+ AuthorizationContext = AuthorizationContext,
+ Logger = Logger,
+ Request = Request,
+ SessionContext = SessionContext,
+ ResultFactory = ResultFactory
+
+ }.Get(new GetSimilarMovies
+ {
+ Fields = request.Fields,
+ Id = request.Id,
+ Limit = request.Limit,
+ UserId = request.UserId
+ });
+ }
+
+ if (item is Series || (program != null && program.IsSeries) || (channelItem != null && channelItem.ContentType == ChannelMediaContentType.Episode))
+ {
+ return new TvShowsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _tvManager)
+ {
+ AuthorizationContext = AuthorizationContext,
+ Logger = Logger,
+ Request = Request,
+ SessionContext = SessionContext,
+ ResultFactory = ResultFactory
+
+ }.Get(new GetSimilarShows
+ {
+ Fields = request.Fields,
+ Id = request.Id,
+ Limit = request.Limit,
+ UserId = request.UserId
+ });
+ }
+
+ return new ItemsResult();
}
public object Get(GetMediaFolders request)
@@ -300,7 +435,59 @@ namespace MediaBrowser.Api.Library
public void Post(PostUpdatedSeries request)
{
- Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None));
+ var series = _libraryManager.GetItems(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(Series).Name }
+
+ }).Items;
+
+ series = series.Where(i => string.Equals(request.TvdbId, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase)).ToArray();
+
+ if (series.Length > 0)
+ {
+ foreach (var item in series)
+ {
+ _libraryMonitor.ReportFileSystemChanged(item.Path);
+ }
+ }
+ else
+ {
+ Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None));
+ }
+ }
+
+ public void Post(PostUpdatedMovies request)
+ {
+ var movies = _libraryManager.GetItems(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(Movie).Name }
+
+ }).Items;
+
+ if (!string.IsNullOrWhiteSpace(request.ImdbId))
+ {
+ movies = movies.Where(i => string.Equals(request.ImdbId, i.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)).ToArray();
+ }
+ else if (!string.IsNullOrWhiteSpace(request.TmdbId))
+ {
+ movies = movies.Where(i => string.Equals(request.TmdbId, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase)).ToArray();
+ }
+ else
+ {
+ movies = new BaseItem[] { };
+ }
+
+ if (movies.Length > 0)
+ {
+ foreach (var item in movies)
+ {
+ _libraryMonitor.ReportFileSystemChanged(item.Path);
+ }
+ }
+ else
+ {
+ Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None));
+ }
}
public object Get(GetDownload request)
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index e55103230..07e64c98d 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -78,6 +78,14 @@ namespace MediaBrowser.Api.LiveTv
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, 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)]
public string Fields { get; set; }
+
+ [ApiMember(Name = "AddCurrentProgram", Description = "Optional. Adds current program info to each channel", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public bool AddCurrentProgram { get; set; }
+
+ public GetChannels()
+ {
+ AddCurrentProgram = true;
+ }
}
[Route("/LiveTv/Channels/{Id}", "GET", Summary = "Gets a live tv channel")]
@@ -418,7 +426,7 @@ namespace MediaBrowser.Api.LiveTv
}
[Route("/LiveTv/ListingProviders", "POST", Summary = "Adds a listing provider")]
- [Authenticated]
+ [Authenticated(AllowBeforeStartupWizard = true)]
public class AddListingProvider : ListingsProviderInfo, IReturn<ListingsProviderInfo>
{
public bool ValidateLogin { get; set; }
@@ -426,7 +434,7 @@ namespace MediaBrowser.Api.LiveTv
}
[Route("/LiveTv/ListingProviders", "DELETE", Summary = "Deletes a listing provider")]
- [Authenticated]
+ [Authenticated(AllowBeforeStartupWizard = true)]
public class DeleteListingProvider : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Provider id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "DELETE")]
@@ -434,7 +442,7 @@ namespace MediaBrowser.Api.LiveTv
}
[Route("/LiveTv/ListingProviders/Lineups", "GET", Summary = "Gets available lineups")]
- [Authenticated]
+ [Authenticated(AllowBeforeStartupWizard = true)]
public class GetLineups : IReturn<List<NameIdPair>>
{
[ApiMember(Name = "Id", Description = "Provider id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
@@ -451,11 +459,25 @@ namespace MediaBrowser.Api.LiveTv
}
[Route("/LiveTv/ListingProviders/SchedulesDirect/Countries", "GET", Summary = "Gets available lineups")]
- [Authenticated]
+ [Authenticated(AllowBeforeStartupWizard = true)]
public class GetSchedulesDirectCountries
{
}
+ [Route("/LiveTv/Registration", "GET")]
+ [Authenticated]
+ public class GetLiveTvRegistrationInfo : IReturn<MBRegistrationRecord>
+ {
+ [ApiMember(Name = "ChannelId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string ChannelId { get; set; }
+
+ [ApiMember(Name = "ProgramId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string ProgramId { get; set; }
+
+ [ApiMember(Name = "Feature", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string Feature { get; set; }
+ }
+
public class LiveTvService : BaseApiService
{
private readonly ILiveTvManager _liveTvManager;
@@ -471,6 +493,13 @@ namespace MediaBrowser.Api.LiveTv
_httpClient = httpClient;
}
+ public async Task<object> Get(GetLiveTvRegistrationInfo request)
+ {
+ var result = await _liveTvManager.GetRegistrationInfo(request.ChannelId, request.ProgramId, request.Feature).ConfigureAwait(false);
+
+ return ToOptimizedResult(result);
+ }
+
public async Task<object> Get(GetSchedulesDirectCountries request)
{
// https://json.schedulesdirect.org/20141201/available/countries
@@ -561,7 +590,8 @@ namespace MediaBrowser.Api.LiveTv
IsFavorite = request.IsFavorite,
IsLiked = request.IsLiked,
IsDisliked = request.IsDisliked,
- EnableFavoriteSorting = request.EnableFavoriteSorting
+ EnableFavoriteSorting = request.EnableFavoriteSorting,
+ AddCurrentProgram = request.AddCurrentProgram
}, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false);
diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs
index 97e9aa9c8..fe8bae1a5 100644
--- a/MediaBrowser.Api/Movies/MoviesService.cs
+++ b/MediaBrowser.Api/Movies/MoviesService.cs
@@ -28,6 +28,14 @@ namespace MediaBrowser.Api.Movies
{
}
+ /// <summary>
+ /// Class GetSimilarTrailers
+ /// </summary>
+ [Route("/Trailers/{Id}/Similar", "GET", Summary = "Finds movies and trailers similar to a given trailer.")]
+ public class GetSimilarTrailers : BaseGetSimilarItemsFromItem
+ {
+ }
+
[Route("/Movies/Recommendations", "GET", Summary = "Gets movie recommendations")]
public class GetMovieRecommendations : IReturn<RecommendationDto[]>, IHasItemFields
{
@@ -117,6 +125,17 @@ namespace MediaBrowser.Api.Movies
return ToOptimizedSerializedResultUsingCache(result);
}
+ public async Task<object> Get(GetSimilarTrailers request)
+ {
+ var result = await GetSimilarItemsResult(
+ // Strip out secondary versions
+ request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue,
+
+ SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false);
+
+ return ToOptimizedSerializedResultUsingCache(result);
+ }
+
public async Task<object> Get(GetMovieRecommendations request)
{
var user = _userManager.GetUserById(request.UserId);
@@ -126,7 +145,7 @@ namespace MediaBrowser.Api.Movies
movies = _libraryManager.ReplaceVideosWithPrimaryVersions(movies);
var listEligibleForCategories = new List<BaseItem>();
- var listEligibleForSuggestion = new List<BaseItem> ();
+ var listEligibleForSuggestion = new List<BaseItem>();
var list = movies.ToList();
@@ -159,7 +178,7 @@ namespace MediaBrowser.Api.Movies
var dtoOptions = GetDtoOptions(request);
dtoOptions.Fields = request.GetItemFields().ToList();
-
+
var result = GetRecommendationCategories(user, listEligibleForCategories, listEligibleForSuggestion, request.CategoryLimit, request.ItemLimit, dtoOptions);
return ToOptimizedResult(result);
@@ -174,14 +193,14 @@ namespace MediaBrowser.Api.Movies
_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
Func<BaseItem, bool> filter = i => i.Id != item.Id && includeInSearch(i);
-
+
var inputItems = user == null
? _libraryManager.RootFolder.GetRecursiveChildren(filter)
: user.RootFolder.GetRecursiveChildren(user, filter);
var list = inputItems.ToList();
- if (item is Movie && user != null && user.Configuration.IncludeTrailersInSuggestions)
+ if (user != null && user.Configuration.IncludeTrailersInSuggestions)
{
var trailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery
{
@@ -224,7 +243,7 @@ namespace MediaBrowser.Api.Movies
}
var dtoOptions = GetDtoOptions(request);
-
+
var result = new ItemsResult
{
Items = _dtoService.GetBaseItemDtos(returnItems, dtoOptions, user).ToArray(),
diff --git a/MediaBrowser.Api/Movies/TrailersService.cs b/MediaBrowser.Api/Movies/TrailersService.cs
index 0847fc7ed..ed197911a 100644
--- a/MediaBrowser.Api/Movies/TrailersService.cs
+++ b/MediaBrowser.Api/Movies/TrailersService.cs
@@ -2,15 +2,12 @@
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using ServiceStack;
-using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -18,14 +15,6 @@ using System.Threading.Tasks;
namespace MediaBrowser.Api.Movies
{
- /// <summary>
- /// Class GetSimilarTrailers
- /// </summary>
- [Route("/Trailers/{Id}/Similar", "GET", Summary = "Finds movies and trailers similar to a given trailer.")]
- public class GetSimilarTrailers : BaseGetSimilarItemsFromItem
- {
- }
-
[Route("/Trailers", "GET", Summary = "Finds movies and trailers similar to a given trailer.")]
public class Getrailers : BaseItemsRequest, IReturn<ItemsResult>
{
@@ -51,7 +40,6 @@ namespace MediaBrowser.Api.Movies
/// </summary>
private readonly ILibraryManager _libraryManager;
- private readonly IItemRepository _itemRepo;
private readonly IDtoService _dtoService;
private readonly IChannelManager _channelManager;
@@ -61,40 +49,15 @@ namespace MediaBrowser.Api.Movies
/// <param name="userManager">The user manager.</param>
/// <param name="userDataRepository">The user data repository.</param>
/// <param name="libraryManager">The library manager.</param>
- public TrailersService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, IChannelManager channelManager)
+ public TrailersService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IDtoService dtoService, IChannelManager channelManager)
{
_userManager = userManager;
_userDataRepository = userDataRepository;
_libraryManager = libraryManager;
- _itemRepo = itemRepo;
_dtoService = dtoService;
_channelManager = channelManager;
}
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public object Get(GetSimilarTrailers request)
- {
- var dtoOptions = GetDtoOptions(request);
-
- var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager,
- _itemRepo,
- _libraryManager,
- _userDataRepository,
- _dtoService,
- Logger,
-
- // Strip out secondary versions
- request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue,
-
- SimilarItemsHelper.GetSimiliarityScore);
-
- return ToOptimizedSerializedResultUsingCache(result);
- }
-
public async Task<object> Get(Getrailers request)
{
var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
diff --git a/MediaBrowser.Api/Music/AlbumsService.cs b/MediaBrowser.Api/Music/AlbumsService.cs
index ea87c3ad3..548598d42 100644
--- a/MediaBrowser.Api/Music/AlbumsService.cs
+++ b/MediaBrowser.Api/Music/AlbumsService.cs
@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Querying;
using ServiceStack;
using System;
using System.Collections.Generic;
@@ -16,6 +17,11 @@ namespace MediaBrowser.Api.Music
{
}
+ [Route("/Artists/{Id}/Similar", "GET", Summary = "Finds albums similar to a given album.")]
+ public class GetSimilarArtists : BaseGetSimilarItemsFromItem
+ {
+ }
+
[Authenticated]
public class AlbumsService : BaseApiService
{
@@ -44,6 +50,17 @@ namespace MediaBrowser.Api.Music
_dtoService = dtoService;
}
+ public object Get(GetSimilarArtists request)
+ {
+ var result = GetSimilarItemsResult(
+
+ request,
+
+ SimilarItemsHelper.GetSimiliarityScore);
+
+ return ToOptimizedSerializedResultUsingCache(result);
+ }
+
/// <summary>
/// Gets the specified request.
/// </summary>
@@ -65,6 +82,39 @@ namespace MediaBrowser.Api.Music
return ToOptimizedSerializedResultUsingCache(result);
}
+ private ItemsResult GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
+ {
+ var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+
+ var item = string.IsNullOrEmpty(request.Id) ?
+ (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
+ _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
+
+ var inputItems = _libraryManager.GetArtists(user.RootFolder.GetRecursiveChildren(user, i => i is IHasArtist).OfType<IHasArtist>());
+
+ var list = inputItems.ToList();
+
+ var items = SimilarItemsHelper.GetSimilaritems(item, _libraryManager, list, getSimilarityScore).ToList();
+
+ IEnumerable<BaseItem> returnItems = items;
+
+ if (request.Limit.HasValue)
+ {
+ returnItems = returnItems.Take(request.Limit.Value);
+ }
+
+ var dtoOptions = GetDtoOptions(request);
+
+ var result = new ItemsResult
+ {
+ Items = _dtoService.GetBaseItemDtos(returnItems, dtoOptions, user).ToArray(),
+
+ TotalRecordCount = items.Count
+ };
+
+ return result;
+ }
+
/// <summary>
/// Gets the album similarity score.
/// </summary>
diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs
index 506b7bc3a..d2a4aa60c 100644
--- a/MediaBrowser.Api/Music/InstantMixService.cs
+++ b/MediaBrowser.Api/Music/InstantMixService.cs
@@ -54,6 +54,11 @@ namespace MediaBrowser.Api.Music
public string Id { get; set; }
}
+ [Route("/Items/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given item")]
+ public class GetInstantMixFromItem : BaseGetSimilarItemsFromItem
+ {
+ }
+
[Authenticated]
public class InstantMixService : BaseApiService
{
@@ -71,6 +76,17 @@ namespace MediaBrowser.Api.Music
_libraryManager = libraryManager;
}
+ public object Get(GetInstantMixFromItem request)
+ {
+ var item = _libraryManager.GetItemById(request.Id);
+
+ var user = _userManager.GetUserById(request.UserId);
+
+ var items = _musicManager.GetInstantMixFromItem(item, user);
+
+ return GetResult(items, user, request);
+ }
+
public object Get(GetInstantMixFromArtistId request)
{
var item = _libraryManager.GetItemById(request.Id);
@@ -138,8 +154,9 @@ namespace MediaBrowser.Api.Music
public object Get(GetInstantMixFromArtist request)
{
var user = _userManager.GetUserById(request.UserId);
+ var artist = _libraryManager.GetArtist(request.Name);
- var items = _musicManager.GetInstantMixFromArtist(request.Name, user);
+ var items = _musicManager.GetInstantMixFromArtist(artist, user);
return GetResult(items, user, request);
}
diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs
index 5ef8b0987..4f9efad50 100644
--- a/MediaBrowser.Api/PackageService.cs
+++ b/MediaBrowser.Api/PackageService.cs
@@ -190,7 +190,7 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns>
public async Task<object> Get(GetPackages request)
{
- var packages = await _installationManager.GetAvailablePackages(CancellationToken.None, request.PackageType, _appHost.ApplicationVersion).ConfigureAwait(false);
+ var packages = await _installationManager.GetAvailablePackages(CancellationToken.None, false, request.PackageType, _appHost.ApplicationVersion).ConfigureAwait(false);
if (!string.IsNullOrEmpty(request.TargetSystems))
{
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 8f5301642..531d67eed 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -619,7 +619,7 @@ namespace MediaBrowser.Api.Playback
// TODO: Perhaps also use original_size=1920x800 ??
return string.Format("subtitles=filename='{0}'{1},setpts=PTS -{2}/TB",
- subtitlePath.Replace('\\', '/').Replace(":/", "\\:/"),
+ MediaEncoder.EscapeSubtitleFilterPath(subtitlePath),
charsetParam,
seconds.ToString(UsCulture));
}
@@ -627,7 +627,7 @@ namespace MediaBrowser.Api.Playback
var mediaPath = state.MediaPath ?? string.Empty;
return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB",
- mediaPath.Replace('\\', '/').Replace(":/", "\\:/"),
+ MediaEncoder.EscapeSubtitleFilterPath(mediaPath),
state.InternalSubtitleStreamOffset.ToString(UsCulture),
seconds.ToString(UsCulture));
}
@@ -724,7 +724,7 @@ namespace MediaBrowser.Api.Playback
var channelLimit = codec.IndexOf("mp3", StringComparison.OrdinalIgnoreCase) != -1
? 2
- : 5;
+ : 6;
// If we don't have any media info then limit it to 5 to prevent encoding errors due to asking for too many channels
return Math.Min(request.MaxAudioChannels.Value, channelLimit);
@@ -854,7 +854,7 @@ namespace MediaBrowser.Api.Playback
state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false);
}
- if (state.MediaSource.RequiresOpening)
+ if (state.MediaSource.RequiresOpening && string.IsNullOrWhiteSpace(state.Request.LiveStreamId))
{
var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest
{
@@ -1634,11 +1634,6 @@ namespace MediaBrowser.Api.Playback
private void TryStreamCopy(StreamState state, VideoStreamRequest videoRequest)
{
- if (!EnableStreamCopy)
- {
- return;
- }
-
if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
{
state.OutputVideoCodec = "copy";
@@ -1650,14 +1645,6 @@ namespace MediaBrowser.Api.Playback
}
}
- protected virtual bool EnableStreamCopy
- {
- get
- {
- return true;
- }
- }
-
private void AttachMediaSourceInfo(StreamState state,
MediaSourceInfo mediaSource,
VideoStreamRequest videoRequest,
@@ -1741,7 +1728,7 @@ namespace MediaBrowser.Api.Playback
state.MediaSource = mediaSource;
}
- private bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
+ protected virtual bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
{
if (videoStream.IsInterlaced)
{
@@ -1889,7 +1876,7 @@ namespace MediaBrowser.Api.Playback
return Array.FindIndex(list.ToArray(), t => string.Equals(t, profile, StringComparison.OrdinalIgnoreCase));
}
- private bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs)
+ protected virtual bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs)
{
// Source and target codecs must match
if (string.IsNullOrEmpty(audioStream.Codec) || !supportedAudioCodecs.Contains(audioStream.Codec, StringComparer.OrdinalIgnoreCase))
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index b2ffeca3d..bfaa1f289 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
@@ -108,7 +109,7 @@ namespace MediaBrowser.Api.Playback.Hls
throw;
}
- await WaitForMinimumSegmentCount(playlist, 3, cancellationTokenSource.Token).ConfigureAwait(false);
+ await WaitForMinimumSegmentCount(playlist, 1, cancellationTokenSource.Token).ConfigureAwait(false);
}
}
finally
@@ -310,5 +311,35 @@ namespace MediaBrowser.Api.Playback.Hls
{
return 0;
}
+
+ protected override bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
+ {
+ if (videoStream.KeyFrames == null || videoStream.KeyFrames.Count == 0)
+ {
+ Logger.Debug("Cannot stream copy video due to missing keyframe info");
+ return false;
+ }
+
+ var previousSegment = 0;
+ foreach (var frame in videoStream.KeyFrames)
+ {
+ var length = frame - previousSegment;
+
+ // Don't allow really long segments because this could result in long download times
+ if (length > 10000)
+ {
+ Logger.Debug("Cannot stream copy video due to long segment length of {0}ms", length);
+ return false;
+ }
+ previousSegment = frame;
+ }
+
+ return base.CanStreamCopyVideo(request, videoStream);
+ }
+
+ protected override bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs)
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 0d501066e..cbea1ca0c 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -13,7 +13,6 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using ServiceStack;
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -161,7 +160,6 @@ namespace MediaBrowser.Api.Playback.Hls
var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
var segmentPath = GetSegmentPath(state, playlistPath, requestedIndex);
- var segmentLength = state.SegmentLength;
var segmentExtension = GetSegmentFileExtension(state);
@@ -170,7 +168,7 @@ namespace MediaBrowser.Api.Playback.Hls
if (File.Exists(segmentPath))
{
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
- return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false);
+ return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
}
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
@@ -179,7 +177,7 @@ namespace MediaBrowser.Api.Playback.Hls
if (File.Exists(segmentPath))
{
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
- return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false);
+ return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
}
else
{
@@ -215,7 +213,7 @@ namespace MediaBrowser.Api.Playback.Hls
DeleteLastFile(playlistPath, segmentExtension, 0);
}
- request.StartTimeTicks = GetSeekPositionTicks(state, playlistPath, requestedIndex);
+ request.StartTimeTicks = GetStartPositionTicks(state, requestedIndex);
job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false);
}
@@ -250,37 +248,76 @@ namespace MediaBrowser.Api.Playback.Hls
Logger.Info("returning {0}", segmentPath);
job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
- return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false);
+ return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
}
// 256k
private const int BufferSize = 262144;
- private long GetSeekPositionTicks(StreamState state, string playlist, int requestedIndex)
+ private long GetStartPositionTicks(StreamState state, int requestedIndex)
{
double startSeconds = 0;
+ var lengths = GetSegmentLengths(state);
for (var i = 0; i < requestedIndex; i++)
{
- var segmentPath = GetSegmentPath(state, playlist, i);
+ startSeconds += lengths[requestedIndex];
+ }
+
+ var position = TimeSpan.FromSeconds(startSeconds).Ticks;
+ return position;
+ }
- //double length;
- //if (SegmentLengths.TryGetValue(Path.GetFileName(segmentPath), out length))
- //{
- // Logger.Debug("Found segment length of {0} for index {1}", length, i);
- // startSeconds += length;
- //}
- //else
- //{
- // startSeconds += state.SegmentLength;
- //}
- startSeconds += state.SegmentLength;
+ private long GetEndPositionTicks(StreamState state, int requestedIndex)
+ {
+ double startSeconds = 0;
+ var lengths = GetSegmentLengths(state);
+
+ for (var i = 0; i <= requestedIndex; i++)
+ {
+ startSeconds += lengths[requestedIndex];
}
var position = TimeSpan.FromSeconds(startSeconds).Ticks;
return position;
}
+ private double[] GetSegmentLengths(StreamState state)
+ {
+ var result = new List<double>();
+ var encoder = GetVideoEncoder(state);
+
+ if (string.Equals(encoder, "copy", StringComparison.OrdinalIgnoreCase))
+ {
+ var videoStream = state.VideoStream;
+ if (videoStream.KeyFrames != null && videoStream.KeyFrames.Count > 0)
+ {
+ foreach (var frame in videoStream.KeyFrames)
+ {
+ var seconds = TimeSpan.FromMilliseconds(frame).TotalSeconds;
+ seconds -= result.Sum();
+ result.Add(seconds);
+ }
+ return result.ToArray();
+ }
+ }
+
+ var ticks = state.RunTimeTicks ?? 0;
+
+ var segmentLengthTicks = TimeSpan.FromSeconds(state.SegmentLength).Ticks;
+
+ while (ticks > 0)
+ {
+ var length = ticks >= segmentLengthTicks ? segmentLengthTicks : ticks;
+
+ result.Add(TimeSpan.FromTicks(length).TotalSeconds);
+
+ ticks -= length;
+ }
+
+ return result.ToArray();
+ }
+
public int? GetCurrentTranscodingIndex(string playlist, string segmentExtension)
{
var job = ApiEntryPoint.Instance.GetTranscodingJob(playlist, TranscodingJobType);
@@ -385,17 +422,16 @@ namespace MediaBrowser.Api.Playback.Hls
return Path.Combine(folder, filename + index.ToString(UsCulture) + GetSegmentFileExtension(state));
}
- private async Task<object> GetSegmentResult(string playlistPath,
+ private async Task<object> GetSegmentResult(StreamState state, string playlistPath,
string segmentPath,
int segmentIndex,
- int segmentLength,
TranscodingJob transcodingJob,
CancellationToken cancellationToken)
{
// If all transcoding has completed, just return immediately
if (transcodingJob != null && transcodingJob.HasExited && File.Exists(segmentPath))
{
- return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
+ return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
}
var segmentFilename = Path.GetFileName(segmentPath);
@@ -415,7 +451,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
if (File.Exists(segmentPath))
{
- return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
+ return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
}
//break;
}
@@ -466,13 +502,12 @@ namespace MediaBrowser.Api.Playback.Hls
//}
cancellationToken.ThrowIfCancellationRequested();
- return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
+ return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
}
- private object GetSegmentResult(string segmentPath, int index, int segmentLength, TranscodingJob transcodingJob)
+ private object GetSegmentResult(StreamState state, string segmentPath, int index, TranscodingJob transcodingJob)
{
- var segmentEndingSeconds = (1 + index) * segmentLength;
- var segmentEndingPositionTicks = TimeSpan.FromSeconds(segmentEndingSeconds).Ticks;
+ var segmentEndingPositionTicks = GetEndPositionTicks(state, index);
return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
{
@@ -699,25 +734,23 @@ namespace MediaBrowser.Api.Playback.Hls
{
var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);
+ var segmentLengths = GetSegmentLengths(state);
+
var builder = new StringBuilder();
builder.AppendLine("#EXTM3U");
builder.AppendLine("#EXT-X-VERSION:3");
- builder.AppendLine("#EXT-X-TARGETDURATION:" + (state.SegmentLength).ToString(UsCulture));
+ builder.AppendLine("#EXT-X-TARGETDURATION:" + Math.Ceiling((segmentLengths.Length > 0 ? segmentLengths.Max() : state.SegmentLength)).ToString(UsCulture));
builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
var queryStringIndex = Request.RawUrl.IndexOf('?');
var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
- var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds;
-
var index = 0;
- while (seconds > 0)
+ foreach (var length in segmentLengths)
{
- var length = seconds >= state.SegmentLength ? state.SegmentLength : seconds;
-
- builder.AppendLine("#EXTINF:" + length.ToString(UsCulture) + ",");
+ builder.AppendLine("#EXTINF:" + length.ToString("0.000000", UsCulture) + ",");
builder.AppendLine(string.Format("hlsdynamic/{0}/{1}{2}{3}",
@@ -726,7 +759,6 @@ namespace MediaBrowser.Api.Playback.Hls
GetSegmentFileExtension(isOutputVideo),
queryString));
- seconds -= state.SegmentLength;
index++;
}
@@ -837,7 +869,7 @@ namespace MediaBrowser.Api.Playback.Hls
// Add resolution params, if specified
if (!hasGraphicalSubs)
{
- args += GetOutputSizeParam(state, codec, false);
+ args += GetOutputSizeParam(state, codec, EnableCopyTs(state));
}
// This is for internal graphical subs
@@ -846,17 +878,17 @@ namespace MediaBrowser.Api.Playback.Hls
args += GetGraphicalSubtitleParam(state, codec);
}
- args += " -flags +loop-global_header -sc_threshold 0";
- }
-
- if (!EnableSplitTranscoding(state))
- {
- //args += " -copyts";
+ args += " -flags -global_header -sc_threshold 0";
}
return args;
}
+ private bool EnableCopyTs(StreamState state)
+ {
+ return state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream;
+ }
+
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
{
var threads = GetNumberOfThreads(state, false);
@@ -869,22 +901,7 @@ namespace MediaBrowser.Api.Playback.Hls
var toTimeParam = string.Empty;
var timestampOffsetParam = string.Empty;
- if (EnableSplitTranscoding(state))
- {
- var startTime = state.Request.StartTimeTicks ?? 0;
- var durationSeconds = ApiEntryPoint.Instance.GetEncodingOptions().ThrottleThresholdInSeconds;
-
- var endTime = startTime + TimeSpan.FromSeconds(durationSeconds).Ticks;
- endTime = Math.Min(endTime, state.RunTimeTicks.Value);
-
- if (endTime < state.RunTimeTicks.Value)
- {
- //toTimeParam = " -to " + MediaEncoder.GetTimeParameter(endTime);
- toTimeParam = " -t " + MediaEncoder.GetTimeParameter(TimeSpan.FromSeconds(durationSeconds).Ticks);
- }
- }
-
- if (state.IsOutputVideo && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && (state.Request.StartTimeTicks ?? 0) > 0)
+ if (state.IsOutputVideo && !EnableCopyTs(state) && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && (state.Request.StartTimeTicks ?? 0) > 0)
{
timestampOffsetParam = " -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0).ToString(CultureInfo.InvariantCulture);
}
@@ -926,36 +943,7 @@ namespace MediaBrowser.Api.Playback.Hls
protected override bool EnableThrottling(StreamState state)
{
- return !EnableSplitTranscoding(state);
- }
-
- private bool EnableSplitTranscoding(StreamState state)
- {
- return false;
- if (string.Equals(Request.QueryString["EnableSplitTranscoding"], "false", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- return state.RunTimeTicks.HasValue && state.IsOutputVideo;
- }
-
- protected override bool EnableStreamCopy
- {
- get
- {
- return false;
- }
+ return true;
}
/// <summary>
diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs
index 277e02bf9..e9ac45fa2 100644
--- a/MediaBrowser.Api/StartupWizardService.cs
+++ b/MediaBrowser.Api/StartupWizardService.cs
@@ -3,8 +3,10 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Connect;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.LiveTv;
using ServiceStack;
using System;
using System.Linq;
@@ -49,13 +51,15 @@ namespace MediaBrowser.Api
private readonly IServerApplicationHost _appHost;
private readonly IUserManager _userManager;
private readonly IConnectManager _connectManager;
+ private readonly ILiveTvManager _liveTvManager;
- public StartupWizardService(IServerConfigurationManager config, IServerApplicationHost appHost, IUserManager userManager, IConnectManager connectManager)
+ public StartupWizardService(IServerConfigurationManager config, IServerApplicationHost appHost, IUserManager userManager, IConnectManager connectManager, ILiveTvManager liveTvManager)
{
_config = config;
_appHost = appHost;
_userManager = userManager;
_connectManager = connectManager;
+ _liveTvManager = liveTvManager;
}
public void Post(ReportStartupWizardComplete request)
@@ -68,6 +72,7 @@ namespace MediaBrowser.Api
_config.Configuration.EnableUserSpecificUserViews = true;
_config.Configuration.EnableCustomPathSubFolders = true;
_config.Configuration.DisableXmlSavers = true;
+ _config.Configuration.DisableStartupScan = true;
_config.SaveConfiguration();
}
@@ -83,7 +88,7 @@ namespace MediaBrowser.Api
public object Get(GetStartupConfiguration request)
{
- return new StartupConfiguration
+ var result = new StartupConfiguration
{
UICulture = _config.Configuration.UICulture,
EnableInternetProviders = _config.Configuration.EnableInternetProviders,
@@ -91,6 +96,22 @@ namespace MediaBrowser.Api
MetadataCountryCode = _config.Configuration.MetadataCountryCode,
PreferredMetadataLanguage = _config.Configuration.PreferredMetadataLanguage
};
+
+ var tvConfig = GetLiveTVConfiguration();
+
+ if (tvConfig.TunerHosts.Count > 0)
+ {
+ result.LiveTvTunerPath = tvConfig.TunerHosts[0].Url;
+ result.LiveTvTunerType = tvConfig.TunerHosts[0].Type;
+ }
+
+ if (tvConfig.ListingProviders.Count > 0)
+ {
+ result.LiveTvGuideProviderId = tvConfig.ListingProviders[0].Id;
+ result.LiveTvGuideProviderType = tvConfig.ListingProviders[0].Type;
+ }
+
+ return result;
}
public void Post(UpdateStartupConfiguration request)
@@ -101,6 +122,9 @@ namespace MediaBrowser.Api
_config.Configuration.MetadataCountryCode = request.MetadataCountryCode;
_config.Configuration.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
_config.SaveConfiguration();
+
+ var task = UpdateTuners(request);
+ Task.WaitAll(task);
}
public object Get(GetStartupUser request)
@@ -141,6 +165,51 @@ namespace MediaBrowser.Api
return result;
}
+
+ private async Task UpdateTuners(UpdateStartupConfiguration request)
+ {
+ var config = GetLiveTVConfiguration();
+ var save = false;
+
+ if (string.IsNullOrWhiteSpace(request.LiveTvTunerPath) ||
+ string.IsNullOrWhiteSpace(request.LiveTvTunerType))
+ {
+ if (config.TunerHosts.Count > 0)
+ {
+ config.TunerHosts.Clear();
+ save = true;
+ }
+ }
+ else
+ {
+ if (!config.TunerHosts.Any(i => string.Equals(i.Type, request.LiveTvTunerType, StringComparison.OrdinalIgnoreCase) && string.Equals(i.Url, request.LiveTvTunerPath, StringComparison.OrdinalIgnoreCase)))
+ {
+ // Add tuner
+ await _liveTvManager.SaveTunerHost(new TunerHostInfo
+ {
+ IsEnabled = true,
+ Type = request.LiveTvTunerType,
+ Url = request.LiveTvTunerPath
+
+ }).ConfigureAwait(false);
+ }
+ }
+
+ if (save)
+ {
+ SaveLiveTVConfiguration(config);
+ }
+ }
+
+ private void SaveLiveTVConfiguration(LiveTvOptions config)
+ {
+ _config.SaveConfiguration("livetv", config);
+ }
+
+ private LiveTvOptions GetLiveTVConfiguration()
+ {
+ return _config.GetConfiguration<LiveTvOptions>("livetv");
+ }
}
public class StartupConfiguration
@@ -150,6 +219,10 @@ namespace MediaBrowser.Api
public bool SaveLocalMeta { get; set; }
public string MetadataCountryCode { get; set; }
public string PreferredMetadataLanguage { get; set; }
+ public string LiveTvTunerType { get; set; }
+ public string LiveTvTunerPath { get; set; }
+ public string LiveTvGuideProviderId { get; set; }
+ public string LiveTvGuideProviderType { get; set; }
}
public class StartupInfo
diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs
index 0a49cf47c..aa1226901 100644
--- a/MediaBrowser.Api/System/SystemService.cs
+++ b/MediaBrowser.Api/System/SystemService.cs
@@ -178,7 +178,8 @@ namespace MediaBrowser.Api.System
ServerName = result.ServerName,
Version = result.Version,
LocalAddress = result.LocalAddress,
- WanAddress = result.WanAddress
+ WanAddress = result.WanAddress,
+ OperatingSystem = result.OperatingSystem
};
return ToOptimizedResult(publicInfo);
diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs
index 9e56907da..d383bd0ad 100644
--- a/MediaBrowser.Api/UserLibrary/GenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/GenresService.cs
@@ -1,12 +1,10 @@
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
using ServiceStack;
using System;
using System.Collections.Generic;
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index b412a6480..7d3290c2f 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -117,7 +117,16 @@ namespace MediaBrowser.Api.UserLibrary
if (!string.IsNullOrEmpty(request.Ids))
{
request.Recursive = true;
- var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
+ var query = GetItemsQuery(request, user);
+ var result = await ((Folder)item).GetItems(query).ConfigureAwait(false);
+
+ if (string.IsNullOrWhiteSpace(request.SortBy))
+ {
+ var ids = query.ItemIds.ToList();
+
+ // Try to preserve order
+ result.Items = result.Items.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToArray();
+ }
return new Tuple<QueryResult<BaseItem>, bool>(result, true);
}
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs
index de7987bd2..7034113d7 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs
+++ b/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs
@@ -106,9 +106,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
public void QueueScheduledTask<T>(TaskExecutionOptions options)
where T : IScheduledTask
{
- var scheduledTask = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
+ var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == typeof(T));
- QueueScheduledTask(scheduledTask, options);
+ if (scheduledTask == null)
+ {
+ Logger.Error("Unable to find scheduled task of type {0} in QueueScheduledTask.", typeof(T).Name);
+ }
+ else
+ {
+ QueueScheduledTask(scheduledTask, options);
+ }
}
public void QueueScheduledTask<T>()
@@ -124,9 +131,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
/// <param name="options">The task options.</param>
public void QueueScheduledTask(IScheduledTask task, TaskExecutionOptions options)
{
- var scheduledTask = ScheduledTasks.First(t => t.ScheduledTask.GetType() == task.GetType());
+ var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == task.GetType());
- QueueScheduledTask(scheduledTask, options);
+ if (scheduledTask == null)
+ {
+ Logger.Error("Unable to find scheduled task of type {0} in QueueScheduledTask.", task.GetType().Name);
+ }
+ else
+ {
+ QueueScheduledTask(scheduledTask, options);
+ }
}
/// <summary>
diff --git a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs b/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
index 23785083b..5f205d69e 100644
--- a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
+++ b/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
@@ -149,10 +149,12 @@ namespace MediaBrowser.Common.Implementations.Updates
/// Gets all available packages.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
+ /// <param name="withRegistration">if set to <c>true</c> [with registration].</param>
/// <param name="packageType">Type of the package.</param>
/// <param name="applicationVersion">The application version.</param>
/// <returns>Task{List{PackageInfo}}.</returns>
public async Task<IEnumerable<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
+ bool withRegistration = true,
PackageType? packageType = null,
Version applicationVersion = null)
{
@@ -163,13 +165,22 @@ namespace MediaBrowser.Common.Implementations.Updates
{ "systemid", _applicationHost.SystemId }
};
- using (var json = await _httpClient.Post(MbAdmin.HttpsUrl + "service/package/retrieveall", data, cancellationToken).ConfigureAwait(false))
+ if (withRegistration)
{
- cancellationToken.ThrowIfCancellationRequested();
+ using (var json = await _httpClient.Post(MbAdmin.HttpsUrl + "service/package/retrieveall", data, cancellationToken).ConfigureAwait(false))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
- var packages = _jsonSerializer.DeserializeFromStream<List<PackageInfo>>(json).ToList();
+ var packages = _jsonSerializer.DeserializeFromStream<List<PackageInfo>>(json).ToList();
+
+ return FilterPackages(packages, packageType, applicationVersion);
+ }
+ }
+ else
+ {
+ var packages = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
- return FilterPackages(packages, packageType, applicationVersion);
+ return FilterPackages(packages.ToList(), packageType, applicationVersion);
}
}
diff --git a/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs b/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs
index 15109be4f..b615adf81 100644
--- a/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs
+++ b/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs
@@ -31,6 +31,17 @@ namespace MediaBrowser.Common.ScheduledTasks
public TaskExecutionOptions TaskOptions { get; set; }
/// <summary>
+ /// Gets or sets the first run delay.
+ /// </summary>
+ /// <value>The first run delay.</value>
+ public TimeSpan FirstRunDelay { get; set; }
+
+ public IntervalTrigger()
+ {
+ FirstRunDelay = TimeSpan.FromHours(1);
+ }
+
+ /// <summary>
/// Stars waiting for the trigger action
/// </summary>
/// <param name="lastResult">The last result.</param>
@@ -41,7 +52,7 @@ namespace MediaBrowser.Common.ScheduledTasks
var triggerDate = lastResult != null ?
lastResult.EndTimeUtc.Add(Interval) :
- DateTime.UtcNow.Add(Interval);
+ DateTime.UtcNow.Add(FirstRunDelay);
if (DateTime.UtcNow > triggerDate)
{
diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs
index 592613c54..7d721da6f 100644
--- a/MediaBrowser.Common/Updates/IInstallationManager.cs
+++ b/MediaBrowser.Common/Updates/IInstallationManager.cs
@@ -45,10 +45,12 @@ namespace MediaBrowser.Common.Updates
/// Gets all available packages.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
+ /// <param name="withRegistration">if set to <c>true</c> [with registration].</param>
/// <param name="packageType">Type of the package.</param>
/// <param name="applicationVersion">The application version.</param>
/// <returns>Task{List{PackageInfo}}.</returns>
Task<IEnumerable<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
+ bool withRegistration = true,
PackageType? packageType = null,
Version applicationVersion = null);
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index c4917b0d1..f74a8bda7 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -142,7 +142,7 @@ namespace MediaBrowser.Controller.Entities
public virtual string Path { get; set; }
[IgnoreDataMember]
- protected internal bool IsOffline { get; set; }
+ public bool IsOffline { get; set; }
/// <summary>
/// Returns the folder containing the item.
@@ -419,6 +419,10 @@ namespace MediaBrowser.Controller.Entities
return _sortName ?? (_sortName = CreateSortName());
}
+ set
+ {
+ _sortName = value;
+ }
}
public string GetInternalMetadataPath()
@@ -480,20 +484,15 @@ namespace MediaBrowser.Controller.Entities
public Guid ParentId { get; set; }
- private Folder _parent;
/// <summary>
/// Gets or sets the parent.
/// </summary>
/// <value>The parent.</value>
+ [IgnoreDataMember]
public Folder Parent
{
get
{
- if (_parent != null)
- {
- return _parent;
- }
-
if (ParentId != Guid.Empty)
{
return LibraryManager.GetItemById(ParentId) as Folder;
@@ -501,12 +500,14 @@ namespace MediaBrowser.Controller.Entities
return null;
}
- set { _parent = value; }
+ set
+ {
+
+ }
}
public void SetParent(Folder parent)
{
- Parent = parent;
ParentId = parent == null ? Guid.Empty : parent.Id;
}
@@ -1109,7 +1110,7 @@ namespace MediaBrowser.Controller.Entities
// Could not determine the integer value
if (!value.HasValue)
{
- return true;
+ return !GetBlockUnratedValue(user.Policy);
}
return value.Value <= maxAllowedRating.Value;
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index c3ac77328..84e5e985b 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -48,7 +48,7 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public virtual bool IsPreSorted
{
- get { return ConfigurationManager.Configuration.EnableWindowsShortcuts; }
+ get { return false; }
}
/// <summary>
@@ -120,7 +120,7 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
protected virtual bool SupportsShortcutChildren
{
- get { return false; }
+ get { return ConfigurationManager.Configuration.EnableWindowsShortcuts; }
}
/// <summary>
diff --git a/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs b/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs
index 391c8f7a7..9938a4489 100644
--- a/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs
+++ b/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs
@@ -11,6 +11,7 @@ namespace MediaBrowser.Controller.Entities
bool IsKids { get; set; }
bool IsRepeat { get; set; }
bool? IsHD { get; set; }
+ bool IsSeries { get; set; }
bool IsLive { get; set; }
bool IsPremiere { get; set; }
ProgramAudio? Audio { get; set; }
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index 227a6bd0e..0af4972f7 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -96,6 +96,9 @@ namespace MediaBrowser.Controller.Entities
internal List<Guid> ItemIdsFromPersonFilters { get; set; }
public int? MaxParentalRating { get; set; }
+ public bool? IsCurrentSchema { get; set; }
+ public bool? HasDeadParentId { get; set; }
+
public InternalItemsQuery()
{
Tags = new string[] { };
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index fc07f6778..083ec0cb4 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -100,16 +100,23 @@ namespace MediaBrowser.Controller.Entities.Movies
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{
- var key = this.GetProviderId(MetadataProviders.Tmdb);
+ var key = GetMovieUserDataKey(this);
if (string.IsNullOrWhiteSpace(key))
{
- key = this.GetProviderId(MetadataProviders.Imdb);
+ key = base.CreateUserDataKey();
}
+ return key;
+ }
+
+ public static string GetMovieUserDataKey(BaseItem movie)
+ {
+ var key = movie.GetProviderId(MetadataProviders.Tmdb);
+
if (string.IsNullOrWhiteSpace(key))
{
- key = base.CreateUserDataKey();
+ key = movie.GetProviderId(MetadataProviders.Imdb);
}
return key;
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 6906a25fb..9331ca759 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -502,5 +502,12 @@ namespace MediaBrowser.Controller.Library
/// <param name="query">The query.</param>
/// <returns>List&lt;System.String&gt;.</returns>
List<string> GetPeopleNames(InternalPeopleQuery query);
+
+ /// <summary>
+ /// Queries the items.
+ /// </summary>
+ /// <param name="query">The query.</param>
+ /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
+ QueryResult<BaseItem> QueryItems(InternalItemsQuery query);
}
} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Library/IMusicManager.cs b/MediaBrowser.Controller/Library/IMusicManager.cs
index 0ce0687cc..9baf8b6f1 100644
--- a/MediaBrowser.Controller/Library/IMusicManager.cs
+++ b/MediaBrowser.Controller/Library/IMusicManager.cs
@@ -16,10 +16,10 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Gets the instant mix from artist.
/// </summary>
- /// <param name="name">The name.</param>
+ /// <param name="artist">The artist.</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable{Audio}.</returns>
- IEnumerable<Audio> GetInstantMixFromArtist(string name, User user);
+ IEnumerable<Audio> GetInstantMixFromArtist(MusicArtist artist, User user);
/// <summary>
/// Gets the instant mix from genre.
/// </summary>
diff --git a/MediaBrowser.Controller/LiveTv/IHasRegistrationInfo.cs b/MediaBrowser.Controller/LiveTv/IHasRegistrationInfo.cs
new file mode 100644
index 000000000..3626c18e5
--- /dev/null
+++ b/MediaBrowser.Controller/LiveTv/IHasRegistrationInfo.cs
@@ -0,0 +1,15 @@
+using MediaBrowser.Model.Entities;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.LiveTv
+{
+ public interface IHasRegistrationInfo
+ {
+ /// <summary>
+ /// Gets the registration information.
+ /// </summary>
+ /// <param name="feature">The feature.</param>
+ /// <returns>Task&lt;MBRegistrationRecord&gt;.</returns>
+ Task<MBRegistrationRecord> GetRegistrationInfo(string feature);
+ }
+}
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index df09d39b2..1458c1bc2 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying;
using System.Collections.Generic;
@@ -362,5 +363,23 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="location">The location.</param>
/// <returns>Task&lt;List&lt;NameIdPair&gt;&gt;.</returns>
Task<List<NameIdPair>> GetLineups(string providerType, string providerId, string country, string location);
+
+ /// <summary>
+ /// Gets the registration information.
+ /// </summary>
+ /// <param name="channelId">The channel identifier.</param>
+ /// <param name="programId">The program identifier.</param>
+ /// <param name="feature">The feature.</param>
+ /// <returns>Task&lt;MBRegistrationRecord&gt;.</returns>
+ Task<MBRegistrationRecord> GetRegistrationInfo(string channelId, string programId, string feature);
+
+ /// <summary>
+ /// Adds the channel information.
+ /// </summary>
+ /// <param name="dto">The dto.</param>
+ /// <param name="channel">The channel.</param>
+ /// <param name="options">The options.</param>
+ /// <param name="user">The user.</param>
+ void AddChannelInfo(BaseItemDto dto, LiveTvChannel channel, DtoOptions options, User user);
}
}
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
index 1dd267c93..ba0b82a0b 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
@@ -37,10 +37,12 @@ namespace MediaBrowser.Controller.LiveTv
string ExternalId { get; set; }
string EpisodeTitle { get; set; }
- bool IsSeries { get; set; }
string SeriesTimerId { get; set; }
RecordingStatus Status { get; set; }
DateTime? EndDate { get; set; }
ChannelType ChannelType { get; set; }
+ DateTime DateLastSaved { get; set; }
+ DateTime DateCreated { get; set; }
+ DateTime DateModified { get; set; }
}
}
diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
index a6c8021d9..bedbcffe3 100644
--- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs
+++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
@@ -21,34 +21,30 @@ namespace MediaBrowser.Controller.LiveTv
/// <summary>
/// Gets the channels.
/// </summary>
- /// <param name="info">The information.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;IEnumerable&lt;ChannelInfo&gt;&gt;.</returns>
- Task<IEnumerable<ChannelInfo>> GetChannels(TunerHostInfo info, CancellationToken cancellationToken);
+ Task<IEnumerable<ChannelInfo>> GetChannels(CancellationToken cancellationToken);
/// <summary>
/// Gets the tuner infos.
/// </summary>
- /// <param name="info">The information.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;List&lt;LiveTvTunerInfo&gt;&gt;.</returns>
- Task<List<LiveTvTunerInfo>> GetTunerInfos(TunerHostInfo info, CancellationToken cancellationToken);
+ Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken);
/// <summary>
/// Gets the channel stream.
/// </summary>
- /// <param name="info">The information.</param>
/// <param name="channelId">The channel identifier.</param>
/// <param name="streamId">The stream identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
- Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken);
+ Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken);
/// <summary>
/// Gets the channel stream media sources.
/// </summary>
- /// <param name="info">The information.</param>
/// <param name="channelId">The channel identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns>
- Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken);
+ Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
/// <summary>
/// Validates the specified information.
/// </summary>
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
index b54a7aaee..12052905f 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.LiveTv;
@@ -17,10 +18,25 @@ namespace MediaBrowser.Controller.LiveTv
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{
+ if (IsMovie)
+ {
+ var key = Movie.GetMovieUserDataKey(this);
+
+ if (!string.IsNullOrWhiteSpace(key))
+ {
+ return key;
+ }
+ }
return GetClientTypeName() + "-" + Name;
}
/// <summary>
+ /// Gets or sets the etag.
+ /// </summary>
+ /// <value>The etag.</value>
+ public string Etag { get; set; }
+
+ /// <summary>
/// Id of the program.
/// </summary>
public string ExternalId { get; set; }
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
index aaaff6bdb..960f8054a 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -40,6 +41,16 @@ namespace MediaBrowser.Controller.LiveTv
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{
+ if (IsMovie)
+ {
+ var key = Movie.GetMovieUserDataKey(this);
+
+ if (!string.IsNullOrWhiteSpace(key))
+ {
+ return key;
+ }
+ }
+
var name = GetClientTypeName();
if (!string.IsNullOrEmpty(ProgramId))
diff --git a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs
index 3f8fc639f..a6a3e6108 100644
--- a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs
@@ -33,6 +33,11 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <value>The overview.</value>
public string Overview { get; set; }
+ /// <summary>
+ /// Gets or sets the short overview.
+ /// </summary>
+ /// <value>The short overview.</value>
+ public string ShortOverview { get; set; }
/// <summary>
/// The start date of the program, in UTC.
@@ -165,7 +170,22 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <value>The show identifier.</value>
public string ShowId { get; set; }
-
+ /// <summary>
+ /// Gets or sets the season number.
+ /// </summary>
+ /// <value>The season number.</value>
+ public int? SeasonNumber { get; set; }
+ /// <summary>
+ /// Gets or sets the episode number.
+ /// </summary>
+ /// <value>The episode number.</value>
+ public int? EpisodeNumber { get; set; }
+ /// <summary>
+ /// Gets or sets the etag.
+ /// </summary>
+ /// <value>The etag.</value>
+ public string Etag { get; set; }
+
public ProgramInfo()
{
Genres = new List<string>();
diff --git a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
index 42064cf50..3006b9bbe 100644
--- a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
@@ -191,6 +191,12 @@ namespace MediaBrowser.Controller.LiveTv
/// <value>The show identifier.</value>
public string ShowId { get; set; }
+ /// <summary>
+ /// Gets or sets the date last updated.
+ /// </summary>
+ /// <value>The date last updated.</value>
+ public DateTime DateLastUpdated { get; set; }
+
public RecordingInfo()
{
Genres = new List<string>();
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index b84fe3c71..24309734f 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -199,6 +199,7 @@
<Compile Include="Library\NameExtensions.cs" />
<Compile Include="Library\PlaybackStopEventArgs.cs" />
<Compile Include="Library\UserDataSaveEventArgs.cs" />
+ <Compile Include="LiveTv\IHasRegistrationInfo.cs" />
<Compile Include="LiveTv\IListingsProvider.cs" />
<Compile Include="LiveTv\ILiveTvItem.cs" />
<Compile Include="LiveTv\ITunerHost.cs" />
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index 5bec7980a..23285b612 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -116,5 +116,12 @@ namespace MediaBrowser.Controller.MediaEncoding
Task<string> EncodeVideo(EncodingJobOptions options,
IProgress<double> progress,
CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Escapes the subtitle filter path.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <returns>System.String.</returns>
+ string EscapeSubtitleFilterPath(string path);
}
}
diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs
index 64b20c13e..b3e82f925 100644
--- a/MediaBrowser.Controller/Session/SessionInfo.cs
+++ b/MediaBrowser.Controller/Session/SessionInfo.cs
@@ -126,6 +126,12 @@ namespace MediaBrowser.Controller.Session
public ISessionController SessionController { get; set; }
/// <summary>
+ /// Gets or sets the application icon URL.
+ /// </summary>
+ /// <value>The application icon URL.</value>
+ public string AppIconUrl { get; set; }
+
+ /// <summary>
/// Gets or sets the supported commands.
/// </summary>
/// <value>The supported commands.</value>
diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
index 860c736ea..36ad27290 100644
--- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
+++ b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
@@ -63,4 +63,15 @@ namespace MediaBrowser.Controller.Sync
/// <returns>Task&lt;SyncedFileInfo&gt;.</returns>
Task<SyncedFileInfo> SendFile(string path, string[] pathParts, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken);
}
+
+ public interface IHasDuplicateCheck
+ {
+ /// <summary>
+ /// Allows the duplicate job item.
+ /// </summary>
+ /// <param name="original">The original.</param>
+ /// <param name="duplicate">The duplicate.</param>
+ /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
+ bool AllowDuplicateJobItem(SyncJobItem original, SyncJobItem duplicate);
+ }
}
diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs
index 25514f1dc..390f44267 100644
--- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs
+++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs
@@ -193,6 +193,9 @@ namespace MediaBrowser.Dlna.Didl
if (string.Equals(subtitleMode, "CaptionInfoEx", StringComparison.OrdinalIgnoreCase))
{
+ // <sec:CaptionInfoEx sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfoEx>
+ // <sec:CaptionInfo sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfo>
+
//var res = container.OwnerDocument.CreateElement("SEC", "CaptionInfoEx");
//res.InnerText = info.Url;
@@ -201,6 +204,16 @@ namespace MediaBrowser.Dlna.Didl
//res.SetAttribute("type", info.Format.ToLower());
//container.AppendChild(res);
}
+ else if (string.Equals(subtitleMode, "smi", StringComparison.OrdinalIgnoreCase))
+ {
+ var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
+
+ res.InnerText = info.Url;
+
+ res.SetAttribute("protocolInfo", "http-get:*:smi/caption:*");
+
+ container.AppendChild(res);
+ }
else
{
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs
index 9ce62034b..95e2aaa77 100644
--- a/MediaBrowser.Dlna/DlnaManager.cs
+++ b/MediaBrowser.Dlna/DlnaManager.cs
@@ -530,6 +530,7 @@ namespace MediaBrowser.Dlna
new SonyBravia2011Profile(),
new SonyBravia2012Profile(),
new SonyBravia2013Profile(),
+ new SonyBravia2014Profile(),
new SonyBlurayPlayer2013Profile(),
new SonyBlurayPlayerProfile(),
new PanasonicVieraProfile(),
@@ -547,7 +548,8 @@ namespace MediaBrowser.Dlna
new DefaultProfile(),
new PopcornHourProfile(),
new VlcProfile(),
- new BubbleUpnpProfile()
+ new BubbleUpnpProfile(),
+ new KodiProfile(),
};
foreach (var item in list)
diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
index 06aaff734..c49cbc654 100644
--- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
+++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
@@ -81,7 +81,9 @@
<Compile Include="Profiles\DefaultProfile.cs" />
<Compile Include="Profiles\DirectTvProfile.cs" />
<Compile Include="Profiles\DishHopperJoeyProfile.cs" />
+ <Compile Include="Profiles\KodiProfile.cs" />
<Compile Include="Profiles\PopcornHourProfile.cs" />
+ <Compile Include="Profiles\SonyBravia2014Profile.cs" />
<Compile Include="Profiles\SonyPs4Profile.cs" />
<Compile Include="Profiles\VlcProfile.cs" />
<Compile Include="Ssdp\DeviceDiscoveryInfo.cs" />
@@ -166,7 +168,9 @@
<EmbeddedResource Include="Profiles\Xml\Sony Bravia %282010%29.xml" />
<EmbeddedResource Include="Profiles\Xml\Sony Bravia %282011%29.xml" />
<EmbeddedResource Include="Profiles\Xml\Sony Bravia %282012%29.xml" />
- <EmbeddedResource Include="Profiles\Xml\Sony Bravia %282013%29.xml" />
+ <EmbeddedResource Include="Profiles\Xml\Sony Bravia %282013%29.xml">
+ <SubType>Designer</SubType>
+ </EmbeddedResource>
<EmbeddedResource Include="Profiles\Xml\Sony PlayStation 3.xml" />
<EmbeddedResource Include="Profiles\Xml\WDTV Live.xml" />
<EmbeddedResource Include="Profiles\Xml\Xbox 360.xml">
@@ -175,7 +179,9 @@
<EmbeddedResource Include="Profiles\Xml\Xbox One.xml" />
</ItemGroup>
<ItemGroup>
- <EmbeddedResource Include="Profiles\Xml\Default.xml" />
+ <EmbeddedResource Include="Profiles\Xml\Default.xml">
+ <SubType>Designer</SubType>
+ </EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Images\logo120.jpg" />
@@ -214,6 +220,16 @@
<ItemGroup>
<EmbeddedResource Include="Profiles\Xml\Sony PlayStation 4.xml" />
</ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Profiles\Xml\Kodi.xml">
+ <SubType>Designer</SubType>
+ </EmbeddedResource>
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Profiles\Xml\Sony Bravia %282014%29.xml">
+ <SubType>Designer</SubType>
+ </EmbeddedResource>
+ </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
diff --git a/MediaBrowser.Dlna/Profiles/KodiProfile.cs b/MediaBrowser.Dlna/Profiles/KodiProfile.cs
new file mode 100644
index 000000000..75c323a1a
--- /dev/null
+++ b/MediaBrowser.Dlna/Profiles/KodiProfile.cs
@@ -0,0 +1,97 @@
+using MediaBrowser.Model.Dlna;
+using System.Xml.Serialization;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+ [XmlRoot("Profile")]
+ public class KodiProfile : DefaultProfile
+ {
+ public KodiProfile()
+ {
+ Name = "Kodi";
+
+ MaxStreamingBitrate = 100000000;
+ MaxStaticBitrate = 100000000;
+ MusicStreamingTranscodingBitrate = 1280000;
+ MusicSyncBitrate = 1280000;
+
+ TimelineOffsetSeconds = 5;
+
+ Identification = new DeviceIdentification
+ {
+ ModelName = "Kodi",
+
+ Headers = new[]
+ {
+ new HttpHeaderInfo {Name = "User-Agent", Value = "Kodi", Match = HeaderMatchType.Substring}
+ }
+ };
+
+ TranscodingProfiles = new[]
+ {
+ new TranscodingProfile
+ {
+ Container = "mp3",
+ AudioCodec = "mp3",
+ Type = DlnaProfileType.Audio
+ },
+
+ new TranscodingProfile
+ {
+ Container = "ts",
+ Type = DlnaProfileType.Video,
+ AudioCodec = "aac",
+ VideoCodec = "h264"
+ },
+
+ new TranscodingProfile
+ {
+ Container = "jpeg",
+ Type = DlnaProfileType.Photo
+ }
+ };
+
+ DirectPlayProfiles = new[]
+ {
+ new DirectPlayProfile
+ {
+ Container = "",
+ Type = DlnaProfileType.Video
+ },
+
+ new DirectPlayProfile
+ {
+ Container = "",
+ Type = DlnaProfileType.Audio
+ },
+
+ new DirectPlayProfile
+ {
+ Container = "",
+ Type = DlnaProfileType.Photo,
+ }
+ };
+
+ ResponseProfiles = new ResponseProfile[] { };
+
+ ContainerProfiles = new ContainerProfile[] { };
+
+ CodecProfiles = new CodecProfile[] { };
+
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.External,
+ },
+
+ new SubtitleProfile
+ {
+ Format = "sub",
+ Method = SubtitleDeliveryMethod.External,
+ }
+ };
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/Profiles/SonyBravia2014Profile.cs b/MediaBrowser.Dlna/Profiles/SonyBravia2014Profile.cs
new file mode 100644
index 000000000..02dbc88ab
--- /dev/null
+++ b/MediaBrowser.Dlna/Profiles/SonyBravia2014Profile.cs
@@ -0,0 +1,309 @@
+using MediaBrowser.Model.Dlna;
+using System.Xml.Serialization;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+ [XmlRoot("Profile")]
+ public class SonyBravia2014Profile : DefaultProfile
+ {
+ public SonyBravia2014Profile()
+ {
+ Name = "Sony Bravia (2014)";
+
+ Identification = new DeviceIdentification
+ {
+ FriendlyName = @"(KDL-\d{2}W[5-9]\d{2}B|KDL-\d{2}R480|XBR-\d{2}X[89]\d{2}B|KD-\d{2}[SX][89]\d{3}B).*",
+ Manufacturer = "Sony",
+
+ Headers = new[]
+ {
+ new HttpHeaderInfo
+ {
+ Name = "X-AV-Client-Info",
+ Value = @".*(KDL-\d{2}W[5-9]\d{2}B|KDL-\d{2}R480|XBR-\d{2}X[89]\d{2}B|KD-\d{2}[SX][89]\d{3}B).*",
+ Match = HeaderMatchType.Regex
+ }
+ }
+ };
+
+ AddXmlRootAttribute("xmlns:av", "urn:schemas-sony-com:av");
+
+ AlbumArtPn = "JPEG_TN";
+
+ ModelName = "Windows Media Player Sharing";
+ ModelNumber = "3.0";
+ ModelUrl = "http://www.microsoft.com/";
+ Manufacturer = "Microsoft Corporation";
+ ManufacturerUrl = "http://www.microsoft.com/";
+ SonyAggregationFlags = "10";
+ EnableSingleAlbumArtLimit = true;
+ EnableAlbumArtInDidl = true;
+
+ TranscodingProfiles = new[]
+ {
+ new TranscodingProfile
+ {
+ Container = "mp3",
+ Type = DlnaProfileType.Audio
+ },
+ new TranscodingProfile
+ {
+ Container = "ts",
+ VideoCodec = "h264",
+ AudioCodec = "ac3",
+ Type = DlnaProfileType.Video,
+ EnableMpegtsM2TsMode = true
+ },
+ new TranscodingProfile
+ {
+ Container = "jpeg",
+ Type = DlnaProfileType.Photo
+ }
+ };
+
+ DirectPlayProfiles = new[]
+ {
+ new DirectPlayProfile
+ {
+ Container = "ts",
+ VideoCodec = "h264",
+ AudioCodec = "ac3,eac3,aac,mp3",
+ Type = DlnaProfileType.Video
+ },
+ new DirectPlayProfile
+ {
+ Container = "ts",
+ VideoCodec = "mpeg2video",
+ AudioCodec = "mp3,mp2",
+ Type = DlnaProfileType.Video
+ },
+ new DirectPlayProfile
+ {
+ Container = "mp4",
+ VideoCodec = "h264,mpeg4",
+ AudioCodec = "ac3,eac3,aac,mp3,mp2",
+ Type = DlnaProfileType.Video
+ },
+ new DirectPlayProfile
+ {
+ Container = "mov",
+ VideoCodec = "h264,mpeg4,mjpeg",
+ AudioCodec = "ac3,eac3,aac,mp3,mp2",
+ Type = DlnaProfileType.Video
+ },
+ new DirectPlayProfile
+ {
+ Container = "mkv",
+ VideoCodec = "h264,mpeg4,vp8",
+ AudioCodec = "ac3,eac3,aac,mp3,mp2,pcm,vorbis",
+ Type = DlnaProfileType.Video
+ },
+ new DirectPlayProfile
+ {
+ Container = "avi",
+ VideoCodec = "mpeg4",
+ AudioCodec = "ac3,eac3,mp3",
+ Type = DlnaProfileType.Video
+ },
+ new DirectPlayProfile
+ {
+ Container = "avi",
+ VideoCodec = "mjpeg",
+ AudioCodec = "pcm",
+ Type = DlnaProfileType.Video
+ },
+ new DirectPlayProfile
+ {
+ Container = "mpeg",
+ VideoCodec = "mpeg2video,mpeg1video",
+ AudioCodec = "mp3,mp2",
+ Type = DlnaProfileType.Video
+ },
+ new DirectPlayProfile
+ {
+ Container = "asf",
+ VideoCodec = "wmv2,wmv3,vc1",
+ AudioCodec = "wmav2,wmapro,wmavoice",
+ Type = DlnaProfileType.Video
+ },
+ new DirectPlayProfile
+ {
+ Container = "mp3",
+ AudioCodec = "mp3",
+ Type = DlnaProfileType.Audio
+ },
+ new DirectPlayProfile
+ {
+ Container = "mp4",
+ AudioCodec = "aac",
+ Type = DlnaProfileType.Audio
+ },
+ new DirectPlayProfile
+ {
+ Container = "wav",
+ AudioCodec = "pcm",
+ Type = DlnaProfileType.Audio
+ },
+ new DirectPlayProfile
+ {
+ Container = "asf",
+ AudioCodec = "wmav2,wmapro,wmavoice",
+ Type = DlnaProfileType.Audio
+ },
+ new DirectPlayProfile
+ {
+ Container = "jpeg",
+ Type = DlnaProfileType.Photo
+ }
+ };
+
+ ContainerProfiles = new[]
+ {
+ new ContainerProfile
+ {
+ Type = DlnaProfileType.Photo,
+
+ Conditions = new []
+ {
+ new ProfileCondition
+ {
+ Condition = ProfileConditionType.LessThanEqual,
+ Property = ProfileConditionValue.Width,
+ Value = "1920"
+ },
+ new ProfileCondition
+ {
+ Condition = ProfileConditionType.LessThanEqual,
+ Property = ProfileConditionValue.Height,
+ Value = "1080"
+ }
+ }
+ }
+ };
+
+ ResponseProfiles = new[]
+ {
+ new ResponseProfile
+ {
+ Container = "ts",
+ VideoCodec="h264",
+ AudioCodec="ac3,aac,mp3",
+ MimeType = "video/vnd.dlna.mpeg-tts",
+ OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
+ Type = DlnaProfileType.Video,
+
+ Conditions = new []
+ {
+ new ProfileCondition
+ {
+ Condition = ProfileConditionType.Equals,
+ Property = ProfileConditionValue.PacketLength,
+ Value = "192"
+ },
+ new ProfileCondition
+ {
+ Condition = ProfileConditionType.Equals,
+ Property = ProfileConditionValue.VideoTimestamp,
+ Value = "Valid"
+ }
+ }
+ },
+
+ new ResponseProfile
+ {
+ Container = "ts",
+ VideoCodec="h264",
+ AudioCodec="ac3,aac,mp3",
+ MimeType = "video/mpeg",
+ OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
+ Type = DlnaProfileType.Video,
+
+ Conditions = new []
+ {
+ new ProfileCondition
+ {
+ Condition = ProfileConditionType.Equals,
+ Property = ProfileConditionValue.PacketLength,
+ Value = "188"
+ }
+ }
+ },
+
+ new ResponseProfile
+ {
+ Container = "ts",
+ VideoCodec="h264",
+ AudioCodec="ac3,aac,mp3",
+ MimeType = "video/vnd.dlna.mpeg-tts",
+ OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
+ Type = DlnaProfileType.Video
+ },
+
+ new ResponseProfile
+ {
+ Container = "ts",
+ VideoCodec="mpeg2video",
+ MimeType = "video/vnd.dlna.mpeg-tts",
+ OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
+ Type = DlnaProfileType.Video
+ },
+
+ new ResponseProfile
+ {
+ Container = "mpeg",
+ VideoCodec="mpeg1video,mpeg2video",
+ MimeType = "video/mpeg",
+ OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
+ Type = DlnaProfileType.Video
+ }
+ };
+
+
+ CodecProfiles = new[]
+ {
+ new CodecProfile
+ {
+ Type = CodecType.Video,
+
+ Conditions = new []
+ {
+ new ProfileCondition
+ {
+ Condition = ProfileConditionType.LessThanEqual,
+ Property = ProfileConditionValue.Width,
+ Value = "1920"
+ },
+ new ProfileCondition
+ {
+ Condition = ProfileConditionType.LessThanEqual,
+ Property = ProfileConditionValue.Height,
+ Value = "1080"
+ },
+ new ProfileCondition
+ {
+ Condition = ProfileConditionType.LessThanEqual,
+ Property = ProfileConditionValue.VideoFramerate,
+ Value = "30"
+ }
+ }
+ },
+
+ new CodecProfile
+ {
+ Type = CodecType.VideoAudio,
+ Codec = "mp3,mp2",
+
+ Conditions = new []
+ {
+ new ProfileCondition
+ {
+ Condition = ProfileConditionType.LessThanEqual,
+ Property = ProfileConditionValue.AudioChannels,
+ Value = "2"
+ }
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Kodi.xml b/MediaBrowser.Dlna/Profiles/Xml/Kodi.xml
new file mode 100644
index 000000000..a174610cb
--- /dev/null
+++ b/MediaBrowser.Dlna/Profiles/Xml/Kodi.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <Name>Kodi</Name>
+ <Identification>
+ <ModelName>Kodi</ModelName>
+ <Headers>
+ <HttpHeaderInfo name="User-Agent" value="Kodi" match="Substring" />
+ </Headers>
+ </Identification>
+ <FriendlyName>Emby</FriendlyName>
+ <Manufacturer>Emby</Manufacturer>
+ <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
+ <ModelName>Emby</ModelName>
+ <ModelDescription>Emby</ModelDescription>
+ <ModelNumber>Emby</ModelNumber>
+ <ModelUrl>http://emby.media/</ModelUrl>
+ <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
+ <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
+ <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
+ <AlbumArtPn>JPEG_SM</AlbumArtPn>
+ <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
+ <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
+ <MaxIconWidth>48</MaxIconWidth>
+ <MaxIconHeight>48</MaxIconHeight>
+ <MaxStreamingBitrate>100000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>100000000</MaxStaticBitrate>
+ <MusicStreamingTranscodingBitrate>1280000</MusicStreamingTranscodingBitrate>
+ <MusicSyncBitrate>1280000</MusicSyncBitrate>
+ <XDlnaDoc>DMS-1.50</XDlnaDoc>
+ <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <TimelineOffsetSeconds>5</TimelineOffsetSeconds>
+ <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
+ <RequiresPlainFolders>false</RequiresPlainFolders>
+ <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
+ <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
+ <EnableDlnaProtocol>true</EnableDlnaProtocol>
+ <XmlRootAttributes />
+ <DirectPlayProfiles>
+ <DirectPlayProfile container="" type="Video" />
+ <DirectPlayProfile container="" type="Audio" />
+ <DirectPlayProfile container="" type="Photo" />
+ </DirectPlayProfiles>
+ <TranscodingProfiles>
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
+ </TranscodingProfiles>
+ <ContainerProfiles />
+ <CodecProfiles />
+ <ResponseProfiles />
+ <SubtitleProfiles>
+ <SubtitleProfile format="srt" method="External" />
+ <SubtitleProfile format="sub" method="External" />
+ </SubtitleProfiles>
+</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml
new file mode 100644
index 000000000..2859743fb
--- /dev/null
+++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml
@@ -0,0 +1,108 @@
+<?xml version="1.0"?>
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <Name>Sony Bravia (2014)</Name>
+ <Identification>
+ <FriendlyName>(KDL-\d{2}W[5-9]\d{2}B|KDL-\d{2}R480|XBR-\d{2}X[89]\d{2}B|KD-\d{2}[SX][89]\d{3}B).*</FriendlyName>
+ <Manufacturer>Sony</Manufacturer>
+ <Headers>
+ <HttpHeaderInfo name="X-AV-Client-Info" value=".*(KDL-\d{2}W[5-9]\d{2}B|KDL-\d{2}R480|XBR-\d{2}X[89]\d{2}B|KD-\d{2}[SX][89]\d{3}B).*" match="Regex" />
+ </Headers>
+ </Identification>
+ <FriendlyName>Emby</FriendlyName>
+ <Manufacturer>Microsoft Corporation</Manufacturer>
+ <ManufacturerUrl>http://www.microsoft.com/</ManufacturerUrl>
+ <ModelName>Windows Media Player Sharing</ModelName>
+ <ModelDescription>Emby</ModelDescription>
+ <ModelNumber>3.0</ModelNumber>
+ <ModelUrl>http://www.microsoft.com/</ModelUrl>
+ <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
+ <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit>
+ <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
+ <AlbumArtPn>JPEG_TN</AlbumArtPn>
+ <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
+ <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
+ <MaxIconWidth>48</MaxIconWidth>
+ <MaxIconHeight>48</MaxIconHeight>
+ <MaxStreamingBitrate>10000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>10000000</MaxStaticBitrate>
+ <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate>
+ <MusicSyncBitrate>128000</MusicSyncBitrate>
+ <XDlnaDoc>DMS-1.50</XDlnaDoc>
+ <SonyAggregationFlags>10</SonyAggregationFlags>
+ <ProtocolInfo>http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000</ProtocolInfo>
+ <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
+ <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
+ <RequiresPlainFolders>false</RequiresPlainFolders>
+ <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
+ <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
+ <EnableDlnaProtocol>true</EnableDlnaProtocol>
+ <XmlRootAttributes>
+ <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
+ </XmlRootAttributes>
+ <DirectPlayProfiles>
+ <DirectPlayProfile container="ts" audioCodec="ac3,eac3,aac,mp3" videoCodec="h264" type="Video" />
+ <DirectPlayProfile container="ts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" />
+ <DirectPlayProfile container="mp4" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" />
+ <DirectPlayProfile container="mov" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4,mjpeg" type="Video" />
+ <DirectPlayProfile container="mkv" audioCodec="ac3,eac3,aac,mp3,mp2,pcm,vorbis" videoCodec="h264,mpeg4,vp8" type="Video" />
+ <DirectPlayProfile container="avi" audioCodec="ac3,eac3,mp3" videoCodec="mpeg4" type="Video" />
+ <DirectPlayProfile container="avi" audioCodec="pcm" videoCodec="mjpeg" type="Video" />
+ <DirectPlayProfile container="mpeg" audioCodec="mp3,mp2" videoCodec="mpeg2video,mpeg1video" type="Video" />
+ <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" videoCodec="wmv2,wmv3,vc1" type="Video" />
+ <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
+ <DirectPlayProfile container="mp4" audioCodec="aac" type="Audio" />
+ <DirectPlayProfile container="wav" audioCodec="pcm" type="Audio" />
+ <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
+ <DirectPlayProfile container="jpeg" type="Photo" />
+ </DirectPlayProfiles>
+ <TranscodingProfiles>
+ <TranscodingProfile container="mp3" type="Audio" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" context="Streaming" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
+ </TranscodingProfiles>
+ <ContainerProfiles>
+ <ContainerProfile type="Photo">
+ <Conditions>
+ <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
+ <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
+ </Conditions>
+ </ContainerProfile>
+ </ContainerProfiles>
+ <CodecProfiles>
+ <CodecProfile type="Video">
+ <Conditions>
+ <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
+ <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
+ <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
+ </Conditions>
+ </CodecProfile>
+ <CodecProfile type="VideoAudio" codec="mp3,mp2">
+ <Conditions>
+ <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="true" />
+ </Conditions>
+ </CodecProfile>
+ </CodecProfiles>
+ <ResponseProfiles>
+ <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
+ <Conditions>
+ <ProfileCondition condition="Equals" property="PacketLength" value="192" isRequired="true" />
+ <ProfileCondition condition="Equals" property="VideoTimestamp" value="Valid" isRequired="true" />
+ </Conditions>
+ </ResponseProfile>
+ <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
+ <Conditions>
+ <ProfileCondition condition="Equals" property="PacketLength" value="188" isRequired="true" />
+ </Conditions>
+ </ResponseProfile>
+ <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
+ <Conditions />
+ </ResponseProfile>
+ <ResponseProfile container="ts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
+ <Conditions />
+ </ResponseProfile>
+ <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">
+ <Conditions />
+ </ResponseProfile>
+ </ResponseProfiles>
+ <SubtitleProfiles />
+</Profile> \ No newline at end of file
diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
index dd88512fb..181f147b4 100644
--- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
@@ -865,13 +865,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
// TODO: Perhaps also use original_size=1920x800 ??
return string.Format("subtitles=filename='{0}'{1},setpts=PTS -{2}/TB",
- subtitlePath.Replace('\\', '/').Replace(":/", "\\:/"),
+ MediaEncoder.EscapeSubtitleFilterPath(subtitlePath),
charsetParam,
seconds.ToString(UsCulture));
}
return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB",
- state.MediaPath.Replace('\\', '/').Replace(":/", "\\:/"),
+ MediaEncoder.EscapeSubtitleFilterPath(state.MediaPath),
state.InternalSubtitleStreamOffset.ToString(UsCulture),
seconds.ToString(UsCulture));
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index c059a8b54..503399f8d 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -8,6 +8,7 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.MediaEncoding.Probing;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
@@ -242,22 +243,27 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (extractKeyFrameInterval && mediaInfo.RunTimeTicks.HasValue)
{
- foreach (var stream in mediaInfo.MediaStreams)
+ if (ConfigurationManager.Configuration.EnableVideoFrameAnalysis && mediaInfo.Size.HasValue && mediaInfo.Size.Value <= ConfigurationManager.Configuration.VideoFrameAnalysisLimitBytes)
{
- if (stream.Type == MediaStreamType.Video && string.Equals(stream.Codec, "h264", StringComparison.OrdinalIgnoreCase))
+ foreach (var stream in mediaInfo.MediaStreams)
{
- try
+ if (stream.Type == MediaStreamType.Video &&
+ string.Equals(stream.Codec, "h264", StringComparison.OrdinalIgnoreCase) &&
+ !stream.IsInterlaced &&
+ !(stream.IsAnamorphic ?? false))
{
- //stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken)
- // .ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
-
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting key frame interval", ex);
+ try
+ {
+ stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken).ConfigureAwait(false);
+ }
+ catch (OperationCanceledException)
+ {
+
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting key frame interval", ex);
+ }
}
}
}
@@ -283,7 +289,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
private async Task<List<int>> GetKeyFrames(string inputPath, int videoStreamIndex, CancellationToken cancellationToken)
{
- const string args = "-i {0} -select_streams v:{1} -show_frames -show_entries frame=pkt_dts,key_frame -print_format compact";
+ inputPath = inputPath.Split(new[] { ':' }, 2).Last().Trim('"');
+
+ const string args = "-show_packets -print_format compact -select_streams v:{1} -show_entries packet=flags -show_entries packet=pts_time \"{0}\"";
var process = new Process
{
@@ -295,7 +303,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
// Must consume both or ffmpeg may hang due to deadlocks. See comments below.
RedirectStandardOutput = true,
RedirectStandardError = true,
- RedirectStandardInput = true,
FileName = FFProbePath,
Arguments = string.Format(args, inputPath, videoStreamIndex.ToString(CultureInfo.InvariantCulture)).Trim(),
@@ -308,9 +315,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
- using (var processWrapper = new ProcessWrapper(process, this, _logger))
+ using (process)
{
- StartProcess(processWrapper);
+ var start = DateTime.UtcNow;
+
+ process.Start();
var lines = new List<int>();
@@ -318,7 +327,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
process.BeginErrorReadLine();
- await StartReadingOutput(process.StandardOutput.BaseStream, lines, 120000, cancellationToken).ConfigureAwait(false);
+ await StartReadingOutput(process.StandardOutput.BaseStream, lines, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@@ -327,41 +336,46 @@ namespace MediaBrowser.MediaEncoding.Encoder
throw;
}
}
- finally
- {
- StopProcess(processWrapper, 100, true);
- }
+ process.WaitForExit();
+
+ _logger.Debug("Keyframe extraction took {0} seconds", (DateTime.UtcNow - start).TotalSeconds);
+ //_logger.Debug("Found keyframes {0}", string.Join(",", lines.ToArray()));
return lines;
}
}
- private async Task StartReadingOutput(Stream source, List<int> lines, int timeoutMs, CancellationToken cancellationToken)
+ private async Task StartReadingOutput(Stream source, List<int> keyframes, CancellationToken cancellationToken)
{
try
{
using (var reader = new StreamReader(source))
{
- while (!reader.EndOfStream)
- {
- cancellationToken.ThrowIfCancellationRequested();
+ var text = await reader.ReadToEndAsync().ConfigureAwait(false);
- var line = await reader.ReadLineAsync().ConfigureAwait(false);
+ var lines = StringHelper.RegexSplit(text, "\r\n");
+ foreach (var line in lines)
+ {
+ if (string.IsNullOrWhiteSpace(line))
+ {
+ continue;
+ }
- var values = (line ?? string.Empty).Split('|')
+ var values = line.Split('|')
.Where(i => !string.IsNullOrWhiteSpace(i))
.Select(i => i.Split('='))
.Where(i => i.Length == 2)
.ToDictionary(i => i[0], i => i[1]);
- string pktDts;
- int frameMs;
- if (values.TryGetValue("pkt_dts", out pktDts) && int.TryParse(pktDts, NumberStyles.Any, CultureInfo.InvariantCulture, out frameMs))
+ string flags;
+ if (values.TryGetValue("flags", out flags) && string.Equals(flags, "k", StringComparison.OrdinalIgnoreCase))
{
- string keyFrame;
- if (values.TryGetValue("key_frame", out keyFrame) && string.Equals(keyFrame, "1", StringComparison.OrdinalIgnoreCase))
+ string pts_time;
+ double frameSeconds;
+ if (values.TryGetValue("pts_time", out pts_time) && double.TryParse(pts_time, NumberStyles.Any, CultureInfo.InvariantCulture, out frameSeconds))
{
- lines.Add(frameMs);
+ var ms = frameSeconds * 1000;
+ keyframes.Add(Convert.ToInt32(ms));
}
}
}
@@ -376,7 +390,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger.ErrorException("Error reading ffprobe output", ex);
}
}
-
/// <summary>
/// The us culture
/// </summary>
@@ -765,6 +778,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
+ public string EscapeSubtitleFilterPath(string path)
+ {
+ return path.Replace('\\', '/').Replace(":/", "\\:/").Replace("'", "'\\\\\\''");
+ }
+
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
@@ -797,7 +815,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
public ProcessWrapper(Process process, MediaEncoder mediaEncoder, ILogger logger)
{
Process = process;
- this._mediaEncoder = mediaEncoder;
+ _mediaEncoder = mediaEncoder;
_logger = logger;
Process.Exited += Process_Exited;
}
@@ -814,7 +832,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
catch (Exception ex)
{
- _logger.ErrorException("Error determing process exit code", ex);
}
lock (_mediaEncoder._runningProcesses)
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index fe616c63e..9d43cafb8 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -586,7 +586,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
process.StandardError.BaseStream.CopyToAsync(logFileStream);
- var ranToCompletion = process.WaitForExit(60000);
+ var ranToCompletion = process.WaitForExit(120000);
if (!ranToCompletion)
{
diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs
index e52e7535b..6107c0fbe 100644
--- a/MediaBrowser.Model/ApiClient/IApiClient.cs
+++ b/MediaBrowser.Model/ApiClient/IApiClient.cs
@@ -320,32 +320,11 @@ namespace MediaBrowser.Model.ApiClient
Task<ItemsResult> GetUserViews(string userId, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
- /// Gets the instant mix from song async.
+ /// Gets the instant mix from item asynchronous.
/// </summary>
/// <param name="query">The query.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetInstantMixFromSongAsync(SimilarItemsQuery query);
-
- /// <summary>
- /// Gets the instant mix from album async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetInstantMixFromAlbumAsync(SimilarItemsQuery query);
-
- /// <summary>
- /// Gets the instant mix from artist async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetInstantMixFromArtistAsync(SimilarItemsQuery query);
-
- /// <summary>
- /// Gets the instant mix from music genre async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetInstantMixFromMusicGenreAsync(SimilarItemsQuery query);
+ /// <returns>Task&lt;ItemsResult&gt;.</returns>
+ Task<ItemsResult> GetInstantMixFromItemAsync(SimilarItemsQuery query);
/// <summary>
/// Gets the similar movies async.
@@ -353,39 +332,7 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="query">The query.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetSimilarMoviesAsync(SimilarItemsQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the similar trailers async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetSimilarTrailersAsync(SimilarItemsQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the similar series async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetSimilarSeriesAsync(SimilarItemsQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the similar albums async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetSimilarAlbumsAsync(SimilarItemsQuery query, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
- /// Gets the similar games async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetSimilarGamesAsync(SimilarItemsQuery query, CancellationToken cancellationToken = default(CancellationToken));
+ Task<ItemsResult> GetSimilarItemsAsync(SimilarItemsQuery query, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Gets the people async.
@@ -443,20 +390,6 @@ namespace MediaBrowser.Model.ApiClient
Task<ItemsResult> GetGenresAsync(ItemsByNameQuery query);
/// <summary>
- /// Gets the music genres async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetMusicGenresAsync(ItemsByNameQuery query);
-
- /// <summary>
- /// Gets the game genres async.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>Task{ItemsResult}.</returns>
- Task<ItemsResult> GetGameGenresAsync(ItemsByNameQuery query);
-
- /// <summary>
/// Gets the studios async.
/// </summary>
/// <param name="query">The query.</param>
@@ -1195,14 +1128,6 @@ namespace MediaBrowser.Model.ApiClient
Task CancelLiveTvSeriesTimerAsync(string id, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
- /// Deletes the live tv recording asynchronous.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task DeleteLiveTvRecordingAsync(string id, CancellationToken cancellationToken = default(CancellationToken));
-
- /// <summary>
/// Gets the default timer information.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
diff --git a/MediaBrowser.Model/Configuration/ChannelOptions.cs b/MediaBrowser.Model/Configuration/ChannelOptions.cs
index dfefa4edc..9bd0ef9c5 100644
--- a/MediaBrowser.Model/Configuration/ChannelOptions.cs
+++ b/MediaBrowser.Model/Configuration/ChannelOptions.cs
@@ -3,19 +3,6 @@
public class ChannelOptions
{
public int? PreferredStreamingWidth { get; set; }
-
public string DownloadPath { get; set; }
- public int? MaxDownloadAge { get; set; }
-
- public string[] DownloadingChannels { get; set; }
-
- public double? DownloadSizeLimit { get; set; }
-
- public ChannelOptions()
- {
- DownloadingChannels = new string[] { };
- DownloadSizeLimit = .5;
- MaxDownloadAge = 30;
- }
}
} \ No newline at end of file
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index 46f9db580..9f95953cf 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -99,6 +99,12 @@ namespace MediaBrowser.Model.Configuration
public bool EnableLocalizedGuids { get; set; }
/// <summary>
+ /// Gets or sets a value indicating whether [disable startup scan].
+ /// </summary>
+ /// <value><c>true</c> if [disable startup scan]; otherwise, <c>false</c>.</value>
+ public bool DisableStartupScan { get; set; }
+
+ /// <summary>
/// Gets or sets a value indicating whether [enable library metadata sub folder].
/// </summary>
/// <value><c>true</c> if [enable library metadata sub folder]; otherwise, <c>false</c>.</value>
@@ -216,11 +222,13 @@ namespace MediaBrowser.Model.Configuration
public bool DisableXmlSavers { get; set; }
public bool EnableWindowsShortcuts { get; set; }
+ public bool EnableVideoFrameAnalysis { get; set; }
+ public long VideoFrameAnalysisLimitBytes { get; set; }
+
/// <summary>
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
/// </summary>
public ServerConfiguration()
- : base()
{
ImageSavingConvention = ImageSavingConvention.Compatible;
PublicPort = 8096;
@@ -265,6 +273,9 @@ namespace MediaBrowser.Model.Configuration
PeopleMetadataOptions = new PeopleMetadataOptions();
+ EnableVideoFrameAnalysis = true;
+ VideoFrameAnalysisLimitBytes = 600000000;
+
InsecureApps9 = new[]
{
"Chromecast",
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index aa68e09a0..a1c075563 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -1081,6 +1081,15 @@ namespace MediaBrowser.Model.Dto
get { return StringHelper.EqualsIgnoreCase(Type, "Studio"); }
}
+ [IgnoreDataMember]
+ public bool SupportsSimilarItems
+ {
+ get
+ {
+ return IsType("Movie") || IsType("Series") || IsType("MusicAlbum") || IsType("MusicArtist") || IsType("Program") || IsType("Recording") || IsType("ChannelVideoItem") || IsType("Game");
+ }
+ }
+
/// <summary>
/// Occurs when [property changed].
/// </summary>
@@ -1186,6 +1195,10 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value>The timer identifier.</value>
public string TimerId { get; set; }
-
+ /// <summary>
+ /// Gets or sets the current program.
+ /// </summary>
+ /// <value>The current program.</value>
+ public BaseItemDto CurrentProgram { get; set; }
}
}
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index 0aaa8035c..519d3a04c 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -1,4 +1,6 @@
-using MediaBrowser.Model.Dlna;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Extensions;
using System.Diagnostics;
@@ -225,5 +227,8 @@ namespace MediaBrowser.Model.Entities
/// </summary>
/// <value><c>null</c> if [is cabac] contains no value, <c>true</c> if [is cabac]; otherwise, <c>false</c>.</value>
public bool? IsCabac { get; set; }
+
+ [IgnoreDataMember]
+ public List<int> KeyFrames { get; set; }
}
}
diff --git a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
index 900537b7a..3a6ad0444 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
@@ -53,5 +53,11 @@ namespace MediaBrowser.Model.LiveTv
/// </summary>
/// <value>The limit.</value>
public int? Limit { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether [add current program].
+ /// </summary>
+ /// <value><c>true</c> if [add current program]; otherwise, <c>false</c>.</value>
+ public bool AddCurrentProgram { get; set; }
}
}
diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
index c5e8f4636..2b45422ec 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
@@ -7,10 +7,14 @@ namespace MediaBrowser.Model.LiveTv
public int? GuideDays { get; set; }
public bool EnableMovieProviders { get; set; }
public string RecordingPath { get; set; }
+ public bool EnableAutoOrganize { get; set; }
public List<TunerHostInfo> TunerHosts { get; set; }
public List<ListingsProviderInfo> ListingProviders { get; set; }
-
+
+ public int PrePaddingSeconds { get; set; }
+ public int PostPaddingSeconds { get; set; }
+
public LiveTvOptions()
{
EnableMovieProviders = true;
diff --git a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
index 7c590307f..4b88e42f3 100644
--- a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
@@ -6,6 +6,9 @@ using System.Runtime.Serialization;
namespace MediaBrowser.Model.LiveTv
{
+ /// <summary>
+ /// Class SeriesTimerInfoDto.
+ /// </summary>
[DebuggerDisplay("Name = {Name}")]
public class SeriesTimerInfoDto : BaseTimerInfoDto
{
diff --git a/MediaBrowser.Model/Session/SessionInfoDto.cs b/MediaBrowser.Model/Session/SessionInfoDto.cs
index 98df3efe5..da8ab9b8a 100644
--- a/MediaBrowser.Model/Session/SessionInfoDto.cs
+++ b/MediaBrowser.Model/Session/SessionInfoDto.cs
@@ -99,6 +99,12 @@ namespace MediaBrowser.Model.Session
/// </summary>
/// <value>The device id.</value>
public string DeviceId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the application icon URL.
+ /// </summary>
+ /// <value>The application icon URL.</value>
+ public string AppIconUrl { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [supports remote control].
diff --git a/MediaBrowser.Model/System/PublicSystemInfo.cs b/MediaBrowser.Model/System/PublicSystemInfo.cs
index 3afe72e81..b9a3260b0 100644
--- a/MediaBrowser.Model/System/PublicSystemInfo.cs
+++ b/MediaBrowser.Model/System/PublicSystemInfo.cs
@@ -27,6 +27,12 @@ namespace MediaBrowser.Model.System
public string Version { get; set; }
/// <summary>
+ /// Gets or sets the operating sytem.
+ /// </summary>
+ /// <value>The operating sytem.</value>
+ public string OperatingSystem { get; set; }
+
+ /// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs
index 094c4fda6..c8209baa8 100644
--- a/MediaBrowser.Model/System/SystemInfo.cs
+++ b/MediaBrowser.Model/System/SystemInfo.cs
@@ -9,11 +9,6 @@ namespace MediaBrowser.Model.System
public class SystemInfo : PublicSystemInfo
{
/// <summary>
- /// Gets or sets the operating sytem.
- /// </summary>
- /// <value>The operating sytem.</value>
- public string OperatingSystem { get; set; }
- /// <summary>
/// Gets or sets the display name of the operating system.
/// </summary>
/// <value>The display name of the operating system.</value>
@@ -30,7 +25,7 @@ namespace MediaBrowser.Model.System
/// </summary>
/// <value><c>true</c> if [supports running as service]; otherwise, <c>false</c>.</value>
public bool SupportsRunningAsService { get; set; }
-
+
/// <summary>
/// Gets or sets the mac address.
/// </summary>
@@ -108,7 +103,7 @@ namespace MediaBrowser.Model.System
/// </summary>
/// <value>The cache path.</value>
public string CachePath { get; set; }
-
+
/// <summary>
/// Gets or sets the log path.
/// </summary>
@@ -120,7 +115,7 @@ namespace MediaBrowser.Model.System
/// </summary>
/// <value>The internal metadata path.</value>
public string InternalMetadataPath { get; set; }
-
+
/// <summary>
/// Gets or sets the transcoding temporary path.
/// </summary>
diff --git a/MediaBrowser.Model/Updates/PackageInfo.cs b/MediaBrowser.Model/Updates/PackageInfo.cs
index 5945f87ad..191deeb8f 100644
--- a/MediaBrowser.Model/Updates/PackageInfo.cs
+++ b/MediaBrowser.Model/Updates/PackageInfo.cs
@@ -160,6 +160,12 @@ namespace MediaBrowser.Model.Updates
public bool enableInAppStore { get; set; }
/// <summary>
+ /// Gets or sets the installs.
+ /// </summary>
+ /// <value>The installs.</value>
+ public int installs { get; set; }
+
+ /// <summary>
/// Initializes a new instance of the <see cref="PackageInfo"/> class.
/// </summary>
public PackageInfo()
diff --git a/MediaBrowser.Providers/Folders/DefaultImageProvider.cs b/MediaBrowser.Providers/Folders/DefaultImageProvider.cs
new file mode 100644
index 000000000..13e486ae9
--- /dev/null
+++ b/MediaBrowser.Providers/Folders/DefaultImageProvider.cs
@@ -0,0 +1,166 @@
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
+using MediaBrowser.Providers.Genres;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Providers.Folders
+{
+ public class DefaultImageProvider : IRemoteImageProvider, IHasItemChangeMonitor
+ {
+ private readonly IHttpClient _httpClient;
+
+ public DefaultImageProvider(IHttpClient httpClient)
+ {
+ _httpClient = httpClient;
+ }
+
+ public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+ {
+ return new List<ImageType>
+ {
+ ImageType.Primary,
+ ImageType.Thumb
+ };
+ }
+
+ public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+ {
+ var view = item as UserView;
+
+ if (view != null)
+ {
+ return GetImages(view.ViewType, cancellationToken);
+ }
+
+ var folder = (ICollectionFolder)item;
+ return GetImages(folder.CollectionType, cancellationToken);
+ }
+
+ private Task<IEnumerable<RemoteImageInfo>> GetImages(string viewType, CancellationToken cancellationToken)
+ {
+ var url = GetImageUrl(viewType);
+
+ var list = new List<RemoteImageInfo>();
+
+ if (!string.IsNullOrWhiteSpace(url))
+ {
+ list.AddRange(new List<RemoteImageInfo>{
+ new RemoteImageInfo
+ {
+ ProviderName = Name,
+ Url = url,
+ Type = ImageType.Primary
+ },
+
+ new RemoteImageInfo
+ {
+ ProviderName = Name,
+ Url = url,
+ Type = ImageType.Thumb
+ }
+ });
+ }
+
+ return Task.FromResult<IEnumerable<RemoteImageInfo>>(list);
+ }
+
+ private string GetImageUrl(string viewType)
+ {
+ const string urlPrefix = "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/folders/";
+
+ if (string.Equals(viewType, CollectionType.Books, StringComparison.OrdinalIgnoreCase))
+ {
+ return urlPrefix + "books.png";
+ }
+ if (string.Equals(viewType, CollectionType.Games, StringComparison.OrdinalIgnoreCase))
+ {
+ return urlPrefix + "games.png";
+ }
+ if (string.Equals(viewType, CollectionType.Music, StringComparison.OrdinalIgnoreCase))
+ {
+ //return urlPrefix + "music.png";
+ }
+ if (string.Equals(viewType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase))
+ {
+ //return urlPrefix + "photos.png";
+ }
+ if (string.Equals(viewType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
+ {
+ //return urlPrefix + "tv.png";
+ }
+ if (string.Equals(viewType, CollectionType.Channels, StringComparison.OrdinalIgnoreCase))
+ {
+ return urlPrefix + "generic.png";
+ }
+ if (string.Equals(viewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
+ {
+ return urlPrefix + "livetv.png";
+ }
+ if (string.Equals(viewType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
+ {
+ //return urlPrefix + "movies.png";
+ }
+ if (string.Equals(viewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
+ {
+ return urlPrefix + "playlists.png";
+ }
+ if (string.Equals(viewType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
+ {
+ return urlPrefix + "homevideos.png";
+ }
+ if (string.Equals(viewType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
+ {
+ return urlPrefix + "musicvideos.png";
+ }
+ if (string.Equals(viewType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
+ {
+ return urlPrefix + "generic.png";
+ }
+ if (string.IsNullOrWhiteSpace(viewType))
+ {
+ return urlPrefix + "generic.png";
+ }
+
+ return null;
+ }
+
+ public string Name
+ {
+ get { return "Default Image Provider"; }
+ }
+
+ public bool Supports(IHasImages item)
+ {
+ var view = item as UserView;
+
+ if (view != null)
+ {
+ return true;
+ }
+
+ return item is ICollectionFolder;
+ }
+
+ public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+ {
+ return _httpClient.GetResponse(new HttpRequestOptions
+ {
+ CancellationToken = cancellationToken,
+ Url = url,
+ ResourcePool = GenreImageProvider.ImageDownloadResourcePool
+ });
+ }
+
+ public bool HasChanged(IHasMetadata item, MetadataStatus status, IDirectoryService directoryService)
+ {
+ return GetSupportedImages(item).Any(i => !item.HasImage(i));
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index 453e07987..fe0e4890c 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -189,7 +189,7 @@ namespace MediaBrowser.Providers.Manager
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
- var images = results.SelectMany(i => i);
+ var images = results.SelectMany(i => i.ToList());
return images;
}
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 444567afa..1d323e567 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -83,6 +83,7 @@
<Compile Include="BoxSets\MovieDbBoxSetProvider.cs" />
<Compile Include="Channels\ChannelMetadataService.cs" />
<Compile Include="Chapters\ChapterManager.cs" />
+ <Compile Include="Folders\DefaultImageProvider.cs" />
<Compile Include="Folders\FolderMetadataService.cs" />
<Compile Include="Channels\AudioChannelItemMetadataService.cs" />
<Compile Include="Folders\UserViewMetadataService.cs" />
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index 395d95cc5..9fc8e1f8a 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -132,7 +132,7 @@ namespace MediaBrowser.Providers.MediaInfo
return ItemUpdateType.MetadataImport;
}
- private const string SchemaVersion = "5";
+ private const string SchemaVersion = "6";
private async Task<Model.MediaInfo.MediaInfo> GetMediaInfo(Video item,
IIsoMount isoMount,
@@ -140,14 +140,14 @@ namespace MediaBrowser.Providers.MediaInfo
{
cancellationToken.ThrowIfCancellationRequested();
- var idString = item.Id.ToString("N");
- var cachePath = Path.Combine(_appPaths.CachePath,
- "ffprobe-video",
- idString.Substring(0, 2), idString, "v" + SchemaVersion + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json");
+ //var idString = item.Id.ToString("N");
+ //var cachePath = Path.Combine(_appPaths.CachePath,
+ // "ffprobe-video",
+ // idString.Substring(0, 2), idString, "v" + SchemaVersion + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json");
try
{
- return _json.DeserializeFromFile<Model.MediaInfo.MediaInfo>(cachePath);
+ //return _json.DeserializeFromFile<Model.MediaInfo.MediaInfo>(cachePath);
}
catch (FileNotFoundException)
{
@@ -174,8 +174,8 @@ namespace MediaBrowser.Providers.MediaInfo
}, cancellationToken).ConfigureAwait(false);
- Directory.CreateDirectory(Path.GetDirectoryName(cachePath));
- _json.SerializeToFile(result, cachePath);
+ //Directory.CreateDirectory(Path.GetDirectoryName(cachePath));
+ //_json.SerializeToFile(result, cachePath);
return result;
}
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
index 0f092b554..4953621f5 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
@@ -131,7 +131,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
return new ITaskTrigger[]
{
- new DailyTrigger { TimeOfDay = TimeSpan.FromHours(3) },
+ new IntervalTrigger{ Interval = TimeSpan.FromHours(8)}
};
}
}
diff --git a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
index 5b5afc6c7..f5ee33d6b 100644
--- a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
+++ b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
@@ -185,18 +185,25 @@ namespace MediaBrowser.Providers.Movies
//release date and certification are retrieved based on configured country and we fall back on US if not there and to minimun release date if still no match
if (movieData.releases != null && movieData.releases.countries != null)
{
- var ourRelease = movieData.releases.countries.FirstOrDefault(c => c.iso_3166_1.Equals(preferredCountryCode, StringComparison.OrdinalIgnoreCase)) ?? new MovieDbProvider.Country();
- var usRelease = movieData.releases.countries.FirstOrDefault(c => c.iso_3166_1.Equals("US", StringComparison.OrdinalIgnoreCase)) ?? new MovieDbProvider.Country();
- var minimunRelease = movieData.releases.countries.OrderBy(c => c.release_date).FirstOrDefault() ?? new MovieDbProvider.Country();
-
- var ratingPrefix = string.Equals(preferredCountryCode, "us", StringComparison.OrdinalIgnoreCase) ? "" : preferredCountryCode + "-";
- movie.OfficialRating = !string.IsNullOrEmpty(ourRelease.certification)
- ? ratingPrefix + ourRelease.certification
- : !string.IsNullOrEmpty(usRelease.certification)
- ? usRelease.certification
- : !string.IsNullOrEmpty(minimunRelease.certification)
- ? minimunRelease.iso_3166_1 + "-" + minimunRelease.certification
- : null;
+ var releases = movieData.releases.countries.Where(i => !string.IsNullOrWhiteSpace(i.certification)).ToList();
+
+ var ourRelease = releases.FirstOrDefault(c => c.iso_3166_1.Equals(preferredCountryCode, StringComparison.OrdinalIgnoreCase));
+ var usRelease = releases.FirstOrDefault(c => c.iso_3166_1.Equals("US", StringComparison.OrdinalIgnoreCase));
+ var minimunRelease = releases.OrderBy(c => c.release_date).FirstOrDefault();
+
+ if (ourRelease != null)
+ {
+ var ratingPrefix = string.Equals(preferredCountryCode, "us", StringComparison.OrdinalIgnoreCase) ? "" : preferredCountryCode + "-";
+ movie.OfficialRating = ratingPrefix + ourRelease.certification;
+ }
+ else if (usRelease != null)
+ {
+ movie.OfficialRating = usRelease.certification;
+ }
+ else if (minimunRelease != null)
+ {
+ movie.OfficialRating = minimunRelease.iso_3166_1 + "-" + minimunRelease.certification;
+ }
}
if (!string.IsNullOrWhiteSpace(movieData.release_date))
@@ -232,7 +239,7 @@ namespace MediaBrowser.Providers.Movies
}
resultItem.ResetPeople();
-
+
//Actors, Directors, Writers - all in People
//actors come from cast
if (movieData.casts != null && movieData.casts.cast != null)
diff --git a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
index dffabd83c..36e7697d7 100644
--- a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
+++ b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
@@ -172,7 +172,7 @@ namespace MediaBrowser.Providers.Omdb
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
result.HasMetadata = true;
- await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, cancellationToken).ConfigureAwait(false);
+ await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
}
return result;
@@ -211,7 +211,7 @@ namespace MediaBrowser.Providers.Omdb
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
result.HasMetadata = true;
- await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, cancellationToken).ConfigureAwait(false);
+ await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
}
return result;
diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs
index aee1abd72..c7ce57fca 100644
--- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs
+++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs
@@ -28,7 +28,7 @@ namespace MediaBrowser.Providers.Omdb
Current = this;
}
- public async Task Fetch(BaseItem item, string imdbId, string language, CancellationToken cancellationToken)
+ public async Task Fetch(BaseItem item, string imdbId, string language, string country, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(imdbId))
{
@@ -51,10 +51,15 @@ namespace MediaBrowser.Providers.Omdb
{
var result = _jsonSerializer.DeserializeFromStream<RootObject>(stream);
- // Only take the name if the user's language is set to english, since Omdb has no localization
+ // Only take the name and rating if the user's language is set to english, since Omdb has no localization
if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
{
item.Name = result.Title;
+
+ if (string.Equals(country, "us", StringComparison.OrdinalIgnoreCase))
+ {
+ item.OfficialRating = result.Rated;
+ }
}
int year;
@@ -66,9 +71,6 @@ namespace MediaBrowser.Providers.Omdb
item.ProductionYear = year;
}
- item.OfficialRating = result.Rated;
-
-
var hasCriticRating = item as IHasCriticRating;
if (hasCriticRating != null)
{
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs b/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs
deleted file mode 100644
index 337e26e8d..000000000
--- a/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs
+++ /dev/null
@@ -1,409 +0,0 @@
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Common.ScheduledTasks;
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Querying;
-using MoreLinq;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Channels
-{
- public class ChannelDownloadScheduledTask : IScheduledTask, IConfigurableScheduledTask
- {
- private readonly IChannelManager _manager;
- private readonly IServerConfigurationManager _config;
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly ILibraryManager _libraryManager;
- private readonly IUserManager _userManager;
-
- public ChannelDownloadScheduledTask(IChannelManager manager, IServerConfigurationManager config, ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager, IUserManager userManager)
- {
- _manager = manager;
- _config = config;
- _logger = logger;
- _fileSystem = fileSystem;
- _libraryManager = libraryManager;
- _userManager = userManager;
- }
-
- public string Name
- {
- get { return "Download channel content"; }
- }
-
- public string Description
- {
- get { return "Downloads channel content based on configuration."; }
- }
-
- public string Category
- {
- get { return "Channels"; }
- }
-
- public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
- {
- CleanChannelContent(cancellationToken);
-
- var users = _userManager.Users
- .DistinctBy(GetUserDistinctValue)
- .Select(i => i.Id.ToString("N"))
- .ToList();
-
- var numComplete = 0;
-
- foreach (var user in users)
- {
- double percentPerUser = 1;
- percentPerUser /= users.Count;
- var startingPercent = numComplete * percentPerUser * 100;
-
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p => progress.Report(startingPercent + (percentPerUser * p)));
-
- await DownloadContent(user, cancellationToken, innerProgress).ConfigureAwait(false);
-
- numComplete++;
- double percent = numComplete;
- percent /= users.Count;
- progress.Report(percent * 100);
- }
-
- progress.Report(100);
- }
-
- public static string GetUserDistinctValue(User user)
- {
- var channels = user.Policy.EnabledChannels
- .OrderBy(i => i)
- .ToList();
-
- return string.Join("|", channels.ToArray());
- }
-
- private async Task DownloadContent(string user,
- CancellationToken cancellationToken,
- IProgress<double> progress)
- {
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p => progress.Report(0 + (.8 * p)));
- await DownloadAllChannelContent(user, cancellationToken, innerProgress).ConfigureAwait(false);
- progress.Report(80);
-
- innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p => progress.Report(80 + (.2 * p)));
- await DownloadLatestChannelContent(user, cancellationToken, progress).ConfigureAwait(false);
- progress.Report(100);
- }
-
- private async Task DownloadLatestChannelContent(string userId,
- CancellationToken cancellationToken,
- IProgress<double> progress)
- {
- var result = await _manager.GetLatestChannelItemsInternal(new AllChannelMediaQuery
- {
- UserId = userId
-
- }, cancellationToken).ConfigureAwait(false);
-
- progress.Report(5);
-
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p => progress.Report(5 + (.95 * p)));
-
- var path = _manager.ChannelDownloadPath;
-
- await DownloadChannelContent(result, path, cancellationToken, innerProgress).ConfigureAwait(false);
- }
-
- private async Task DownloadAllChannelContent(string userId,
- CancellationToken cancellationToken,
- IProgress<double> progress)
- {
- var result = await _manager.GetAllMediaInternal(new AllChannelMediaQuery
- {
- UserId = userId
-
- }, cancellationToken).ConfigureAwait(false);
-
- progress.Report(5);
-
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p => progress.Report(5 + (.95 * p)));
-
- var path = _manager.ChannelDownloadPath;
-
- await DownloadChannelContent(result, path, cancellationToken, innerProgress).ConfigureAwait(false);
- }
-
- private async Task DownloadChannelContent(QueryResult<BaseItem> result,
- string path,
- CancellationToken cancellationToken,
- IProgress<double> progress)
- {
- var numComplete = 0;
-
- var options = _config.GetChannelsConfiguration();
-
- foreach (var item in result.Items)
- {
- var channelItem = item as IChannelMediaItem;
-
- if (channelItem != null)
- {
- var channelFeatures = _manager.GetChannelFeatures(channelItem.ChannelId);
-
- if (channelFeatures.SupportsContentDownloading)
- {
- if (options.DownloadingChannels.Contains(channelItem.ChannelId))
- {
- try
- {
- await DownloadChannelItem(channelItem, options, cancellationToken, path);
- }
- catch (OperationCanceledException)
- {
- break;
- }
- catch (ChannelDownloadException)
- {
- // Logged at lower levels
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error downloading channel content for {0}", ex, item.Name);
- }
- }
- }
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= result.Items.Length;
- progress.Report(percent * 100);
- }
-
- progress.Report(100);
- }
-
- private double? GetDownloadLimit(ChannelOptions channelOptions)
- {
- return channelOptions.DownloadSizeLimit;
- }
-
- private async Task DownloadChannelItem(IChannelMediaItem item,
- ChannelOptions channelOptions,
- CancellationToken cancellationToken,
- string path)
- {
- var itemId = item.Id.ToString("N");
- var sources = await _manager.GetStaticMediaSources(item, true, cancellationToken)
- .ConfigureAwait(false);
-
- var cachedVersions = sources.Where(i => i.Protocol == MediaProtocol.File).ToList();
-
- if (cachedVersions.Count > 0)
- {
- await RefreshMediaSourceItems(cachedVersions, cancellationToken).ConfigureAwait(false);
- return;
- }
-
- var limit = GetDownloadLimit(channelOptions);
-
- if (limit.HasValue)
- {
- if (IsSizeLimitReached(path, limit.Value))
- {
- return;
- }
- }
-
- var destination = Path.Combine(path, item.ChannelId, itemId);
-
- await _manager.DownloadChannelItem(item, destination, new Progress<double>(), cancellationToken)
- .ConfigureAwait(false);
-
- await RefreshMediaSourceItem(destination, cancellationToken).ConfigureAwait(false);
- }
-
- private async Task RefreshMediaSourceItems(IEnumerable<MediaSourceInfo> items, CancellationToken cancellationToken)
- {
- foreach (var item in items)
- {
- await RefreshMediaSourceItem(item.Path, cancellationToken).ConfigureAwait(false);
- }
- }
-
- private async Task RefreshMediaSourceItem(string path, CancellationToken cancellationToken)
- {
- var item = _libraryManager.ResolvePath(new FileInfo(path));
-
- if (item != null)
- {
- var forceSave = false;
-
- // Get the version from the database
- var dbItem = _libraryManager.GetItemById(item.Id);
-
- if (dbItem == null)
- {
- forceSave = true;
- }
- else
- {
- item = dbItem;
- }
-
- await item.RefreshMetadata(new MetadataRefreshOptions
- {
- ForceSave = forceSave
-
- }, cancellationToken).ConfigureAwait(false);
- }
- }
-
- private bool IsSizeLimitReached(string path, double gbLimit)
- {
- try
- {
- var byteLimit = gbLimit * 1000000000;
-
- long total = 0;
-
- foreach (var file in new DirectoryInfo(path).EnumerateFiles("*", SearchOption.AllDirectories))
- {
- total += file.Length;
-
- if (total >= byteLimit)
- {
- return true;
- }
- }
-
- return false;
- }
- catch (DirectoryNotFoundException)
- {
- return false;
- }
- }
-
- public IEnumerable<ITaskTrigger> GetDefaultTriggers()
- {
- return new ITaskTrigger[]
- {
- new IntervalTrigger{ Interval = TimeSpan.FromHours(3)},
- };
- }
-
- private void CleanChannelContent(CancellationToken cancellationToken)
- {
- var options = _config.GetChannelsConfiguration();
-
- if (!options.MaxDownloadAge.HasValue)
- {
- return;
- }
-
- var minDateModified = DateTime.UtcNow.AddDays(0 - options.MaxDownloadAge.Value);
-
- var path = _manager.ChannelDownloadPath;
-
- try
- {
- DeleteCacheFilesFromDirectory(cancellationToken, path, minDateModified, new Progress<double>());
- }
- catch (DirectoryNotFoundException)
- {
- // No biggie here. Nothing to delete
- }
- }
-
- /// <summary>
- /// Deletes the cache files from directory with a last write time less than a given date
- /// </summary>
- /// <param name="cancellationToken">The task cancellation token.</param>
- /// <param name="directory">The directory.</param>
- /// <param name="minDateModified">The min date modified.</param>
- /// <param name="progress">The progress.</param>
- private void DeleteCacheFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress<double> progress)
- {
- var filesToDelete = new DirectoryInfo(directory).EnumerateFiles("*", SearchOption.AllDirectories)
- .Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
- .ToList();
-
- var index = 0;
-
- foreach (var file in filesToDelete)
- {
- double percent = index;
- percent /= filesToDelete.Count;
-
- progress.Report(100 * percent);
-
- cancellationToken.ThrowIfCancellationRequested();
-
- DeleteFile(file.FullName);
-
- index++;
- }
-
- progress.Report(100);
- }
-
- /// <summary>
- /// Deletes the file.
- /// </summary>
- /// <param name="path">The path.</param>
- private void DeleteFile(string path)
- {
- try
- {
- _fileSystem.DeleteFile(path);
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error deleting file {0}", ex, path);
- }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance is hidden.
- /// </summary>
- /// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value>
- public bool IsHidden
- {
- get
- {
- return !_manager.GetAllChannelFeatures().Any();
- }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance is enabled.
- /// </summary>
- /// <value><c>true</c> if this instance is enabled; otherwise, <c>false</c>.</value>
- public bool IsEnabled
- {
- get
- {
- return true;
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs b/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs
index baf446942..976fa5615 100644
--- a/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs
+++ b/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Logging;
@@ -17,18 +18,20 @@ namespace MediaBrowser.Server.Implementations.Channels
private readonly IChannelManager _channelManager;
private readonly IUserManager _userManager;
private readonly ILogger _logger;
+ private readonly ILibraryManager _libraryManager;
- public ChannelPostScanTask(IChannelManager channelManager, IUserManager userManager, ILogger logger)
+ public ChannelPostScanTask(IChannelManager channelManager, IUserManager userManager, ILogger logger, ILibraryManager libraryManager)
{
_channelManager = channelManager;
_userManager = userManager;
_logger = logger;
+ _libraryManager = libraryManager;
}
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var users = _userManager.Users
- .DistinctBy(ChannelDownloadScheduledTask.GetUserDistinctValue)
+ .DistinctBy(GetUserDistinctValue)
.Select(i => i.Id.ToString("N"))
.ToList();
@@ -51,9 +54,20 @@ namespace MediaBrowser.Server.Implementations.Channels
progress.Report(percent * 100);
}
+ await CleanDatabase(cancellationToken).ConfigureAwait(false);
+
progress.Report(100);
}
+ public static string GetUserDistinctValue(User user)
+ {
+ var channels = user.Policy.EnabledChannels
+ .OrderBy(i => i)
+ .ToList();
+
+ return string.Join("|", channels.ToArray());
+ }
+
private async Task DownloadContent(string user, CancellationToken cancellationToken, IProgress<double> progress)
{
var channels = await _channelManager.GetChannelsInternal(new ChannelQuery
@@ -106,6 +120,59 @@ namespace MediaBrowser.Server.Implementations.Channels
progress.Report(100);
}
+ private async Task CleanDatabase(CancellationToken cancellationToken)
+ {
+ var allChannels = await _channelManager.GetChannelsInternal(new ChannelQuery { }, cancellationToken);
+
+ var allIds = _libraryManager.GetItemIds(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(Channel).Name }
+ });
+
+ var invalidIds = allIds
+ .Except(allChannels.Items.Select(i => i.Id).ToList())
+ .ToList();
+
+ foreach (var id in invalidIds)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ await CleanChannel(id, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ private async Task CleanChannel(Guid id, CancellationToken cancellationToken)
+ {
+ _logger.Debug("Cleaning channel {0} from database", id);
+
+ // Delete all channel items
+ var allIds = _libraryManager.GetItemIds(new InternalItemsQuery
+ {
+ ChannelIds = new[] { id.ToString("N") }
+ });
+
+ foreach (var deleteId in allIds)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ await DeleteItem(deleteId).ConfigureAwait(false);
+ }
+
+ // Finally, delete the channel itself
+ await DeleteItem(id).ConfigureAwait(false);
+ }
+
+ private Task DeleteItem(Guid id)
+ {
+ var item = _libraryManager.GetItemById(id);
+
+ return _libraryManager.DeleteItem(item, new DeleteOptions
+ {
+ DeleteFileLocation = false
+
+ });
+ }
+
private async Task GetAllItems(string user, string channelId, string folderId, int currentRefreshLevel, int maxRefreshLevel, IProgress<double> progress, CancellationToken cancellationToken)
{
var folderItems = new List<string>();
diff --git a/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
index df94580a5..005bbb852 100644
--- a/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
+++ b/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
@@ -13,12 +13,14 @@ namespace MediaBrowser.Server.Implementations.Channels
private readonly IChannelManager _channelManager;
private readonly IUserManager _userManager;
private readonly ILogger _logger;
+ private readonly ILibraryManager _libraryManager;
- public RefreshChannelsScheduledTask(IChannelManager channelManager, IUserManager userManager, ILogger logger)
+ public RefreshChannelsScheduledTask(IChannelManager channelManager, IUserManager userManager, ILogger logger, ILibraryManager libraryManager)
{
_channelManager = channelManager;
_userManager = userManager;
_logger = logger;
+ _libraryManager = libraryManager;
}
public string Name
@@ -42,7 +44,7 @@ namespace MediaBrowser.Server.Implementations.Channels
await manager.RefreshChannels(new Progress<double>(), cancellationToken).ConfigureAwait(false);
- await new ChannelPostScanTask(_channelManager, _userManager, _logger).Run(progress, cancellationToken)
+ await new ChannelPostScanTask(_channelManager, _userManager, _logger, _libraryManager).Run(progress, cancellationToken)
.ConfigureAwait(false);
}
diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs b/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs
index 973519a77..8a659fb65 100644
--- a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs
+++ b/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs
@@ -41,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.Connect
_timer = new Timer(TimerCallback, null, TimeSpan.FromSeconds(5), TimeSpan.FromHours(3));
}
- private readonly string[] _ipLookups = { "http://bot.whatismyipaddress.com", "https://connect.mediabrowser.tv/service/ip" };
+ private readonly string[] _ipLookups = { "http://bot.whatismyipaddress.com", "https://connect.emby.media/service/ip" };
private async void TimerCallback(object state)
{
diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs
index 4569503c0..7cd96c5f3 100644
--- a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs
+++ b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs
@@ -371,7 +371,7 @@ namespace MediaBrowser.Server.Implementations.Connect
private string GetConnectUrl(string handler)
{
- return "https://connect.mediabrowser.tv/service/" + handler;
+ return "https://connect.emby.media/service/" + handler;
}
public async Task<UserLinkResult> LinkUser(string userId, string connectUsername)
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
index 5c9f72b7d..edfef38fd 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
@@ -98,7 +98,7 @@ namespace MediaBrowser.Server.Implementations.Dto
var byName = item as IItemByName;
- if (byName != null && !(item is LiveTvChannel))
+ if (byName != null)
{
if (options.Fields.Contains(ItemFields.ItemCounts))
{
@@ -140,7 +140,7 @@ namespace MediaBrowser.Server.Implementations.Dto
var byName = item as IItemByName;
- if (byName != null && !(item is LiveTvChannel))
+ if (byName != null)
{
if (options.Fields.Contains(ItemFields.ItemCounts))
{
@@ -351,6 +351,12 @@ namespace MediaBrowser.Server.Implementations.Dto
AttachBasicFields(dto, item, owner, options);
+ var tvChannel = item as LiveTvChannel;
+ if (tvChannel != null)
+ {
+ _livetvManager().AddChannelInfo(dto, tvChannel, options, user);
+ }
+
var collectionFolder = item as ICollectionFolder;
if (collectionFolder != null)
{
@@ -1520,16 +1526,11 @@ namespace MediaBrowser.Server.Implementations.Dto
SetPhotoProperties(dto, photo);
}
- var tvChannel = item as LiveTvChannel;
- if (tvChannel != null)
- {
- dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(tvChannel, true).ToList();
- }
-
+ dto.ChannelId = item.ChannelId;
+
var channelItem = item as IChannelItem;
if (channelItem != null)
{
- dto.ChannelId = channelItem.ChannelId;
dto.ChannelName = _channelManagerFactory().GetChannel(channelItem.ChannelId).Name;
}
@@ -1646,7 +1647,8 @@ namespace MediaBrowser.Server.Implementations.Dto
IsFolder = false,
Recursive = true,
IsVirtualUnaired = false,
- IsMissing = false
+ IsMissing = false,
+ User = user
}).Result.Items;
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
index e134670e3..06b72e4ef 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
+++ b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
@@ -43,6 +43,13 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
_providerManager = providerManager;
}
+ public Task<FileOrganizationResult> OrganizeEpisodeFile(string path, CancellationToken cancellationToken)
+ {
+ var options = _config.GetAutoOrganizeOptions().TvOptions;
+
+ return OrganizeEpisodeFile(path, options, false, cancellationToken);
+ }
+
public async Task<FileOrganizationResult> OrganizeEpisodeFile(string path, TvFileOrganizationOptions options, bool overwriteExisting, CancellationToken cancellationToken)
{
_logger.Info("Sorting file {0}", path);
@@ -56,7 +63,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
FileSize = new FileInfo(path).Length
};
- var namingOptions = ((LibraryManager) _libraryManager).GetNamingOptions();
+ var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
var resolver = new Naming.TV.EpisodeResolver(namingOptions, new PatternsLogger());
var episodeInfo = resolver.Resolve(path, false) ??
@@ -254,7 +261,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
.ToList();
var targetFilenameWithoutExtension = Path.GetFileNameWithoutExtension(targetPath);
-
+
foreach (var file in files)
{
directory = Path.GetDirectoryName(file);
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs
index 557b531b4..0caa8c26e 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs
+++ b/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs
@@ -48,8 +48,6 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
progress.Report(10);
- var scanLibrary = false;
-
if (eligibleFiles.Count > 0)
{
var numComplete = 0;
@@ -61,12 +59,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
try
{
- var result = await organizer.OrganizeEpisodeFile(file.FullName, options, options.OverwriteExistingEpisodes, cancellationToken).ConfigureAwait(false);
-
- if (result.Status == FileSortingStatus.Success)
- {
- scanLibrary = true;
- }
+ await organizer.OrganizeEpisodeFile(file.FullName, options, options.OverwriteExistingEpisodes, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -106,12 +99,6 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
}
}
- if (scanLibrary)
- {
- await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None)
- .ConfigureAwait(false);
- }
-
progress.Report(100);
}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs b/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs
index a5c69317b..9106fa059 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs
@@ -235,62 +235,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
}
}
- /// <summary>
- /// Writes to async.
- /// </summary>
- /// <param name="responseStream">The response stream.</param>
- /// <returns>Task.</returns>
- private async Task WriteToAsync(Stream responseStream)
- {
- try
- {
- // Headers only
- if (IsHeadRequest)
- {
- return;
- }
-
- using (var source = SourceStream)
- {
- // If the requested range is "0-", we can optimize by just doing a stream copy
- if (RangeEnd >= TotalContentLength - 1)
- {
- await source.CopyToAsync(responseStream, BufferSize).ConfigureAwait(false);
- }
- else
- {
- await CopyToAsyncInternal(source, responseStream, Convert.ToInt32(RangeLength), CancellationToken.None).ConfigureAwait(false);
- }
- }
- }
- finally
- {
- if (OnComplete != null)
- {
- OnComplete();
- }
- }
- }
-
- private async Task CopyToAsyncInternal(Stream source, Stream destination, int copyLength, CancellationToken cancellationToken)
- {
- var array = new byte[BufferSize];
- int count;
- while ((count = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
- {
- var bytesToCopy = Math.Min(count, copyLength);
-
- await destination.WriteAsync(array, 0, bytesToCopy, cancellationToken).ConfigureAwait(false);
-
- copyLength -= bytesToCopy;
-
- if (copyLength <= 0)
- {
- break;
- }
- }
- }
-
public string ContentType { get; set; }
public IRequest RequestContext { get; set; }
diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
index e1c529187..5bd26ce18 100644
--- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
+++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Logging;
using MediaBrowser.Server.Implementations.ScheduledTasks;
@@ -700,4 +701,23 @@ namespace MediaBrowser.Server.Implementations.IO
}
}
}
+
+ public class LibraryMonitorStartup : IServerEntryPoint
+ {
+ private readonly ILibraryMonitor _monitor;
+
+ public LibraryMonitorStartup(ILibraryMonitor monitor)
+ {
+ _monitor = monitor;
+ }
+
+ public void Run()
+ {
+ _monitor.Start();
+ }
+
+ public void Dispose()
+ {
+ }
+ }
}
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index 0cfd38479..cc9d9551c 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -3,12 +3,14 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Progress;
using MediaBrowser.Common.ScheduledTasks;
+using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
@@ -244,10 +246,6 @@ namespace MediaBrowser.Server.Implementations.Library
}
/// <summary>
- /// The _items by name path
- /// </summary>
- private string _itemsByNamePath;
- /// <summary>
/// The _season zero display name
/// </summary>
private string _seasonZeroDisplayName;
@@ -260,7 +258,6 @@ namespace MediaBrowser.Server.Implementations.Library
private void RecordConfigurationValues(ServerConfiguration configuration)
{
_seasonZeroDisplayName = configuration.SeasonZeroDisplayName;
- _itemsByNamePath = ConfigurationManager.ApplicationPaths.ItemsByNamePath;
_wizardCompleted = configuration.IsStartupWizardCompleted;
}
@@ -273,56 +270,24 @@ namespace MediaBrowser.Server.Implementations.Library
{
var config = ConfigurationManager.Configuration;
- var ibnPathChanged = !string.Equals(_itemsByNamePath, ConfigurationManager.ApplicationPaths.ItemsByNamePath, StringComparison.Ordinal);
-
- if (ibnPathChanged)
- {
- RemoveItemsByNameFromCache();
- }
-
var newSeasonZeroName = ConfigurationManager.Configuration.SeasonZeroDisplayName;
var seasonZeroNameChanged = !string.Equals(_seasonZeroDisplayName, newSeasonZeroName, StringComparison.Ordinal);
var wizardChanged = config.IsStartupWizardCompleted != _wizardCompleted;
RecordConfigurationValues(config);
- Task.Run(async () =>
+ if (seasonZeroNameChanged || wizardChanged)
{
- if (seasonZeroNameChanged)
- {
- await UpdateSeasonZeroNames(newSeasonZeroName, CancellationToken.None).ConfigureAwait(false);
- }
-
- if (seasonZeroNameChanged || ibnPathChanged || wizardChanged)
- {
- _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
- }
- });
- }
+ _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
+ }
- private void RemoveItemsByNameFromCache()
- {
- RemoveItemsFromCache(i => i is Person);
- RemoveItemsFromCache(i => i is Year);
- RemoveItemsFromCache(i => i is Genre);
- RemoveItemsFromCache(i => i is MusicGenre);
- RemoveItemsFromCache(i => i is GameGenre);
- RemoveItemsFromCache(i => i is Studio);
- RemoveItemsFromCache(i =>
+ if (seasonZeroNameChanged)
{
- var artist = i as MusicArtist;
- return artist != null && artist.IsAccessedByName;
- });
- }
-
- private void RemoveItemsFromCache(Func<BaseItem, bool> remove)
- {
- var items = _libraryItemsCache.ToList().Where(i => remove(i.Value)).ToList();
+ Task.Run(async () =>
+ {
+ await UpdateSeasonZeroNames(newSeasonZeroName, CancellationToken.None).ConfigureAwait(false);
- foreach (var item in items)
- {
- BaseItem value;
- _libraryItemsCache.TryRemove(item.Key, out value);
+ });
}
}
@@ -374,6 +339,21 @@ namespace MediaBrowser.Server.Implementations.Library
private void RegisterItem(Guid id, BaseItem item)
{
+ if (item is LiveTvProgram)
+ {
+ return;
+ }
+ if (item is IChannelItem)
+ {
+ return;
+ }
+ if (item is IItemByName)
+ {
+ if (!(item is MusicArtist))
+ {
+ return;
+ }
+ }
LibraryItemsCache.AddOrUpdate(id, item, delegate { return item; });
}
@@ -951,11 +931,14 @@ namespace MediaBrowser.Server.Implementations.Library
DateModified = DateTime.UtcNow,
Path = path
};
- }
- if (isArtist)
- {
- (item as MusicArtist).IsAccessedByName = true;
+ if (isArtist)
+ {
+ (item as MusicArtist).IsAccessedByName = true;
+ }
+
+ var task = CreateItem(item, CancellationToken.None);
+ Task.WaitAll(task);
}
return item;
@@ -1259,6 +1242,11 @@ namespace MediaBrowser.Server.Implementations.Library
};
}
+ public QueryResult<BaseItem> QueryItems(InternalItemsQuery query)
+ {
+ return ItemRepository.GetItems(query);
+ }
+
public List<Guid> GetItemIds(InternalItemsQuery query)
{
return ItemRepository.GetItemIdsList(query);
diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
index c5ff100f9..e3ec99392 100644
--- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
@@ -479,11 +479,19 @@ namespace MediaBrowser.Server.Implementations.Library
private Tuple<IMediaSourceProvider, string> GetProvider(string key)
{
+ if (string.IsNullOrWhiteSpace(key))
+ {
+ throw new ArgumentException("key");
+ }
+
var keys = key.Split(new[] { LiveStreamIdDelimeter }, 2);
var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), keys[0], StringComparison.OrdinalIgnoreCase));
- return new Tuple<IMediaSourceProvider, string>(provider, keys[1]);
+ var splitIndex = key.IndexOf(LiveStreamIdDelimeter);
+ var keyId = key.Substring(splitIndex + 1);
+
+ return new Tuple<IMediaSourceProvider, string>(provider, keyId);
}
private Timer _closeTimer;
diff --git a/MediaBrowser.Server.Implementations/Library/MusicManager.cs b/MediaBrowser.Server.Implementations/Library/MusicManager.cs
index 1a9e98268..683e6c5cc 100644
--- a/MediaBrowser.Server.Implementations/Library/MusicManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/MusicManager.cs
@@ -27,10 +27,8 @@ namespace MediaBrowser.Server.Implementations.Library
return list.Concat(GetInstantMixFromGenres(item.Genres, user));
}
- public IEnumerable<Audio> GetInstantMixFromArtist(string name, User user)
+ public IEnumerable<Audio> GetInstantMixFromArtist(MusicArtist artist, User user)
{
- var artist = _libraryManager.GetArtist(name);
-
var genres = user.RootFolder
.GetRecursiveChildren(user, i => i is Audio)
.Cast<Audio>()
@@ -54,6 +52,18 @@ namespace MediaBrowser.Server.Implementations.Library
return GetInstantMixFromGenres(genres, user);
}
+ public IEnumerable<Audio> GetInstantMixFromFolder(Folder item, User user)
+ {
+ var genres = item
+ .GetRecursiveChildren(user, i => i is Audio)
+ .Cast<Audio>()
+ .SelectMany(i => i.Genres)
+ .Concat(item.Genres)
+ .DistinctNames();
+
+ return GetInstantMixFromGenres(genres, user);
+ }
+
public IEnumerable<Audio> GetInstantMixFromPlaylist(Playlist item, User user)
{
var genres = item
@@ -107,7 +117,7 @@ namespace MediaBrowser.Server.Implementations.Library
var artist = item as MusicArtist;
if (artist != null)
{
- return GetInstantMixFromArtist(artist.Name, user);
+ return GetInstantMixFromArtist(artist, user);
}
var song = item as Audio;
@@ -115,6 +125,12 @@ namespace MediaBrowser.Server.Implementations.Library
{
return GetInstantMixFromSong(song, user);
}
+
+ var folder = item as Folder;
+ if (folder != null)
+ {
+ return GetInstantMixFromFolder(folder, user);
+ }
return new Audio[] { };
}
diff --git a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs
index 21e92786d..d4ff89b4f 100644
--- a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs
+++ b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs
@@ -33,26 +33,17 @@ namespace MediaBrowser.Server.Implementations.Library
public async Task<QueryResult<SearchHintInfo>> GetSearchHints(SearchQuery query)
{
- IEnumerable<BaseItem> inputItems;
-
- Func<BaseItem, bool> filter = i => !(i is ICollectionFolder);
-
User user = null;
if (string.IsNullOrWhiteSpace(query.UserId))
{
- inputItems = _libraryManager.RootFolder.GetRecursiveChildren(filter);
}
else
{
user = _userManager.GetUserById(query.UserId);
-
- inputItems = user.RootFolder.GetRecursiveChildren(user, filter);
}
- inputItems = _libraryManager.ReplaceVideosWithPrimaryVersions(inputItems);
-
- var results = await GetSearchHints(inputItems, query, user).ConfigureAwait(false);
+ var results = await GetSearchHints(query, user).ConfigureAwait(false);
var searchResultArray = results.ToArray();
results = searchResultArray;
@@ -77,15 +68,22 @@ namespace MediaBrowser.Server.Implementations.Library
};
}
+ private void AddIfMissing(List<string> list, string value)
+ {
+ if (!list.Contains(value, StringComparer.OrdinalIgnoreCase))
+ {
+ list.Add(value);
+ }
+ }
+
/// <summary>
/// Gets the search hints.
/// </summary>
- /// <param name="inputItems">The input items.</param>
/// <param name="query">The query.</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable{SearchHintResult}.</returns>
/// <exception cref="System.ArgumentNullException">searchTerm</exception>
- private Task<IEnumerable<SearchHintInfo>> GetSearchHints(IEnumerable<BaseItem> inputItems, SearchQuery query, User user)
+ private Task<IEnumerable<SearchHintInfo>> GetSearchHints(SearchQuery query, User user)
{
var searchTerm = query.SearchTerm;
@@ -100,207 +98,80 @@ namespace MediaBrowser.Server.Implementations.Library
var hints = new List<Tuple<BaseItem, string, int>>();
- var items = inputItems.Where(i => !(i is MusicArtist)).ToList();
+ var excludeItemTypes = new List<string>();
+ var includeItemTypes = (query.IncludeItemTypes ?? new string[] { }).ToList();
- if (query.IncludeMedia)
- {
- var mediaItems = _libraryManager.GetItems(new InternalItemsQuery
- {
- NameContains = searchTerm,
- ExcludeItemTypes = new[]
- {
- typeof (Person).Name,
- typeof (Genre).Name,
- typeof (MusicArtist).Name,
- typeof (GameGenre).Name,
- typeof (MusicGenre).Name,
- typeof (Year).Name,
- typeof (Studio).Name
- },
- IncludeItemTypes = query.IncludeItemTypes
-
- }).Items;
-
- // Add search hints based on item name
- hints.AddRange(mediaItems.Where(i => IncludeInSearch(i) && (user == null || i.IsVisibleStandalone(user)) && !(i is CollectionFolder)).Select(item =>
- {
- var index = GetIndex(item.Name, searchTerm, terms);
+ excludeItemTypes.Add(typeof(Year).Name);
- return new Tuple<BaseItem, string, int>(item, index.Item1, index.Item2);
- }));
- }
-
- if (query.IncludeArtists && (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains("MusicArtist", StringComparer.OrdinalIgnoreCase)))
+ if (query.IncludeGenres && (includeItemTypes.Count == 0 || includeItemTypes.Contains("Genre", StringComparer.OrdinalIgnoreCase)))
{
- // Find artists
- var artists = items.OfType<Audio>()
- .SelectMany(i => i.AllArtists)
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .DistinctNames()
- .ToList();
-
- foreach (var item in artists)
+ if (!query.IncludeMedia)
{
- var index = GetIndex(item, searchTerm, terms);
-
- if (index.Item2 != -1)
- {
- try
- {
- var artist = _libraryManager.GetArtist(item);
-
- hints.Add(new Tuple<BaseItem, string, int>(artist, index.Item1, index.Item2));
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting {0}", ex, item);
- }
- }
+ AddIfMissing(includeItemTypes, typeof(Genre).Name);
+ AddIfMissing(includeItemTypes, typeof(GameGenre).Name);
+ AddIfMissing(includeItemTypes, typeof(MusicGenre).Name);
}
}
-
- if (query.IncludeGenres && (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains("Genre", StringComparer.OrdinalIgnoreCase)))
+ else
{
- // Find genres, from non-audio items
- var genres = items.Where(i => !(i is IHasMusicGenres) && !(i is Game))
- .SelectMany(i => i.Genres)
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToList();
-
- foreach (var item in genres)
- {
- var index = GetIndex(item, searchTerm, terms);
-
- if (index.Item2 != -1)
- {
- try
- {
- var genre = _libraryManager.GetGenre(item);
-
- hints.Add(new Tuple<BaseItem, string, int>(genre, index.Item1, index.Item2));
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting {0}", ex, item);
- }
- }
- }
-
- // Find music genres
- var musicGenres = items.Where(i => i is IHasMusicGenres)
- .SelectMany(i => i.Genres)
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToList();
+ AddIfMissing(excludeItemTypes, typeof(Genre).Name);
+ AddIfMissing(excludeItemTypes, typeof(GameGenre).Name);
+ AddIfMissing(excludeItemTypes, typeof(MusicGenre).Name);
+ }
- foreach (var item in musicGenres)
+ if (query.IncludePeople && (includeItemTypes.Count == 0 || includeItemTypes.Contains("People", StringComparer.OrdinalIgnoreCase)))
+ {
+ if (!query.IncludeMedia)
{
- var index = GetIndex(item, searchTerm, terms);
-
- if (index.Item2 != -1)
- {
- try
- {
- var genre = _libraryManager.GetMusicGenre(item);
-
- hints.Add(new Tuple<BaseItem, string, int>(genre, index.Item1, index.Item2));
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting {0}", ex, item);
- }
- }
+ AddIfMissing(includeItemTypes, typeof(Person).Name);
}
+ }
+ else
+ {
+ AddIfMissing(excludeItemTypes, typeof(Person).Name);
+ }
- // Find music genres
- var gameGenres = items.OfType<Game>()
- .SelectMany(i => i.Genres)
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToList();
-
- foreach (var item in gameGenres)
+ if (query.IncludeStudios && (includeItemTypes.Count == 0 || includeItemTypes.Contains("Studio", StringComparer.OrdinalIgnoreCase)))
+ {
+ if (!query.IncludeMedia)
{
- var index = GetIndex(item, searchTerm, terms);
-
- if (index.Item2 != -1)
- {
- try
- {
- var genre = _libraryManager.GetGameGenre(item);
-
- hints.Add(new Tuple<BaseItem, string, int>(genre, index.Item1, index.Item2));
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting {0}", ex, item);
- }
- }
+ AddIfMissing(includeItemTypes, typeof(Studio).Name);
}
}
-
- if (query.IncludeStudios && (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains("Studio", StringComparer.OrdinalIgnoreCase)))
+ else
{
- // Find studios
- var studios = items.SelectMany(i => i.Studios)
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToList();
+ AddIfMissing(excludeItemTypes, typeof(Studio).Name);
+ }
- foreach (var item in studios)
+ if (query.IncludeArtists && (includeItemTypes.Count == 0 || includeItemTypes.Contains("MusicArtist", StringComparer.OrdinalIgnoreCase)))
+ {
+ if (!query.IncludeMedia)
{
- var index = GetIndex(item, searchTerm, terms);
-
- if (index.Item2 != -1)
- {
- try
- {
- var studio = _libraryManager.GetStudio(item);
-
- hints.Add(new Tuple<BaseItem, string, int>(studio, index.Item1, index.Item2));
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting {0}", ex, item);
- }
- }
+ AddIfMissing(includeItemTypes, typeof(MusicArtist).Name);
}
}
+ else
+ {
+ AddIfMissing(excludeItemTypes, typeof(MusicArtist).Name);
+ }
- if (query.IncludePeople && (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains("Person", StringComparer.OrdinalIgnoreCase)))
+ var mediaItems = _libraryManager.GetItems(new InternalItemsQuery
{
- var itemIds = items.Select(i => i.Id).ToList();
+ NameContains = searchTerm,
+ ExcludeItemTypes = excludeItemTypes.ToArray(),
+ IncludeItemTypes = includeItemTypes.ToArray(),
+ MaxParentalRating = user == null ? null : user.Policy.MaxParentalRating,
+ Limit = query.Limit.HasValue ? query.Limit * 3 : null
- // Find persons
- var persons = _libraryManager.GetPeople(new InternalPeopleQuery
- {
- NameContains = searchTerm
- })
- .Where(i => itemIds.Contains(i.ItemId))
- .Select(i => i.Name)
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToList();
-
- foreach (var item in persons)
- {
- var index = GetIndex(item, searchTerm, terms);
+ }).Items;
- if (index.Item2 != -1)
- {
- try
- {
- var person = _libraryManager.GetPerson(item);
-
- hints.Add(new Tuple<BaseItem, string, int>(person, index.Item1, index.Item2));
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting {0}", ex, item);
- }
- }
- }
- }
+ // Add search hints based on item name
+ hints.AddRange(mediaItems.Where(i => IncludeInSearch(i) && IsVisible(i, user) && !(i is CollectionFolder)).Select(item =>
+ {
+ var index = GetIndex(item.Name, searchTerm, terms);
+
+ return new Tuple<BaseItem, string, int>(item, index.Item1, index.Item2);
+ }));
var returnValue = hints.Where(i => i.Item3 >= 0).OrderBy(i => i.Item3).Select(i => new SearchHintInfo
{
@@ -311,13 +182,32 @@ namespace MediaBrowser.Server.Implementations.Library
return Task.FromResult(returnValue);
}
+ private bool IsVisible(BaseItem item, User user)
+ {
+ if (user == null)
+ {
+ return true;
+ }
+
+ if (item is IItemByName)
+ {
+ var dual = item as IHasDualAccess;
+ if (dual == null || dual.IsAccessedByName)
+ {
+ return true;
+ }
+ }
+
+ return item.IsVisibleStandalone(user);
+ }
+
private bool IncludeInSearch(BaseItem item)
{
var episode = item as Episode;
if (episode != null)
{
- if (episode.IsVirtualUnaired || episode.IsMissingEpisode)
+ if (episode.IsMissingEpisode)
{
return false;
}
diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
index a609c53ab..990799e71 100644
--- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
@@ -136,7 +136,8 @@ namespace MediaBrowser.Server.Implementations.Library
if (parents.Count > 0)
{
- list.Add(await GetUserView(parents, list, CollectionType.Playlists, string.Empty, user, cancellationToken).ConfigureAwait(false));
+ var name = _localizationManager.GetLocalizedString("ViewType" + CollectionType.Playlists);
+ list.Add(await _libraryManager.GetNamedView(name, CollectionType.Playlists, string.Empty, cancellationToken).ConfigureAwait(false));
}
if (user.Configuration.DisplayFoldersView)
@@ -224,7 +225,6 @@ namespace MediaBrowser.Server.Implementations.Library
if (enableUserSpecificViews)
{
- viewType = enableRichView ? viewType : null;
var view = await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
if (view.ParentId != parentId)
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs
index fe2e6a114..b57e128d3 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Entities;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Logging;
using System;
@@ -42,12 +43,16 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
var numComplete = 0;
var count = items.Count;
+ var validIds = new List<Guid>();
+
foreach (var name in items)
{
try
{
var itemByName = _libraryManager.GetGameGenre(name);
+ validIds.Add(itemByName.Id);
+
await itemByName.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
@@ -68,6 +73,28 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
progress.Report(percent);
}
+ var allIds = _libraryManager.GetItemIds(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(GameGenre).Name }
+ });
+
+ var invalidIds = allIds
+ .Except(validIds)
+ .ToList();
+
+ foreach (var id in invalidIds)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var item = _libraryManager.GetItemById(id);
+
+ await _libraryManager.DeleteItem(item, new DeleteOptions
+ {
+ DeleteFileLocation = false
+
+ }).ConfigureAwait(false);
+ }
+
progress.Report(100);
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs
index fac5cfc35..11d4c9f16 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Entities;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Logging;
@@ -43,12 +44,16 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
var numComplete = 0;
var count = items.Count;
+ var validIds = new List<Guid>();
+
foreach (var name in items)
{
try
{
var itemByName = _libraryManager.GetGenre(name);
+ validIds.Add(itemByName.Id);
+
await itemByName.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
@@ -69,6 +74,28 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
progress.Report(percent);
}
+ var allIds = _libraryManager.GetItemIds(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(Genre).Name }
+ });
+
+ var invalidIds = allIds
+ .Except(validIds)
+ .ToList();
+
+ foreach (var id in invalidIds)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var item = _libraryManager.GetItemById(id);
+
+ await _libraryManager.DeleteItem(item, new DeleteOptions
+ {
+ DeleteFileLocation = false
+
+ }).ConfigureAwait(false);
+ }
+
progress.Report(100);
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs
index e3be75e9b..0a66b4b41 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs
@@ -1,4 +1,6 @@
-using MediaBrowser.Controller.Entities.Audio;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Logging;
using System;
@@ -42,12 +44,16 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
var numComplete = 0;
var count = items.Count;
+ var validIds = new List<Guid>();
+
foreach (var name in items)
{
try
{
var itemByName = _libraryManager.GetMusicGenre(name);
+ validIds.Add(itemByName.Id);
+
await itemByName.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
@@ -68,6 +74,28 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
progress.Report(percent);
}
+ var allIds = _libraryManager.GetItemIds(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(MusicGenre).Name }
+ });
+
+ var invalidIds = allIds
+ .Except(validIds)
+ .ToList();
+
+ foreach (var id in invalidIds)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var item = _libraryManager.GetItemById(id);
+
+ await _libraryManager.DeleteItem(item, new DeleteOptions
+ {
+ DeleteFileLocation = false
+
+ }).ConfigureAwait(false);
+ }
+
progress.Report(100);
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs
index ef9dee8b5..a4c43af5d 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs
@@ -92,15 +92,25 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
foreach (var person in people)
{
- bool current;
- if (!dict.TryGetValue(person.Name, out current) || !current)
+ var isMetadataEnabled = DownloadMetadata(person, peopleOptions);
+
+ bool currentValue;
+ if (dict.TryGetValue(person.Name, out currentValue))
+ {
+ if (!currentValue && isMetadataEnabled)
+ {
+ dict[person.Name] = true;
+ }
+ }
+ else
{
- dict[person.Name] = DownloadMetadata(person, peopleOptions);
+ dict[person.Name] = isMetadataEnabled;
}
}
var numComplete = 0;
-
+ var validIds = new List<Guid>();
+
foreach (var person in dict)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -109,6 +119,8 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
{
var item = _libraryManager.GetPerson(person.Key);
+ validIds.Add(item.Id);
+
var options = new MetadataRefreshOptions
{
MetadataRefreshMode = person.Value ? MetadataRefreshMode.Default : MetadataRefreshMode.ValidationOnly,
@@ -130,6 +142,28 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
progress.Report(100 * percent);
}
+ var allIds = _libraryManager.GetItemIds(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(Person).Name }
+ });
+
+ var invalidIds = allIds
+ .Except(validIds)
+ .ToList();
+
+ foreach (var id in invalidIds)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var item = _libraryManager.GetItemById(id);
+
+ await _libraryManager.DeleteItem(item, new DeleteOptions
+ {
+ DeleteFileLocation = false
+
+ }).ConfigureAwait(false);
+ }
+
progress.Report(100);
_logger.Info("People validation complete");
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs
index 066b96853..c122d64d3 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs
@@ -1,6 +1,8 @@
-using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Logging;
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -41,12 +43,16 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
var numComplete = 0;
var count = items.Count;
+ var validIds = new List<Guid>();
+
foreach (var name in items)
{
try
{
var itemByName = _libraryManager.GetStudio(name);
+ validIds.Add(itemByName.Id);
+
await itemByName.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
@@ -67,6 +73,28 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
progress.Report(percent);
}
+ var allIds = _libraryManager.GetItemIds(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(Studio).Name }
+ });
+
+ var invalidIds = allIds
+ .Except(validIds)
+ .ToList();
+
+ foreach (var id in invalidIds)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var item = _libraryManager.GetItemById(id);
+
+ await _libraryManager.DeleteItem(item, new DeleteOptions
+ {
+ DeleteFileLocation = false
+
+ }).ConfigureAwait(false);
+ }
+
progress.Report(100);
}
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index cf294997b..1786d14e4 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -1,18 +1,25 @@
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
+using MediaBrowser.Common.Security;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.FileOrganization;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
+using MediaBrowser.Model.FileOrganization;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Server.Implementations.FileOrganization;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
@@ -20,12 +27,12 @@ using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{
- public class EmbyTV : ILiveTvService, IDisposable
+ public class EmbyTV : ILiveTvService, IHasRegistrationInfo, IDisposable
{
private readonly IApplicationHost _appHpst;
private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
- private readonly IConfigurationManager _config;
+ private readonly IServerConfigurationManager _config;
private readonly IJsonSerializer _jsonSerializer;
private readonly ItemDataProvider<RecordingInfo> _recordingProvider;
@@ -33,10 +40,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
private readonly TimerManager _timerProvider;
private readonly LiveTvManager _liveTvManager;
+ private readonly IFileSystem _fileSystem;
+ private readonly ISecurityManager _security;
+
+ private readonly ILibraryMonitor _libraryMonitor;
+ private readonly ILibraryManager _libraryManager;
+ private readonly IProviderManager _providerManager;
+ private readonly IFileOrganizationService _organizationService;
public static EmbyTV Current;
- public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IConfigurationManager config, ILiveTvManager liveTvManager)
+ public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ISecurityManager security, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService)
{
Current = this;
@@ -44,6 +58,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
_logger = logger;
_httpClient = httpClient;
_config = config;
+ _fileSystem = fileSystem;
+ _security = security;
+ _libraryManager = libraryManager;
+ _libraryMonitor = libraryMonitor;
+ _providerManager = providerManager;
+ _organizationService = organizationService;
_liveTvManager = (LiveTvManager)liveTvManager;
_jsonSerializer = jsonSerializer;
@@ -85,11 +105,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
var status = new LiveTvServiceStatusInfo();
var list = new List<LiveTvTunerInfo>();
- foreach (var hostInstance in GetTunerHosts())
+ foreach (var hostInstance in _liveTvManager.TunerHosts)
{
try
{
- var tuners = await hostInstance.Item1.GetTunerInfos(hostInstance.Item2, cancellationToken).ConfigureAwait(false);
+ var tuners = await hostInstance.GetTunerInfos(cancellationToken).ConfigureAwait(false);
list.AddRange(tuners);
}
@@ -106,23 +126,24 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
return status;
}
- public async Task<IEnumerable<ChannelInfo>> GetChannelsAsync(CancellationToken cancellationToken)
+ private List<ChannelInfo> _channelCache = null;
+ private async Task<IEnumerable<ChannelInfo>> GetChannelsAsync(bool enableCache, CancellationToken cancellationToken)
{
+ if (enableCache && _channelCache != null)
+ {
+
+ return _channelCache.ToList();
+ }
+
var list = new List<ChannelInfo>();
- foreach (var hostInstance in GetTunerHosts())
+ foreach (var hostInstance in _liveTvManager.TunerHosts)
{
try
{
- var channels = await hostInstance.Item1.GetChannels(hostInstance.Item2, cancellationToken).ConfigureAwait(false);
- var newChannels = channels.ToList();
+ var channels = await hostInstance.GetChannels(cancellationToken).ConfigureAwait(false);
- foreach (var channel in newChannels)
- {
- channel.Id = hostInstance.Item1.Type.GetMD5().ToString("N") + "-" + channel.Id;
- }
-
- list.AddRange(newChannels);
+ list.AddRange(channels);
}
catch (Exception ex)
{
@@ -148,26 +169,23 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
}
}
-
+ _channelCache = list;
return list;
}
- private List<Tuple<ITunerHost, TunerHostInfo>> GetTunerHosts()
+ public Task<IEnumerable<ChannelInfo>> GetChannelsAsync(CancellationToken cancellationToken)
{
- return GetConfiguration().TunerHosts
- .Where(i => i.IsEnabled)
- .Select(i =>
- {
- var provider = _liveTvManager.TunerHosts.FirstOrDefault(l => string.Equals(l.Type, i.Type, StringComparison.OrdinalIgnoreCase));
-
- return provider == null ? null : new Tuple<ITunerHost, TunerHostInfo>(provider, i);
- })
- .Where(i => i != null)
- .ToList();
+ return GetChannelsAsync(false, cancellationToken);
}
public Task CancelSeriesTimerAsync(string timerId, CancellationToken cancellationToken)
{
+ var timers = _timerProvider.GetAll().Where(i => string.Equals(i.SeriesTimerId, timerId, StringComparison.OrdinalIgnoreCase));
+ foreach (var timer in timers)
+ {
+ CancelTimerInternal(timer.Id);
+ }
+
var remove = _seriesTimerProvider.GetAll().FirstOrDefault(r => string.Equals(r.Id, timerId, StringComparison.OrdinalIgnoreCase));
if (remove != null)
{
@@ -211,7 +229,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
if (enableDelay)
{
// A hack yes, but need to make sure the file is closed before attempting to delete it
- await Task.Delay(3000).ConfigureAwait(false);
+ await Task.Delay(3000, cancellationToken).ConfigureAwait(false);
}
}
@@ -238,20 +256,54 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
return Task.FromResult(0);
}
- public Task CreateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken)
+ public async Task CreateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken)
{
info.Id = Guid.NewGuid().ToString("N");
- UpdateTimersForSeriesTimer(info);
+ List<ProgramInfo> epgData;
+ if (info.RecordAnyChannel)
+ {
+ var channels = await GetChannelsAsync(true, CancellationToken.None).ConfigureAwait(false);
+ var channelIds = channels.Select(i => i.Id).ToList();
+ epgData = GetEpgDataForChannels(channelIds);
+ }
+ else
+ {
+ epgData = GetEpgDataForChannel(info.ChannelId);
+ }
+
+ // populate info.seriesID
+ var program = epgData.FirstOrDefault(i => string.Equals(i.Id, info.ProgramId, StringComparison.OrdinalIgnoreCase));
+
+ if (program != null)
+ {
+ info.SeriesId = program.SeriesId;
+ }
+ else
+ {
+ throw new InvalidOperationException("SeriesId for program not found");
+ }
+
_seriesTimerProvider.Add(info);
- return Task.FromResult(true);
+ await UpdateTimersForSeriesTimer(epgData, info).ConfigureAwait(false);
}
- public Task UpdateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken)
+ public async Task UpdateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken)
{
_seriesTimerProvider.Update(info);
- UpdateTimersForSeriesTimer(info);
- return Task.FromResult(true);
+ List<ProgramInfo> epgData;
+ if (info.RecordAnyChannel)
+ {
+ var channels = await GetChannelsAsync(true, CancellationToken.None).ConfigureAwait(false);
+ var channelIds = channels.Select(i => i.Id).ToList();
+ epgData = GetEpgDataForChannels(channelIds);
+ }
+ else
+ {
+ epgData = GetEpgDataForChannel(info.ChannelId);
+ }
+
+ await UpdateTimersForSeriesTimer(epgData, info).ConfigureAwait(false);
}
public Task UpdateTimerAsync(TimerInfo info, CancellationToken cancellationToken)
@@ -287,10 +339,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
public Task<SeriesTimerInfo> GetNewTimerDefaultsAsync(CancellationToken cancellationToken, ProgramInfo program = null)
{
+ var config = GetConfiguration();
+
var defaults = new SeriesTimerInfo()
{
- PostPaddingSeconds = 60,
- PrePaddingSeconds = 60,
+ PostPaddingSeconds = Math.Max(config.PostPaddingSeconds, 0),
+ PrePaddingSeconds = Math.Max(config.PrePaddingSeconds, 0),
RecordAnyChannel = false,
RecordAnyTime = false,
RecordNewOnly = false
@@ -310,18 +364,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
return Task.FromResult((IEnumerable<SeriesTimerInfo>)_seriesTimerProvider.GetAll());
}
- private string GetOriginalChannelId(string channelId)
- {
- var parts = channelId.Split('-');
-
- return string.Join("-", parts.Skip(1).ToArray());
- }
-
public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
{
+ var channels = await GetChannelsAsync(true, cancellationToken).ConfigureAwait(false);
+ var channel = channels.First(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase));
+
foreach (var provider in GetListingProviders())
{
- var programs = await provider.Item1.GetProgramsAsync(provider.Item2, GetOriginalChannelId(channelId), startDateUtc, endDateUtc, cancellationToken)
+ var programs = await provider.Item1.GetProgramsAsync(provider.Item2, channel.Number, startDateUtc, endDateUtc, cancellationToken)
.ConfigureAwait(false);
var list = programs.ToList();
@@ -364,38 +414,23 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{
_logger.Info("Streaming Channel " + channelId);
- var configurationId = channelId.Split('-')[0];
-
- foreach (var hostInstance in GetTunerHosts())
+ foreach (var hostInstance in _liveTvManager.TunerHosts)
{
- if (!string.Equals(configurationId, hostInstance.Item1.Type.GetMD5().ToString("N"), StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
-
- if (!string.IsNullOrWhiteSpace(streamId))
- {
- var originalStreamId = string.Join("-", streamId.Split('-').Skip(1).ToArray());
-
- if (!string.Equals(hostInstance.Item2.Id, originalStreamId, StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
- }
-
MediaSourceInfo mediaSourceInfo = null;
try
{
- mediaSourceInfo = await hostInstance.Item1.GetChannelStream(hostInstance.Item2, GetOriginalChannelId(channelId), streamId, cancellationToken).ConfigureAwait(false);
+ mediaSourceInfo = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false);
}
- catch (ApplicationException e)
+ catch (Exception e)
{
- _logger.Info(e.Message);
- continue;
+ _logger.ErrorException("Error getting channel stream", e);
}
- mediaSourceInfo.Id = Guid.NewGuid().ToString("N");
- return mediaSourceInfo;
+ if (mediaSourceInfo != null)
+ {
+ mediaSourceInfo.Id = Guid.NewGuid().ToString("N");
+ return mediaSourceInfo;
+ }
}
throw new ApplicationException("Tuner not found.");
@@ -403,24 +438,24 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
{
- var configurationId = channelId.Split('-')[0];
-
- foreach (var hostInstance in GetTunerHosts())
+ foreach (var hostInstance in _liveTvManager.TunerHosts)
{
- if (string.Equals(configurationId, hostInstance.Item1.Type.GetMD5().ToString("N"), StringComparison.OrdinalIgnoreCase))
+ try
{
- var sources = await hostInstance.Item1.GetChannelStreamMediaSources(hostInstance.Item2, GetOriginalChannelId(channelId), cancellationToken).ConfigureAwait(false);
+ var sources = await hostInstance.GetChannelStreamMediaSources(channelId, cancellationToken).ConfigureAwait(false);
- foreach (var source in sources)
+ if (sources.Count > 0)
{
- source.Id = hostInstance.Item2.Id + "-" + source.Id;
+ return sources;
}
+ }
+ catch (NotImplementedException)
+ {
- return sources;
}
}
- throw new ApplicationException("Tuner not found.");
+ throw new NotImplementedException();
}
public Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken)
@@ -445,13 +480,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
async void _timerProvider_TimerFired(object sender, GenericEventArgs<TimerInfo> e)
{
+ var timer = e.Argument;
+
+ _logger.Info("Recording timer fired.");
+
try
{
var cancellationTokenSource = new CancellationTokenSource();
- if (_activeRecordings.TryAdd(e.Argument.Id, cancellationTokenSource))
+ if (_activeRecordings.TryAdd(timer.Id, cancellationTokenSource))
{
- await RecordStream(e.Argument, cancellationTokenSource.Token).ConfigureAwait(false);
+ await RecordStream(timer, cancellationTokenSource.Token).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
@@ -476,21 +515,36 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
HttpRequestOptions httpRequestOptions = new HttpRequestOptions()
{
- Url = mediaStreamInfo.Path + "?duration=" + duration.TotalSeconds.ToString(CultureInfo.InvariantCulture)
+ Url = mediaStreamInfo.Path
};
var info = GetProgramInfoFromCache(timer.ChannelId, timer.ProgramId);
var recordPath = RecordingPath;
+
if (info.IsMovie)
{
- recordPath = Path.Combine(recordPath, "Movies", RecordingHelper.RemoveSpecialCharacters(info.Name));
+ recordPath = Path.Combine(recordPath, "Movies", _fileSystem.GetValidFilename(info.Name));
+ }
+ else if (info.IsSeries)
+ {
+ recordPath = Path.Combine(recordPath, "Series", _fileSystem.GetValidFilename(info.Name));
+ }
+ else if (info.IsKids)
+ {
+ recordPath = Path.Combine(recordPath, "Kids", _fileSystem.GetValidFilename(info.Name));
+ }
+ else if (info.IsSports)
+ {
+ recordPath = Path.Combine(recordPath, "Sports", _fileSystem.GetValidFilename(info.Name));
}
else
{
- recordPath = Path.Combine(recordPath, "TV", RecordingHelper.RemoveSpecialCharacters(info.Name));
+ recordPath = Path.Combine(recordPath, "Other", _fileSystem.GetValidFilename(info.Name));
}
- recordPath = Path.Combine(recordPath, RecordingHelper.GetRecordingName(timer, info));
+ var recordingFileName = _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer, info)) + ".ts";
+
+ recordPath = Path.Combine(recordPath, recordingFileName);
Directory.CreateDirectory(Path.GetDirectoryName(recordPath));
var recording = _recordingProvider.GetAll().FirstOrDefault(x => string.Equals(x.ProgramId, info.Id, StringComparison.OrdinalIgnoreCase));
@@ -531,8 +585,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
recording.Path = recordPath;
recording.Status = RecordingStatus.InProgress;
+ recording.DateLastUpdated = DateTime.UtcNow;
_recordingProvider.Update(recording);
+ _logger.Info("Beginning recording.");
+
try
{
httpRequestOptions.BufferContent = false;
@@ -544,7 +601,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{
using (var output = File.Open(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read))
{
- await response.Content.CopyToAsync(output, 131072, linkedToken);
+ await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken);
}
}
@@ -553,7 +610,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
catch (OperationCanceledException)
{
- _logger.Info("Recording cancelled");
+ _logger.Info("Recording stopped");
recording.Status = RecordingStatus.Completed;
}
catch (Exception ex)
@@ -561,20 +618,63 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
_logger.ErrorException("Error recording", ex);
recording.Status = RecordingStatus.Error;
}
+ finally
+ {
+ CancellationTokenSource removed;
+ _activeRecordings.TryRemove(timer.Id, out removed);
+ }
+ recording.DateLastUpdated = DateTime.UtcNow;
_recordingProvider.Update(recording);
- _timerProvider.Delete(timer);
- _logger.Info("Recording was a success");
+
+ if (recording.Status == RecordingStatus.Completed)
+ {
+ OnSuccessfulRecording(recording);
+ _timerProvider.Delete(timer);
+ }
+ else if (DateTime.UtcNow < timer.EndDate)
+ {
+ const int retryIntervalSeconds = 60;
+ _logger.Info("Retrying recording in {0} seconds.", retryIntervalSeconds);
+
+ _timerProvider.StartTimer(timer, TimeSpan.FromSeconds(retryIntervalSeconds));
+ }
+ else
+ {
+ _timerProvider.Delete(timer);
+ _recordingProvider.Delete(recording);
+ }
}
- private ProgramInfo GetProgramInfoFromCache(string channelId, string programId)
+ private async void OnSuccessfulRecording(RecordingInfo recording)
{
- var epgData = GetEpgDataForChannel(channelId);
- if (epgData.Any())
+ if (GetConfiguration().EnableAutoOrganize)
{
- return epgData.FirstOrDefault(p => p.Id == programId);
+ if (recording.IsSeries)
+ {
+ try
+ {
+ var organize = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager);
+
+ var result = await organize.OrganizeEpisodeFile(recording.Path, CancellationToken.None).ConfigureAwait(false);
+
+ if (result.Status == FileSortingStatus.Success)
+ {
+ _recordingProvider.Delete(recording);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error processing new recording", ex);
+ }
+ }
}
- return null;
+ }
+
+ private ProgramInfo GetProgramInfoFromCache(string channelId, string programId)
+ {
+ var epgData = GetEpgDataForChannel(channelId);
+ return epgData.FirstOrDefault(p => string.Equals(p.Id, programId, StringComparison.OrdinalIgnoreCase));
}
private string RecordingPath
@@ -594,47 +694,31 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
return _config.GetConfiguration<LiveTvOptions>("livetv");
}
- private void UpdateTimersForSeriesTimer(SeriesTimerInfo seriesTimer)
+ private async Task UpdateTimersForSeriesTimer(List<ProgramInfo> epgData, SeriesTimerInfo seriesTimer)
{
- List<ProgramInfo> epgData;
- if (seriesTimer.RecordAnyChannel)
- {
- epgData = GetEpgDataForAllChannels();
- }
- else
- {
- epgData = GetEpgDataForChannel(seriesTimer.ChannelId);
- }
+ var registration = await GetRegistrationInfo("seriesrecordings").ConfigureAwait(false);
- var newTimers = GetTimersForSeries(seriesTimer, epgData, _recordingProvider.GetAll()).ToList();
-
- var existingTimers = _timerProvider.GetAll()
- .Where(i => string.Equals(i.SeriesTimerId, seriesTimer.Id, StringComparison.OrdinalIgnoreCase))
- .ToList();
-
- foreach (var timer in newTimers)
+ if (registration.IsValid)
{
- _timerProvider.AddOrUpdate(timer);
- }
+ var newTimers = GetTimersForSeries(seriesTimer, epgData, _recordingProvider.GetAll()).ToList();
- var newTimerIds = newTimers.Select(i => i.Id).ToList();
-
- foreach (var timer in existingTimers)
- {
- if (!newTimerIds.Contains(timer.Id, StringComparer.OrdinalIgnoreCase))
+ foreach (var timer in newTimers)
{
- CancelTimerInternal(timer.Id);
+ _timerProvider.AddOrUpdate(timer);
}
}
}
private IEnumerable<TimerInfo> GetTimersForSeries(SeriesTimerInfo seriesTimer, IEnumerable<ProgramInfo> allPrograms, IReadOnlyList<RecordingInfo> currentRecordings)
{
+ // Exclude programs that have already ended
+ allPrograms = allPrograms.Where(i => i.EndDate > DateTime.UtcNow);
+
allPrograms = GetProgramsForSeries(seriesTimer, allPrograms);
- var recordingShowIds = currentRecordings.Select(i => i.ShowId).ToList();
+ var recordingShowIds = currentRecordings.Select(i => i.ProgramId).Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
- allPrograms = allPrograms.Where(epg => !recordingShowIds.Contains(epg.ShowId, StringComparer.OrdinalIgnoreCase));
+ allPrograms = allPrograms.Where(i => !recordingShowIds.Contains(i.Id, StringComparer.OrdinalIgnoreCase));
return allPrograms.Select(i => RecordingHelper.CreateTimer(i, seriesTimer));
}
@@ -648,7 +732,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
if (seriesTimer.RecordNewOnly)
{
- allPrograms = allPrograms.Where(epg => !epg.IsRepeat);
+ allPrograms = allPrograms.Where(epg => !epg.IsRepeat);
}
if (!seriesTimer.RecordAnyChannel)
@@ -656,9 +740,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
allPrograms = allPrograms.Where(epg => string.Equals(epg.ChannelId, seriesTimer.ChannelId, StringComparison.OrdinalIgnoreCase));
}
- allPrograms = allPrograms.Where(epg => seriesTimer.Days.Contains(epg.StartDate.DayOfWeek));
+ allPrograms = allPrograms.Where(i => seriesTimer.Days.Contains(i.StartDate.ToLocalTime().DayOfWeek));
+
+ if (string.IsNullOrWhiteSpace(seriesTimer.SeriesId))
+ {
+ _logger.Error("seriesTimer.SeriesId is null. Cannot find programs for series");
+ return new List<ProgramInfo>();
+ }
- return allPrograms.Where(epg => string.Equals(epg.SeriesId, seriesTimer.SeriesId, StringComparison.OrdinalIgnoreCase));
+ return allPrograms.Where(i => string.Equals(i.SeriesId, seriesTimer.SeriesId, StringComparison.OrdinalIgnoreCase));
}
private string GetChannelEpgCachePath(string channelId)
@@ -690,16 +780,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
return new List<ProgramInfo>();
}
}
- private List<ProgramInfo> GetEpgDataForAllChannels()
+ private List<ProgramInfo> GetEpgDataForChannels(List<string> channelIds)
{
- List<ProgramInfo> channelEpg = new List<ProgramInfo>();
- DirectoryInfo dir = new DirectoryInfo(Path.Combine(DataPath, "epg"));
- List<string> channels = dir.GetFiles("*").Where(i => string.Equals(i.Extension, ".json", StringComparison.OrdinalIgnoreCase)).Select(f => f.Name).ToList();
- foreach (var channel in channels)
- {
- channelEpg.AddRange(GetEpgDataForChannel(channel));
- }
- return channelEpg;
+ return channelIds.SelectMany(GetEpgDataForChannel).ToList();
}
public void Dispose()
@@ -709,5 +792,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
pair.Value.Cancel();
}
}
+
+ public Task<MBRegistrationRecord> GetRegistrationInfo(string feature)
+ {
+ if (string.Equals(feature, "seriesrecordings", StringComparison.OrdinalIgnoreCase))
+ {
+ return _security.GetRegistrationStatus("embytvseriesrecordings");
+ }
+
+ return Task.FromResult(new MBRegistrationRecord
+ {
+ IsValid = true,
+ IsRegistered = true
+ });
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
index a297c866a..713cb9cd3 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
@@ -1,41 +1,12 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Security;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Controller.Plugins;
namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{
public class EntryPoint : IServerEntryPoint
{
- private readonly IConfigurationManager _config;
- private readonly ISecurityManager _manager;
-
- public EntryPoint(IConfigurationManager config, ISecurityManager manager)
- {
- _config = config;
- _manager = manager;
- }
-
- public async void Run()
+ public void Run()
{
EmbyTV.Current.Start();
-
- if (GetConfiguration().ListingProviders.Count > 0 || GetConfiguration().TunerHosts.Count > 0)
- {
- try
- {
- await _manager.GetRegistrationStatus("livetvguide").ConfigureAwait(false);
- }
- catch
- {
-
- }
- }
- }
-
- private LiveTvOptions GetConfiguration()
- {
- return _config.GetConfiguration<LiveTvOptions>("livetv");
}
public void Dispose()
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
index 31fb072cd..5b83d63b1 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.LiveTv;
using System;
-using System.Text;
+using System.Globalization;
namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{
@@ -37,39 +37,42 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{
if (info == null)
{
- return (timer.ProgramId + ".ts");
- }
- var fancyName = info.Name;
- if (info.ProductionYear != null)
- {
- fancyName += "_(" + info.ProductionYear + ")";
+ return timer.ProgramId;
}
+
+ var name = info.Name;
+
if (info.IsSeries)
{
- fancyName += "_" + info.EpisodeTitle.Replace("Season: ", "S").Replace(" Episode: ", "E");
- }
- if (info.IsHD ?? false)
- {
- fancyName += "_HD";
- }
- if (info.OriginalAirDate != null)
- {
- fancyName += "_" + info.OriginalAirDate.Value.ToString("yyyy-MM-dd");
- }
- return RemoveSpecialCharacters(fancyName) + ".ts";
- }
+ var addHyphen = true;
- public static string RemoveSpecialCharacters(string str)
- {
- StringBuilder sb = new StringBuilder();
- foreach (char c in str)
- {
- if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '.' || c == '_' || c == '-' || c == ' ')
+ if (info.SeasonNumber.HasValue && info.EpisodeNumber.HasValue)
{
- sb.Append(c);
+ name += string.Format(" S{0}E{1}", info.SeasonNumber.Value.ToString("00", CultureInfo.InvariantCulture), info.EpisodeNumber.Value.ToString("00", CultureInfo.InvariantCulture));
+ addHyphen = false;
}
+ else if (info.OriginalAirDate.HasValue)
+ {
+ name += " " + info.OriginalAirDate.Value.ToString("yyyy-MM-dd");
+ }
+
+ if (addHyphen)
+ {
+ name += " -";
+ }
+
+ if (!string.IsNullOrWhiteSpace(info.EpisodeTitle))
+ {
+ name += " " + info.EpisodeTitle;
+ }
+ }
+
+ else if (info.ProductionYear != null)
+ {
+ name += " (" + info.ProductionYear + ")";
}
- return sb.ToString();
+
+ return name;
}
}
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index 0c8d2ca2b..3ae38f382 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -44,12 +44,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
public override void Delete(TimerInfo item)
{
base.Delete(item);
-
- Timer timer;
- if (_timers.TryRemove(item.Id, out timer))
- {
- timer.Dispose();
- }
+ StopTimer(item);
}
public override void Update(TimerInfo item)
@@ -104,9 +99,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
return;
}
- var timespan = startDate - now;
+ var timerLength = startDate - now;
+ StartTimer(item, timerLength);
+ }
- var timer = new Timer(TimerCallback, item.Id, timespan, TimeSpan.Zero);
+ public void StartTimer(TimerInfo item, TimeSpan length)
+ {
+ StopTimer(item);
+
+ var timer = new Timer(TimerCallback, item.Id, length, TimeSpan.Zero);
if (!_timers.TryAdd(item.Id, timer))
{
@@ -114,6 +115,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
}
+ private void StopTimer(TimerInfo item)
+ {
+ Timer timer;
+ if (_timers.TryRemove(item.Id, out timer))
+ {
+ timer.Dispose();
+ }
+ }
+
private void TimerCallback(object state)
{
var timerId = (string)state;
diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs
index fca1bb012..2993740d6 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs
@@ -50,7 +50,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby
{
Id = info.listingID.ToString(CultureInfo.InvariantCulture),
Name = GetStringValue(info.showName),
- EpisodeTitle = GetStringValue(info.episodeTitle),
HomePageUrl = GetStringValue(info.webLink),
Overview = info.description,
IsHD = info.hd,
@@ -78,6 +77,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby
{
// They don't have dashes so try to normalize
program.OfficialRating = info.rating.Replace("TV", "TV-").Replace("--", "-");
+
+ var invalid = new[] { "N/A", "Approved", "Not Rated" };
+ if (invalid.Contains(program.OfficialRating, StringComparer.OrdinalIgnoreCase))
+ {
+ program.OfficialRating = null;
+ }
}
if (!string.IsNullOrWhiteSpace(info.year))
@@ -95,6 +100,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby
program.SeriesId = info.seriesID.ToString(CultureInfo.InvariantCulture);
program.IsSeries = true;
program.IsRepeat = info.repeat;
+
+ program.EpisodeTitle = GetStringValue(info.episodeTitle);
+
+ if (string.Equals(program.Name, program.EpisodeTitle, StringComparison.OrdinalIgnoreCase))
+ {
+ program.EpisodeTitle = null;
+ }
}
if (info.starRating > 0)
diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 36710c052..d53c08150 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -1,9 +1,11 @@
-using MediaBrowser.Common;
+using System.Net;
+using MediaBrowser.Common;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Concurrent;
@@ -62,7 +64,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
{
List<ProgramInfo> programsInfo = new List<ProgramInfo>();
- var token = await GetToken(info, cancellationToken);
+ var token = await GetToken(info, cancellationToken).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(token))
{
@@ -78,7 +80,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
{
Url = ApiUrl + "/schedules",
UserAgent = UserAgent,
- CancellationToken = cancellationToken
+ CancellationToken = cancellationToken,
+ // The data can be large so give it some extra time
+ TimeoutMs = 60000
};
httpOptions.RequestHeaders["token"] = token;
@@ -132,6 +136,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
{
StreamReader innerReader = new StreamReader(innerResponse.Content);
responseString = innerReader.ReadToEnd();
+
var programDetails =
_jsonSerializer.DeserializeFromString<List<ScheduleDirect.ProgramDetails>>(
responseString);
@@ -142,10 +147,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
var schedules = dailySchedules.SelectMany(d => d.programs);
foreach (ScheduleDirect.Program schedule in schedules)
{
- _logger.Debug("Proccesing Schedule for statio ID " + stationID +
- " which corresponds to channel " + channelNumber + " and program id " +
- schedule.programID + " which says it has images? " +
- programDict[schedule.programID].hasImageArtwork);
+ //_logger.Debug("Proccesing Schedule for statio ID " + stationID +
+ // " which corresponds to channel " + channelNumber + " and program id " +
+ // schedule.programID + " which says it has images? " +
+ // programDict[schedule.programID].hasImageArtwork);
var imageIndex = images.FindIndex(i => i.programID == schedule.programID.Substring(0, 10));
if (imageIndex > -1)
@@ -244,7 +249,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
CultureInfo.InvariantCulture);
DateTime endAt = startAt.AddSeconds(programInfo.duration);
ProgramAudio audioType = ProgramAudio.Stereo;
- bool hdtv = false;
+
bool repeat = (programInfo.@new == null);
string newID = programInfo.programID + "T" + startAt.Ticks + "C" + channel;
@@ -268,78 +273,83 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
}
}
- if ((programInfo.videoProperties != null))
- {
- hdtv = programInfo.videoProperties.Exists(item => item == "hdtv");
- }
-
- string desc = "";
- if (details.descriptions != null)
- {
- if (details.descriptions.description1000 != null)
- {
- desc = details.descriptions.description1000[0].description;
- }
- else if (details.descriptions.description100 != null)
- {
- desc = details.descriptions.description100[0].description;
- }
- }
- ScheduleDirect.Gracenote gracenote;
string episodeTitle = null;
- if (details.metadata != null)
- {
- gracenote = details.metadata.Find(x => x.Gracenote != null).Gracenote;
- if ((details.showType ?? "No ShowType") == "Series")
- {
- episodeTitle = "Season: " + gracenote.season + " Episode: " + gracenote.episode;
- }
- }
if (details.episodeTitle150 != null)
{
- episodeTitle = ((episodeTitle ?? string.Empty) + " " + details.episodeTitle150).Trim();
+ episodeTitle = details.episodeTitle150;
}
- var imageLink = "";
+ string imageUrl = null;
if (details.hasImageArtwork)
{
- imageLink = details.images;
+ imageUrl = details.images;
}
+ var showType = details.showType ?? string.Empty;
+
var info = new ProgramInfo
{
ChannelId = channel,
Id = newID,
- Overview = desc,
StartDate = startAt,
EndDate = endAt,
Name = details.titles[0].title120 ?? "Unkown",
- OfficialRating = "0",
+ OfficialRating = null,
CommunityRating = null,
EpisodeTitle = episodeTitle,
Audio = audioType,
- IsHD = hdtv,
IsRepeat = repeat,
- IsSeries =
- ((details.showType ?? "No ShowType") == "Series") ||
- (details.showType ?? "No ShowType") == "Miniseries",
- ImageUrl = imageLink,
+ IsSeries = showType.IndexOf("series", StringComparison.OrdinalIgnoreCase) != -1,
+ ImageUrl = imageUrl,
HasImage = details.hasImageArtwork,
- IsNews = false,
- IsKids = false,
- IsSports =
- ((details.showType ?? "No ShowType") == "Sports non-event") ||
- (details.showType ?? "No ShowType") == "Sports event",
- IsLive = false,
- IsMovie =
- (details.showType ?? "No ShowType") == "Feature Film" ||
- (details.showType ?? "No ShowType") == "TV Movie" ||
- (details.showType ?? "No ShowType") == "Short Film",
- IsPremiere = false,
- ShowId = programInfo.programID
+ IsKids = string.Equals(details.audience, "children", StringComparison.OrdinalIgnoreCase),
+ IsSports = showType.IndexOf("sports", StringComparison.OrdinalIgnoreCase) != -1,
+ IsMovie = showType.IndexOf("movie", StringComparison.OrdinalIgnoreCase) != -1 || showType.IndexOf("film", StringComparison.OrdinalIgnoreCase) != -1,
+ ShowId = programInfo.programID,
+ Etag = programInfo.md5
};
+ if (programInfo.videoProperties != null)
+ {
+ info.IsHD = programInfo.videoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase);
+ }
+
+ if (details.contentRating != null && details.contentRating.Count > 0)
+ {
+ info.OfficialRating = details.contentRating[0].code.Replace("TV", "TV-").Replace("--", "-");
+
+ var invalid = new[] { "N/A", "Approved", "Not Rated", "Passed" };
+ if (invalid.Contains(info.OfficialRating, StringComparer.OrdinalIgnoreCase))
+ {
+ info.OfficialRating = null;
+ }
+ }
+
+ if (details.descriptions != null)
+ {
+ if (details.descriptions.description1000 != null)
+ {
+ info.Overview = details.descriptions.description1000[0].description;
+ }
+ else if (details.descriptions.description100 != null)
+ {
+ info.ShortOverview = details.descriptions.description100[0].description;
+ }
+ }
+
+ if (info.IsSeries)
+ {
+ info.SeriesId = programInfo.programID.Substring(0, 10);
+
+ if (details.metadata != null)
+ {
+ var gracenote = details.metadata.Find(x => x.Gracenote != null).Gracenote;
+ info.SeasonNumber = gracenote.season;
+ info.EpisodeNumber = gracenote.episode;
+ }
+ }
+
if (!string.IsNullOrWhiteSpace(details.originalAirDate))
{
info.OriginalAirDate = DateTime.Parse(details.originalAirDate);
@@ -349,8 +359,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
{
info.Genres = details.genres.Where(g => !string.IsNullOrWhiteSpace(g)).ToList();
info.IsNews = details.genres.Contains("news", StringComparer.OrdinalIgnoreCase);
- info.IsKids = false;
+
+ if (info.Genres.Contains("children", StringComparer.OrdinalIgnoreCase))
+ {
+ info.IsKids = true;
+ }
}
+
return info;
}
@@ -396,7 +411,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
;
});
imageIdString = imageIdString.TrimEnd(',') + "]";
- _logger.Debug("Json for show images = " + imageIdString);
+
var httpOptions = new HttpRequestOptions()
{
Url = ApiUrl + "/metadata/programs",
@@ -425,8 +440,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
return lineups;
}
- _logger.Info("Headends on account ");
-
var options = new HttpRequestOptions()
{
Url = ApiUrl + "/headends?country=" + country + "&postalcode=" + location,
@@ -441,16 +454,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
using (Stream responce = await _httpClient.Get(options).ConfigureAwait(false))
{
var root = _jsonSerializer.DeserializeFromStream<List<ScheduleDirect.Headends>>(responce);
- _logger.Info("Lineups on account ");
+
if (root != null)
{
foreach (ScheduleDirect.Headends headend in root)
{
- _logger.Info("Headend: " + headend.headend);
foreach (ScheduleDirect.Lineup lineup in headend.lineups)
{
- _logger.Info("Headend: " + lineup.uri);
-
lineups.Add(new NameIdPair
{
Name = string.IsNullOrWhiteSpace(lineup.name) ? lineup.lineup : lineup.name,
@@ -461,7 +471,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
}
else
{
- _logger.Info("No lineups on account");
+ _logger.Info("No lineups available");
}
}
}
@@ -474,7 +484,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
}
private readonly ConcurrentDictionary<string, NameValuePair> _tokens = new ConcurrentDictionary<string, NameValuePair>();
-
+ private DateTime _lastErrorResponse;
private async Task<string> GetToken(ListingsProviderInfo info, CancellationToken cancellationToken)
{
var username = info.Username;
@@ -491,6 +501,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
return null;
}
+ // Avoid hammering SD
+ if ((DateTime.UtcNow - _lastErrorResponse).TotalMinutes < 1)
+ {
+ return null;
+ }
+
NameValuePair savedToken = null;
if (!_tokens.TryGetValue(username, out savedToken))
{
@@ -519,6 +535,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
savedToken.Value = DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture);
return result;
}
+ catch (HttpException ex)
+ {
+ if (ex.StatusCode.HasValue)
+ {
+ if ((int)ex.StatusCode.Value == 400)
+ {
+ _tokens.Clear();
+ _lastErrorResponse = DateTime.UtcNow;
+ }
+ }
+ throw;
+ }
finally
{
_tokenSemaphore.Release();
@@ -616,11 +644,24 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
options.RequestHeaders["token"] = token;
- using (var response = await _httpClient.Get(options).ConfigureAwait(false))
+ try
{
- var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Lineups>(response);
+ using (var response = await _httpClient.Get(options).ConfigureAwait(false))
+ {
+ var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Lineups>(response);
+
+ return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
+ }
+ }
+ catch (HttpException ex)
+ {
+ // Apparently we're supposed to swallow this
+ if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.BadRequest)
+ {
+ return false;
+ }
- return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
+ throw;
}
}
@@ -888,6 +929,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
public class ProgramDetails
{
+ public string audience { get; set; }
public string programID { get; set; }
public List<Title> titles { get; set; }
public EventDetails eventDetails { get; set; }
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index 782473bea..d73b144b8 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
using MediaBrowser.Common.Progress;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Configuration;
@@ -24,6 +25,7 @@ using MoreLinq;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -57,12 +59,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private readonly SemaphoreSlim _refreshRecordingsLock = new SemaphoreSlim(1, 1);
- private readonly ConcurrentDictionary<Guid, Guid> _refreshedPrograms = new ConcurrentDictionary<Guid, Guid>();
-
private readonly List<ITunerHost> _tunerHosts = new List<ITunerHost>();
private readonly List<IListingsProvider> _listingProviders = new List<IListingsProvider>();
+ private readonly IFileSystem _fileSystem;
- public LiveTvManager(IApplicationHost appHost, IServerConfigurationManager config, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer, IProviderManager providerManager)
+ public LiveTvManager(IApplicationHost appHost, IServerConfigurationManager config, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer, IProviderManager providerManager, IFileSystem fileSystem)
{
_config = config;
_logger = logger;
@@ -73,6 +74,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_localization = localization;
_jsonSerializer = jsonSerializer;
_providerManager = providerManager;
+ _fileSystem = fileSystem;
_dtoService = dtoService;
_userDataManager = userDataManager;
@@ -249,13 +251,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var now = DateTime.UtcNow;
- var programs = _libraryManager.GetItems(new InternalItemsQuery
+ var programs = query.AddCurrentProgram ? _libraryManager.QueryItems(new InternalItemsQuery
{
IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
MaxStartDate = now,
- MinEndDate = now
+ MinEndDate = now,
+ ChannelIds = internalResult.Items.Select(i => i.Id.ToString("N")).ToArray()
- }).Items.Cast<LiveTvProgram>().OrderBy(i => i.StartDate).ToList();
+ }).Items.Cast<LiveTvProgram>().OrderBy(i => i.StartDate).ToList() : new List<LiveTvProgram>();
foreach (var channel in internalResult.Items)
{
@@ -331,6 +334,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var service = GetService(item);
var sources = await service.GetChannelStreamMediaSources(item.ExternalId, cancellationToken).ConfigureAwait(false);
+
+ if (sources.Count == 0)
+ {
+ throw new NotImplementedException();
+ }
+
var list = sources.ToList();
foreach (var source in list)
@@ -355,6 +364,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
+ if (string.Equals(id, mediaSourceId, StringComparison.OrdinalIgnoreCase))
+ {
+ mediaSourceId = null;
+ }
+
try
{
MediaSourceInfo info;
@@ -591,7 +605,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
Name = info.Name,
Id = id,
DateCreated = DateTime.UtcNow,
- DateModified = DateTime.UtcNow
+ DateModified = DateTime.UtcNow,
+ Etag = info.Etag
};
}
@@ -628,33 +643,27 @@ namespace MediaBrowser.Server.Implementations.LiveTv
item.ProductionYear = info.ProductionYear;
item.PremiereDate = item.PremiereDate ?? info.OriginalAirDate;
+ item.IndexNumber = info.EpisodeNumber;
+ item.ParentIndexNumber = info.SeasonNumber;
+
if (isNew)
{
await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
}
else
{
- await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
+ if (string.IsNullOrWhiteSpace(info.Etag) || !string.Equals(info.Etag, item.Etag, StringComparison.OrdinalIgnoreCase))
+ {
+ item.Etag = info.Etag;
+ await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
+ }
}
- var maxStartDate = DateTime.UtcNow.AddDays(3);
-
- _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions
- {
- ImageRefreshMode = info.StartDate <= maxStartDate ? ImageRefreshMode.Default : ImageRefreshMode.ValidationOnly
- });
+ _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions());
return item;
}
- private void RefreshIfNeeded(LiveTvProgram program)
- {
- if (_refreshedPrograms.TryAdd(program.Id, program.Id))
- {
- _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions());
- }
- }
-
private async Task<Guid> CreateRecordingRecord(RecordingInfo info, string serviceName, CancellationToken cancellationToken)
{
var isNew = false;
@@ -729,6 +738,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (!string.IsNullOrEmpty(info.Path))
{
item.Path = info.Path;
+ var fileInfo = new FileInfo(info.Path);
+
+ recording.DateCreated = _fileSystem.GetCreationTimeUtc(fileInfo);
+ recording.DateModified = _fileSystem.GetLastWriteTimeUtc(fileInfo);
}
else if (!string.IsNullOrEmpty(info.Url))
{
@@ -741,7 +754,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
}
- else if (pathChanged)
+ else if (pathChanged || info.DateLastUpdated > recording.DateLastSaved)
{
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
}
@@ -755,8 +768,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
var program = GetInternalProgram(id);
- RefreshIfNeeded(program);
-
var dto = _dtoService.GetBaseItemDto(program, new DtoOptions(), user);
await AddRecordingInfo(new[] { dto }, cancellationToken).ConfigureAwait(false);
@@ -777,7 +788,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
IsMovie = query.IsMovie,
IsSports = query.IsSports,
IsKids = query.IsKids,
- Genres = query.Genres
+ Genres = query.Genres,
+ StartIndex = query.StartIndex,
+ Limit = query.Limit,
+ SortBy = query.SortBy,
+ SortOrder = query.SortOrder ?? SortOrder.Ascending
};
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
@@ -803,30 +818,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
}
}
- IEnumerable<LiveTvProgram> programs = _libraryManager.GetItems(internalQuery).Items.Cast<LiveTvProgram>();
-
- programs = _libraryManager.Sort(programs, user, query.SortBy, query.SortOrder ?? SortOrder.Ascending)
- .Cast<LiveTvProgram>();
-
- var programList = programs.ToList();
- IEnumerable<LiveTvProgram> returnPrograms = programList;
-
- if (query.StartIndex.HasValue)
- {
- returnPrograms = returnPrograms.Skip(query.StartIndex.Value);
- }
+ var queryResult = _libraryManager.QueryItems(internalQuery);
- if (query.Limit.HasValue)
- {
- returnPrograms = returnPrograms.Take(query.Limit.Value);
- }
-
- var returnArray = returnPrograms
- .Select(i =>
- {
- RefreshIfNeeded(i);
- return _dtoService.GetBaseItemDto(i, options, user);
- })
+ var returnArray = queryResult.Items
+ .Select(i => _dtoService.GetBaseItemDto(i, options, user))
.ToArray();
await AddRecordingInfo(returnArray, cancellationToken).ConfigureAwait(false);
@@ -834,7 +829,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var result = new QueryResult<BaseItemDto>
{
Items = returnArray,
- TotalRecordCount = programList.Count
+ TotalRecordCount = queryResult.TotalRecordCount
};
return result;
@@ -874,7 +869,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
}
}
- IEnumerable<LiveTvProgram> programs = _libraryManager.GetItems(internalQuery).Items.Cast<LiveTvProgram>();
+ IEnumerable<LiveTvProgram> programs = _libraryManager.QueryItems(internalQuery).Items.Cast<LiveTvProgram>();
var programList = programs.ToList();
@@ -891,19 +886,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (query.Limit.HasValue)
{
- programs = programs.Take(query.Limit.Value)
- .OrderBy(i => i.StartDate);
+ programs = programs.Take(query.Limit.Value);
}
programList = programs.ToList();
var returnArray = programList.ToArray();
- foreach (var program in returnArray)
- {
- RefreshIfNeeded(program);
- }
-
var result = new QueryResult<LiveTvProgram>
{
Items = returnArray,
@@ -1062,6 +1051,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
cancellationToken.ThrowIfCancellationRequested();
+ _logger.Debug("Refreshing guide from {0}", service.Name);
+
try
{
var innerProgress = new ActionableProgress<double>();
@@ -1091,8 +1082,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
await CleanDatabaseInternal(newChannelIdList, new[] { typeof(LiveTvChannel).Name }, progress, cancellationToken).ConfigureAwait(false);
await CleanDatabaseInternal(newProgramIdList, new[] { typeof(LiveTvProgram).Name }, progress, cancellationToken).ConfigureAwait(false);
- _refreshedPrograms.Clear();
-
// Load these now which will prefetch metadata
var dtoOptions = new DtoOptions();
dtoOptions.Fields.Remove(ItemFields.SyncInfo);
@@ -1395,8 +1384,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
dto.Id = _tvDtoService.GetInternalProgramId(service.Name, program.ExternalId).ToString("N");
- dto.ChannelId = item.ChannelId;
-
dto.StartDate = program.StartDate;
dto.IsRepeat = program.IsRepeat;
dto.EpisodeTitle = program.EpisodeTitle;
@@ -1463,8 +1450,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
? null
: _tvDtoService.GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N");
- dto.ChannelId = item.ChannelId;
-
dto.StartDate = info.StartDate;
dto.RecordingStatus = info.Status;
dto.IsRepeat = info.IsRepeat;
@@ -1749,6 +1734,32 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return dto;
}
+ public void AddChannelInfo(BaseItemDto dto, LiveTvChannel channel, DtoOptions options, User user)
+ {
+ dto.MediaSources = channel.GetMediaSources(true).ToList();
+
+ var now = DateTime.UtcNow;
+
+ var programs = _libraryManager.GetItems(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
+ ChannelIds = new[] { channel.Id.ToString("N") },
+ MaxStartDate = now,
+ MinEndDate = now,
+ Limit = 1
+
+ }).Items.Cast<LiveTvProgram>();
+
+ var currentProgram = programs
+ .OrderBy(i => i.StartDate)
+ .FirstOrDefault();
+
+ if (currentProgram != null)
+ {
+ dto.CurrentProgram = _dtoService.GetBaseItemDto(currentProgram, options, user);
+ }
+ }
+
private async Task<Tuple<SeriesTimerInfo, ILiveTvService>> GetNewTimerDefaultsInternal(CancellationToken cancellationToken, LiveTvProgram program = null)
{
var service = program != null && !string.IsNullOrWhiteSpace(program.ServiceName) ?
@@ -2304,5 +2315,34 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return provider.GetLineups(info, country, location);
}
}
+
+ public Task<MBRegistrationRecord> GetRegistrationInfo(string channelId, string programId, string feature)
+ {
+ ILiveTvService service;
+
+ if (string.IsNullOrWhiteSpace(programId))
+ {
+ var channel = GetInternalChannel(channelId);
+ service = GetService(channel);
+ }
+ else
+ {
+ var program = GetInternalProgram(programId);
+ service = GetService(program);
+ }
+
+ var hasRegistration = service as IHasRegistrationInfo;
+
+ if (hasRegistration != null)
+ {
+ return hasRegistration.GetRegistrationInfo(feature);
+ }
+
+ return Task.FromResult(new MBRegistrationRecord
+ {
+ IsValid = true,
+ IsRegistered = true
+ });
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index 66a21830e..ff102b0f7 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
@@ -90,6 +90,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
foreach (var source in list)
{
source.Type = MediaSourceType.Default;
+ source.BufferMs = source.BufferMs ?? 1500;
if (source.RequiresOpening || forceRequireOpening)
{
@@ -103,9 +104,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
openKeys.Add(item.Id.ToString("N"));
openKeys.Add(source.Id ?? string.Empty);
source.OpenToken = string.Join(StreamIdDelimeterString, openKeys.ToArray());
- }
-
- source.BufferMs = source.BufferMs ?? 1500;
+ }
// Dummy this up so that direct play checks can still run
if (string.IsNullOrEmpty(source.Path) && source.Protocol == MediaProtocol.Http)
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
new file mode 100644
index 000000000..909e2bba5
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -0,0 +1,218 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
+{
+ public abstract class BaseTunerHost
+ {
+ protected readonly IConfigurationManager Config;
+ protected readonly ILogger Logger;
+
+ private readonly ConcurrentDictionary<string, ChannelCache> _channelCache =
+ new ConcurrentDictionary<string, ChannelCache>(StringComparer.OrdinalIgnoreCase);
+
+ public BaseTunerHost(IConfigurationManager config, ILogger logger)
+ {
+ Config = config;
+ Logger = logger;
+ }
+
+ protected abstract Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken);
+ public abstract string Type { get; }
+
+ public async Task<IEnumerable<ChannelInfo>> GetChannels(TunerHostInfo tuner, bool enableCache, CancellationToken cancellationToken)
+ {
+ ChannelCache cache = null;
+ var key = tuner.Id;
+
+ if (enableCache && !string.IsNullOrWhiteSpace(key) && _channelCache.TryGetValue(key, out cache))
+ {
+ if ((DateTime.UtcNow - cache.Date) < TimeSpan.FromMinutes(60))
+ {
+ return cache.Channels.ToList();
+ }
+ }
+
+ var result = await GetChannelsInternal(tuner, cancellationToken).ConfigureAwait(false);
+ var list = result.ToList();
+
+ if (!string.IsNullOrWhiteSpace(key))
+ {
+ cache = cache ?? new ChannelCache();
+ cache.Date = DateTime.UtcNow;
+ cache.Channels = list;
+ _channelCache.AddOrUpdate(key, cache, (k, v) => cache);
+ }
+
+ return list;
+ }
+
+ private List<TunerHostInfo> GetTunerHosts()
+ {
+ return GetConfiguration().TunerHosts
+ .Where(i => i.IsEnabled && string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase))
+ .ToList();
+ }
+
+ public async Task<IEnumerable<ChannelInfo>> GetChannels(CancellationToken cancellationToken)
+ {
+ var list = new List<ChannelInfo>();
+
+ var hosts = GetTunerHosts();
+
+ foreach (var host in hosts)
+ {
+ try
+ {
+ var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false);
+ var newChannels = channels.Where(i => !list.Any(l => string.Equals(i.Id, l.Id, StringComparison.OrdinalIgnoreCase))).ToList();
+
+ list.AddRange(newChannels);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error getting channel list", ex);
+ }
+ }
+
+ return list;
+ }
+
+ protected abstract Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken);
+
+ public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
+ {
+ if (IsValidChannelId(channelId))
+ {
+ var hosts = GetTunerHosts();
+
+ var hostsWithChannel = new List<TunerHostInfo>();
+
+ foreach (var host in hosts)
+ {
+ var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false);
+
+ if (channels.Any(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase)))
+ {
+ hostsWithChannel.Add(host);
+ }
+ }
+
+ foreach (var host in hostsWithChannel)
+ {
+ // Check to make sure the tuner is available
+ // If there's only one tuner, don't bother with the check and just let the tuner be the one to throw an error
+ if (hostsWithChannel.Count > 1 && !await IsAvailable(host, channelId, cancellationToken).ConfigureAwait(false))
+ {
+ Logger.Error("Tuner is not currently available");
+ continue;
+ }
+
+ var mediaSources = await GetChannelStreamMediaSources(host, channelId, cancellationToken).ConfigureAwait(false);
+
+ // Prefix the id with the host Id so that we can easily find it
+ foreach (var mediaSource in mediaSources)
+ {
+ mediaSource.Id = host.Id + mediaSource.Id;
+ }
+
+ return mediaSources;
+ }
+ }
+
+ return new List<MediaSourceInfo>();
+ }
+
+ protected abstract Task<MediaSourceInfo> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken);
+
+ public async Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
+ {
+ if (IsValidChannelId(channelId))
+ {
+ var hosts = GetTunerHosts();
+
+ var hostsWithChannel = new List<TunerHostInfo>();
+
+ foreach (var host in hosts)
+ {
+ if (string.IsNullOrWhiteSpace(streamId))
+ {
+ var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false);
+
+ if (channels.Any(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase)))
+ {
+ hostsWithChannel.Add(host);
+ }
+ }
+ else if (streamId.StartsWith(host.Id, StringComparison.OrdinalIgnoreCase))
+ {
+ hostsWithChannel = new List<TunerHostInfo> { host };
+ streamId = streamId.Substring(host.Id.Length);
+ break;
+ }
+ }
+
+ foreach (var host in hostsWithChannel)
+ {
+ // Check to make sure the tuner is available
+ // If there's only one tuner, don't bother with the check and just let the tuner be the one to throw an error
+ // If a streamId is specified then availibility has already been checked in GetChannelStreamMediaSources
+ if (string.IsNullOrWhiteSpace(streamId) && hostsWithChannel.Count > 1)
+ {
+ if (!await IsAvailable(host, channelId, cancellationToken).ConfigureAwait(false))
+ {
+ Logger.Error("Tuner is not currently available");
+ continue;
+ }
+ }
+
+ var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false);
+
+ if (stream != null)
+ {
+ return stream;
+ }
+ }
+ }
+
+ throw new LiveTvConflictException();
+ }
+
+ protected async Task<bool> IsAvailable(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
+ {
+ try
+ {
+ return await IsAvailableInternal(tuner, channelId, cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error checking tuner availability", ex);
+ return false;
+ }
+ }
+
+ protected abstract Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken);
+
+ protected abstract bool IsValidChannelId(string channelId);
+
+ protected LiveTvOptions GetConfiguration()
+ {
+ return Config.GetConfiguration<LiveTvOptions>("livetv");
+ }
+
+ private class ChannelCache
+ {
+ public DateTime Date;
+ public List<ChannelInfo> Channels;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index c01eb63ab..bccb0db0a 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -17,19 +17,16 @@ using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
- public class HdHomerunHost : ITunerHost
+ public class HdHomerunHost : BaseTunerHost, ITunerHost
{
private readonly IHttpClient _httpClient;
- private readonly ILogger _logger;
private readonly IJsonSerializer _jsonSerializer;
- private readonly IConfigurationManager _config;
- public HdHomerunHost(IHttpClient httpClient, ILogger logger, IJsonSerializer jsonSerializer, IConfigurationManager config)
+ public HdHomerunHost(IConfigurationManager config, ILogger logger, IHttpClient httpClient, IJsonSerializer jsonSerializer)
+ : base(config, logger)
{
_httpClient = httpClient;
- _logger = logger;
_jsonSerializer = jsonSerializer;
- _config = config;
}
public string Name
@@ -37,7 +34,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
get { return "HD Homerun"; }
}
- public string Type
+ public override string Type
{
get { return DeviceType; }
}
@@ -47,7 +44,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
get { return "hdhomerun"; }
}
- public async Task<IEnumerable<ChannelInfo>> GetChannels(TunerHostInfo info, CancellationToken cancellationToken)
+ private const string ChannelIdPrefix = "hdhr_";
+
+ protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
{
var options = new HttpRequestOptions
{
@@ -64,7 +63,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
Name = i.GuideName,
Number = i.GuideNumber.ToString(CultureInfo.InvariantCulture),
- Id = i.GuideNumber.ToString(CultureInfo.InvariantCulture),
+ Id = ChannelIdPrefix + i.GuideNumber.ToString(CultureInfo.InvariantCulture),
IsFavorite = i.Favorite
});
@@ -109,7 +108,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public async Task<List<LiveTvTunerInfo>> GetTunerInfos(TunerHostInfo info, CancellationToken cancellationToken)
{
- string model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false);
+ var model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false);
using (var stream = await _httpClient.Get(new HttpRequestOptions()
{
@@ -130,7 +129,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var name = line.Substring(0, index - 1);
var currentChannel = line.Substring(index + 7);
if (currentChannel != "none") { status = LiveTvTunerStatus.LiveTv; } else { status = LiveTvTunerStatus.Available; }
- tuners.Add(new LiveTvTunerInfo()
+ tuners.Add(new LiveTvTunerInfo
{
Name = name,
SourceType = string.IsNullOrWhiteSpace(model) ? Name : model,
@@ -144,10 +143,35 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
+ public async Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken)
+ {
+ var list = new List<LiveTvTunerInfo>();
+
+ foreach (var host in GetConfiguration().TunerHosts
+ .Where(i => i.IsEnabled && string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase)))
+ {
+ try
+ {
+ list.AddRange(await GetTunerInfos(host, cancellationToken).ConfigureAwait(false));
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error getting tuner info", ex);
+ }
+ }
+
+ return list;
+ }
+
private string GetApiUrl(TunerHostInfo info, bool isPlayback)
{
var url = info.Url;
+ if (string.IsNullOrWhiteSpace(url))
+ {
+ throw new ArgumentException("Invalid tuner info");
+ }
+
if (!url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
url = "http://" + url;
@@ -202,11 +226,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public bool DRM { get; set; }
}
- private LiveTvOptions GetConfiguration()
- {
- return _config.GetConfiguration<LiveTvOptions>("livetv");
- }
-
private MediaSourceInfo GetMediaSource(TunerHostInfo info, string channelId, string profile)
{
int? width = null;
@@ -316,10 +335,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
return mediaSource;
}
- public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
+ protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
{
var list = new List<MediaSourceInfo>();
+ if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase))
+ {
+ return list;
+ }
+ channelId = channelId.Substring(ChannelIdPrefix.Length);
+
list.Add(GetMediaSource(info, channelId, "native"));
try
@@ -339,20 +364,43 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
catch (Exception ex)
{
-
+
}
return list;
}
- public async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
+ protected override bool IsValidChannelId(string channelId)
{
+ return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase);
+ }
+
+ protected override async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
+ {
+ Logger.Debug("GetChannelStream: channel id: {0}. stream id: {1}", channelId, streamId ?? string.Empty);
+
+ if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+ channelId = channelId.Substring(ChannelIdPrefix.Length);
+
return GetMediaSource(info, channelId, streamId);
}
public async Task Validate(TunerHostInfo info)
{
- await GetChannels(info, CancellationToken.None).ConfigureAwait(false);
+ if (info.IsEnabled)
+ {
+ await GetChannels(info, false, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+
+ protected override async Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
+ {
+ var info = await GetTunerInfos(tuner, cancellationToken).ConfigureAwait(false);
+
+ return info.Any(i => i.Status == LiveTvTunerStatus.Available || string.Equals(i.ChannelId, channelId, StringComparison.OrdinalIgnoreCase));
}
}
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index bb31390f2..3783e4b08 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -4,6 +4,7 @@ using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using System;
using System.Collections.Generic;
@@ -14,9 +15,14 @@ using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
{
- public class M3UTunerHost : ITunerHost
+ public class M3UTunerHost : BaseTunerHost, ITunerHost
{
- public string Type
+ public M3UTunerHost(IConfigurationManager config, ILogger logger)
+ : base(config, logger)
+ {
+ }
+
+ public override string Type
{
get { return "m3u"; }
}
@@ -26,127 +32,136 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
get { return "M3U Tuner"; }
}
- private readonly IConfigurationManager _config;
+ private const string ChannelIdPrefix = "m3u_";
- public M3UTunerHost(IConfigurationManager config)
+ protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
{
- _config = config;
- }
+ var url = info.Url;
+ var urlHash = url.GetMD5().ToString("N");
- public Task<IEnumerable<ChannelInfo>> GetChannels(TunerHostInfo info, CancellationToken cancellationToken)
- {
- int position = 0;
string line;
// Read the file and display it line by line.
- var file = new StreamReader(info.Url);
+ var file = new StreamReader(url);
var channels = new List<M3UChannel>();
+
+ string channnelName = null;
+ string channelNumber = null;
+
while ((line = file.ReadLine()) != null)
{
line = line.Trim();
- if (!String.IsNullOrWhiteSpace(line))
+ if (string.IsNullOrWhiteSpace(line))
{
- if (position == 0 && !line.StartsWith("#EXTM3U"))
- {
- throw new ApplicationException("wrong file");
- }
- if (position % 2 == 0)
- {
- if (position != 0)
- {
- channels.Last().Path = line;
- }
- else
- {
- line = line.Replace("#EXTM3U", "");
- line = line.Trim();
- var vars = line.Split(' ').ToList();
- foreach (var variable in vars)
- {
- var list = variable.Replace('"', ' ').Split('=');
- switch (list[0])
- {
- case ("id"):
- //_id = list[1];
- break;
- }
- }
- }
- }
- else
+ continue;
+ }
+
+ if (line.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ if (line.StartsWith("#EXTINF:", StringComparison.OrdinalIgnoreCase))
+ {
+ var parts = line.Split(new[] { ':' }, 2).Last().Split(new[] { ',' }, 2);
+ channelNumber = parts[0];
+ channnelName = parts[1];
+ }
+ else if (!string.IsNullOrWhiteSpace(channelNumber))
+ {
+ channels.Add(new M3UChannel
{
- if (!line.StartsWith("#EXTINF:")) { throw new ApplicationException("Bad file"); }
- line = line.Replace("#EXTINF:", "");
- var nameStart = line.LastIndexOf(',');
- line = line.Substring(0, nameStart);
- var vars = line.Split(' ').ToList();
- vars.RemoveAt(0);
- channels.Add(new M3UChannel());
- foreach (var variable in vars)
- {
- var list = variable.Replace('"', ' ').Split('=');
- switch (list[0])
- {
- case "tvg-id":
- channels.Last().Id = list[1];
- channels.Last().Number = list[1];
- break;
- case "tvg-name":
- channels.Last().Name = list[1];
- break;
- }
- }
- }
- position++;
+ Name = channnelName,
+ Number = channelNumber,
+ Id = ChannelIdPrefix + urlHash + channelNumber,
+ Path = line
+ });
+
+ channelNumber = null;
+ channnelName = null;
}
}
file.Close();
- return Task.FromResult((IEnumerable<ChannelInfo>)channels);
+ return channels;
}
- public Task<List<LiveTvTunerInfo>> GetTunerInfos(TunerHostInfo info, CancellationToken cancellationToken)
+ public Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken)
{
- var list = new List<LiveTvTunerInfo>();
-
- list.Add(new LiveTvTunerInfo()
+ var list = GetConfiguration().TunerHosts
+ .Where(i => i.IsEnabled && string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase))
+ .Select(i => new LiveTvTunerInfo()
{
Name = Name,
SourceType = Type,
Status = LiveTvTunerStatus.Available,
- Id = info.Url.GetMD5().ToString("N"),
- Url = info.Url
- });
+ Id = i.Url.GetMD5().ToString("N"),
+ Url = i.Url
+ })
+ .ToList();
return Task.FromResult(list);
}
- private LiveTvOptions GetConfiguration()
+ protected override async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
+ {
+ var sources = await GetChannelStreamMediaSources(info, channelId, cancellationToken).ConfigureAwait(false);
+
+ return sources.First();
+ }
+
+ class M3UChannel : ChannelInfo
+ {
+ public string Path { get; set; }
+
+ public M3UChannel()
+ {
+ }
+ }
+
+ public async Task Validate(TunerHostInfo info)
{
- return _config.GetConfiguration<LiveTvOptions>("livetv");
+ if (!File.Exists(info.Url))
+ {
+ throw new FileNotFoundException();
+ }
}
- public async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
+ protected override bool IsValidChannelId(string channelId)
{
- var channels = await GetChannels(info, cancellationToken).ConfigureAwait(false);
+ return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase);
+ }
+
+ protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
+ {
+ var urlHash = info.Url.GetMD5().ToString("N");
+ var prefix = ChannelIdPrefix + urlHash;
+ if (!channelId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ //channelId = channelId.Substring(prefix.Length);
+
+ var channels = await GetChannels(info, true, cancellationToken).ConfigureAwait(false);
var m3uchannels = channels.Cast<M3UChannel>();
- var channel = m3uchannels.FirstOrDefault(c => c.Id == channelId);
+ var channel = m3uchannels.FirstOrDefault(c => string.Equals(c.Id, channelId, StringComparison.OrdinalIgnoreCase));
if (channel != null)
{
var path = channel.Path;
MediaProtocol protocol = MediaProtocol.File;
- if (path.StartsWith("http"))
+ if (path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
protocol = MediaProtocol.Http;
}
- else if (path.StartsWith("rtmp"))
+ else if (path.StartsWith("rtmp", StringComparison.OrdinalIgnoreCase))
{
protocol = MediaProtocol.Rtmp;
}
- else if (path.StartsWith("rtsp"))
+ else if (path.StartsWith("rtsp", StringComparison.OrdinalIgnoreCase))
{
protocol = MediaProtocol.Rtsp;
}
- return new MediaSourceInfo
+ var mediaSource = new MediaSourceInfo
{
Path = channel.Path,
Protocol = protocol,
@@ -170,31 +185,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
RequiresOpening = false,
RequiresClosing = false
};
- }
- throw new ApplicationException("Host doesnt provide this channel");
- }
-
- class M3UChannel : ChannelInfo
- {
- public string Path { get; set; }
-
- public M3UChannel()
- {
- }
- }
- public async Task Validate(TunerHostInfo info)
- {
- if (!File.Exists(info.Url))
- {
- throw new FileNotFoundException();
+ return new List<MediaSourceInfo> { mediaSource };
}
+ return new List<MediaSourceInfo> { };
}
-
- public Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
+ protected override Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
{
- throw new NotImplementedException();
+ return Task.FromResult(true);
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/kk.json b/MediaBrowser.Server.Implementations/Localization/Core/kk.json
index f57b9ee12..7e17dea6b 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/kk.json
+++ b/MediaBrowser.Server.Implementations/Localization/Core/kk.json
@@ -121,7 +121,7 @@
"UserStoppedPlayingItemWithValues": "{0} - {1} \u043e\u0439\u043d\u0430\u0442\u0443\u044b \u0442\u043e\u049b\u0442\u0430\u043b\u0434\u044b",
"SubtitleDownloadFailureForItem": "\u0421\u0443\u0431\u0442\u0438\u0442\u0440\u043b\u0435\u0440 {0} \u04af\u0448\u0456\u043d \u0436\u04af\u043a\u0442\u0435\u043f \u0430\u043b\u044b\u043d\u0443\u044b \u0441\u04d9\u0442\u0441\u0456\u0437",
"HeaderUnidentified": "\u0410\u043d\u044b\u049b\u0442\u0430\u043b\u043c\u0430\u0493\u0430\u043d",
- "HeaderImagePrimary": "\u0411\u0430\u0441\u0442\u0430\u043f\u049b\u044b",
+ "HeaderImagePrimary": "\u041d\u0435\u0433\u0456\u0437\u0433\u0456",
"HeaderImageBackdrop": "\u0410\u0440\u0442\u049b\u044b \u0441\u0443\u0440\u0435\u0442",
"HeaderImageLogo": "\u041b\u043e\u0433\u043e\u0442\u0438\u043f",
"HeaderUserPrimaryImage": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b \u0441\u0443\u0440\u0435\u0442\u0456",
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/ru.json b/MediaBrowser.Server.Implementations/Localization/Core/ru.json
index 6a8bae45a..c705ddef4 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/ru.json
+++ b/MediaBrowser.Server.Implementations/Localization/Core/ru.json
@@ -121,7 +121,7 @@
"UserStoppedPlayingItemWithValues": "{0} - \u0432\u043e\u0441\u043f\u0440-\u0438\u0435 \u00ab{1}\u00bb \u043e\u0441\u0442-\u043d\u043e",
"SubtitleDownloadFailureForItem": "\u0421\u0443\u0431\u0442\u0438\u0442\u0440\u044b \u043a {0} \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c",
"HeaderUnidentified": "\u041d\u0435 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u043d\u043e",
- "HeaderImagePrimary": "\u041f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439",
+ "HeaderImagePrimary": "\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439",
"HeaderImageBackdrop": "\u0417\u0430\u0434\u043d\u0438\u043a",
"HeaderImageLogo": "\u041b\u043e\u0433\u043e\u0442\u0438\u043f",
"HeaderUserPrimaryImage": "\u0420\u0438\u0441\u0443\u043d\u043e\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f",
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index 2571f9b2c..f0312cef6 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -109,7 +109,6 @@
<Compile Include="Activity\ActivityRepository.cs" />
<Compile Include="Branding\BrandingConfigurationFactory.cs" />
<Compile Include="Channels\ChannelConfigurations.cs" />
- <Compile Include="Channels\ChannelDownloadScheduledTask.cs" />
<Compile Include="Channels\ChannelDynamicMediaSourceProvider.cs" />
<Compile Include="Channels\ChannelImageProvider.cs" />
<Compile Include="Channels\ChannelItemImageProvider.cs" />
@@ -231,6 +230,7 @@
<Compile Include="LiveTv\LiveTvDtoService.cs" />
<Compile Include="LiveTv\LiveTvManager.cs" />
<Compile Include="LiveTv\LiveTvMediaSourceProvider.cs" />
+ <Compile Include="LiveTv\TunerHosts\BaseTunerHost.cs" />
<Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunHost.cs" />
<Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunDiscovery.cs" />
<Compile Include="LiveTv\TunerHosts\M3UTunerHost.cs" />
@@ -241,6 +241,7 @@
<Compile Include="Logging\PatternsLogger.cs" />
<Compile Include="MediaEncoder\EncodingManager.cs" />
<Compile Include="Persistence\BaseSqliteRepository.cs" />
+ <Compile Include="Persistence\CleanDatabaseScheduledTask.cs" />
<Compile Include="Social\SharingManager.cs" />
<Compile Include="Social\SharingRepository.cs" />
<Compile Include="Sorting\StartDateComparer.cs" />
@@ -494,14 +495,6 @@
<Link>swagger-ui\swagger-ui.min.js</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <EmbeddedResource Include="UserViews\livetv\1.jpg" />
- <EmbeddedResource Include="UserViews\livetv\2.jpg" />
- <EmbeddedResource Include="UserViews\livetv\3.jpg" />
- <EmbeddedResource Include="UserViews\livetv\4.jpg" />
- <EmbeddedResource Include="UserViews\livetv\5.jpg" />
- <EmbeddedResource Include="UserViews\livetv\6.jpg" />
- <EmbeddedResource Include="UserViews\livetv\7.jpg" />
- <EmbeddedResource Include="UserViews\livetv\8.jpg" />
<EmbeddedResource Include="Localization\iso6392.txt" />
<EmbeddedResource Include="Localization\Ratings\be.txt" />
</ItemGroup>
diff --git a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
new file mode 100644
index 000000000..0b45c468a
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
@@ -0,0 +1,164 @@
+using MediaBrowser.Common.Progress;
+using MediaBrowser.Common.ScheduledTasks;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Persistence
+{
+ class CleanDatabaseScheduledTask : IScheduledTask
+ {
+ private readonly ILibraryManager _libraryManager;
+ private readonly IItemRepository _itemRepo;
+ private readonly ILogger _logger;
+ private readonly IServerConfigurationManager _config;
+
+ public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IServerConfigurationManager config)
+ {
+ _libraryManager = libraryManager;
+ _itemRepo = itemRepo;
+ _logger = logger;
+ _config = config;
+ }
+
+ public string Name
+ {
+ get { return "Clean Database"; }
+ }
+
+ public string Description
+ {
+ get { return "Deletes obsolete content from the database."; }
+ }
+
+ public string Category
+ {
+ get { return "Library"; }
+ }
+
+ public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+ {
+ var innerProgress = new ActionableProgress<double>();
+ innerProgress.RegisterAction(p => progress.Report(.95 * p));
+
+ await UpdateToLatestSchema(cancellationToken, innerProgress).ConfigureAwait(false);
+
+ innerProgress = new ActionableProgress<double>();
+ innerProgress.RegisterAction(p => progress.Report(95 + (.05 * p)));
+
+ await CleanDeadItems(cancellationToken, innerProgress).ConfigureAwait(false);
+
+ progress.Report(100);
+ }
+
+ private async Task UpdateToLatestSchema(CancellationToken cancellationToken, IProgress<double> progress)
+ {
+ var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery
+ {
+ IsCurrentSchema = false,
+
+ // These are constantly getting regenerated so don't bother with them here
+ ExcludeItemTypes = new[] { typeof(LiveTvProgram).Name }
+ });
+
+ var numComplete = 0;
+ var numItems = itemIds.Count;
+
+ _logger.Debug("Upgrading schema for {0} items", numItems);
+
+ foreach (var itemId in itemIds)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (itemId == Guid.Empty)
+ {
+ // Somehow some invalid data got into the db. It probably predates the boundary checking
+ continue;
+ }
+
+ var item = _libraryManager.GetItemById(itemId);
+
+ if (item != null)
+ {
+ try
+ {
+ await _itemRepo.SaveItem(item, cancellationToken).ConfigureAwait(false);
+ }
+ catch (OperationCanceledException)
+ {
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error saving item", ex);
+ }
+ }
+
+ numComplete++;
+ double percent = numComplete;
+ percent /= numItems;
+ progress.Report(percent * 100);
+ }
+
+ if (!_config.Configuration.DisableStartupScan)
+ {
+ _config.Configuration.DisableStartupScan = true;
+ _config.SaveConfiguration();
+ }
+
+ progress.Report(100);
+ }
+
+ private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress<double> progress)
+ {
+ var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery
+ {
+ HasDeadParentId = true
+ });
+
+ var numComplete = 0;
+ var numItems = itemIds.Count;
+
+ _logger.Debug("Cleaning {0} items with dead parent links", numItems);
+
+ foreach (var itemId in itemIds)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var item = _libraryManager.GetItemById(itemId);
+
+ if (item != null)
+ {
+ _logger.Debug("Cleaning item {0} type: {1} path: {2}", item.Name, item.GetType().Name, item.Path ?? string.Empty);
+
+ await _libraryManager.DeleteItem(item, new DeleteOptions
+ {
+ DeleteFileLocation = false
+ });
+ }
+
+ numComplete++;
+ double percent = numComplete;
+ percent /= numItems;
+ progress.Report(percent * 100);
+ }
+
+ progress.Report(100);
+ }
+
+ public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ {
+ return new ITaskTrigger[]
+ {
+ new IntervalTrigger{ Interval = TimeSpan.FromDays(1)}
+ };
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
index 3ec45c4de..00ebf7ea6 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
@@ -71,6 +71,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
private IDbCommand _deletePeopleCommand;
private IDbCommand _savePersonCommand;
+
+ private const int LatestSchemaVersion = 6;
+
/// <summary>
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
/// </summary>
@@ -159,6 +162,19 @@ namespace MediaBrowser.Server.Implementations.Persistence
_connection.AddColumn(_logger, "TypedBaseItems", "ParentId", "GUID");
_connection.AddColumn(_logger, "TypedBaseItems", "Genres", "Text");
_connection.AddColumn(_logger, "TypedBaseItems", "ParentalRatingValue", "INT");
+ _connection.AddColumn(_logger, "TypedBaseItems", "SchemaVersion", "INT");
+ _connection.AddColumn(_logger, "TypedBaseItems", "SortName", "Text");
+ _connection.AddColumn(_logger, "TypedBaseItems", "RunTimeTicks", "BIGINT");
+
+ _connection.AddColumn(_logger, "TypedBaseItems", "OfficialRatingDescription", "Text");
+ _connection.AddColumn(_logger, "TypedBaseItems", "HomePageUrl", "Text");
+ _connection.AddColumn(_logger, "TypedBaseItems", "VoteCount", "INT");
+ _connection.AddColumn(_logger, "TypedBaseItems", "DisplayMediaType", "Text");
+ _connection.AddColumn(_logger, "TypedBaseItems", "DateCreated", "DATETIME");
+ _connection.AddColumn(_logger, "TypedBaseItems", "DateModified", "DATETIME");
+
+ _connection.AddColumn(_logger, "TypedBaseItems", "ForcedSortName", "Text");
+ _connection.AddColumn(_logger, "TypedBaseItems", "IsOffline", "BIT");
PrepareStatements();
@@ -171,6 +187,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// </summary>
private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1);
+ private string[] _retriveItemColumns =
+ {
+ "type",
+ "data",
+ "IsOffline"
+ };
+
/// <summary>
/// Prepares the statements.
/// </summary>
@@ -201,14 +224,33 @@ namespace MediaBrowser.Server.Implementations.Persistence
"ProductionYear",
"ParentId",
"Genres",
- "ParentalRatingValue"
+ "ParentalRatingValue",
+ "SchemaVersion",
+ "SortName",
+ "RunTimeTicks",
+ "OfficialRatingDescription",
+ "HomePageUrl",
+ "VoteCount",
+ "DisplayMediaType",
+ "DateCreated",
+ "DateModified",
+ "ForcedSortName",
+ "IsOffline"
};
_saveItemCommand = _connection.CreateCommand();
- _saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values (@1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23, @24)";
+ _saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values (";
+
for (var i = 1; i <= saveColumns.Count; i++)
{
+ if (i > 1)
+ {
+ _saveItemCommand.CommandText += ",";
+ }
+ _saveItemCommand.CommandText += "@" + i.ToString(CultureInfo.InvariantCulture);
+
_saveItemCommand.Parameters.Add(_saveItemCommand, "@" + i.ToString(CultureInfo.InvariantCulture));
}
+ _saveItemCommand.CommandText += ")";
_deleteChildrenCommand = _connection.CreateCommand();
_deleteChildrenCommand.CommandText = "delete from ChildrenIds where ParentId=@ParentId";
@@ -350,6 +392,20 @@ namespace MediaBrowser.Server.Implementations.Persistence
_saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Genres.ToArray());
_saveItemCommand.GetParameter(index++).Value = item.GetParentalRatingValue();
+ _saveItemCommand.GetParameter(index++).Value = LatestSchemaVersion;
+ _saveItemCommand.GetParameter(index++).Value = item.SortName;
+ _saveItemCommand.GetParameter(index++).Value = item.RunTimeTicks;
+
+ _saveItemCommand.GetParameter(index++).Value = item.OfficialRatingDescription;
+ _saveItemCommand.GetParameter(index++).Value = item.HomePageUrl;
+ _saveItemCommand.GetParameter(index++).Value = item.VoteCount;
+ _saveItemCommand.GetParameter(index++).Value = item.DisplayMediaType;
+ _saveItemCommand.GetParameter(index++).Value = item.DateCreated;
+ _saveItemCommand.GetParameter(index++).Value = item.DateModified;
+
+ _saveItemCommand.GetParameter(index++).Value = item.ForcedSortName;
+ _saveItemCommand.GetParameter(index++).Value = item.IsOffline;
+
_saveItemCommand.Transaction = transaction;
_saveItemCommand.ExecuteNonQuery();
@@ -406,7 +462,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
using (var cmd = _connection.CreateCommand())
{
- cmd.CommandText = "select type,data from TypedBaseItems where guid = @guid";
+ cmd.CommandText = "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems where guid = @guid";
cmd.Parameters.Add(cmd, "@guid", DbType.Guid).Value = id;
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
@@ -433,11 +489,18 @@ namespace MediaBrowser.Server.Implementations.Persistence
return null;
}
+ BaseItem item;
+
using (var stream = reader.GetMemoryStream(1))
{
try
{
- return _jsonSerializer.DeserializeFromStream(stream, type) as BaseItem;
+ item = _jsonSerializer.DeserializeFromStream(stream, type) as BaseItem;
+
+ if (item == null)
+ {
+ return null;
+ }
}
catch (SerializationException ex)
{
@@ -445,6 +508,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
return null;
}
}
+
+ if (!reader.IsDBNull(2))
+ {
+ item.IsOffline = reader.GetBoolean(2);
+ }
+
+ return item;
}
/// <summary>
@@ -636,7 +706,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
using (var cmd = _connection.CreateCommand())
{
- cmd.CommandText = "select type,data from TypedBaseItems where guid in (select ItemId from ChildrenIds where ParentId = @ParentId)";
+ cmd.CommandText = "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems where guid in (select ItemId from ChildrenIds where ParentId = @ParentId)";
cmd.Parameters.Add(cmd, "@ParentId", DbType.Guid).Value = parentId;
@@ -666,7 +736,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
using (var cmd = _connection.CreateCommand())
{
- cmd.CommandText = "select type,data from TypedBaseItems where type = @type";
+ cmd.CommandText = "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems where type = @type";
cmd.Parameters.Add(cmd, "@type", DbType.String).Value = type.FullName;
@@ -696,7 +766,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
using (var cmd = _connection.CreateCommand())
{
- cmd.CommandText = "select type,data from TypedBaseItems";
+ cmd.CommandText = "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems";
var whereClauses = GetWhereClauses(query, cmd, false);
@@ -712,6 +782,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += whereText;
+ cmd.CommandText += GetOrderByText(query);
+
if (query.Limit.HasValue)
{
cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(CultureInfo.InvariantCulture);
@@ -719,6 +791,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += "; select count (guid) from TypedBaseItems" + whereTextWithoutPaging;
+ _logger.Debug(cmd.CommandText);
+
var list = new List<BaseItem>();
var count = 0;
@@ -747,6 +821,23 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
}
+ private string GetOrderByText(InternalItemsQuery query)
+ {
+ if (query.SortBy == null || query.SortBy.Length == 0)
+ {
+ return string.Empty;
+ }
+
+ var sortOrder = query.SortOrder == SortOrder.Descending ? "DESC" : "ASC";
+
+ return " ORDER BY " + string.Join(",", query.SortBy.Select(i => MapOrderByField(i) + " " + sortOrder).ToArray());
+ }
+
+ private string MapOrderByField(string name)
+ {
+ return name;
+ }
+
public List<Guid> GetItemIdsList(InternalItemsQuery query)
{
if (query == null)
@@ -768,6 +859,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += whereText;
+ cmd.CommandText += GetOrderByText(query);
+
if (query.Limit.HasValue)
{
cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(CultureInfo.InvariantCulture);
@@ -816,6 +909,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += whereText;
+ cmd.CommandText += GetOrderByText(query);
+
if (query.Limit.HasValue)
{
cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(CultureInfo.InvariantCulture);
@@ -853,6 +948,18 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
var whereClauses = new List<string>();
+ if (query.IsCurrentSchema.HasValue)
+ {
+ if (query.IsCurrentSchema.Value)
+ {
+ whereClauses.Add("(SchemaVersion not null AND SchemaVersion=@SchemaVersion)");
+ }
+ else
+ {
+ whereClauses.Add("(SchemaVersion is null or SchemaVersion<>@SchemaVersion)");
+ }
+ cmd.Parameters.Add(cmd, "@SchemaVersion", DbType.Int32).Value = LatestSchemaVersion;
+ }
if (query.IsMovie.HasValue)
{
whereClauses.Add("IsMovie=@IsMovie");
@@ -870,7 +977,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
var includeTypes = query.IncludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
-
if (includeTypes.Length == 1)
{
whereClauses.Add("type=@type");
@@ -881,6 +987,19 @@ namespace MediaBrowser.Server.Implementations.Persistence
var inClause = string.Join(",", includeTypes.Select(i => "'" + i + "'").ToArray());
whereClauses.Add(string.Format("type in ({0})", inClause));
}
+
+ var excludeTypes = query.ExcludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
+ if (excludeTypes.Length == 1)
+ {
+ whereClauses.Add("type<>@type");
+ cmd.Parameters.Add(cmd, "@type", DbType.String).Value = excludeTypes[0];
+ }
+ else if (excludeTypes.Length > 1)
+ {
+ var inClause = string.Join(",", excludeTypes.Select(i => "'" + i + "'").ToArray());
+ whereClauses.Add(string.Format("type not in ({0})", inClause));
+ }
+
if (query.ChannelIds.Length == 1)
{
whereClauses.Add("ChannelId=@ChannelId");
@@ -977,6 +1096,14 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
}
+ if (query.HasDeadParentId.HasValue)
+ {
+ if (query.HasDeadParentId.Value)
+ {
+ whereClauses.Add("ParentId NOT NULL AND ParentId NOT IN (select guid from TypedBaseItems)");
+ }
+ }
+
if (addPaging)
{
if (query.StartIndex.HasValue && query.StartIndex.Value > 0)
@@ -985,7 +1112,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
string.Empty :
" where " + string.Join(" AND ", whereClauses.ToArray());
- whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM TypedBaseItems {0} ORDER BY DateCreated DESC LIMIT {1})",
+ var orderBy = GetOrderByText(query);
+
+ whereClauses.Add(string.Format("guid NOT IN (SELECT guid FROM TypedBaseItems {0}" + orderBy + " LIMIT {1})",
pagingWhereText,
query.StartIndex.Value.ToString(CultureInfo.InvariantCulture)));
}
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs
index 994397624..9e97858d0 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Persistence;
+using System.Globalization;
+using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
@@ -38,7 +39,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
// Add PixelFormat column
- createTableCommand += "(ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, IsCabac BIT NULL, PRIMARY KEY (ItemId, StreamIndex))";
+ createTableCommand += "(ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, IsCabac BIT NULL, KeyFrames TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))";
string[] queries = {
@@ -58,6 +59,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
AddBitDepthCommand();
AddIsAnamorphicColumn();
AddIsCabacColumn();
+ AddKeyFramesColumn();
AddRefFramesCommand();
PrepareStatements();
@@ -187,6 +189,37 @@ namespace MediaBrowser.Server.Implementations.Persistence
_connection.RunQueries(new[] { builder.ToString() }, _logger);
}
+ private void AddKeyFramesColumn()
+ {
+ using (var cmd = _connection.CreateCommand())
+ {
+ cmd.CommandText = "PRAGMA table_info(mediastreams)";
+
+ using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+ {
+ while (reader.Read())
+ {
+ if (!reader.IsDBNull(1))
+ {
+ var name = reader.GetString(1);
+
+ if (string.Equals(name, "KeyFrames", StringComparison.OrdinalIgnoreCase))
+ {
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ var builder = new StringBuilder();
+
+ builder.AppendLine("alter table mediastreams");
+ builder.AppendLine("add column KeyFrames TEXT NULL");
+
+ _connection.RunQueries(new[] { builder.ToString() }, _logger);
+ }
+
private void AddIsAnamorphicColumn()
{
using (var cmd = _connection.CreateCommand())
@@ -245,7 +278,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
"BitDepth",
"IsAnamorphic",
"RefFrames",
- "IsCabac"
+ "IsCabac",
+ "KeyFrames"
};
/// <summary>
@@ -429,6 +463,15 @@ namespace MediaBrowser.Server.Implementations.Persistence
item.IsCabac = reader.GetBoolean(25);
}
+ if (!reader.IsDBNull(26))
+ {
+ var frames = reader.GetString(26);
+ if (!string.IsNullOrWhiteSpace(frames))
+ {
+ item.KeyFrames = frames.Split(',').Select(i => int.Parse(i, CultureInfo.InvariantCulture)).ToList();
+ }
+ }
+
return item;
}
@@ -498,6 +541,15 @@ namespace MediaBrowser.Server.Implementations.Persistence
_saveStreamCommand.GetParameter(index++).Value = stream.RefFrames;
_saveStreamCommand.GetParameter(index++).Value = stream.IsCabac;
+ if (stream.KeyFrames == null || stream.KeyFrames.Count == 0)
+ {
+ _saveStreamCommand.GetParameter(index++).Value = null;
+ }
+ else
+ {
+ _saveStreamCommand.GetParameter(index++).Value = string.Join(",", stream.KeyFrames.Select(i => i.ToString(CultureInfo.InvariantCulture)).ToArray());
+ }
+
_saveStreamCommand.Transaction = transaction;
_saveStreamCommand.ExecuteNonQuery();
}
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs
index ed284a90d..2f219c299 100644
--- a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs
+++ b/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs
@@ -1,4 +1,6 @@
-using MediaBrowser.Common.ScheduledTasks;
+using System.Linq;
+using MediaBrowser.Common.ScheduledTasks;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Server.Implementations.Library;
using System;
@@ -17,14 +19,16 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
/// The _library manager
/// </summary>
private readonly ILibraryManager _libraryManager;
+ private readonly IServerConfigurationManager _config;
/// <summary>
/// Initializes a new instance of the <see cref="RefreshMediaLibraryTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
- public RefreshMediaLibraryTask(ILibraryManager libraryManager)
+ public RefreshMediaLibraryTask(ILibraryManager libraryManager, IServerConfigurationManager config)
{
_libraryManager = libraryManager;
+ _config = config;
}
/// <summary>
@@ -33,12 +37,18 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
public IEnumerable<ITaskTrigger> GetDefaultTriggers()
{
- return new ITaskTrigger[] {
+ var list = new ITaskTrigger[] {
- new StartupTrigger(),
+ new IntervalTrigger{ Interval = TimeSpan.FromHours(12)}
- new IntervalTrigger{ Interval = TimeSpan.FromHours(8)}
- };
+ }.ToList();
+
+ if (!_config.Configuration.DisableStartupScan)
+ {
+ list.Add(new StartupTrigger());
+ }
+
+ return list;
}
/// <summary>
diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
index c2160f48a..70a4cb439 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs
+++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
@@ -161,6 +161,7 @@ namespace MediaBrowser.Server.Implementations.Session
if (capabilities != null)
{
+ info.AppIconUrl = capabilities.IconUrl;
ReportCapabilities(info, capabilities, false);
}
}
@@ -1476,6 +1477,7 @@ namespace MediaBrowser.Server.Implementations.Session
NowPlayingItem = session.NowPlayingItem,
SupportsRemoteControl = session.SupportsMediaControl,
PlayState = session.PlayState,
+ AppIconUrl = session.AppIconUrl,
TranscodingInfo = session.NowPlayingItem == null ? null : session.TranscodingInfo
};
diff --git a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs
index b473444ac..602bc4876 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs
+++ b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -85,16 +85,23 @@ namespace MediaBrowser.Server.Implementations.Session
async void _httpServer_WebSocketConnecting(object sender, WebSocketConnectingEventArgs e)
{
- var token = e.QueryString["api_key"];
- if (!string.IsNullOrWhiteSpace(token))
- {
- var session = await GetSession(e.QueryString, e.Endpoint).ConfigureAwait(false);
-
- if (session == null)
- {
- //e.AllowConnection = false;
- }
- }
+ //var token = e.QueryString["api_key"];
+ //if (!string.IsNullOrWhiteSpace(token))
+ //{
+ // try
+ // {
+ // var session = await GetSession(e.QueryString, e.Endpoint).ConfigureAwait(false);
+
+ // if (session == null)
+ // {
+ // e.AllowConnection = false;
+ // }
+ // }
+ // catch (Exception ex)
+ // {
+ // _logger.ErrorException("Error getting session info", ex);
+ // }
+ //}
}
private Task<SessionInfo> GetSession(NameValueCollection queryString, string remoteEndpoint)
diff --git a/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs b/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs
index 7b1fa4dec..03485012f 100644
--- a/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs
+++ b/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs
@@ -9,7 +9,7 @@ using System.Linq;
namespace MediaBrowser.Server.Implementations.Sync
{
- public class AppSyncProvider : ISyncProvider, IHasUniqueTargetIds, IHasSyncQuality
+ public class AppSyncProvider : ISyncProvider, IHasUniqueTargetIds, IHasSyncQuality, IHasDuplicateCheck
{
private readonly IDeviceManager _deviceManager;
@@ -104,5 +104,10 @@ namespace MediaBrowser.Server.Implementations.Sync
IsConverting = isConverting
};
}
+
+ public bool AllowDuplicateJobItem(SyncJobItem original, SyncJobItem duplicate)
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
index 86ef58e42..83dd13c05 100644
--- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
+++ b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
@@ -147,7 +147,14 @@ namespace MediaBrowser.Server.Implementations.Sync
progress.Report(totalProgress);
});
- await GetItem(provider, dataProvider, target, serverId, serverName, jobItem, innerProgress, cancellationToken).ConfigureAwait(false);
+ try
+ {
+ await GetItem(provider, dataProvider, target, serverId, serverName, jobItem, innerProgress, cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error syncing item", ex);
+ }
numComplete++;
startingPercent = numComplete;
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs
index 04ebcd903..1061a373e 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs
+++ b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs
@@ -466,6 +466,32 @@ namespace MediaBrowser.Server.Implementations.Sync
return;
}
+ // See if there's already another active job item for the same target
+ var existingJobItems = _syncManager.GetJobItems(new SyncJobItemQuery
+ {
+ AddMetadata = false,
+ ItemId = jobItem.ItemId,
+ TargetId = job.TargetId,
+ Statuses = new[] { SyncJobItemStatus.Converting, SyncJobItemStatus.Queued, SyncJobItemStatus.ReadyToTransfer, SyncJobItemStatus.Synced, SyncJobItemStatus.Transferring }
+ });
+
+ var duplicateJobItems = existingJobItems.Items
+ .Where(i => !string.Equals(i.Id, jobItem.Id, StringComparison.OrdinalIgnoreCase))
+ .ToList();
+
+ if (duplicateJobItems.Count > 0)
+ {
+ var syncProvider = _syncManager.GetSyncProvider(jobItem, job) as IHasDuplicateCheck;
+
+ if (!duplicateJobItems.Any(i => AllowDuplicateJobItem(syncProvider, i, jobItem)))
+ {
+ _logger.Debug("Cancelling sync job item because there is already another active job for the same target.");
+ jobItem.Status = SyncJobItemStatus.Cancelled;
+ await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
+ return;
+ }
+ }
+
var video = item as Video;
if (video != null)
{
@@ -488,6 +514,16 @@ namespace MediaBrowser.Server.Implementations.Sync
}
}
+ private bool AllowDuplicateJobItem(IHasDuplicateCheck provider, SyncJobItem original, SyncJobItem duplicate)
+ {
+ if (provider != null)
+ {
+ return provider.AllowDuplicateJobItem(original, duplicate);
+ }
+
+ return true;
+ }
+
private async Task Sync(SyncJobItem jobItem, SyncJob job, Video item, User user, bool enableConversion, SyncOptions syncOptions, IProgress<double> progress, CancellationToken cancellationToken)
{
var jobOptions = _syncManager.GetVideoOptions(jobItem, job);
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
index 36f8984dd..18fcb4e79 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
+++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
@@ -1,8 +1,8 @@
-using MediaBrowser.Common;
-using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
+using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Drawing;
@@ -32,7 +32,6 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.ScheduledTasks;
namespace MediaBrowser.Server.Implementations.Sync
{
@@ -1160,6 +1159,21 @@ namespace MediaBrowser.Server.Implementations.Sync
return options;
}
+ public ISyncProvider GetSyncProvider(SyncJobItem jobItem, SyncJob job)
+ {
+ foreach (var provider in _providers)
+ {
+ foreach (var target in GetSyncTargets(provider))
+ {
+ if (string.Equals(target.Id, jobItem.TargetId, StringComparison.OrdinalIgnoreCase))
+ {
+ return provider;
+ }
+ }
+ }
+ return null;
+ }
+
public SyncJobOptions GetVideoOptions(SyncJobItem jobItem, SyncJob job)
{
var options = GetSyncJobOptions(jobItem.TargetId, job.Profile, job.Quality);
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
index 524bf9294..efd37fa00 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
+++ b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
@@ -97,12 +97,11 @@ namespace MediaBrowser.Server.Implementations.Sync
}
// Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message.
- private const char StreamIdDelimeter = '_';
- private const string StreamIdDelimeterString = "|";
+ private const string StreamIdDelimeterString = "_";
public async Task<MediaSourceInfo> OpenMediaSource(string openToken, CancellationToken cancellationToken)
{
- var openKeys = openToken.Split(new[] { StreamIdDelimeter }, 3);
+ var openKeys = openToken.Split(new[] { StreamIdDelimeterString[0] }, 3);
var provider = _syncManager.ServerSyncProviders
.FirstOrDefault(i => string.Equals(openKeys[0], i.GetType().FullName.GetMD5().ToString("N"), StringComparison.OrdinalIgnoreCase));
diff --git a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs
index b11a7d167..8321ab952 100644
--- a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs
+++ b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs
@@ -11,8 +11,6 @@ using MediaBrowser.Server.Implementations.Photos;
using MoreLinq;
using System;
using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
using System.Linq;
using System.Threading.Tasks;
@@ -149,17 +147,7 @@ namespace MediaBrowser.Server.Implementations.UserViews
{
CollectionType.Movies,
CollectionType.TvShows,
- CollectionType.Games,
- CollectionType.Music,
- CollectionType.BoxSets,
- CollectionType.Playlists,
- CollectionType.Channels,
- CollectionType.LiveTv,
- CollectionType.Books,
- CollectionType.Photos,
- CollectionType.HomeVideos,
- CollectionType.MusicVideos,
- string.Empty
+ CollectionType.Music
};
return collectionStripViewTypes.Contains(view.ViewType ?? string.Empty);
@@ -170,7 +158,7 @@ namespace MediaBrowser.Server.Implementations.UserViews
var view = (UserView)item;
if (imageType == ImageType.Primary && IsUsingCollectionStrip(view))
{
- if (itemsWithImages.Count == 0 && !string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
+ if (itemsWithImages.Count == 0)
{
return false;
}
@@ -180,39 +168,5 @@ namespace MediaBrowser.Server.Implementations.UserViews
return await base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex).ConfigureAwait(false);
}
-
- protected override IEnumerable<String> GetStripCollageImagePaths(IHasImages primaryItem, IEnumerable<BaseItem> items)
- {
- var userView = primaryItem as UserView;
-
- if (userView != null && string.Equals(userView.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
- {
- var list = new List<string>();
- for (int i = 1; i <= 8; i++)
- {
- list.Add(ExtractLiveTvResource(i.ToString(CultureInfo.InvariantCulture), ApplicationPaths));
- }
- return list;
- }
-
- return base.GetStripCollageImagePaths(primaryItem, items);
- }
-
- private string ExtractLiveTvResource(string name, IApplicationPaths paths)
- {
- var namespacePath = GetType().Namespace + ".livetv." + name + ".jpg";
- var tempPath = Path.Combine(paths.TempDirectory, Guid.NewGuid().ToString("N") + ".jpg");
- Directory.CreateDirectory(Path.GetDirectoryName(tempPath));
-
- using (var stream = GetType().Assembly.GetManifestResourceStream(namespacePath))
- {
- using (var fileStream = new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.Read))
- {
- stream.CopyTo(fileStream);
- }
- }
-
- return tempPath;
- }
}
}
diff --git a/MediaBrowser.Server.Implementations/UserViews/livetv/1.jpg b/MediaBrowser.Server.Implementations/UserViews/livetv/1.jpg
deleted file mode 100644
index 2594b68a4..000000000
--- a/MediaBrowser.Server.Implementations/UserViews/livetv/1.jpg
+++ /dev/null
Binary files differ
diff --git a/MediaBrowser.Server.Implementations/UserViews/livetv/2.jpg b/MediaBrowser.Server.Implementations/UserViews/livetv/2.jpg
deleted file mode 100644
index e5c87b96b..000000000
--- a/MediaBrowser.Server.Implementations/UserViews/livetv/2.jpg
+++ /dev/null
Binary files differ
diff --git a/MediaBrowser.Server.Implementations/UserViews/livetv/3.jpg b/MediaBrowser.Server.Implementations/UserViews/livetv/3.jpg
deleted file mode 100644
index c19f7e612..000000000
--- a/MediaBrowser.Server.Implementations/UserViews/livetv/3.jpg
+++ /dev/null
Binary files differ
diff --git a/MediaBrowser.Server.Implementations/UserViews/livetv/4.jpg b/MediaBrowser.Server.Implementations/UserViews/livetv/4.jpg
deleted file mode 100644
index 93ee18044..000000000
--- a/MediaBrowser.Server.Implementations/UserViews/livetv/4.jpg
+++ /dev/null
Binary files differ
diff --git a/MediaBrowser.Server.Implementations/UserViews/livetv/5.jpg b/MediaBrowser.Server.Implementations/UserViews/livetv/5.jpg
deleted file mode 100644
index 4c2cd580d..000000000
--- a/MediaBrowser.Server.Implementations/UserViews/livetv/5.jpg
+++ /dev/null
Binary files differ
diff --git a/MediaBrowser.Server.Implementations/UserViews/livetv/6.jpg b/MediaBrowser.Server.Implementations/UserViews/livetv/6.jpg
deleted file mode 100644
index 6f496b6ac..000000000
--- a/MediaBrowser.Server.Implementations/UserViews/livetv/6.jpg
+++ /dev/null
Binary files differ
diff --git a/MediaBrowser.Server.Implementations/UserViews/livetv/7.jpg b/MediaBrowser.Server.Implementations/UserViews/livetv/7.jpg
deleted file mode 100644
index e7dba2760..000000000
--- a/MediaBrowser.Server.Implementations/UserViews/livetv/7.jpg
+++ /dev/null
Binary files differ
diff --git a/MediaBrowser.Server.Implementations/UserViews/livetv/8.jpg b/MediaBrowser.Server.Implementations/UserViews/livetv/8.jpg
deleted file mode 100644
index c69ba908c..000000000
--- a/MediaBrowser.Server.Implementations/UserViews/livetv/8.jpg
+++ /dev/null
Binary files differ
diff --git a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
index 4ea7d7c69..c0a48d46b 100644
--- a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
+++ b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
@@ -8,7 +8,7 @@
<OutputType>Exe</OutputType>
<RootNamespace>MediaBrowser.Server.Mac</RootNamespace>
<MonoMacResourcePrefix>Resources</MonoMacResourcePrefix>
- <AssemblyName>MediaBrowser.Server.Mac</AssemblyName>
+ <AssemblyName>Emby.Server.Mac</AssemblyName>
<ApplicationIcon>..\MediaBrowser.WebDashboard\dashboard-ui\css\images\favicon.ico</ApplicationIcon>
<ReleaseVersion>
</ReleaseVersion>
@@ -259,6 +259,9 @@
</BundleResource>
<BundleResource Include="Resources\appicon.icns" />
<BundleResource Include="Resources\MediaBrowser.Server.Mac\Images.xcassets\AppIcon.appiconset\Contents.json" />
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\.DS_Store">
+ <Link>Resources\dashboard-ui\.DS_Store</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\about.html">
<Link>Resources\dashboard-ui\about.html</Link>
</BundleResource>
@@ -286,9 +289,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\channelsettings.html">
<Link>Resources\dashboard-ui\channelsettings.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\channelslatest.html">
- <Link>Resources\dashboard-ui\channelslatest.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cinemamodeconfiguration.html">
<Link>Resources\dashboard-ui\cinemamodeconfiguration.html</Link>
</BundleResource>
@@ -328,18 +328,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dlnasettings.html">
<Link>Resources\dashboard-ui\dlnasettings.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\editcollectionitems.html">
- <Link>Resources\dashboard-ui\editcollectionitems.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\edititemimages.html">
- <Link>Resources\dashboard-ui\edititemimages.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\edititemmetadata.html">
<Link>Resources\dashboard-ui\edititemmetadata.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\edititemsubtitles.html">
- <Link>Resources\dashboard-ui\edititemsubtitles.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\encodingsettings.html">
<Link>Resources\dashboard-ui\encodingsettings.html</Link>
</BundleResource>
@@ -349,9 +340,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\favicon.ico">
<Link>Resources\dashboard-ui\favicon.ico</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\favorites.html">
- <Link>Resources\dashboard-ui\favorites.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\forgotpassword.html">
<Link>Resources\dashboard-ui\forgotpassword.html</Link>
</BundleResource>
@@ -373,15 +361,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\gamesystems.html">
<Link>Resources\dashboard-ui\gamesystems.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\homelatest.html">
- <Link>Resources\dashboard-ui\homelatest.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\index.html">
<Link>Resources\dashboard-ui\index.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\itembynamedetails.html">
- <Link>Resources\dashboard-ui\itembynamedetails.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\itemdetails.html">
<Link>Resources\dashboard-ui\itemdetails.html</Link>
</BundleResource>
@@ -400,14 +382,11 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\librarysettings.html">
<Link>Resources\dashboard-ui\librarysettings.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvchannel.html">
- <Link>Resources\dashboard-ui\livetvchannel.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvchannels.html">
- <Link>Resources\dashboard-ui\livetvchannels.html</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetv.html">
+ <Link>Resources\dashboard-ui\livetv.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvguide.html">
- <Link>Resources\dashboard-ui\livetvguide.html</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvguideprovider.html">
+ <Link>Resources\dashboard-ui\livetvguideprovider.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvitems.html">
<Link>Resources\dashboard-ui\livetvitems.html</Link>
@@ -415,38 +394,26 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvnewrecording.html">
<Link>Resources\dashboard-ui\livetvnewrecording.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvprogram.html">
- <Link>Resources\dashboard-ui\livetvprogram.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvrecording.html">
- <Link>Resources\dashboard-ui\livetvrecording.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvrecordinglist.html">
<Link>Resources\dashboard-ui\livetvrecordinglist.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvrecordings.html">
- <Link>Resources\dashboard-ui\livetvrecordings.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvseriestimer.html">
<Link>Resources\dashboard-ui\livetvseriestimer.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvseriestimers.html">
- <Link>Resources\dashboard-ui\livetvseriestimers.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvsettings.html">
<Link>Resources\dashboard-ui\livetvsettings.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvstatus.html">
<Link>Resources\dashboard-ui\livetvstatus.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvsuggested.html">
- <Link>Resources\dashboard-ui\livetvsuggested.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvtimer.html">
<Link>Resources\dashboard-ui\livetvtimer.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvtimers.html">
- <Link>Resources\dashboard-ui\livetvtimers.html</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvtunerprovider-hdhomerun.html">
+ <Link>Resources\dashboard-ui\livetvtunerprovider-hdhomerun.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvtunerprovider-m3u.html">
+ <Link>Resources\dashboard-ui\livetvtunerprovider-m3u.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\log.html">
<Link>Resources\dashboard-ui\log.html</Link>
@@ -469,24 +436,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\metadatasubtitles.html">
<Link>Resources\dashboard-ui\metadatasubtitles.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\moviegenres.html">
- <Link>Resources\dashboard-ui\moviegenres.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\moviepeople.html">
- <Link>Resources\dashboard-ui\moviepeople.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\movies.html">
<Link>Resources\dashboard-ui\movies.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\moviesrecommended.html">
- <Link>Resources\dashboard-ui\moviesrecommended.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\moviestudios.html">
- <Link>Resources\dashboard-ui\moviestudios.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\movietrailers.html">
- <Link>Resources\dashboard-ui\movietrailers.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\musicalbumartists.html">
<Link>Resources\dashboard-ui\musicalbumartists.html</Link>
</BundleResource>
@@ -508,11 +460,14 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\mypreferencesdisplay.html">
<Link>Resources\dashboard-ui\mypreferencesdisplay.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\mypreferenceshome.html">
+ <Link>Resources\dashboard-ui\mypreferenceshome.html</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\mypreferenceslanguages.html">
<Link>Resources\dashboard-ui\mypreferenceslanguages.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\mypreferenceswebclient.html">
- <Link>Resources\dashboard-ui\mypreferenceswebclient.html</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\mypreferencesmenu.html">
+ <Link>Resources\dashboard-ui\mypreferencesmenu.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\myprofile.html">
<Link>Resources\dashboard-ui\myprofile.html</Link>
@@ -523,6 +478,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\mysyncjob.html">
<Link>Resources\dashboard-ui\mysyncjob.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\mysyncsettings.html">
+ <Link>Resources\dashboard-ui\mysyncsettings.html</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\notificationlist.html">
<Link>Resources\dashboard-ui\notificationlist.html</Link>
</BundleResource>
@@ -541,9 +499,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\playbackconfiguration.html">
<Link>Resources\dashboard-ui\playbackconfiguration.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\playlistedit.html">
- <Link>Resources\dashboard-ui\playlistedit.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\playlists.html">
<Link>Resources\dashboard-ui\playlists.html</Link>
</BundleResource>
@@ -562,12 +517,18 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scheduledtasks.html">
<Link>Resources\dashboard-ui\scheduledtasks.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\secondaryitems.html">
+ <Link>Resources\dashboard-ui\secondaryitems.html</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\selectserver.html">
<Link>Resources\dashboard-ui\selectserver.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\serversecurity.html">
<Link>Resources\dashboard-ui\serversecurity.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\shared.html">
+ <Link>Resources\dashboard-ui\shared.html</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\songs.html">
<Link>Resources\dashboard-ui\songs.html</Link>
</BundleResource>
@@ -631,6 +592,15 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\userprofiles.html">
<Link>Resources\dashboard-ui\userprofiles.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\vulcanize-in.html">
+ <Link>Resources\dashboard-ui\vulcanize-in.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\vulcanize-out.html">
+ <Link>Resources\dashboard-ui\vulcanize-out.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\vulcanize.txt">
+ <Link>Resources\dashboard-ui\vulcanize.txt</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\wizardagreement.html">
<Link>Resources\dashboard-ui\wizardagreement.html</Link>
</BundleResource>
@@ -640,6 +610,12 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\wizardlibrary.html">
<Link>Resources\dashboard-ui\wizardlibrary.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\wizardlivetvguide.html">
+ <Link>Resources\dashboard-ui\wizardlivetvguide.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\wizardlivetvtuner.html">
+ <Link>Resources\dashboard-ui\wizardlivetvtuner.html</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\wizardservice.html">
<Link>Resources\dashboard-ui\wizardservice.html</Link>
</BundleResource>
@@ -652,6 +628,2640 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\wizarduser.html">
<Link>Resources\dashboard-ui\wizarduser.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\ajax.js">
+ <Link>Resources\dashboard-ui\apiclient\ajax.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\apiclient.js">
+ <Link>Resources\dashboard-ui\apiclient\apiclient.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\connectionmanager.js">
+ <Link>Resources\dashboard-ui\apiclient\connectionmanager.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\connectservice.js">
+ <Link>Resources\dashboard-ui\apiclient\connectservice.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\credentials.js">
+ <Link>Resources\dashboard-ui\apiclient\credentials.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\deferred.js">
+ <Link>Resources\dashboard-ui\apiclient\deferred.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\device.js">
+ <Link>Resources\dashboard-ui\apiclient\device.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\events.js">
+ <Link>Resources\dashboard-ui\apiclient\events.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\localassetmanager.js">
+ <Link>Resources\dashboard-ui\apiclient\localassetmanager.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\logger.js">
+ <Link>Resources\dashboard-ui\apiclient\logger.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\md5.js">
+ <Link>Resources\dashboard-ui\apiclient\md5.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\serverdiscovery.js">
+ <Link>Resources\dashboard-ui\apiclient\serverdiscovery.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\sha1.js">
+ <Link>Resources\dashboard-ui\apiclient\sha1.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\store.js">
+ <Link>Resources\dashboard-ui\apiclient\store.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\wakeonlan.js">
+ <Link>Resources\dashboard-ui\apiclient\wakeonlan.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\alt\ajax.js">
+ <Link>Resources\dashboard-ui\apiclient\alt\ajax.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\alt\bean.js">
+ <Link>Resources\dashboard-ui\apiclient\alt\bean.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\alt\deferred.js">
+ <Link>Resources\dashboard-ui\apiclient\alt\deferred.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\apiclient\alt\events.js">
+ <Link>Resources\dashboard-ui\apiclient\alt\events.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fastclick\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\fastclick\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fastclick\LICENSE">
+ <Link>Resources\dashboard-ui\bower_components\fastclick\LICENSE</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fastclick\README.md">
+ <Link>Resources\dashboard-ui\bower_components\fastclick\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fastclick\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\fastclick\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\fastclick\lib\fastclick.js">
+ <Link>Resources\dashboard-ui\bower_components\fastclick\lib\fastclick.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\font-roboto\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\font-roboto\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\font-roboto\README.md">
+ <Link>Resources\dashboard-ui\bower_components\font-roboto\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\font-roboto\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\font-roboto\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\font-roboto\roboto.html">
+ <Link>Resources\dashboard-ui\bower_components\font-roboto\roboto.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\.bowerrc">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\.bowerrc</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\.jscsrc">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\.jscsrc</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\.jshintrc">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\.jshintrc</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\.travis.yml">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\.travis.yml</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\CHANGELOG.md">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\CHANGELOG.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\CONTRIBUTING.md">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\CONTRIBUTING.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\Gruntfile.coffee">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\Gruntfile.coffee</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\LICENSE.md">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\LICENSE.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\README.md">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\component.json">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\component.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\hammer.js">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\hammer.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\hammer.min.js">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\hammer.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\hammer.min.map">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\hammer.min.map</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hammerjs\package.json">
+ <Link>Resources\dashboard-ui\bower_components\hammerjs\package.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-announcer\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-announcer\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-announcer\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-announcer\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-announcer\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-announcer\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-announcer\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-announcer\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-announcer\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-announcer\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-announcer\iron-a11y-announcer.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-announcer\iron-a11y-announcer.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-announcer\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-announcer\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-announcer\demo\x-announces.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-announcer\demo\x-announces.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-announcer\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-announcer\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-announcer\test\iron-a11y-announcer.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-announcer\test\iron-a11y-announcer.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-keys-behavior\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-keys-behavior\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-keys-behavior\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-keys-behavior\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-keys-behavior\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-keys-behavior\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-keys-behavior\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-keys-behavior\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-keys-behavior\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-keys-behavior\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-keys-behavior\iron-a11y-keys-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-keys-behavior\iron-a11y-keys-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-keys-behavior\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-keys-behavior\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-keys-behavior\demo\x-key-aware.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-keys-behavior\demo\x-key-aware.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-keys-behavior\test\basic-test.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-keys-behavior\test\basic-test.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-a11y-keys-behavior\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-a11y-keys-behavior\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\iron-autogrow-textarea.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\iron-autogrow-textarea.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-autogrow-textarea\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-autogrow-textarea\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-behaviors\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-behaviors\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-behaviors\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-behaviors\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-behaviors\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\iron-button-state.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-behaviors\iron-button-state.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\iron-control-state.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-behaviors\iron-control-state.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-behaviors\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\demo\simple-button.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-behaviors\demo\simple-button.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\test\active-state.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-behaviors\test\active-state.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\test\disabled-state.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-behaviors\test\disabled-state.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\test\focused-state.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-behaviors\test\focused-state.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-behaviors\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-behaviors\test\test-elements.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-behaviors\test\test-elements.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-checked-element-behavior\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-checked-element-behavior\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-checked-element-behavior\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-checked-element-behavior\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-checked-element-behavior\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-checked-element-behavior\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-checked-element-behavior\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-checked-element-behavior\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-checked-element-behavior\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-checked-element-behavior\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-checked-element-behavior\iron-checked-element-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-checked-element-behavior\iron-checked-element-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-checked-element-behavior\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-checked-element-behavior\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-checked-element-behavior\demo\simple-checkbox.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-checked-element-behavior\demo\simple-checkbox.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-checked-element-behavior\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-checked-element-behavior\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-checked-element-behavior\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-checked-element-behavior\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-checked-element-behavior\test\simple-checkbox.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-checked-element-behavior\test\simple-checkbox.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-dropdown\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-dropdown\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-dropdown\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-dropdown\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-dropdown\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-dropdown\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-dropdown\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-dropdown\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-dropdown\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-dropdown\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-dropdown\iron-dropdown-scroll-manager.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-dropdown\iron-dropdown-scroll-manager.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-dropdown\iron-dropdown.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-dropdown\iron-dropdown.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-dropdown\demo\grow-height-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-dropdown\demo\grow-height-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-dropdown\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-dropdown\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-dropdown\demo\x-select.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-dropdown\demo\x-select.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-dropdown\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-dropdown\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-dropdown\test\iron-dropdown-scroll-manager.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-dropdown\test\iron-dropdown-scroll-manager.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-dropdown\test\iron-dropdown.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-dropdown\test\iron-dropdown.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-fit-behavior\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-fit-behavior\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-fit-behavior\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-fit-behavior\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-fit-behavior\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-fit-behavior\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-fit-behavior\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-fit-behavior\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-fit-behavior\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-fit-behavior\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-fit-behavior\iron-fit-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-fit-behavior\iron-fit-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-fit-behavior\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-fit-behavior\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-fit-behavior\demo\simple-fit.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-fit-behavior\demo\simple-fit.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-fit-behavior\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-fit-behavior\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-fit-behavior\test\iron-fit-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-fit-behavior\test\iron-fit-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-fit-behavior\test\test-fit.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-fit-behavior\test\test-fit.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-flex-layout\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-flex-layout\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-flex-layout\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-flex-layout\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\iron-flex-layout.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-flex-layout\iron-flex-layout.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\classes\iron-flex-layout.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-flex-layout\classes\iron-flex-layout.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\classes\iron-shadow-flex-layout.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-flex-layout\classes\iron-shadow-flex-layout.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-flex-layout\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-flex-layout\demo\x-app.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-flex-layout\demo\x-app.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-form-element-behavior\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-form-element-behavior\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-form-element-behavior\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-form-element-behavior\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-form-element-behavior\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-form-element-behavior\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-form-element-behavior\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-form-element-behavior\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-form-element-behavior\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-form-element-behavior\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-form-element-behavior\iron-form-element-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-form-element-behavior\iron-form-element-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-form-element-behavior\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-form-element-behavior\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-form-element-behavior\demo\simple-element.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-form-element-behavior\demo\simple-element.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-form-element-behavior\demo\simple-form.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-form-element-behavior\demo\simple-form.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icon\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-icon\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icon\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-icon\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icon\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-icon\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icon\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-icon\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icon\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\iron-icon\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icon\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icon\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icon\iron-icon.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icon\iron-icon.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icon\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icon\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icon\demo\location.png">
+ <Link>Resources\dashboard-ui\bower_components\iron-icon\demo\location.png</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icon\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icon\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icon\test\iron-icon.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icon\test\iron-icon.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\av-icons.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\av-icons.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\communication-icons.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\communication-icons.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\device-icons.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\device-icons.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\editor-icons.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\editor-icons.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\hardware-icons.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\hardware-icons.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\image-icons.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\image-icons.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\iron-icons.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\iron-icons.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\maps-icons.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\maps-icons.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\notification-icons.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\notification-icons.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\social-icons.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\social-icons.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-icons\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-icons\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-iconset-svg\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-iconset-svg\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-iconset-svg\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-iconset-svg\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-iconset-svg\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-iconset-svg\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-iconset-svg\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-iconset-svg\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-iconset-svg\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-iconset-svg\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-iconset-svg\iron-iconset-svg.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-iconset-svg\iron-iconset-svg.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-iconset-svg\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-iconset-svg\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-iconset-svg\demo\svg-sample-icons.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-iconset-svg\demo\svg-sample-icons.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-iconset-svg\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-iconset-svg\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-iconset-svg\test\iron-iconset-svg.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-iconset-svg\test\iron-iconset-svg.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-input\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-input\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-input\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-input\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\iron-input\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-input\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\iron-input.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-input\iron-input.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-input\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-input\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\test\iron-input.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-input\test\iron-input.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-input\test\letters-only.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-input\test\letters-only.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-media-query\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-media-query\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-media-query\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-media-query\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-media-query\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-media-query\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-media-query\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-media-query\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-media-query\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\iron-media-query\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-media-query\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-media-query\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-media-query\iron-media-query.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-media-query\iron-media-query.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-media-query\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-media-query\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-media-query\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-media-query\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-media-query\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-media-query\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-menu-behavior\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-menu-behavior\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-menu-behavior\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-menu-behavior\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-menu-behavior\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-menu-behavior\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-menu-behavior\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-menu-behavior\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-menu-behavior\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-menu-behavior\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-menu-behavior\iron-menu-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-menu-behavior\iron-menu-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-menu-behavior\iron-menubar-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-menu-behavior\iron-menubar-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-menu-behavior\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-menu-behavior\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-menu-behavior\demo\simple-menu.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-menu-behavior\demo\simple-menu.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-menu-behavior\demo\simple-menubar.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-menu-behavior\demo\simple-menubar.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-menu-behavior\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-menu-behavior\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-menu-behavior\test\iron-menu-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-menu-behavior\test\iron-menu-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-menu-behavior\test\iron-menubar-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-menu-behavior\test\iron-menubar-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-menu-behavior\test\test-menu.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-menu-behavior\test\test-menu.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-menu-behavior\test\test-menubar.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-menu-behavior\test\test-menubar.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-meta\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-meta\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-meta\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-meta\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\iron-meta\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-meta\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\iron-meta.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-meta\iron-meta.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-meta\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-meta\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-meta\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-meta\test\iron-meta.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-meta\test\iron-meta.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-overlay-behavior\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-overlay-behavior\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-overlay-behavior\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-overlay-behavior\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-overlay-behavior\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-overlay-behavior\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-overlay-behavior\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-overlay-behavior\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-overlay-behavior\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-overlay-behavior\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-overlay-behavior\iron-overlay-backdrop.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-overlay-behavior\iron-overlay-backdrop.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-overlay-behavior\iron-overlay-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-overlay-behavior\iron-overlay-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-overlay-behavior\iron-overlay-manager.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-overlay-behavior\iron-overlay-manager.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-overlay-behavior\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-overlay-behavior\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-overlay-behavior\demo\simple-overlay.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-overlay-behavior\demo\simple-overlay.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-overlay-behavior\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-overlay-behavior\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-overlay-behavior\test\iron-overlay-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-overlay-behavior\test\iron-overlay-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-overlay-behavior\test\test-overlay.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-overlay-behavior\test\test-overlay.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-pages\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-pages\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-pages\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-pages\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-pages\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-pages\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-pages\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-pages\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-pages\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\iron-pages\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-pages\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-pages\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-pages\iron-pages.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-pages\iron-pages.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-pages\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-pages\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-pages\test\attr-for-selected.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-pages\test\attr-for-selected.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-pages\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-pages\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-pages\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-pages\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\iron-range-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\iron-range-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-range-behavior\test\x-progressbar.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-range-behavior\test\x-progressbar.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\iron-resizable-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\iron-resizable-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\demo\src\x-app.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\demo\src\x-app.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\test\iron-resizable-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\test\iron-resizable-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-resizable-behavior\test\test-elements.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-resizable-behavior\test\test-elements.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\iron-multi-selectable.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\iron-multi-selectable.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\iron-selectable.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\iron-selectable.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\iron-selection.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\iron-selection.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\iron-selector.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\iron-selector.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\test\activate-event.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\test\activate-event.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\test\content-element.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\test\content-element.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\test\content.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\test\content.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\test\multi.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\test\multi.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\test\next-previous.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\test\next-previous.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\test\selected-attribute.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\test\selected-attribute.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-selector\test\template-repeat.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-selector\test\template-repeat.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-validatable-behavior\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-validatable-behavior\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-validatable-behavior\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\iron-validatable-behavior\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-validatable-behavior\README.md">
+ <Link>Resources\dashboard-ui\bower_components\iron-validatable-behavior\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-validatable-behavior\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\iron-validatable-behavior\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-validatable-behavior\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-validatable-behavior\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-validatable-behavior\iron-validatable-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-validatable-behavior\iron-validatable-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-validatable-behavior\demo\cats-only.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-validatable-behavior\demo\cats-only.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-validatable-behavior\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-validatable-behavior\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-validatable-behavior\demo\validatable-input.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-validatable-behavior\demo\validatable-input.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-validatable-behavior\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-validatable-behavior\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-validatable-behavior\test\iron-validatable-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-validatable-behavior\test\iron-validatable-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\iron-validatable-behavior\test\test-validatable.html">
+ <Link>Resources\dashboard-ui\bower_components\iron-validatable-behavior\test\test-validatable.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\jquery\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\MIT-LICENSE.txt">
+ <Link>Resources\dashboard-ui\bower_components\jquery\MIT-LICENSE.txt</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\jquery\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\dist\jquery.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\dist\jquery.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\dist\jquery.min.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\dist\jquery.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\dist\jquery.min.map">
+ <Link>Resources\dashboard-ui\bower_components\jquery\dist\jquery.min.map</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\ajax.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\ajax.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\attributes.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\attributes.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\callbacks.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\callbacks.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\core.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\core.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\css.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\css.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\data.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\data.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\deferred.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\deferred.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\deprecated.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\deprecated.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\dimensions.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\dimensions.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\effects.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\effects.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\event.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\event.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\intro.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\intro.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\jquery.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\jquery.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\manipulation.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\manipulation.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\offset.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\offset.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\outro.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\outro.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\queue.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\queue.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\selector-native.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\selector-native.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\selector-sizzle.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\selector-sizzle.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\selector.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\selector.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\serialize.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\serialize.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\traversing.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\traversing.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\wrap.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\wrap.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\ajax\jsonp.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\ajax\jsonp.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\ajax\load.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\ajax\load.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\ajax\parseJSON.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\ajax\parseJSON.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\ajax\parseXML.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\ajax\parseXML.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\ajax\script.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\ajax\script.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\ajax\xhr.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\ajax\xhr.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\attributes\attr.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\attributes\attr.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\attributes\classes.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\attributes\classes.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\attributes\prop.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\attributes\prop.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\attributes\support.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\attributes\support.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\attributes\val.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\attributes\val.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\core\access.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\core\access.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\core\init.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\core\init.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\core\parseHTML.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\core\parseHTML.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\core\ready.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\core\ready.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\css\addGetHookIf.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\css\addGetHookIf.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\css\curCSS.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\css\curCSS.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\css\defaultDisplay.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\css\defaultDisplay.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\css\hiddenVisibleSelectors.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\css\hiddenVisibleSelectors.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\css\support.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\css\support.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\css\swap.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\css\swap.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\data\Data.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\data\Data.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\data\accepts.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\data\accepts.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\effects\Tween.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\effects\Tween.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\effects\animatedSelector.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\effects\animatedSelector.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\event\ajax.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\event\ajax.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\event\alias.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\event\alias.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\event\support.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\event\support.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\exports\amd.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\exports\amd.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\exports\global.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\exports\global.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\manipulation\_evalUrl.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\manipulation\_evalUrl.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\manipulation\support.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\manipulation\support.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\queue\delay.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\queue\delay.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\sizzle\dist\sizzle.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\sizzle\dist\sizzle.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\sizzle\dist\sizzle.min.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\sizzle\dist\sizzle.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\sizzle\dist\sizzle.min.map">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\sizzle\dist\sizzle.min.map</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jquery\src\traversing\findFilter.js">
+ <Link>Resources\dashboard-ui\bower_components\jquery\src\traversing\findFilter.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\jstree\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\LICENSE-MIT">
+ <Link>Resources\dashboard-ui\bower_components\jstree\LICENSE-MIT</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\jstree\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\composer.json">
+ <Link>Resources\dashboard-ui\bower_components\jstree\composer.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\dist\jstree.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\dist\jstree.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\dist\jstree.min.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\dist\jstree.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\dist\themes\default\32px.png">
+ <Link>Resources\dashboard-ui\bower_components\jstree\dist\themes\default\32px.png</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\dist\themes\default\40px.png">
+ <Link>Resources\dashboard-ui\bower_components\jstree\dist\themes\default\40px.png</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\dist\themes\default\style.css">
+ <Link>Resources\dashboard-ui\bower_components\jstree\dist\themes\default\style.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\dist\themes\default\style.min.css">
+ <Link>Resources\dashboard-ui\bower_components\jstree\dist\themes\default\style.min.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\dist\themes\default\throbber.gif">
+ <Link>Resources\dashboard-ui\bower_components\jstree\dist\themes\default\throbber.gif</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\dist\themes\default-dark\32px.png">
+ <Link>Resources\dashboard-ui\bower_components\jstree\dist\themes\default-dark\32px.png</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\dist\themes\default-dark\40px.png">
+ <Link>Resources\dashboard-ui\bower_components\jstree\dist\themes\default-dark\40px.png</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\dist\themes\default-dark\style.css">
+ <Link>Resources\dashboard-ui\bower_components\jstree\dist\themes\default-dark\style.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\dist\themes\default-dark\style.min.css">
+ <Link>Resources\dashboard-ui\bower_components\jstree\dist\themes\default-dark\style.min.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\dist\themes\default-dark\throbber.gif">
+ <Link>Resources\dashboard-ui\bower_components\jstree\dist\themes\default-dark\throbber.gif</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\intro.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\intro.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\jstree.checkbox.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\jstree.checkbox.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\jstree.contextmenu.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\jstree.contextmenu.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\jstree.dnd.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\jstree.dnd.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\jstree.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\jstree.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\jstree.massload.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\jstree.massload.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\jstree.search.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\jstree.search.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\jstree.sort.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\jstree.sort.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\jstree.state.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\jstree.state.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\jstree.types.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\jstree.types.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\jstree.unique.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\jstree.unique.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\jstree.wholerow.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\jstree.wholerow.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\misc.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\misc.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\outro.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\outro.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\sample.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\sample.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\vakata-jstree.js">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\vakata-jstree.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\themes\base.less">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\themes\base.less</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\themes\main.less">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\themes\main.less</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\themes\mixins.less">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\themes\mixins.less</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\themes\responsive.less">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\themes\responsive.less</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\themes\default\32px.png">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\themes\default\32px.png</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\themes\default\40px.png">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\themes\default\40px.png</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\themes\default\style.css">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\themes\default\style.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\themes\default\style.less">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\themes\default\style.less</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\themes\default\throbber.gif">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\themes\default\throbber.gif</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\themes\default-dark\32px.png">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\themes\default-dark\32px.png</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\themes\default-dark\40px.png">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\themes\default-dark\40px.png</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\themes\default-dark\style.css">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\themes\default-dark\style.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\themes\default-dark\style.less">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\themes\default-dark\style.less</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\jstree\src\themes\default-dark\throbber.gif">
+ <Link>Resources\dashboard-ui\bower_components\jstree\src\themes\default-dark\throbber.gif</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\README.md">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\index.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\neon-animatable-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\neon-animatable-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\neon-animatable.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\neon-animatable.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\neon-animated-pages.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\neon-animated-pages.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\neon-animation-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\neon-animation-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\neon-animation-runner-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\neon-animation-runner-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\neon-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\neon-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\neon-animations.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\neon-animations.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\neon-shared-element-animatable-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\neon-shared-element-animatable-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\neon-shared-element-animation-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\neon-shared-element-animation-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\web-animations.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\web-animations.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\cascaded-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\cascaded-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\fade-in-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\fade-in-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\fade-out-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\fade-out-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\hero-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\hero-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\opaque-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\opaque-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\reverse-ripple-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\reverse-ripple-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\ripple-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\ripple-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\scale-down-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\scale-down-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\scale-up-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\scale-up-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\slide-down-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\slide-down-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\slide-from-left-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\slide-from-left-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\slide-from-right-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\slide-from-right-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\slide-left-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\slide-left-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\slide-right-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\slide-right-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\slide-up-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\slide-up-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\animations\transform-animation.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\animations\transform-animation.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\shared.css">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\shared.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\card\index.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\card\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\card\x-card.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\card\x-card.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\card\x-cards-list.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\card\x-cards-list.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\declarative\index.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\declarative\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\doc\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\doc\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\doc\my-animatable.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\doc\my-animatable.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\doc\my-dialog.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\doc\my-dialog.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\doc\types.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\doc\types.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\dropdown\animated-dropdown.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\dropdown\animated-dropdown.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\dropdown\index.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\dropdown\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\grid\animated-grid.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\grid\animated-grid.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\grid\fullsize-page-with-card.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\grid\fullsize-page-with-card.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\grid\index.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\grid\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\list\full-view.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\list\full-view.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\list\index.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\list\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\list\list-demo.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\list\list-demo.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\list\list-view.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\list\list-view.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\load\animated-grid.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\load\animated-grid.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\load\full-page.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\load\full-page.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\load\index.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\load\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\reprojection\animated-grid.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\reprojection\animated-grid.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\reprojection\fullsize-page-with-card.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\reprojection\fullsize-page-with-card.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\reprojection\index.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\reprojection\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\reprojection\reprojected-pages.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\reprojection\reprojected-pages.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\tiles\circles-page.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\tiles\circles-page.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\tiles\index.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\tiles\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\demo\tiles\squares-page.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\demo\tiles\squares-page.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\guides\neon-animation.md">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\guides\neon-animation.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\test\neon-animated-pages.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\test\neon-animated-pages.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\neon-animation\test\test-resizable-pages.html">
+ <Link>Resources\dashboard-ui\bower_components\neon-animation\test\test-resizable-pages.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-behaviors\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-behaviors\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-behaviors\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-behaviors\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-behaviors\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-behaviors\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-behaviors\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-behaviors\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-behaviors\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-behaviors\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-behaviors\paper-button-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-behaviors\paper-button-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-behaviors\paper-inky-focus-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-behaviors\paper-inky-focus-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-behaviors\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-behaviors\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-behaviors\demo\paper-button.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-behaviors\demo\paper-button.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-behaviors\demo\paper-radio-button.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-behaviors\demo\paper-radio-button.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-behaviors\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-behaviors\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-behaviors\test\paper-button-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-behaviors\test\paper-button-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-behaviors\test\paper-radio-button-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-behaviors\test\paper-radio-button-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-behaviors\test\test-button.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-behaviors\test\test-button.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-behaviors\test\test-radio-button.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-behaviors\test\test-radio-button.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-button\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-button\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-button\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-button\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-button\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-button\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-button\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-button\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-button\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-button\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-button\paper-button.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-button\paper-button.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-button\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-button\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-button\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-button\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-button\test\paper-button.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-button\test\paper-button.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-checkbox\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-checkbox\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-checkbox\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-checkbox\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-checkbox\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-checkbox\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-checkbox\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-checkbox\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-checkbox\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-checkbox\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-checkbox\metadata.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-checkbox\metadata.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-checkbox\paper-checkbox.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-checkbox\paper-checkbox.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-checkbox\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-checkbox\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-checkbox\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-checkbox\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-checkbox\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-checkbox\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog\paper-dialog.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog\paper-dialog.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog\test\paper-dialog.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog\test\paper-dialog.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-behavior\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-behavior\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-behavior\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-behavior\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-behavior\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-behavior\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-behavior\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-behavior\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-behavior\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-behavior\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-behavior\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-behavior\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-behavior\paper-dialog-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-behavior\paper-dialog-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-behavior\paper-dialog-common.css">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-behavior\paper-dialog-common.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-behavior\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-behavior\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-behavior\demo\simple-dialog.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-behavior\demo\simple-dialog.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-behavior\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-behavior\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-behavior\test\paper-dialog-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-behavior\test\paper-dialog-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-behavior\test\test-dialog.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-behavior\test\test-dialog.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-scrollable\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-scrollable\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-scrollable\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-scrollable\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-scrollable\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-scrollable\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-scrollable\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-scrollable\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-scrollable\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-scrollable\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-scrollable\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-scrollable\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-scrollable\paper-dialog-scrollable.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-scrollable\paper-dialog-scrollable.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-scrollable\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-scrollable\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-scrollable\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-scrollable\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dialog-scrollable\test\paper-dialog-scrollable.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dialog-scrollable\test\paper-dialog-scrollable.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-drawer-panel\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-drawer-panel\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-drawer-panel\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-drawer-panel\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-drawer-panel\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-drawer-panel\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-drawer-panel\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-drawer-panel\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-drawer-panel\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-drawer-panel\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-drawer-panel\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-drawer-panel\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-drawer-panel\paper-drawer-panel.css">
+ <Link>Resources\dashboard-ui\bower_components\paper-drawer-panel\paper-drawer-panel.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-drawer-panel\paper-drawer-panel.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-drawer-panel\paper-drawer-panel.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-drawer-panel\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-drawer-panel\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dropdown-menu\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-dropdown-menu\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dropdown-menu\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-dropdown-menu\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dropdown-menu\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-dropdown-menu\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dropdown-menu\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-dropdown-menu\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dropdown-menu\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dropdown-menu\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dropdown-menu\paper-dropdown-menu.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dropdown-menu\paper-dropdown-menu.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dropdown-menu\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dropdown-menu\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dropdown-menu\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dropdown-menu\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-dropdown-menu\test\paper-dropdown-menu.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-dropdown-menu\test\paper-dropdown-menu.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-fab\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-fab\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-fab\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-fab\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-fab\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-fab\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-fab\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-fab\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-fab\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-fab\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-fab\paper-fab.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-fab\paper-fab.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-fab\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-fab\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-fab\test\a11y.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-fab\test\a11y.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-fab\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-fab\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-fab\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-fab\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-icon-button\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-icon-button\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-icon-button\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-icon-button\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-icon-button\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-icon-button\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-icon-button\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-icon-button\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-icon-button\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-icon-button\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-icon-button\paper-icon-button.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-icon-button\paper-icon-button.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-icon-button\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-icon-button\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-icon-button\test\a11y.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-icon-button\test\a11y.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-icon-button\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-icon-button\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-icon-button\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-icon-button\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\all-imports.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\all-imports.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input-addon-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\paper-input-addon-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input-behavior.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\paper-input-behavior.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input-char-counter.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\paper-input-char-counter.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input-container.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\paper-input-container.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input-error.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\paper-input-error.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-input.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\paper-input.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\paper-textarea.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\paper-textarea.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\demo\ssn-input.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\demo\ssn-input.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\demo\ssn-validator.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\demo\ssn-validator.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\letters-only.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\test\letters-only.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\paper-input-char-counter.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\test\paper-input-char-counter.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\paper-input-container.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\test\paper-input-container.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\paper-input-error.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\test\paper-input-error.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\paper-input.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\test\paper-input.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-input\test\paper-textarea.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-input\test\paper-textarea.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-item\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-item\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-item\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-item\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-item\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-item\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-item\all-imports.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-item\all-imports.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-item\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-item\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-item\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-item\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-item\paper-icon-item.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-item\paper-icon-item.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-item\paper-item-body.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-item\paper-item-body.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-item\paper-item-shared.css">
+ <Link>Resources\dashboard-ui\bower_components\paper-item\paper-item-shared.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-item\paper-item.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-item\paper-item.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-item\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-item\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-item\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-item\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-item\test\paper-item.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-item\test\paper-item.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-material\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-material\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-material\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-material\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-material\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-material\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-material\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-material\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-material\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-material\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-material\paper-material.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-material\paper-material.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-material\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-material\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-material\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-material\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-material\test\paper-material.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-material\test\paper-material.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu\paper-menu.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu\paper-menu.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu\test\paper-menu.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu\test\paper-menu.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu-button\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu-button\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu-button\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu-button\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu-button\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu-button\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu-button\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu-button\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu-button\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu-button\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu-button\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu-button\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu-button\paper-menu-button-animations.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu-button\paper-menu-button-animations.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu-button\paper-menu-button.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu-button\paper-menu-button.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu-button\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu-button\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu-button\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu-button\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-menu-button\test\paper-menu-button.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-menu-button\test\paper-menu-button.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-progress\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-progress\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-progress\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-progress\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-progress\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-progress\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\paper-progress.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-progress\paper-progress.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-progress\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-progress\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-progress\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-progress\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-button\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-button\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-button\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-button\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-button\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-button\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-button\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-button\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-button\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-button\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-button\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-button\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-button\paper-radio-button.css">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-button\paper-radio-button.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-button\paper-radio-button.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-button\paper-radio-button.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-button\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-button\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-button\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-button\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-button\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-button\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-group\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-group\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-group\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-group\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-group\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-group\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-group\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-group\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-group\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-group\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-group\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-group\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-group\paper-radio-group.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-group\paper-radio-group.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-group\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-group\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-group\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-group\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-radio-group\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-radio-group\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-ripple\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-ripple\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-ripple\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-ripple\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-ripple\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-ripple\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-ripple\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-ripple\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-ripple\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-ripple\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-ripple\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-ripple\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-ripple\paper-ripple.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-ripple\paper-ripple.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-ripple\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-ripple\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-ripple\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-ripple\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-ripple\test\paper-ripple.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-ripple\test\paper-ripple.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-slider\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-slider\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-slider\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-slider\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-slider\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-slider\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-slider\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-slider\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-slider\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-slider\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-slider\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-slider\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-slider\paper-slider.css">
+ <Link>Resources\dashboard-ui\bower_components\paper-slider\paper-slider.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-slider\paper-slider.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-slider\paper-slider.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-slider\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-slider\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-slider\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-slider\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-slider\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-slider\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-spinner\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-spinner\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-spinner\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-spinner\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-spinner\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-spinner\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-spinner\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-spinner\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-spinner\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-spinner\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-spinner\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-spinner\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-spinner\paper-spinner.css">
+ <Link>Resources\dashboard-ui\bower_components\paper-spinner\paper-spinner.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-spinner\paper-spinner.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-spinner\paper-spinner.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-spinner\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-spinner\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-spinner\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-spinner\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-spinner\test\paper-spinner.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-spinner\test\paper-spinner.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\color.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\color.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\default-theme.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\default-theme.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\demo-pages.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\demo-pages.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\demo.css">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\demo.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\paper-styles-classes.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\paper-styles-classes.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\paper-styles.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\paper-styles.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\shadow.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\shadow.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\typography.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\typography.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\classes\global.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\classes\global.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\classes\shadow-layout.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\classes\shadow-layout.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\classes\shadow.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\classes\shadow.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\classes\typography.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\classes\typography.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-styles\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-styles\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-tabs\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-tabs\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-tabs\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-tabs\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-tabs\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-tabs\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-tabs\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-tabs\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-tabs\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-tabs\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-tabs\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-tabs\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-tabs\paper-tab.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-tabs\paper-tab.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-tabs\paper-tabs-icons.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-tabs\paper-tabs-icons.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-tabs\paper-tabs.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-tabs\paper-tabs.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-tabs\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-tabs\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-tabs\test\attr-for-selected.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-tabs\test\attr-for-selected.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-tabs\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-tabs\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-tabs\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-tabs\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toast\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-toast\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toast\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-toast\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toast\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-toast\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toast\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-toast\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toast\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-toast\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toast\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-toast\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toast\paper-toast.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-toast\paper-toast.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toast\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-toast\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toggle-button\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-toggle-button\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toggle-button\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\paper-toggle-button\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toggle-button\README.md">
+ <Link>Resources\dashboard-ui\bower_components\paper-toggle-button\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toggle-button\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\paper-toggle-button\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toggle-button\hero.svg">
+ <Link>Resources\dashboard-ui\bower_components\paper-toggle-button\hero.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toggle-button\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-toggle-button\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toggle-button\paper-toggle-button.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-toggle-button\paper-toggle-button.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toggle-button\demo\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-toggle-button\demo\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toggle-button\test\basic.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-toggle-button\test\basic.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\paper-toggle-button\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\paper-toggle-button\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\polymer\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\polymer\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\polymer\LICENSE.txt">
+ <Link>Resources\dashboard-ui\bower_components\polymer\LICENSE.txt</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\polymer\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\polymer\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\polymer\polymer-micro.html">
+ <Link>Resources\dashboard-ui\bower_components\polymer\polymer-micro.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\polymer\polymer-mini.html">
+ <Link>Resources\dashboard-ui\bower_components\polymer\polymer-mini.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\polymer\polymer.html">
+ <Link>Resources\dashboard-ui\bower_components\polymer\polymer.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\promise-polyfill\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\promise-polyfill\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\promise-polyfill\Gruntfile.js">
+ <Link>Resources\dashboard-ui\bower_components\promise-polyfill\Gruntfile.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\promise-polyfill\LICENSE">
+ <Link>Resources\dashboard-ui\bower_components\promise-polyfill\LICENSE</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\promise-polyfill\Promise.js">
+ <Link>Resources\dashboard-ui\bower_components\promise-polyfill\Promise.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\promise-polyfill\Promise.min.js">
+ <Link>Resources\dashboard-ui\bower_components\promise-polyfill\Promise.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\promise-polyfill\README.md">
+ <Link>Resources\dashboard-ui\bower_components\promise-polyfill\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\promise-polyfill\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\promise-polyfill\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\promise-polyfill\jasmine.json">
+ <Link>Resources\dashboard-ui\bower_components\promise-polyfill\jasmine.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\promise-polyfill\package.json">
+ <Link>Resources\dashboard-ui\bower_components\promise-polyfill\package.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\requirejs\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\requirejs\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\requirejs\README.md">
+ <Link>Resources\dashboard-ui\bower_components\requirejs\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\requirejs\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\requirejs\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\requirejs\require.js">
+ <Link>Resources\dashboard-ui\bower_components\requirejs\require.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\.gitattributes">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\.gitattributes</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\.gitignore">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\.gitignore</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\README.md">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\index.html">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\demo\bagpakk.min.css">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\demo\bagpakk.min.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\demo\fork.png">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\demo\fork.png</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\demo\normalize.css">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\demo\normalize.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\demo\style.css">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\demo\style.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\grunt\.jshintrc">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\grunt\.jshintrc</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\grunt\README.md">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\grunt\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\grunt\gruntfile.js">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\grunt\gruntfile.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\grunt\package.json">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\grunt\package.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\lib\ios-orientationchange-fix.js">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\lib\ios-orientationchange-fix.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\lib\jquery-2.1.0.min.js">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\lib\jquery-2.1.0.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\scss\swipebox.scss">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\scss\swipebox.scss</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\src\css\swipebox.css">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\src\css\swipebox.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\src\css\swipebox.min.css">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\src\css\swipebox.min.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\src\img\icons.png">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\src\img\icons.png</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\src\img\icons.svg">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\src\img\icons.svg</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\src\img\loader.gif">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\src\img\loader.gif</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\src\js\jquery.swipebox.js">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\src\js\jquery.swipebox.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\swipebox\src\js\jquery.swipebox.min.js">
+ <Link>Resources\dashboard-ui\bower_components\swipebox\src\js\jquery.swipebox.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\velocity\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\velocity\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\velocity\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\velocity\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\velocity\velocity.js">
+ <Link>Resources\dashboard-ui\bower_components\velocity\velocity.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\velocity\velocity.min.js">
+ <Link>Resources\dashboard-ui\bower_components\velocity\velocity.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\velocity\velocity.ui.js">
+ <Link>Resources\dashboard-ui\bower_components\velocity\velocity.ui.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\velocity\velocity.ui.min.js">
+ <Link>Resources\dashboard-ui\bower_components\velocity\velocity.ui.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\velocity\test\bluebird.js">
+ <Link>Resources\dashboard-ui\bower_components\velocity\test\bluebird.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\velocity\test\index.html">
+ <Link>Resources\dashboard-ui\bower_components\velocity\test\index.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\velocity\test\jquery-1.11.1.js">
+ <Link>Resources\dashboard-ui\bower_components\velocity\test\jquery-1.11.1.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\velocity\test\jquery-1.4.3.js">
+ <Link>Resources\dashboard-ui\bower_components\velocity\test\jquery-1.4.3.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\velocity\test\q.js">
+ <Link>Resources\dashboard-ui\bower_components\velocity\test\q.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\velocity\test\qunit-1.14.0.css">
+ <Link>Resources\dashboard-ui\bower_components\velocity\test\qunit-1.14.0.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\velocity\test\qunit-1.14.0.js">
+ <Link>Resources\dashboard-ui\bower_components\velocity\test\qunit-1.14.0.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\velocity\test\when.js">
+ <Link>Resources\dashboard-ui\bower_components\velocity\test\when.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\velocity\test\zepto.js">
+ <Link>Resources\dashboard-ui\bower_components\velocity\test\zepto.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\web-animations-js\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\COPYING">
+ <Link>Resources\dashboard-ui\bower_components\web-animations-js\COPYING</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\History.md">
+ <Link>Resources\dashboard-ui\bower_components\web-animations-js\History.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\README.md">
+ <Link>Resources\dashboard-ui\bower_components\web-animations-js\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\web-animations-js\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\web-animations-next-lite.min.js">
+ <Link>Resources\dashboard-ui\bower_components\web-animations-js\web-animations-next-lite.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\web-animations-next-lite.min.js.map">
+ <Link>Resources\dashboard-ui\bower_components\web-animations-js\web-animations-next-lite.min.js.map</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\web-animations-next.min.js">
+ <Link>Resources\dashboard-ui\bower_components\web-animations-js\web-animations-next.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\web-animations-next.min.js.map">
+ <Link>Resources\dashboard-ui\bower_components\web-animations-js\web-animations-next.min.js.map</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\web-animations.html">
+ <Link>Resources\dashboard-ui\bower_components\web-animations-js\web-animations.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\web-animations.min.js">
+ <Link>Resources\dashboard-ui\bower_components\web-animations-js\web-animations.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\web-animations.min.js.gz">
+ <Link>Resources\dashboard-ui\bower_components\web-animations-js\web-animations.min.js.gz</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\web-animations.min.js.map">
+ <Link>Resources\dashboard-ui\bower_components\web-animations-js\web-animations.min.js.map</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\.bower.json">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\.bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\CustomElements.js">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\CustomElements.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\CustomElements.min.js">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\CustomElements.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\HTMLImports.js">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\HTMLImports.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\HTMLImports.min.js">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\HTMLImports.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\MutationObserver.js">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\MutationObserver.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\MutationObserver.min.js">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\MutationObserver.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\README.md">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\ShadowDOM.js">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\ShadowDOM.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\ShadowDOM.min.js">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\ShadowDOM.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\bower.json">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\bower.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\package.json">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\package.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\webcomponents-lite.js">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\webcomponents-lite.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\webcomponents-lite.min.js">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\webcomponents-lite.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\webcomponents.js">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\webcomponents.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\webcomponents.min.js">
+ <Link>Resources\dashboard-ui\bower_components\webcomponentsjs\webcomponents.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\actionsheet.js">
+ <Link>Resources\dashboard-ui\cordova\actionsheet.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\back.js">
+ <Link>Resources\dashboard-ui\cordova\back.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\chromecast.js">
+ <Link>Resources\dashboard-ui\cordova\chromecast.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\connectsdk.js">
+ <Link>Resources\dashboard-ui\cordova\connectsdk.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\externalplayer.js">
+ <Link>Resources\dashboard-ui\cordova\externalplayer.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\filesystem.js">
+ <Link>Resources\dashboard-ui\cordova\filesystem.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\generaldevice.js">
+ <Link>Resources\dashboard-ui\cordova\generaldevice.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\iap.js">
+ <Link>Resources\dashboard-ui\cordova\iap.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\imagestore.js">
+ <Link>Resources\dashboard-ui\cordova\imagestore.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\registrationservices.js">
+ <Link>Resources\dashboard-ui\cordova\registrationservices.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\remotecontrols.js">
+ <Link>Resources\dashboard-ui\cordova\remotecontrols.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\searchmenu.js">
+ <Link>Resources\dashboard-ui\cordova\searchmenu.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\serverdiscovery.js">
+ <Link>Resources\dashboard-ui\cordova\serverdiscovery.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\sharingwidget.js">
+ <Link>Resources\dashboard-ui\cordova\sharingwidget.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\volume.js">
+ <Link>Resources\dashboard-ui\cordova\volume.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\wakeonlan.js">
+ <Link>Resources\dashboard-ui\cordova\wakeonlan.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\android\androidcredentials.js">
+ <Link>Resources\dashboard-ui\cordova\android\androidcredentials.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\android\appstorage.js">
+ <Link>Resources\dashboard-ui\cordova\android\appstorage.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\android\filesystem.js">
+ <Link>Resources\dashboard-ui\cordova\android\filesystem.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\android\iap.js">
+ <Link>Resources\dashboard-ui\cordova\android\iap.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\android\immersive.js">
+ <Link>Resources\dashboard-ui\cordova\android\immersive.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\android\localassetmanager.js">
+ <Link>Resources\dashboard-ui\cordova\android\localassetmanager.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\android\localsync.js">
+ <Link>Resources\dashboard-ui\cordova\android\localsync.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\android\logging.js">
+ <Link>Resources\dashboard-ui\cordova\android\logging.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\android\mediasession.js">
+ <Link>Resources\dashboard-ui\cordova\android\mediasession.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\android\nativedirectorychooser.js">
+ <Link>Resources\dashboard-ui\cordova\android\nativedirectorychooser.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\android\vlcplayer.js">
+ <Link>Resources\dashboard-ui\cordova\android\vlcplayer.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\cordova\ios\orientation.js">
+ <Link>Resources\dashboard-ui\cordova\ios\orientation.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\.DS_Store">
+ <Link>Resources\dashboard-ui\css\.DS_Store</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\card.css">
<Link>Resources\dashboard-ui\css\card.css</Link>
</BundleResource>
@@ -661,9 +3271,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\detailtable.css">
<Link>Resources\dashboard-ui\css\detailtable.css</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\icons.css">
- <Link>Resources\dashboard-ui\css\icons.css</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\librarybrowser.css">
<Link>Resources\dashboard-ui\css\librarybrowser.css</Link>
</BundleResource>
@@ -691,8 +3298,8 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\nowplaying.css">
<Link>Resources\dashboard-ui\css\nowplaying.css</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\pluginupdates.css">
- <Link>Resources\dashboard-ui\css\pluginupdates.css</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\nowplayingbar.css">
+ <Link>Resources\dashboard-ui\css\nowplayingbar.css</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\remotecontrol.css">
<Link>Resources\dashboard-ui\css\remotecontrol.css</Link>
@@ -712,20 +3319,80 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\Montserrat.woff">
<Link>Resources\dashboard-ui\css\fonts\Montserrat.woff</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\RobotoBold.woff">
- <Link>Resources\dashboard-ui\css\fonts\RobotoBold.woff</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\-L14Jk06m6pUHB-5mXQQnRJtnKITppOI_IvcXXDNrsc.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\-L14Jk06m6pUHB-5mXQQnRJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\0eC6fl06luXEYWpBSJvXCBJtnKITppOI_IvcXXDNrsc.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\0eC6fl06luXEYWpBSJvXCBJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\2tsd397wLxj96qwHyNIkxPesZW2xOQ-xsNqO47m55DA.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\2tsd397wLxj96qwHyNIkxPesZW2xOQ-xsNqO47m55DA.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\97uahxiqZRoncBaCEI3aWxJtnKITppOI_IvcXXDNrsc.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\97uahxiqZRoncBaCEI3aWxJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\CWB0XYA8bzo0kSThX0UTuA.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\CWB0XYA8bzo0kSThX0UTuA.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\Fcx7Wwv8OzT71A3E1XOAjvesZW2xOQ-xsNqO47m55DA.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\Fcx7Wwv8OzT71A3E1XOAjvesZW2xOQ-xsNqO47m55DA.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\Fl4y0QdOxyyTHEGMXX8kcRJtnKITppOI_IvcXXDNrsc.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\Fl4y0QdOxyyTHEGMXX8kcRJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\Hgo13k-tfSpn0qi1SFdUfVtXRa8TVwTICgirnJhmVJw.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\Hgo13k-tfSpn0qi1SFdUfVtXRa8TVwTICgirnJhmVJw.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\I3S1wsgSg9YCurV6PUkTORJtnKITppOI_IvcXXDNrsc.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\I3S1wsgSg9YCurV6PUkTORJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\NYDWBdD4gIq26G5XYbHsFBJtnKITppOI_IvcXXDNrsc.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\NYDWBdD4gIq26G5XYbHsFBJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\Pru33qjShpZSmG3z6VYwnRJtnKITppOI_IvcXXDNrsc.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\Pru33qjShpZSmG3z6VYwnRJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\RobotoBold.woff">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\RobotoBold.woff</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\RobotoLight.woff">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\RobotoLight.woff</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\RobotoMedium.woff">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\RobotoMedium.woff</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\RobotoRegular.woff">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\RobotoRegular.woff</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\RobotoThin.woff">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\RobotoThin.woff</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\RobotoLight.woff">
- <Link>Resources\dashboard-ui\css\fonts\RobotoLight.woff</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\RxZJdnzeo3R5zSexge8UUVtXRa8TVwTICgirnJhmVJw.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\RxZJdnzeo3R5zSexge8UUVtXRa8TVwTICgirnJhmVJw.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\RobotoMedium.woff">
- <Link>Resources\dashboard-ui\css\fonts\RobotoMedium.woff</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\VvXUGKZXbHtX_S_VCTLpGhTbgVql8nDJpwnrE27mub0.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\VvXUGKZXbHtX_S_VCTLpGhTbgVql8nDJpwnrE27mub0.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\RobotoRegular.woff">
- <Link>Resources\dashboard-ui\css\fonts\RobotoRegular.woff</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\aZMswpodYeVhtRvuABJWvBTbgVql8nDJpwnrE27mub0.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\aZMswpodYeVhtRvuABJWvBTbgVql8nDJpwnrE27mub0.woff2</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\RobotoThin.woff">
- <Link>Resources\dashboard-ui\css\fonts\RobotoThin.woff</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\d-6IYplOFocCacKzxwXSOFtXRa8TVwTICgirnJhmVJw.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\d-6IYplOFocCacKzxwXSOFtXRa8TVwTICgirnJhmVJw.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\e7MeVAyvogMqFwwl61PKhBTbgVql8nDJpwnrE27mub0.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\e7MeVAyvogMqFwwl61PKhBTbgVql8nDJpwnrE27mub0.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\frNV30OaYdlFRtH2VnZZdhTbgVql8nDJpwnrE27mub0.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\frNV30OaYdlFRtH2VnZZdhTbgVql8nDJpwnrE27mub0.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\gwVJDERN2Amz39wrSoZ7FxTbgVql8nDJpwnrE27mub0.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\gwVJDERN2Amz39wrSoZ7FxTbgVql8nDJpwnrE27mub0.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\oOeFwZNlrTefzLYmlVV1UBJtnKITppOI_IvcXXDNrsc.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\oOeFwZNlrTefzLYmlVV1UBJtnKITppOI_IvcXXDNrsc.woff2</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\fonts\roboto\ty9dfvLAziwdqQ2dHoyjphTbgVql8nDJpwnrE27mub0.woff2">
+ <Link>Resources\dashboard-ui\css\fonts\roboto\ty9dfvLAziwdqQ2dHoyjphTbgVql8nDJpwnrE27mub0.woff2</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\checkmarkblack.png">
<Link>Resources\dashboard-ui\css\images\checkmarkblack.png</Link>
@@ -763,6 +3430,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\splash.jpg">
<Link>Resources\dashboard-ui\css\images\splash.jpg</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\splash720.jpg">
+ <Link>Resources\dashboard-ui\css\images\splash720.jpg</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\stars.png">
<Link>Resources\dashboard-ui\css\images\stars.png</Link>
</BundleResource>
@@ -793,9 +3463,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\clients\chrome.png">
<Link>Resources\dashboard-ui\css\images\clients\chrome.png</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\clients\chrome_companion.png">
- <Link>Resources\dashboard-ui\css\images\clients\chrome_companion.png</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\clients\chromecast.png">
<Link>Resources\dashboard-ui\css\images\clients\chromecast.png</Link>
</BundleResource>
@@ -817,15 +3484,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\clients\kodi.png">
<Link>Resources\dashboard-ui\css\images\clients\kodi.png</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\clients\mb.png">
- <Link>Resources\dashboard-ui\css\images\clients\mb.png</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\clients\mbc.png">
<Link>Resources\dashboard-ui\css\images\clients\mbc.png</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\clients\nuvue.png">
- <Link>Resources\dashboard-ui\css\images\clients\nuvue.png</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\clients\playstore.png">
<Link>Resources\dashboard-ui\css\images\clients\playstore.png</Link>
</BundleResource>
@@ -859,51 +3520,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\editor\missingtrailer.png">
<Link>Resources\dashboard-ui\css\images\editor\missingtrailer.png</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\icons\audiocd.png">
- <Link>Resources\dashboard-ui\css\images\icons\audiocd.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\icons\ellipsis-v.png">
- <Link>Resources\dashboard-ui\css\images\icons\ellipsis-v.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\icons\expand.png">
- <Link>Resources\dashboard-ui\css\images\icons\expand.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\icons\filter.png">
- <Link>Resources\dashboard-ui\css\images\icons\filter.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\icons\mute.png">
- <Link>Resources\dashboard-ui\css\images\icons\mute.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\icons\nexttrack.png">
- <Link>Resources\dashboard-ui\css\images\icons\nexttrack.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\icons\pause.png">
- <Link>Resources\dashboard-ui\css\images\icons\pause.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\icons\play.png">
- <Link>Resources\dashboard-ui\css\images\icons\play.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\icons\previoustrack.png">
- <Link>Resources\dashboard-ui\css\images\icons\previoustrack.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\icons\remote.png">
- <Link>Resources\dashboard-ui\css\images\icons\remote.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\icons\sort.png">
- <Link>Resources\dashboard-ui\css\images\icons\sort.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\icons\stop.png">
- <Link>Resources\dashboard-ui\css\images\icons\stop.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\icons\subtitles.png">
- <Link>Resources\dashboard-ui\css\images\icons\subtitles.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\icons\volumedown.png">
- <Link>Resources\dashboard-ui\css\images\icons\volumedown.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\icons\volumeup.png">
- <Link>Resources\dashboard-ui\css\images\icons\volumeup.png</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\detail\audio.png">
<Link>Resources\dashboard-ui\css\images\items\detail\audio.png</Link>
</BundleResource>
@@ -919,45 +3535,18 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\detail\video.png">
<Link>Resources\dashboard-ui\css\images\items\detail\video.png</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\list\audio.png">
- <Link>Resources\dashboard-ui\css\images\items\list\audio.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\list\audiocollection.png">
- <Link>Resources\dashboard-ui\css\images\items\list\audiocollection.png</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\list\chapter.png">
<Link>Resources\dashboard-ui\css\images\items\list\chapter.png</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\list\collection.png">
<Link>Resources\dashboard-ui\css\images\items\list\collection.png</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\list\game.png">
- <Link>Resources\dashboard-ui\css\images\items\list\game.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\list\gamecollection.png">
- <Link>Resources\dashboard-ui\css\images\items\list\gamecollection.png</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\list\person.png">
<Link>Resources\dashboard-ui\css\images\items\list\person.png</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\list\remotesearch.png">
<Link>Resources\dashboard-ui\css\images\items\list\remotesearch.png</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\list\video.png">
- <Link>Resources\dashboard-ui\css\images\items\list\video.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\searchhintsv2\film.png">
- <Link>Resources\dashboard-ui\css\images\items\searchhintsv2\film.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\searchhintsv2\game.png">
- <Link>Resources\dashboard-ui\css\images\items\searchhintsv2\game.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\searchhintsv2\music.png">
- <Link>Resources\dashboard-ui\css\images\items\searchhintsv2\music.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\searchhintsv2\person.png">
- <Link>Resources\dashboard-ui\css\images\items\searchhintsv2\person.png</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\items\searchhintsv2\tv.png">
<Link>Resources\dashboard-ui\css\images\items\searchhintsv2\tv.png</Link>
</BundleResource>
@@ -1087,6 +3676,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\aboutpage.js">
<Link>Resources\dashboard-ui\scripts\aboutpage.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\actionsheet.js">
+ <Link>Resources\dashboard-ui\scripts\actionsheet.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\addpluginpage.js">
<Link>Resources\dashboard-ui\scripts\addpluginpage.js</Link>
</BundleResource>
@@ -1129,6 +3721,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\cinemamodeconfiguration.js">
<Link>Resources\dashboard-ui\scripts\cinemamodeconfiguration.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\collectioneditor.js">
+ <Link>Resources\dashboard-ui\scripts\collectioneditor.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\connectlogin.js">
<Link>Resources\dashboard-ui\scripts\connectlogin.js</Link>
</BundleResource>
@@ -1216,8 +3811,14 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\gamesystemspage.js">
<Link>Resources\dashboard-ui\scripts\gamesystemspage.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\homelatest.js">
- <Link>Resources\dashboard-ui\scripts\homelatest.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\globalize.js">
+ <Link>Resources\dashboard-ui\scripts\globalize.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\homenextup.js">
+ <Link>Resources\dashboard-ui\scripts\homenextup.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\homeupcoming.js">
+ <Link>Resources\dashboard-ui\scripts\homeupcoming.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\htmlmediarenderer.js">
<Link>Resources\dashboard-ui\scripts\htmlmediarenderer.js</Link>
@@ -1264,18 +3865,15 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvguide.js">
<Link>Resources\dashboard-ui\scripts\livetvguide.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvguideprovider.js">
+ <Link>Resources\dashboard-ui\scripts\livetvguideprovider.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvitems.js">
<Link>Resources\dashboard-ui\scripts\livetvitems.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvnewrecording.js">
<Link>Resources\dashboard-ui\scripts\livetvnewrecording.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvprogram.js">
- <Link>Resources\dashboard-ui\scripts\livetvprogram.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvrecording.js">
- <Link>Resources\dashboard-ui\scripts\livetvrecording.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvrecordinglist.js">
<Link>Resources\dashboard-ui\scripts\livetvrecordinglist.js</Link>
</BundleResource>
@@ -1303,6 +3901,15 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvtimers.js">
<Link>Resources\dashboard-ui\scripts\livetvtimers.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvtunerprovider-hdhomerun.js">
+ <Link>Resources\dashboard-ui\scripts\livetvtunerprovider-hdhomerun.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvtunerprovider-m3u.js">
+ <Link>Resources\dashboard-ui\scripts\livetvtunerprovider-m3u.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\localsync.js">
+ <Link>Resources\dashboard-ui\scripts\localsync.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\loginpage.js">
<Link>Resources\dashboard-ui\scripts\loginpage.js</Link>
</BundleResource>
@@ -1381,15 +3988,21 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mypreferencesdisplay.js">
<Link>Resources\dashboard-ui\scripts\mypreferencesdisplay.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mypreferenceshome.js">
+ <Link>Resources\dashboard-ui\scripts\mypreferenceshome.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mypreferenceslanguages.js">
<Link>Resources\dashboard-ui\scripts\mypreferenceslanguages.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mypreferenceswebclient.js">
- <Link>Resources\dashboard-ui\scripts\mypreferenceswebclient.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\myprofile.js">
<Link>Resources\dashboard-ui\scripts\myprofile.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mysync.js">
+ <Link>Resources\dashboard-ui\scripts\mysync.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\mysyncsettings.js">
+ <Link>Resources\dashboard-ui\scripts\mysyncsettings.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\notificationlist.js">
<Link>Resources\dashboard-ui\scripts\notificationlist.js</Link>
</BundleResource>
@@ -1453,6 +4066,12 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\search.js">
<Link>Resources\dashboard-ui\scripts\search.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\searchmenu.js">
+ <Link>Resources\dashboard-ui\scripts\searchmenu.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\secondaryitems.js">
+ <Link>Resources\dashboard-ui\scripts\secondaryitems.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\sections.js">
<Link>Resources\dashboard-ui\scripts\sections.js</Link>
</BundleResource>
@@ -1462,9 +4081,21 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\serversecurity.js">
<Link>Resources\dashboard-ui\scripts\serversecurity.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\shared.js">
+ <Link>Resources\dashboard-ui\scripts\shared.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\sharingmanager.js">
+ <Link>Resources\dashboard-ui\scripts\sharingmanager.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\sharingwidget.js">
+ <Link>Resources\dashboard-ui\scripts\sharingwidget.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\site.js">
<Link>Resources\dashboard-ui\scripts\site.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\slideshow.js">
+ <Link>Resources\dashboard-ui\scripts\slideshow.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\songs.js">
<Link>Resources\dashboard-ui\scripts\songs.js</Link>
</BundleResource>
@@ -1540,6 +4171,12 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardfinishpage.js">
<Link>Resources\dashboard-ui\scripts\wizardfinishpage.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardlivetvguide.js">
+ <Link>Resources\dashboard-ui\scripts\wizardlivetvguide.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardlivetvtuner.js">
+ <Link>Resources\dashboard-ui\scripts\wizardlivetvtuner.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizardservice.js">
<Link>Resources\dashboard-ui\scripts\wizardservice.js</Link>
</BundleResource>
@@ -1552,164 +4189,257 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\wizarduserpage.js">
<Link>Resources\dashboard-ui\scripts\wizarduserpage.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\browser.js">
- <Link>Resources\dashboard-ui\thirdparty\browser.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\ar.json">
+ <Link>Resources\dashboard-ui\strings\html\ar.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cast_sender.js">
- <Link>Resources\dashboard-ui\thirdparty\cast_sender.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\bg-BG.json">
+ <Link>Resources\dashboard-ui\strings\html\bg-BG.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\fastclick.js">
- <Link>Resources\dashboard-ui\thirdparty\fastclick.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\ca.json">
+ <Link>Resources\dashboard-ui\strings\html\ca.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\filesystem.js">
- <Link>Resources\dashboard-ui\thirdparty\filesystem.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\cs.json">
+ <Link>Resources\dashboard-ui\strings\html\cs.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\headroom.js">
- <Link>Resources\dashboard-ui\thirdparty\headroom.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\da.json">
+ <Link>Resources\dashboard-ui\strings\html\da.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquery-2.1.1.min.js">
- <Link>Resources\dashboard-ui\thirdparty\jquery-2.1.1.min.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\de.json">
+ <Link>Resources\dashboard-ui\strings\html\de.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquery.unveil-custom.js">
- <Link>Resources\dashboard-ui\thirdparty\jquery.unveil-custom.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\el.json">
+ <Link>Resources\dashboard-ui\strings\html\el.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\en-GB.json">
+ <Link>Resources\dashboard-ui\strings\html\en-GB.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\masonry.pkgd.min.js">
- <Link>Resources\dashboard-ui\thirdparty\masonry.pkgd.min.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\en-US.json">
+ <Link>Resources\dashboard-ui\strings\html\en-US.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\require.js">
- <Link>Resources\dashboard-ui\thirdparty\require.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\es-AR.json">
+ <Link>Resources\dashboard-ui\strings\html\es-AR.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\requirecss.js">
- <Link>Resources\dashboard-ui\thirdparty\requirecss.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\es-MX.json">
+ <Link>Resources\dashboard-ui\strings\html\es-MX.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\velocity.min.js">
- <Link>Resources\dashboard-ui\thirdparty\velocity.min.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\es.json">
+ <Link>Resources\dashboard-ui\strings\html\es.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\ajax.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\ajax.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\fi.json">
+ <Link>Resources\dashboard-ui\strings\html\fi.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\apiclient.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\apiclient.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\fr.json">
+ <Link>Resources\dashboard-ui\strings\html\fr.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\connectionmanager.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\connectionmanager.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\gsw.json">
+ <Link>Resources\dashboard-ui\strings\html\gsw.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\connectservice.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\connectservice.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\he.json">
+ <Link>Resources\dashboard-ui\strings\html\he.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\credentials.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\credentials.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\hr.json">
+ <Link>Resources\dashboard-ui\strings\html\hr.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\deferred.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\deferred.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\it.json">
+ <Link>Resources\dashboard-ui\strings\html\it.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\device.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\device.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\kk.json">
+ <Link>Resources\dashboard-ui\strings\html\kk.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\events.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\events.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\ko.json">
+ <Link>Resources\dashboard-ui\strings\html\ko.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\localassetmanager.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\localassetmanager.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\ms.json">
+ <Link>Resources\dashboard-ui\strings\html\ms.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\logger.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\logger.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\nb.json">
+ <Link>Resources\dashboard-ui\strings\html\nb.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\md5.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\md5.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\nl.json">
+ <Link>Resources\dashboard-ui\strings\html\nl.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\serverdiscovery.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\serverdiscovery.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\pl.json">
+ <Link>Resources\dashboard-ui\strings\html\pl.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\sha1.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\sha1.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\pt-BR.json">
+ <Link>Resources\dashboard-ui\strings\html\pt-BR.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\store.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\store.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\pt-PT.json">
+ <Link>Resources\dashboard-ui\strings\html\pt-PT.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\wakeonlan.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\wakeonlan.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\ro.json">
+ <Link>Resources\dashboard-ui\strings\html\ro.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\alt\ajax.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\alt\ajax.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\ru.json">
+ <Link>Resources\dashboard-ui\strings\html\ru.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\alt\bean.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\alt\bean.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\server.json">
+ <Link>Resources\dashboard-ui\strings\html\server.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\alt\deferred.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\alt\deferred.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\sl-SI.json">
+ <Link>Resources\dashboard-ui\strings\html\sl-SI.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\apiclient\alt\events.js">
- <Link>Resources\dashboard-ui\thirdparty\apiclient\alt\events.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\sv.json">
+ <Link>Resources\dashboard-ui\strings\html\sv.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\back.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\back.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\tr.json">
+ <Link>Resources\dashboard-ui\strings\html\tr.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\chromecast.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\chromecast.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\uk.json">
+ <Link>Resources\dashboard-ui\strings\html\uk.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\connectsdk.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\connectsdk.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\vi.json">
+ <Link>Resources\dashboard-ui\strings\html\vi.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\externalplayer.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\externalplayer.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\zh-CN.json">
+ <Link>Resources\dashboard-ui\strings\html\zh-CN.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\filesystem.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\filesystem.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\html\zh-TW.json">
+ <Link>Resources\dashboard-ui\strings\html\zh-TW.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\generaldevice.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\generaldevice.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\ar.json">
+ <Link>Resources\dashboard-ui\strings\javascript\ar.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\iap.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\iap.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\be-BY.json">
+ <Link>Resources\dashboard-ui\strings\javascript\be-BY.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\imagestore.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\imagestore.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\bg-BG.json">
+ <Link>Resources\dashboard-ui\strings\javascript\bg-BG.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\registrationservices.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\registrationservices.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\ca.json">
+ <Link>Resources\dashboard-ui\strings\javascript\ca.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\remotecontrols.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\remotecontrols.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\cs.json">
+ <Link>Resources\dashboard-ui\strings\javascript\cs.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\serverdiscovery.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\serverdiscovery.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\da.json">
+ <Link>Resources\dashboard-ui\strings\javascript\da.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\volume.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\volume.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\de.json">
+ <Link>Resources\dashboard-ui\strings\javascript\de.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\wakeonlan.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\wakeonlan.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\el.json">
+ <Link>Resources\dashboard-ui\strings\javascript\el.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\android\androidcredentials.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\android\androidcredentials.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\en-GB.json">
+ <Link>Resources\dashboard-ui\strings\javascript\en-GB.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\android\appstorage.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\android\appstorage.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\en-US.json">
+ <Link>Resources\dashboard-ui\strings\javascript\en-US.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\android\filesystem.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\android\filesystem.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\es-AR.json">
+ <Link>Resources\dashboard-ui\strings\javascript\es-AR.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\android\iap.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\android\iap.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\es-MX.json">
+ <Link>Resources\dashboard-ui\strings\javascript\es-MX.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\android\immersive.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\android\immersive.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\es-VE.json">
+ <Link>Resources\dashboard-ui\strings\javascript\es-VE.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\android\localassetmanager.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\android\localassetmanager.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\es.json">
+ <Link>Resources\dashboard-ui\strings\javascript\es.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\android\mediasession.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\android\mediasession.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\fi.json">
+ <Link>Resources\dashboard-ui\strings\javascript\fi.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\android\nativedirectorychooser.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\android\nativedirectorychooser.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\fr.json">
+ <Link>Resources\dashboard-ui\strings\javascript\fr.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\android\vlcplayer.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\android\vlcplayer.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\gsw.json">
+ <Link>Resources\dashboard-ui\strings\javascript\gsw.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cordova\ios\orientation.js">
- <Link>Resources\dashboard-ui\thirdparty\cordova\ios\orientation.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\he.json">
+ <Link>Resources\dashboard-ui\strings\javascript\he.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\hr.json">
+ <Link>Resources\dashboard-ui\strings\javascript\hr.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\hu.json">
+ <Link>Resources\dashboard-ui\strings\javascript\hu.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\it.json">
+ <Link>Resources\dashboard-ui\strings\javascript\it.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\javascript.json">
+ <Link>Resources\dashboard-ui\strings\javascript\javascript.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\kk.json">
+ <Link>Resources\dashboard-ui\strings\javascript\kk.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\ms.json">
+ <Link>Resources\dashboard-ui\strings\javascript\ms.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\nb.json">
+ <Link>Resources\dashboard-ui\strings\javascript\nb.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\nl.json">
+ <Link>Resources\dashboard-ui\strings\javascript\nl.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\pl.json">
+ <Link>Resources\dashboard-ui\strings\javascript\pl.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\pt-BR.json">
+ <Link>Resources\dashboard-ui\strings\javascript\pt-BR.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\pt-PT.json">
+ <Link>Resources\dashboard-ui\strings\javascript\pt-PT.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\ro.json">
+ <Link>Resources\dashboard-ui\strings\javascript\ro.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\ru.json">
+ <Link>Resources\dashboard-ui\strings\javascript\ru.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\sl-SI.json">
+ <Link>Resources\dashboard-ui\strings\javascript\sl-SI.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\sv.json">
+ <Link>Resources\dashboard-ui\strings\javascript\sv.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\tr.json">
+ <Link>Resources\dashboard-ui\strings\javascript\tr.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\uk.json">
+ <Link>Resources\dashboard-ui\strings\javascript\uk.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\vi.json">
+ <Link>Resources\dashboard-ui\strings\javascript\vi.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\zh-CN.json">
+ <Link>Resources\dashboard-ui\strings\javascript\zh-CN.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\javascript\zh-TW.json">
+ <Link>Resources\dashboard-ui\strings\javascript\zh-TW.json</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\themes\android.css">
+ <Link>Resources\dashboard-ui\themes\android.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\themes\ios.css">
+ <Link>Resources\dashboard-ui\themes\ios.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\browser.js">
+ <Link>Resources\dashboard-ui\thirdparty\browser.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\cast_sender.js">
+ <Link>Resources\dashboard-ui\thirdparty\cast_sender.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\emby-icons.html">
+ <Link>Resources\dashboard-ui\thirdparty\emby-icons.html</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\filesystem.js">
+ <Link>Resources\dashboard-ui\thirdparty\filesystem.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\headroom.js">
+ <Link>Resources\dashboard-ui\thirdparty\headroom.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquery.unveil-custom.js">
+ <Link>Resources\dashboard-ui\thirdparty\jquery.unveil-custom.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\paper-button-style.css">
+ <Link>Resources\dashboard-ui\thirdparty\paper-button-style.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\paper-ie10.css">
+ <Link>Resources\dashboard-ui\thirdparty\paper-ie10.css</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\fontawesome\css\font-awesome.css">
<Link>Resources\dashboard-ui\thirdparty\fontawesome\css\font-awesome.css</Link>
@@ -1738,15 +4468,54 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\fontawesome\fonts\fontawesome-webfont.woff2">
<Link>Resources\dashboard-ui\thirdparty\fontawesome\fonts\fontawesome-webfont.woff2</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\custombuild.txt">
+ <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\custombuild.txt</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.popup.css">
+ <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.popup.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.popup.js">
+ <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.popup.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.slider.css">
+ <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.slider.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.slider.js">
+ <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.slider.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.table.css">
+ <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.table.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.table.js">
+ <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.table.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile-1.4.5.min.css">
<Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile-1.4.5.min.css</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile-1.4.5.min.js">
- <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile-1.4.5.min.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile-1.4.5.min.map">
<Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile-1.4.5.min.map</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.icons.css">
+ <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.icons.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.js">
+ <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.min.js">
+ <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.min.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.structure.css">
+ <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.structure.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.structure.min.css">
+ <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.structure.min.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.theme.css">
+ <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.theme.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.theme.min.css">
+ <Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.theme.min.css</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\images\ajax-loader.gif">
<Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\images\ajax-loader.gif</Link>
</BundleResource>
@@ -2350,98 +5119,65 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\video-white.svg">
<Link>Resources\dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\video-white.svg</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jstree3.0.8\jstree.min.js">
- <Link>Resources\dashboard-ui\thirdparty\jstree3.0.8\jstree.min.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jstree3.0.8\themes\default\32px.png">
- <Link>Resources\dashboard-ui\thirdparty\jstree3.0.8\themes\default\32px.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jstree3.0.8\themes\default\40px.png">
- <Link>Resources\dashboard-ui\thirdparty\jstree3.0.8\themes\default\40px.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jstree3.0.8\themes\default\style.css">
- <Link>Resources\dashboard-ui\thirdparty\jstree3.0.8\themes\default\style.css</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jstree3.0.8\themes\default\style.min.css">
- <Link>Resources\dashboard-ui\thirdparty\jstree3.0.8\themes\default\style.min.css</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jstree3.0.8\themes\default\throbber.gif">
- <Link>Resources\dashboard-ui\thirdparty\jstree3.0.8\themes\default\throbber.gif</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\materialicons\MaterialIcons-Regular.eot">
- <Link>Resources\dashboard-ui\thirdparty\materialicons\MaterialIcons-Regular.eot</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\materialicons\MaterialIcons-Regular.ttf">
- <Link>Resources\dashboard-ui\thirdparty\materialicons\MaterialIcons-Regular.ttf</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\materialicons\MaterialIcons-Regular.woff">
- <Link>Resources\dashboard-ui\thirdparty\materialicons\MaterialIcons-Regular.woff</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\materialicons\MaterialIcons-Regular.woff2">
- <Link>Resources\dashboard-ui\thirdparty\materialicons\MaterialIcons-Regular.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\materialicons\codepoints.txt">
- <Link>Resources\dashboard-ui\thirdparty\materialicons\codepoints.txt</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\materialicons\style.css">
- <Link>Resources\dashboard-ui\thirdparty\materialicons\style.css</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jstree\themes\default\32px.png">
+ <Link>Resources\dashboard-ui\thirdparty\jstree\themes\default\32px.png</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\polymer\LICENSE.txt">
- <Link>Resources\dashboard-ui\thirdparty\polymer\LICENSE.txt</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jstree\themes\default\40px.png">
+ <Link>Resources\dashboard-ui\thirdparty\jstree\themes\default\40px.png</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\polymer\bower.json">
- <Link>Resources\dashboard-ui\thirdparty\polymer\bower.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jstree\themes\default\style.css">
+ <Link>Resources\dashboard-ui\thirdparty\jstree\themes\default\style.css</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\polymer\polymer-micro.html">
- <Link>Resources\dashboard-ui\thirdparty\polymer\polymer-micro.html</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jstree\themes\default\style.min.css">
+ <Link>Resources\dashboard-ui\thirdparty\jstree\themes\default\style.min.css</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\polymer\polymer-mini.html">
- <Link>Resources\dashboard-ui\thirdparty\polymer\polymer-mini.html</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\jstree\themes\default\throbber.gif">
+ <Link>Resources\dashboard-ui\thirdparty\jstree\themes\default\throbber.gif</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\polymer\polymer.html">
- <Link>Resources\dashboard-ui\thirdparty\polymer\polymer.html</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\social-share-kit-1.0.4\LICENSE">
+ <Link>Resources\dashboard-ui\thirdparty\social-share-kit-1.0.4\LICENSE</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\swipebox-master\css\swipebox.min.css">
- <Link>Resources\dashboard-ui\thirdparty\swipebox-master\css\swipebox.min.css</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\social-share-kit-1.0.4\README.md">
+ <Link>Resources\dashboard-ui\thirdparty\social-share-kit-1.0.4\README.md</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\swipebox-master\img\icons.png">
- <Link>Resources\dashboard-ui\thirdparty\swipebox-master\img\icons.png</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\css\social-share-kit.css">
+ <Link>Resources\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\css\social-share-kit.css</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\swipebox-master\img\icons.svg">
- <Link>Resources\dashboard-ui\thirdparty\swipebox-master\img\icons.svg</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\fonts\social-share-kit.eot">
+ <Link>Resources\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\fonts\social-share-kit.eot</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\swipebox-master\img\loader.gif">
- <Link>Resources\dashboard-ui\thirdparty\swipebox-master\img\loader.gif</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\fonts\social-share-kit.svg">
+ <Link>Resources\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\fonts\social-share-kit.svg</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\swipebox-master\js\jquery.swipebox.min.js">
- <Link>Resources\dashboard-ui\thirdparty\swipebox-master\js\jquery.swipebox.min.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\fonts\social-share-kit.ttf">
+ <Link>Resources\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\fonts\social-share-kit.ttf</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\webcomponentsjs\CustomElements.min.js">
- <Link>Resources\dashboard-ui\thirdparty\webcomponentsjs\CustomElements.min.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\fonts\social-share-kit.woff">
+ <Link>Resources\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\fonts\social-share-kit.woff</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\webcomponentsjs\HTMLImports.min.js">
- <Link>Resources\dashboard-ui\thirdparty\webcomponentsjs\HTMLImports.min.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\js\social-share-kit.js">
+ <Link>Resources\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\js\social-share-kit.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\webcomponentsjs\MutationObserver.min.js">
- <Link>Resources\dashboard-ui\thirdparty\webcomponentsjs\MutationObserver.min.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\js\social-share-kit.min.js">
+ <Link>Resources\dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\js\social-share-kit.min.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\webcomponentsjs\README.md">
- <Link>Resources\dashboard-ui\thirdparty\webcomponentsjs\README.md</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\viblast\viblast.crypto.js">
+ <Link>Resources\dashboard-ui\thirdparty\viblast\viblast.crypto.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\webcomponentsjs\ShadowDOM.min.js">
- <Link>Resources\dashboard-ui\thirdparty\webcomponentsjs\ShadowDOM.min.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\viblast\viblast.js">
+ <Link>Resources\dashboard-ui\thirdparty\viblast\viblast.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\webcomponentsjs\bower.json">
- <Link>Resources\dashboard-ui\thirdparty\webcomponentsjs\bower.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\viblast\viblast.remuxer.js">
+ <Link>Resources\dashboard-ui\thirdparty\viblast\viblast.remuxer.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\webcomponentsjs\package.json">
- <Link>Resources\dashboard-ui\thirdparty\webcomponentsjs\package.json</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\viblast\worker.html">
+ <Link>Resources\dashboard-ui\thirdparty\viblast\worker.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\webcomponentsjs\webcomponents-lite.min.js">
- <Link>Resources\dashboard-ui\thirdparty\webcomponentsjs\webcomponents-lite.min.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\tvproviders\schedulesdirect.js">
+ <Link>Resources\dashboard-ui\tvproviders\schedulesdirect.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\thirdparty\webcomponentsjs\webcomponents.min.js">
- <Link>Resources\dashboard-ui\thirdparty\webcomponentsjs\webcomponents.min.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\tvproviders\schedulesdirect.template.html">
+ <Link>Resources\dashboard-ui\tvproviders\schedulesdirect.template.html</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\voice\voice.css">
<Link>Resources\dashboard-ui\voice\voice.css</Link>
diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs
index 60ff36c6d..9ecd0764a 100644
--- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs
+++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs
@@ -496,7 +496,7 @@ namespace MediaBrowser.Server.Startup.Common
PlaylistManager = new PlaylistManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("PlaylistManager"), UserManager, ProviderManager);
RegisterSingleInstance<IPlaylistManager>(PlaylistManager);
- LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, ProviderManager);
+ LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, ProviderManager, FileSystemManager);
RegisterSingleInstance(LiveTvManager);
UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, PlaylistManager, CollectionManager, ServerConfigurationManager);
diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs
index 5c5bc10e7..2910479ef 100644
--- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs
+++ b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs
@@ -42,7 +42,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
switch (environment.SystemArchitecture)
{
case Architecture.X86_X64:
- info.Version = "20150110";
+ info.Version = "20150827";
break;
case Architecture.X86:
info.Version = "20150110";
@@ -84,13 +84,13 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
return new[]
{
"http://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-20150717-git-8250943-win64-static.7z",
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20150717-git-8250943-win64-static.7z"
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20150901-git-b54e03c-win64-static.7z"
};
case Architecture.X86:
return new[]
{
"http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20150717-git-8250943-win32-static.7z",
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20150717-git-8250943-win32-static.7z"
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20150901-git-b54e03c-win32-static.7z"
};
}
break;
@@ -102,7 +102,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
case Architecture.X86_X64:
return new[]
{
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.5.3.7z"
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.7.2.7z"
};
case Architecture.X86:
return new[]
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index cbf7f2602..69c4417cb 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -198,10 +198,8 @@ namespace MediaBrowser.WebDashboard.Api
var contentType = MimeTypes.GetMimeType(path);
- var isHtml = IsHtml(path);
-
// Bounce them to the startup wizard if it hasn't been completed yet
- if (isHtml && !_serverConfigurationManager.Configuration.IsStartupWizardCompleted && path.IndexOf("wizard", StringComparison.OrdinalIgnoreCase) == -1)
+ if (!_serverConfigurationManager.Configuration.IsStartupWizardCompleted && path.IndexOf("wizard", StringComparison.OrdinalIgnoreCase) == -1 && GetPackageCreator().IsCoreHtml(path))
{
// But don't redirect if an html import is being requested.
if (path.IndexOf("vulcanize", StringComparison.OrdinalIgnoreCase) == -1 && path.IndexOf("bower_components", StringComparison.OrdinalIgnoreCase) == -1)
@@ -333,6 +331,8 @@ namespace MediaBrowser.WebDashboard.Api
CopyDirectory(Path.Combine(creator.DashboardUIPath, "bower_components", "swipebox", "src", "css"), Path.Combine(path, "bower_components", "swipebox", "src", "css"));
CopyDirectory(Path.Combine(creator.DashboardUIPath, "bower_components", "swipebox", "src", "js"), Path.Combine(path, "bower_components", "swipebox", "src", "js"));
CopyDirectory(Path.Combine(creator.DashboardUIPath, "bower_components", "swipebox", "src", "img"), Path.Combine(path, "bower_components", "swipebox", "src", "img"));
+
+ CopyFile(Path.Combine(creator.DashboardUIPath, "bower_components", "hammerjs", "hammer.min.js"), Path.Combine(path, "bower_components", "hammerjs", "hammer.min.js"));
}
MinifyCssDirectory(Path.Combine(path, "css"));
diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
index 104d02bcf..48ad62bd4 100644
--- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs
+++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
@@ -222,13 +222,18 @@ namespace MediaBrowser.WebDashboard.Api
}
}
- private bool IsCoreHtml(string path)
+ public bool IsCoreHtml(string path)
{
if (path.IndexOf("vulcanize", StringComparison.OrdinalIgnoreCase) != -1)
{
return false;
}
+ if (path.IndexOf(".template.html", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ return false;
+ }
+
path = GetDashboardResourcePath(path);
var parent = Path.GetDirectoryName(path);
@@ -418,7 +423,6 @@ namespace MediaBrowser.WebDashboard.Api
var files = new[]
{
- "thirdparty/fontawesome/css/font-awesome.min.css" + versionString,
"css/all.css" + versionString
};
@@ -491,8 +495,6 @@ namespace MediaBrowser.WebDashboard.Api
await AppendResource(memoryStream, "bower_components/jquery/dist/jquery.min.js", newLineBytes).ConfigureAwait(false);
- await AppendResource(memoryStream, "bower_components/requirejs/require.js", newLineBytes).ConfigureAwait(false);
-
//await AppendLocalization(memoryStream, culture, excludePhrases).ConfigureAwait(false);
await memoryStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false);
@@ -513,6 +515,7 @@ namespace MediaBrowser.WebDashboard.Api
var commonFiles = new[]
{
+ "bower_components/requirejs/require.js",
"thirdparty/jquerymobile-1.4.5/jquery.mobile.custom.js",
"thirdparty/browser.js",
"thirdparty/jquery.unveil-custom.js",
@@ -647,13 +650,11 @@ namespace MediaBrowser.WebDashboard.Api
private async Task<Stream> GetAllCss(bool enableMinification)
{
var memoryStream = new MemoryStream();
- var newLineBytes = Encoding.UTF8.GetBytes(Environment.NewLine);
-
- await AppendResource(memoryStream, "thirdparty/jquerymobile-1.4.5/jquery.mobile.custom.theme.min.css", newLineBytes).ConfigureAwait(false);
- await AppendResource(memoryStream, "thirdparty/jquerymobile-1.4.5/jquery.mobile.custom.structure.min.css", newLineBytes).ConfigureAwait(false);
var files = new[]
{
+ "thirdparty/jquerymobile-1.4.5/jquery.mobile.custom.theme.css",
+ "thirdparty/jquerymobile-1.4.5/jquery.mobile.custom.structure.css",
"css/site.css",
"css/chromecast.css",
"css/nowplayingbar.css",
@@ -664,7 +665,6 @@ namespace MediaBrowser.WebDashboard.Api
"css/card.css",
"css/notifications.css",
"css/search.css",
- "css/pluginupdates.css",
"css/remotecontrol.css",
"css/userimage.css",
"css/nowplaying.css",
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index 42a9c5c1b..ff7457eff 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -90,6 +90,12 @@
<Content Include="dashboard-ui\bower_components\fastclick\lib\fastclick.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\bower_components\hammerjs\hammer.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\bower_components\hammerjs\hammer.min.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\bower_components\jquery\dist\jquery.min.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -156,6 +162,9 @@
<Content Include="dashboard-ui\css\images\clients\chromecast.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\css\images\clients\firefox.png">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\css\images\empty.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -183,9 +192,6 @@
<Content Include="dashboard-ui\css\nowplayingbar.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\livetvguideprovider-scd.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\livetvguideprovider.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -219,9 +225,6 @@
<Content Include="dashboard-ui\scripts\homeupcoming.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\livetvguideprovider-scd.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\livetvguideprovider.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -258,6 +261,15 @@
<Content Include="dashboard-ui\scripts\sharingwidget.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\scripts\slideshow.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\scripts\wizardlivetvguide.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\scripts\wizardlivetvtuner.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\secondaryitems.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -318,6 +330,30 @@
<Content Include="dashboard-ui\thirdparty\emby-icons.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.collapsible.css">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.collapsible.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.listview.css">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.listview.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.popup.css">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.slider.css">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.table.css">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.slider.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.icons.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -362,12 +398,18 @@
<Content Include="dashboard-ui\thirdparty\viblast\viblast.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\viblast\viblast.msp.js">
+ <Content Include="dashboard-ui\thirdparty\viblast\viblast.remuxer.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\thirdparty\viblast\worker.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\tvproviders\schedulesdirect.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\tvproviders\schedulesdirect.template.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\voice\voice.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -565,12 +607,6 @@
<Content Include="dashboard-ui\css\images\clients\amazon.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\css\images\clients\chrome_companion.png">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\css\images\clients\nuvue.png">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\css\images\clients\playstore.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -736,18 +772,12 @@
<Content Include="dashboard-ui\playbackconfiguration.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\playlistedit.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\playlists.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\reports.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\livetvchannel.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\livetvnewrecording.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -766,9 +796,6 @@
<Content Include="dashboard-ui\metadatasubtitles.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\musicalbumartists.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\collections.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -802,9 +829,6 @@
<Content Include="dashboard-ui\css\images\clients\chrome.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\css\images\clients\firefox.png">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\css\images\clients\ie.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -850,9 +874,6 @@
<Content Include="dashboard-ui\css\librarybrowser.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\css\pluginupdates.css">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\css\remotecontrol.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1792,6 +1813,12 @@
<Content Include="dashboard-ui\wizardagreement.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\wizardlivetvguide.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\wizardlivetvtuner.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\wizardservice.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1822,19 +1849,7 @@
<Content Include="dashboard-ui\movies.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\musicalbums.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\musicartists.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\musicgenres.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\musicrecommended.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\musicvideos.html">
+ <Content Include="dashboard-ui\music.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\alphapicker.js">
@@ -1855,9 +1870,6 @@
<Content Include="dashboard-ui\scripts\musicrecommended.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\musicvideos.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\notifications.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1870,9 +1882,6 @@
<Content Include="dashboard-ui\scripts\tvlatest.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\songs.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\moviecollections.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1947,9 +1956,6 @@
</Content>
</ItemGroup>
<ItemGroup>
- <Content Include="dashboard-ui\itembynamedetails.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\tvgenres.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -2208,11 +2214,6 @@
</Content>
</ItemGroup>
<ItemGroup>
- <Content Include="dashboard-ui\css\images\clients\mb.png">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- </ItemGroup>
- <ItemGroup>
<Content Include="dashboard-ui\css\images\clients\windowsrt.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -2660,6 +2661,12 @@
<None Include="dashboard-ui\thirdparty\fontawesome\fonts\FontAwesome.otf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.table.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.popup.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<None Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile-1.4.5.min.map">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
index 53aa390ed..ac39d9d98 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
@@ -1,8 +1,8 @@
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Logging;
using MediaBrowser.XbmcMetadata.Configuration;
using MediaBrowser.XbmcMetadata.Savers;
@@ -12,6 +12,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
@@ -117,7 +118,15 @@ namespace MediaBrowser.XbmcMetadata.Parsers
var xml = streamReader.ReadToEnd();
- var index = xml.LastIndexOf('>');
+ // Find last closing Tag
+ // Need to do this in two steps to account for random > characters after the closing xml
+ var index = xml.LastIndexOf(@"</", StringComparison.Ordinal);
+
+ // If closing tag exists, move to end of Tag
+ if (index != -1)
+ {
+ index = xml.IndexOf('>', index);
+ }
if (index != -1)
{
@@ -149,35 +158,41 @@ namespace MediaBrowser.XbmcMetadata.Parsers
ms.Write(bytes, 0, bytes.Length);
ms.Position = 0;
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(ms, settings))
+ // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions
+ try
{
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(ms, settings))
{
- cancellationToken.ThrowIfCancellationRequested();
+ reader.MoveToContent();
- if (reader.NodeType == XmlNodeType.Element)
+ // Loop through each element
+ while (reader.Read())
{
- FetchDataFromXmlNode(reader, item);
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ FetchDataFromXmlNode(reader, item);
+ }
}
}
}
-
+ catch (XmlException)
+ {
+
+ }
}
}
}
private void ParseProviderLinks(T item, string xml)
{
- var imdbId = xml.Split('/')
- .FirstOrDefault(i => i.StartsWith("tt", StringComparison.OrdinalIgnoreCase));
-
- if (!string.IsNullOrWhiteSpace(imdbId))
+ //Look for a match for the Regex pattern "tt" followed by 7 digits
+ Match m = Regex.Match(xml, @"tt([0-9]{7})", RegexOptions.IgnoreCase);
+ if (m.Success)
{
- item.SetProviderId(MetadataProviders.Imdb, imdbId);
+ item.SetProviderId(MetadataProviders.Imdb, m.Value);
}
// TODO: Support Tmdb
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec
index c43b90c8c..f92e05492 100644
--- a/Nuget/MediaBrowser.Common.Internal.nuspec
+++ b/Nuget/MediaBrowser.Common.Internal.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
- <version>3.0.631</version>
+ <version>3.0.633</version>
<title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Emby 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.631" />
+ <dependency id="MediaBrowser.Common" version="3.0.633" />
<dependency id="NLog" version="3.2.1" />
<dependency id="SimpleInjector" version="2.8.0" />
</dependencies>
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index 32e3b2fc6..04d4deb7c 100644
--- a/Nuget/MediaBrowser.Common.nuspec
+++ b/Nuget/MediaBrowser.Common.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
- <version>3.0.631</version>
+ <version>3.0.633</version>
<title>MediaBrowser.Common</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec
index fc33477a9..2a75ac3a8 100644
--- a/Nuget/MediaBrowser.Model.Signed.nuspec
+++ b/Nuget/MediaBrowser.Model.Signed.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Model.Signed</id>
- <version>3.0.631</version>
+ <version>3.0.633</version>
<title>MediaBrowser.Model - Signed Edition</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index 704c30f95..da8309410 100644
--- a/Nuget/MediaBrowser.Server.Core.nuspec
+++ b/Nuget/MediaBrowser.Server.Core.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
- <version>3.0.631</version>
+ <version>3.0.633</version>
<title>Media Browser.Server.Core</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Emby Server.</description>
<copyright>Copyright © Emby 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.631" />
+ <dependency id="MediaBrowser.Common" version="3.0.633" />
<dependency id="Interfaces.IO" version="1.0.0.5" />
</dependencies>
</metadata>
diff --git a/SharedVersion.cs b/SharedVersion.cs
index 925d6899f..36853f5e2 100644
--- a/SharedVersion.cs
+++ b/SharedVersion.cs
@@ -1,4 +1,4 @@
using System.Reflection;
-[assembly: AssemblyVersion("3.0.*")]
-//[assembly: AssemblyVersion("3.0.5675.1")]
+//[assembly: AssemblyVersion("3.0.*")]
+[assembly: AssemblyVersion("3.0.5724.4")]