From f4c25b24711ddc1e042bf691d5b4c5dfdbf4a8e3 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 27 Aug 2015 15:59:42 -0400 Subject: [PATCH 1/9] update m3u tuners --- .../LiveTv/EmbyTV/EmbyTV.cs | 2 +- .../LiveTv/TunerHosts/M3UTunerHost.cs | 153 ++++++++---------- SharedVersion.cs | 4 +- 3 files changed, 70 insertions(+), 89 deletions(-) diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index b69cdacef3..8b717e5d44 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -455,7 +455,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - throw new ApplicationException("Tuner not found."); + throw new NotImplementedException(); } public Task> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken) diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index e19b17ca42..3783e4b089 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -39,68 +39,45 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts var url = info.Url; var urlHash = url.GetMD5().ToString("N"); - int position = 0; string line; // Read the file and display it line by line. var file = new StreamReader(url); var channels = new List(); + + 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 = ChannelIdPrefix + urlHash + 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(); @@ -125,6 +102,35 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts } protected override async Task 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) + { + if (!File.Exists(info.Url)) + { + throw new FileNotFoundException(); + } + } + + protected override bool IsValidChannelId(string channelId) + { + return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase); + } + + protected override async Task> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken) { var urlHash = info.Url.GetMD5().ToString("N"); var prefix = ChannelIdPrefix + urlHash; @@ -133,29 +139,29 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts return null; } - channelId = channelId.Substring(prefix.Length); + //channelId = channelId.Substring(prefix.Length); var channels = await GetChannels(info, true, cancellationToken).ConfigureAwait(false); var m3uchannels = channels.Cast(); - 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, @@ -179,35 +185,10 @@ 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 { mediaSource }; } - } - - protected override bool IsValidChannelId(string channelId) - { - return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase); - } - - protected override Task> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken) - { - throw new NotImplementedException(); + return new List { }; } protected override Task IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) diff --git a/SharedVersion.cs b/SharedVersion.cs index 732202201f..f22c8a5cbf 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -//[assembly: AssemblyVersion("3.0.*")] -[assembly: AssemblyVersion("3.0.5713.4")] +[assembly: AssemblyVersion("3.0.*")] +//[assembly: AssemblyVersion("3.0.5713.4")] From a0c3bb03809612763f7b06d4f82d2a44a6ed4182 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 28 Aug 2015 00:19:08 -0400 Subject: [PATCH 2/9] update live tv image --- MediaBrowser.Api/Music/InstantMixService.cs | 16 ++ .../Playback/Hls/BaseHlsService.cs | 7 +- MediaBrowser.Controller/Entities/BaseItem.cs | 4 + .../Entities/InternalItemsQuery.cs | 3 +- .../Encoder/MediaEncoder.cs | 15 +- MediaBrowser.Model/ApiClient/IApiClient.cs | 27 +--- .../Folders/DefaultImageProvider.cs | 147 ++++++++++++++++++ .../MediaBrowser.Providers.csproj | 1 + .../Connect/ConnectEntryPoint.cs | 2 +- .../Connect/ConnectManager.cs | 2 +- .../Library/MusicManager.cs | 18 +++ ...MediaBrowser.Server.Implementations.csproj | 8 - .../Persistence/CleanDatabaseScheduledTask.cs | 46 +++++- .../Persistence/SqliteItemRepository.cs | 8 + .../UserViews/DynamicImageProvider.cs | 39 +---- .../UserViews/livetv/1.jpg | Bin 62382 -> 0 bytes .../UserViews/livetv/2.jpg | Bin 20550 -> 0 bytes .../UserViews/livetv/3.jpg | Bin 27983 -> 0 bytes .../UserViews/livetv/4.jpg | Bin 75468 -> 0 bytes .../UserViews/livetv/5.jpg | Bin 50933 -> 0 bytes .../UserViews/livetv/6.jpg | Bin 15931 -> 0 bytes .../UserViews/livetv/7.jpg | Bin 19916 -> 0 bytes .../UserViews/livetv/8.jpg | Bin 67721 -> 0 bytes 23 files changed, 262 insertions(+), 81 deletions(-) create mode 100644 MediaBrowser.Providers/Folders/DefaultImageProvider.cs delete mode 100644 MediaBrowser.Server.Implementations/UserViews/livetv/1.jpg delete mode 100644 MediaBrowser.Server.Implementations/UserViews/livetv/2.jpg delete mode 100644 MediaBrowser.Server.Implementations/UserViews/livetv/3.jpg delete mode 100644 MediaBrowser.Server.Implementations/UserViews/livetv/4.jpg delete mode 100644 MediaBrowser.Server.Implementations/UserViews/livetv/5.jpg delete mode 100644 MediaBrowser.Server.Implementations/UserViews/livetv/6.jpg delete mode 100644 MediaBrowser.Server.Implementations/UserViews/livetv/7.jpg delete mode 100644 MediaBrowser.Server.Implementations/UserViews/livetv/8.jpg diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs index 905ead86c6..d2a4aa60cc 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); diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 1a152790aa..61fd7a55a0 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -1,5 +1,4 @@ -using System.Linq; -using MediaBrowser.Common.IO; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; @@ -317,6 +316,7 @@ namespace MediaBrowser.Api.Playback.Hls { if (videoStream.KeyFrames == null || videoStream.KeyFrames.Count == 0) { + Logger.Debug("Cannot stream copy video due to missing keyframe info"); return false; } @@ -326,8 +326,9 @@ namespace MediaBrowser.Api.Playback.Hls var length = frame - previousSegment; // Don't allow really long segments because this could result in long download times - if (length > 10000) + if (length > 12000) { + Logger.Debug("Cannot stream copy video due to long segment length of {0}ms", length); return false; } previousSegment = frame; diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index a333fc6e99..594b5ca93c 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -419,6 +419,10 @@ namespace MediaBrowser.Controller.Entities return _sortName ?? (_sortName = CreateSortName()); } + set + { + _sortName = value; + } } public string GetInternalMetadataPath() diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 53667c6e18..0af4972f74 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -97,7 +97,8 @@ namespace MediaBrowser.Controller.Entities public int? MaxParentalRating { get; set; } public bool? IsCurrentSchema { get; set; } - + public bool? HasDeadParentId { get; set; } + public InternalItemsQuery() { Tags = new string[] { }; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index d30b105a57..d21a9dc3b9 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -244,7 +244,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { foreach (var stream in mediaInfo.MediaStreams) { - if (stream.Type == MediaStreamType.Video && string.Equals(stream.Codec, "h264", StringComparison.OrdinalIgnoreCase)) + if (stream.Type == MediaStreamType.Video && string.Equals(stream.Codec, "h264", StringComparison.OrdinalIgnoreCase) && !stream.IsInterlaced) { try { @@ -282,7 +282,9 @@ namespace MediaBrowser.MediaEncoding.Encoder private async Task> GetKeyFrames(string inputPath, int videoStreamIndex, CancellationToken cancellationToken) { - const string args = "-i {0} -select_streams v:{1} -show_packets -print_format compact -show_entries packet=flags -show_entries packet=pts_time"; + 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 { @@ -330,7 +332,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { StopProcess(processWrapper, 100, true); } - + //_logger.Debug("Found keyframes {0}", string.Join(",", lines.ToArray())); return lines; } } @@ -347,7 +349,12 @@ namespace MediaBrowser.MediaEncoding.Encoder var line = await reader.ReadLineAsync().ConfigureAwait(false); - var values = (line ?? string.Empty).Split('|') + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + var values = line.Split('|') .Where(i => !string.IsNullOrWhiteSpace(i)) .Select(i => i.Split('=')) .Where(i => i.Length == 2) diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index c2524378ec..6107c0fbea 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -320,32 +320,11 @@ namespace MediaBrowser.Model.ApiClient Task GetUserViews(string userId, CancellationToken cancellationToken = default(CancellationToken)); /// - /// Gets the instant mix from song async. + /// Gets the instant mix from item asynchronous. /// /// The query. - /// Task{ItemsResult}. - Task GetInstantMixFromSongAsync(SimilarItemsQuery query); - - /// - /// Gets the instant mix from album async. - /// - /// The query. - /// Task{ItemsResult}. - Task GetInstantMixFromAlbumAsync(SimilarItemsQuery query); - - /// - /// Gets the instant mix from artist async. - /// - /// The query. - /// Task{ItemsResult}. - Task GetInstantMixFromArtistAsync(SimilarItemsQuery query); - - /// - /// Gets the instant mix from music genre async. - /// - /// The query. - /// Task{ItemsResult}. - Task GetInstantMixFromMusicGenreAsync(SimilarItemsQuery query); + /// Task<ItemsResult>. + Task GetInstantMixFromItemAsync(SimilarItemsQuery query); /// /// Gets the similar movies async. diff --git a/MediaBrowser.Providers/Folders/DefaultImageProvider.cs b/MediaBrowser.Providers/Folders/DefaultImageProvider.cs new file mode 100644 index 0000000000..1fbe20d7f0 --- /dev/null +++ b/MediaBrowser.Providers/Folders/DefaultImageProvider.cs @@ -0,0 +1,147 @@ +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 GetSupportedImages(IHasImages item) + { + return new List + { + ImageType.Primary, + ImageType.Thumb + }; + } + + public Task> 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> GetImages(string viewType, CancellationToken cancellationToken) + { + var url = GetImageUrl(viewType); + + var list = new List(); + + if (!string.IsNullOrWhiteSpace(url)) + { + list.AddRange(new List{ + new RemoteImageInfo + { + ProviderName = Name, + Url = url, + Type = ImageType.Primary + }, + + new RemoteImageInfo + { + ProviderName = Name, + Url = url, + Type = ImageType.Thumb + } + }); + } + + return Task.FromResult>(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 + "channels.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"; + } + + return null; + //return urlPrefix + "generic.png"; + } + + public string Name + { + get { return "Default Image Provider"; } + } + + public bool Supports(IHasImages item) + { + var view = item as UserView; + + if (view != null) + { + return !view.UserId.HasValue; + } + + return item is ICollectionFolder; + } + + public Task 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/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 444567afaf..1d323e567b 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -83,6 +83,7 @@ + diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs b/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs index 973519a77e..8a659fb65c 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 4569503c01..7cd96c5f38 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 LinkUser(string userId, string connectUsername) diff --git a/MediaBrowser.Server.Implementations/Library/MusicManager.cs b/MediaBrowser.Server.Implementations/Library/MusicManager.cs index b832563429..683e6c5cc4 100644 --- a/MediaBrowser.Server.Implementations/Library/MusicManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MusicManager.cs @@ -52,6 +52,18 @@ namespace MediaBrowser.Server.Implementations.Library return GetInstantMixFromGenres(genres, user); } + public IEnumerable