diff --git a/TVHeadEnd/HTSConnectionHandler.cs b/TVHeadEnd/HTSConnectionHandler.cs index 05a0394c2..f4fa11f4d 100644 --- a/TVHeadEnd/HTSConnectionHandler.cs +++ b/TVHeadEnd/HTSConnectionHandler.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Reflection; @@ -162,11 +160,11 @@ private void init() if (_enableSubsMaudios) { // Use HTTP basic auth instead of TVH ticketing system for authentication to allow the users to switch subs or audio tracks at any time - _httpBaseUrl = "http://" + _userName + ":" + _password + "@" + _tvhServerName + ":" + _httpPort + _webRoot; + _httpBaseUrl = $"http://{Uri.EscapeDataString(_userName)}:{Uri.EscapeDataString(_password)}@{_tvhServerName}:{_httpPort}{_webRoot}"; } else { - _httpBaseUrl = "http://" + _tvhServerName + ":" + _httpPort + _webRoot; + _httpBaseUrl = $"http://{_tvhServerName}:{_httpPort}{_webRoot}"; } string authInfo = _userName + ":" + _password; @@ -194,7 +192,7 @@ public async Task GetChannelImage(string channelId, CancellationTok } else { - string requestStr = "http://" + _tvhServerName + ":" + _httpPort + _webRoot + "/" + channelIcon; + string requestStr = $"http://{_tvhServerName}:{_httpPort}{_webRoot}/{channelIcon}"; request.RequestUri = new Uri(requestStr); request.Headers.Authorization = AuthenticationHeaderValue.Parse(_headers[HeaderNames.Authorization]); @@ -278,40 +276,11 @@ public async Task GetChannelImage(string channelId, CancellationTok } } - public string GetChannelImageUrl(string channelId) - { - _logger.LogDebug("[TVHclient] HTSConnectionHandler.GetChannelImage: channelId: {id}", channelId); - - String channelIcon = _channelDataHelper.GetChannelIcon4ChannelId(channelId); - - if (string.IsNullOrEmpty(channelIcon)) - { - return null; - } - - if (channelIcon.StartsWith("http")) - { - return _channelDataHelper.GetChannelIcon4ChannelId(channelId); - } - else - { - return "http://" + _userName + ":" + _password + "@" +_tvhServerName + ":" + _httpPort + _webRoot + "/" + channelIcon; - } - } - public Dictionary GetHeaders() { return new Dictionary(_headers); } - //private static Stream ImageToPNGStream(Image image) - //{ - // Stream stream = new System.IO.MemoryStream(); - // image.Save(stream, ImageFormat.Png); - // stream.Position = 0; - // return stream; - //} - private void ensureConnection() { //_logger.LogDebug("[TVHclient] HTSConnectionHandler.ensureConnection"); diff --git a/TVHeadEnd/LiveTvService.cs b/TVHeadEnd/LiveTvService.cs index 31a4a1a3c..2a848593c 100644 --- a/TVHeadEnd/LiveTvService.cs +++ b/TVHeadEnd/LiveTvService.cs @@ -1,15 +1,13 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; -using System.Net; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Controller.Drawing; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; @@ -23,14 +21,11 @@ namespace TVHeadEnd { - public class LiveTvService : ILiveTvService + public class LiveTvService : ILiveTvService, IDynamicImageProvider { public event EventHandler DataSourceChanged; public event EventHandler RecordingStatusChanged; - //Added for stream probing - private readonly IMediaEncoder _mediaEncoder; - private readonly TimeSpan TIMEOUT = TimeSpan.FromMinutes(5); private HTSConnectionHandler _htsConnectionHandler; @@ -39,9 +34,8 @@ public class LiveTvService : ILiveTvService private readonly ILogger _logger; public DateTime LastRecordingChange = DateTime.MinValue; - public LiveTvService(ILoggerFactory loggerFactory, IMediaEncoder mediaEncoder, IHttpClientFactory httpClientFactory) + public LiveTvService(ILoggerFactory loggerFactory, IHttpClientFactory httpClientFactory) { - //System.Diagnostics.StackTrace t = new System.Diagnostics.StackTrace(); _logger = loggerFactory.CreateLogger(); _logger.LogDebug("[TVHclient] LiveTvService()"); @@ -55,9 +49,6 @@ public LiveTvService(ILoggerFactory loggerFactory, IMediaEncoder mediaEncoder, I _channelTicketHandler = new AccessTicketHandler(loggerFactory, _htsConnectionHandler, requestTimeout, retries, lifeSpan, Channel); _recordingTicketHandler = new AccessTicketHandler(loggerFactory, _htsConnectionHandler, requestTimeout, retries, lifeSpan, Recording); } - - //Added for stream probing - _mediaEncoder = mediaEncoder; } public string HomePageUrl { get { return "http://tvheadend.org/"; } } @@ -201,88 +192,9 @@ await Task.Factory.StartNew(() => }); } - public async Task CreateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken) + public Task CreateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken) { - // Dummy method to avoid warnings - await Task.Factory.StartNew(() => { return 0; }); - throw new NotImplementedException(); - - - //int timeOut = await WaitForInitialLoadTask(cancellationToken); - //if (timeOut == -1 || cancellationToken.IsCancellationRequested) - //{ - // _logger.LogDebug("[TVHclient] LiveTvService.CreateSeriesTimerAsync: call cancelled or timed out - returning empty list"); - // return; - //} - - ////_logger.LogDebug("[TVHclient] LiveTvService.CreateSeriesTimerAsync: got SeriesTimerInfo: {spam}", dump(info)); - - //HTSMessage createSeriesTimerMessage = new HTSMessage(); - //createSeriesTimerMessage.Method = "addAutorecEntry"; - //createSeriesTimerMessage.putField("title", info.Name); - //if (!info.RecordAnyChannel) - //{ - // createSeriesTimerMessage.putField("channelId", info.ChannelId); - //} - //createSeriesTimerMessage.putField("minDuration", 0); - //createSeriesTimerMessage.putField("maxDuration", 0); - - //int tempPriority = info.Priority; - //if (tempPriority == 0) - //{ - // tempPriority = _priority; // info.Priority delivers 0 if timers is newly created - no GUI - //} - //createSeriesTimerMessage.putField("priority", tempPriority); - //createSeriesTimerMessage.putField("configName", _profile); - //createSeriesTimerMessage.putField("daysOfWeek", AutorecDataHelper.getDaysOfWeekFromList(info.Days)); - - //if (!info.RecordAnyTime) - //{ - // createSeriesTimerMessage.putField("approxTime", AutorecDataHelper.getMinutesFromMidnight(info.StartDate)); - //} - //createSeriesTimerMessage.putField("startExtra", (long)(info.PrePaddingSeconds / 60L)); - //createSeriesTimerMessage.putField("stopExtra", (long)(info.PostPaddingSeconds / 60L)); - //createSeriesTimerMessage.putField("comment", info.Overview); - - - ////_logger.LogDebug("[TVHclient] LiveTvService.CreateSeriesTimerAsync: created HTSP message: {msg}", createSeriesTimerMessage.ToString()); - - - ///* - // public DateTime EndDate { get; set; } - // public string ProgramId { get; set; } - // public bool RecordNewOnly { get; set; } - // */ - - ////HTSMessage createSeriesTimerResponse = await Task.Factory.StartNew(() => - ////{ - //// LoopBackResponseHandler lbrh = new LoopBackResponseHandler(); - //// _htsConnection.sendMessage(createSeriesTimerMessage, lbrh); - //// return lbrh.getResponse(); - ////}); - - //TaskWithTimeoutRunner twtr = new TaskWithTimeoutRunner(TIMEOUT); - //TaskWithTimeoutResult twtRes = await twtr.RunWithTimeout(Task.Factory.StartNew(() => - //{ - // LoopBackResponseHandler lbrh = new LoopBackResponseHandler(); - // _htsConnection.sendMessage(createSeriesTimerMessage, lbrh); - // return lbrh.getResponse(); - //})); - - //if (twtRes.HasTimeout) - //{ - // _logger.LogError("[TVHclient] LiveTvService.CreateSeriesTimerAsync: can't create series because the timeout was reached"); - //} - //else - //{ - // HTSMessage createSeriesTimerResponse = twtRes.Result; - // Boolean success = createSeriesTimerResponse.getInt("success", 0) == 1; - // if (!success) - // { - // _logger.LogError("[TVHclient] LiveTvService.CreateSeriesTimerAsync: can't create series timer: '{why}'", createSeriesTimerResponse.getString("error")); - // } - //} } public async Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken) @@ -399,17 +311,7 @@ public async Task> GetChannelsAsync(CancellationToken c return new List(); } - var list = twtRes.Result.ToList(); - - foreach (var channel in list) - { - if (string.IsNullOrEmpty(channel.ImageUrl)) - { - channel.ImageUrl = _htsConnectionHandler.GetChannelImageUrl(channel.Id); - } - } - - return list; + return twtRes.Result.ToList(); } public async Task GetChannelStream(string channelId, string mediaSourceId, CancellationToken cancellationToken) @@ -497,12 +399,6 @@ public async Task GetNewTimerDefaultsAsync(CancellationToken ca }); } - public Task GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken) - { - // Leave as is. This is handled by supplying image url to ProgramInfo - throw new NotImplementedException(); - } - public async Task> GetProgramsAsync(string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) { int timeOut = await WaitForInitialLoadTask(cancellationToken); @@ -535,12 +431,6 @@ public async Task> GetProgramsAsync(string channelId, D return twtRes.Result; } - public Task GetRecordingImageAsync(string recordingId, CancellationToken cancellationToken) - { - // Leave as is. This is handled by supplying image url to RecordingInfo - throw new NotImplementedException(); - } - public async Task> GetAllRecordingsAsync(CancellationToken cancellationToken) { // retrieve all 'Pending', 'Inprogress' and 'Completed' recordings @@ -794,6 +684,32 @@ public async Task UpdateTimerAsync(TimerInfo info, CancellationToken cancellatio } } + public IEnumerable GetSupportedImages(BaseItem item) + { + return new[] { ImageType.Primary }; + } + + public async Task GetImage(BaseItem item, ImageType type, CancellationToken cancellationToken) + { + var image = await _htsConnectionHandler.GetChannelImage(item.ExternalId, cancellationToken); + if (image == null) + { + return new DynamicImageResponse(); + } + + return new DynamicImageResponse + { + Format = image.Format, + Stream = image.Stream, + HasImage = true, + }; + } + + public bool Supports(BaseItem item) + { + return item.ServiceName == Name && item is LiveTvChannel; + } + /***********/ /* Helpers */ /***********/ diff --git a/TVHeadEnd/RecordingsChannel.cs b/TVHeadEnd/RecordingsChannel.cs index 024d62417..2777e3bab 100644 --- a/TVHeadEnd/RecordingsChannel.cs +++ b/TVHeadEnd/RecordingsChannel.cs @@ -113,7 +113,7 @@ public Task GetChannelImage(ImageType type, CancellationTo { return Task.FromResult(new DynamicImageResponse { - Path = "https://github.com/MediaBrowser/Tvheadend/raw/master/TVHeadEnd/Images/TVHeadEnd.png?raw=true", + Path = "https://repo.jellyfin.org/releases/plugin/images/jellyfin-plugin-tvheadend.png", Protocol = MediaProtocol.Http, HasImage = true }); @@ -268,14 +268,14 @@ private static string buildRecordingPath(string Id) var tvhServerName = config.TVH_ServerName.Trim(); var httpPort = config.HTTP_Port; var htspPort = config.HTSP_Port; - var webRoot = config.WebRoot; + var webRoot = config.WebRoot; if (webRoot.EndsWith("/")) { webRoot = webRoot.Substring(0, webRoot.Length - 1); } var userName = config.Username.Trim(); var password = config.Password.Trim(); - return "http://" + userName + ":" + password + "@" + tvhServerName + ":" + httpPort + webRoot + "/dvrfile/" + Id; + return $"http://{Uri.EscapeDataString(userName)}:{Uri.EscapeDataString(password)}@{tvhServerName}:{httpPort}{webRoot}/dvrfile/{Id}"; } catch (Exception) {