diff options
| author | Patrick Barron <barronpm@gmail.com> | 2023-12-28 15:15:03 -0500 |
|---|---|---|
| committer | Patrick Barron <barronpm@gmail.com> | 2024-01-09 10:16:56 -0500 |
| commit | c1a3084312fa4fb7796b83640bfe9ad2b5044afa (patch) | |
| tree | b7e81594c3782128d37f875a0ce54d0bad12c6e5 /Emby.Server.Implementations/LiveTv/Listings | |
| parent | 7eba162879f6d1ff04539cac5c0d6372a955d82b (diff) | |
Move LiveTv to separate project
Diffstat (limited to 'Emby.Server.Implementations/LiveTv/Listings')
35 files changed, 0 insertions, 2329 deletions
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs deleted file mode 100644 index 5be3a7488a..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ /dev/null @@ -1,808 +0,0 @@ -#nullable disable - -#pragma warning disable CS1591 - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Json; -using System.Net.Mime; -using System.Security.Cryptography; -using System.Text; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos; -using Jellyfin.Extensions; -using Jellyfin.Extensions.Json; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Authentication; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.LiveTv; -using Microsoft.Extensions.Logging; - -namespace Emby.Server.Implementations.LiveTv.Listings -{ - public class SchedulesDirect : IListingsProvider, IDisposable - { - private const string ApiUrl = "https://json.schedulesdirect.org/20141201"; - - private readonly ILogger<SchedulesDirect> _logger; - private readonly IHttpClientFactory _httpClientFactory; - private readonly SemaphoreSlim _tokenSemaphore = new SemaphoreSlim(1, 1); - - private readonly ConcurrentDictionary<string, NameValuePair> _tokens = new ConcurrentDictionary<string, NameValuePair>(); - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; - private DateTime _lastErrorResponse; - private bool _disposed = false; - - public SchedulesDirect( - ILogger<SchedulesDirect> logger, - IHttpClientFactory httpClientFactory) - { - _logger = logger; - _httpClientFactory = httpClientFactory; - } - - /// <inheritdoc /> - public string Name => "Schedules Direct"; - - /// <inheritdoc /> - public string Type => nameof(SchedulesDirect); - - private static List<string> GetScheduleRequestDates(DateTime startDateUtc, DateTime endDateUtc) - { - var dates = new List<string>(); - - var start = new[] { startDateUtc, startDateUtc.ToLocalTime() }.Min().Date; - var end = new[] { endDateUtc, endDateUtc.ToLocalTime() }.Max().Date; - - while (start <= end) - { - dates.Add(start.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)); - start = start.AddDays(1); - } - - return dates; - } - - public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) - { - ArgumentException.ThrowIfNullOrEmpty(channelId); - - // Normalize incoming input - channelId = channelId.Replace(".json.schedulesdirect.org", string.Empty, StringComparison.OrdinalIgnoreCase).TrimStart('I'); - - var token = await GetToken(info, cancellationToken).ConfigureAwait(false); - - if (string.IsNullOrEmpty(token)) - { - _logger.LogWarning("SchedulesDirect token is empty, returning empty program list"); - - return Enumerable.Empty<ProgramInfo>(); - } - - var dates = GetScheduleRequestDates(startDateUtc, endDateUtc); - - _logger.LogInformation("Channel Station ID is: {ChannelID}", channelId); - var requestList = new List<RequestScheduleForChannelDto>() - { - new RequestScheduleForChannelDto() - { - StationId = channelId, - Date = dates - } - }; - - _logger.LogDebug("Request string for schedules is: {@RequestString}", requestList); - - using var options = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/schedules"); - options.Content = JsonContent.Create(requestList, options: _jsonOptions); - options.Headers.TryAddWithoutValidation("token", token); - using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false); - var dailySchedules = await response.Content.ReadFromJsonAsync<IReadOnlyList<DayDto>>(_jsonOptions, cancellationToken).ConfigureAwait(false); - if (dailySchedules is null) - { - return Array.Empty<ProgramInfo>(); - } - - _logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId); - - using var programRequestOptions = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/programs"); - programRequestOptions.Headers.TryAddWithoutValidation("token", token); - - var programIds = dailySchedules.SelectMany(d => d.Programs.Select(s => s.ProgramId)).Distinct(); - programRequestOptions.Content = JsonContent.Create(programIds, options: _jsonOptions); - - using var innerResponse = await Send(programRequestOptions, true, info, cancellationToken).ConfigureAwait(false); - var programDetails = await innerResponse.Content.ReadFromJsonAsync<IReadOnlyList<ProgramDetailsDto>>(_jsonOptions, cancellationToken).ConfigureAwait(false); - if (programDetails is null) - { - return Array.Empty<ProgramInfo>(); - } - - var programDict = programDetails.ToDictionary(p => p.ProgramId, y => y); - - var programIdsWithImages = programDetails - .Where(p => p.HasImageArtwork).Select(p => p.ProgramId) - .ToList(); - - var images = await GetImageForPrograms(info, programIdsWithImages, cancellationToken).ConfigureAwait(false); - - var programsInfo = new List<ProgramInfo>(); - foreach (ProgramDto schedule in dailySchedules.SelectMany(d => d.Programs)) - { - // _logger.LogDebug("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); - - if (string.IsNullOrEmpty(schedule.ProgramId)) - { - continue; - } - - if (images is not null) - { - var imageIndex = images.FindIndex(i => i.ProgramId == schedule.ProgramId[..10]); - if (imageIndex > -1) - { - var programEntry = programDict[schedule.ProgramId]; - - var allImages = images[imageIndex].Data; - var imagesWithText = allImages.Where(i => string.Equals(i.Text, "yes", StringComparison.OrdinalIgnoreCase)).ToList(); - var imagesWithoutText = allImages.Where(i => string.Equals(i.Text, "no", StringComparison.OrdinalIgnoreCase)).ToList(); - - const double DesiredAspect = 2.0 / 3; - - programEntry.PrimaryImage = GetProgramImage(ApiUrl, imagesWithText, DesiredAspect, token) ?? - GetProgramImage(ApiUrl, allImages, DesiredAspect, token); - - const double WideAspect = 16.0 / 9; - - programEntry.ThumbImage = GetProgramImage(ApiUrl, imagesWithText, WideAspect, token); - - // Don't supply the same image twice - if (string.Equals(programEntry.PrimaryImage, programEntry.ThumbImage, StringComparison.Ordinal)) - { - programEntry.ThumbImage = null; - } - - programEntry.BackdropImage = GetProgramImage(ApiUrl, imagesWithoutText, WideAspect, token); - - // programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ?? - // GetProgramImage(ApiUrl, data, "Banner-L1", false) ?? - // GetProgramImage(ApiUrl, data, "Banner-LO", false) ?? - // GetProgramImage(ApiUrl, data, "Banner-LOT", false); - } - } - - programsInfo.Add(GetProgram(channelId, schedule, programDict[schedule.ProgramId])); - } - - return programsInfo; - } - - private static int GetSizeOrder(ImageDataDto image) - { - if (int.TryParse(image.Height, out int value)) - { - return value; - } - - return 0; - } - - private static string GetChannelNumber(MapDto map) - { - var channelNumber = map.LogicalChannelNumber; - - if (string.IsNullOrWhiteSpace(channelNumber)) - { - channelNumber = map.Channel; - } - - if (string.IsNullOrWhiteSpace(channelNumber)) - { - channelNumber = map.AtscMajor + "." + map.AtscMinor; - } - - return channelNumber.TrimStart('0'); - } - - private static bool IsMovie(ProgramDetailsDto programInfo) - { - return string.Equals(programInfo.EntityType, "movie", StringComparison.OrdinalIgnoreCase); - } - - private ProgramInfo GetProgram(string channelId, ProgramDto programInfo, ProgramDetailsDto details) - { - if (programInfo.AirDateTime is null) - { - return null; - } - - var startAt = programInfo.AirDateTime.Value; - var endAt = startAt.AddSeconds(programInfo.Duration); - var audioType = ProgramAudio.Stereo; - - var programId = programInfo.ProgramId ?? string.Empty; - - string newID = programId + "T" + startAt.Ticks + "C" + channelId; - - if (programInfo.AudioProperties.Count != 0) - { - if (programInfo.AudioProperties.Contains("atmos", StringComparison.OrdinalIgnoreCase)) - { - audioType = ProgramAudio.Atmos; - } - else if (programInfo.AudioProperties.Contains("dd 5.1", StringComparison.OrdinalIgnoreCase)) - { - audioType = ProgramAudio.DolbyDigital; - } - else if (programInfo.AudioProperties.Contains("dd", StringComparison.OrdinalIgnoreCase)) - { - audioType = ProgramAudio.DolbyDigital; - } - else if (programInfo.AudioProperties.Contains("stereo", StringComparison.OrdinalIgnoreCase)) - { - audioType = ProgramAudio.Stereo; - } - else - { - audioType = ProgramAudio.Mono; - } - } - - string episodeTitle = null; - if (details.EpisodeTitle150 is not null) - { - episodeTitle = details.EpisodeTitle150; - } - - var info = new ProgramInfo - { - ChannelId = channelId, - Id = newID, - StartDate = startAt, - EndDate = endAt, - Name = details.Titles[0].Title120 ?? "Unknown", - OfficialRating = null, - CommunityRating = null, - EpisodeTitle = episodeTitle, - Audio = audioType, - // IsNew = programInfo.@new ?? false, - IsRepeat = programInfo.New is null, - IsSeries = string.Equals(details.EntityType, "episode", StringComparison.OrdinalIgnoreCase), - ImageUrl = details.PrimaryImage, - ThumbImageUrl = details.ThumbImage, - IsKids = string.Equals(details.Audience, "children", StringComparison.OrdinalIgnoreCase), - IsSports = string.Equals(details.EntityType, "sports", StringComparison.OrdinalIgnoreCase), - IsMovie = IsMovie(details), - Etag = programInfo.Md5, - IsLive = string.Equals(programInfo.LiveTapeDelay, "live", StringComparison.OrdinalIgnoreCase), - IsPremiere = programInfo.Premiere || (programInfo.IsPremiereOrFinale ?? string.Empty).Contains("premiere", StringComparison.OrdinalIgnoreCase) - }; - - var showId = programId; - - if (!info.IsSeries) - { - // It's also a series if it starts with SH - info.IsSeries = showId.StartsWith("SH", StringComparison.OrdinalIgnoreCase) && showId.Length >= 14; - } - - // According to SchedulesDirect, these are generic, unidentified episodes - // SH005316560000 - var hasUniqueShowId = !showId.StartsWith("SH", StringComparison.OrdinalIgnoreCase) || - !showId.EndsWith("0000", StringComparison.OrdinalIgnoreCase); - - if (!hasUniqueShowId) - { - showId = newID; - } - - info.ShowId = showId; - - if (programInfo.VideoProperties is not null) - { - info.IsHD = programInfo.VideoProperties.Contains("hdtv", StringComparison.OrdinalIgnoreCase); - info.Is3D = programInfo.VideoProperties.Contains("3d", StringComparison.OrdinalIgnoreCase); - } - - if (details.ContentRating is not null && details.ContentRating.Count > 0) - { - info.OfficialRating = details.ContentRating[0].Code.Replace("TV", "TV-", StringComparison.Ordinal) - .Replace("--", "-", StringComparison.Ordinal); - - var invalid = new[] { "N/A", "Approved", "Not Rated", "Passed" }; - if (invalid.Contains(info.OfficialRating, StringComparison.OrdinalIgnoreCase)) - { - info.OfficialRating = null; - } - } - - if (details.Descriptions is not null) - { - if (details.Descriptions.Description1000 is not null && details.Descriptions.Description1000.Count > 0) - { - info.Overview = details.Descriptions.Description1000[0].Description; - } - else if (details.Descriptions.Description100 is not null && details.Descriptions.Description100.Count > 0) - { - info.Overview = details.Descriptions.Description100[0].Description; - } - } - - if (info.IsSeries) - { - info.SeriesId = programId.Substring(0, 10); - - info.SeriesProviderIds[MetadataProvider.Zap2It.ToString()] = info.SeriesId; - - if (details.Metadata is not null) - { - foreach (var metadataProgram in details.Metadata) - { - var gracenote = metadataProgram.Gracenote; - if (gracenote is not null) - { - info.SeasonNumber = gracenote.Season; - - if (gracenote.Episode > 0) - { - info.EpisodeNumber = gracenote.Episode; - } - - break; - } - } - } - } - - if (details.OriginalAirDate is not null) - { - info.OriginalAirDate = details.OriginalAirDate; - info.ProductionYear = info.OriginalAirDate.Value.Year; - } - - if (details.Movie is not null) - { - if (!string.IsNullOrEmpty(details.Movie.Year) - && int.TryParse(details.Movie.Year, out int year)) - { - info.ProductionYear = year; - } - } - - if (details.Genres is not null) - { - info.Genres = details.Genres.Where(g => !string.IsNullOrWhiteSpace(g)).ToList(); - info.IsNews = details.Genres.Contains("news", StringComparison.OrdinalIgnoreCase); - - if (info.Genres.Contains("children", StringComparison.OrdinalIgnoreCase)) - { - info.IsKids = true; - } - } - - return info; - } - - private static string GetProgramImage(string apiUrl, IEnumerable<ImageDataDto> images, double desiredAspect, string token) - { - var match = images - .OrderBy(i => Math.Abs(desiredAspect - GetAspectRatio(i))) - .ThenByDescending(i => GetSizeOrder(i)) - .FirstOrDefault(); - - if (match is null) - { - return null; - } - - var uri = match.Uri; - - if (string.IsNullOrWhiteSpace(uri)) - { - return null; - } - - if (uri.Contains("http", StringComparison.OrdinalIgnoreCase)) - { - return uri; - } - - return apiUrl + "/image/" + uri + "?token=" + token; - } - - private static double GetAspectRatio(ImageDataDto i) - { - int width = 0; - int height = 0; - - if (!string.IsNullOrWhiteSpace(i.Width)) - { - _ = int.TryParse(i.Width, out width); - } - - if (!string.IsNullOrWhiteSpace(i.Height)) - { - _ = int.TryParse(i.Height, out height); - } - - if (height == 0 || width == 0) - { - return 0; - } - - double result = width; - result /= height; - return result; - } - - private async Task<IReadOnlyList<ShowImagesDto>> GetImageForPrograms( - ListingsProviderInfo info, - IReadOnlyList<string> programIds, - CancellationToken cancellationToken) - { - var token = await GetToken(info, cancellationToken).ConfigureAwait(false); - - if (programIds.Count == 0) - { - return Array.Empty<ShowImagesDto>(); - } - - StringBuilder str = new StringBuilder("[", 1 + (programIds.Count * 13)); - foreach (var i in programIds) - { - str.Append('"') - .Append(i[..10]) - .Append("\","); - } - - // Remove last , - str.Length--; - str.Append(']'); - - using var message = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/metadata/programs") - { - Content = new StringContent(str.ToString(), Encoding.UTF8, MediaTypeNames.Application.Json) - }; - message.Headers.TryAddWithoutValidation("token", token); - - try - { - using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false); - return await innerResponse2.Content.ReadFromJsonAsync<IReadOnlyList<ShowImagesDto>>(_jsonOptions, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error getting image info from schedules direct"); - - return Array.Empty<ShowImagesDto>(); - } - } - - public async Task<List<NameIdPair>> GetHeadends(ListingsProviderInfo info, string country, string location, CancellationToken cancellationToken) - { - var token = await GetToken(info, cancellationToken).ConfigureAwait(false); - - var lineups = new List<NameIdPair>(); - - if (string.IsNullOrWhiteSpace(token)) - { - return lineups; - } - - using var options = new HttpRequestMessage(HttpMethod.Get, ApiUrl + "/headends?country=" + country + "&postalcode=" + location); - options.Headers.TryAddWithoutValidation("token", token); - - try - { - using var httpResponse = await Send(options, false, info, cancellationToken).ConfigureAwait(false); - var root = await httpResponse.Content.ReadFromJsonAsync<IReadOnlyList<HeadendsDto>>(_jsonOptions, cancellationToken).ConfigureAwait(false); - if (root is not null) - { - foreach (HeadendsDto headend in root) - { - foreach (LineupDto lineup in headend.Lineups) - { - lineups.Add(new NameIdPair - { - Name = string.IsNullOrWhiteSpace(lineup.Name) ? lineup.Lineup : lineup.Name, - Id = lineup.Uri?[18..] - }); - } - } - } - else - { - _logger.LogInformation("No lineups available"); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Error getting headends"); - } - - return lineups; - } - - private async Task<string> GetToken(ListingsProviderInfo info, CancellationToken cancellationToken) - { - var username = info.Username; - - // Reset the token if there's no username - if (string.IsNullOrWhiteSpace(username)) - { - return null; - } - - var password = info.Password; - if (string.IsNullOrEmpty(password)) - { - return null; - } - - // Avoid hammering SD - if ((DateTime.UtcNow - _lastErrorResponse).TotalMinutes < 1) - { - return null; - } - - if (!_tokens.TryGetValue(username, out NameValuePair savedToken)) - { - savedToken = new NameValuePair(); - _tokens.TryAdd(username, savedToken); - } - - if (!string.IsNullOrEmpty(savedToken.Name) - && long.TryParse(savedToken.Value, CultureInfo.InvariantCulture, out long ticks)) - { - // If it's under 24 hours old we can still use it - if (DateTime.UtcNow.Ticks - ticks < TimeSpan.FromHours(20).Ticks) - { - return savedToken.Name; - } - } - - await _tokenSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - var result = await GetTokenInternal(username, password, cancellationToken).ConfigureAwait(false); - savedToken.Name = result; - savedToken.Value = DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture); - return result; - } - catch (HttpRequestException ex) - { - if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.BadRequest) - { - _tokens.Clear(); - _lastErrorResponse = DateTime.UtcNow; - } - - throw; - } - finally - { - _tokenSemaphore.Release(); - } - } - - private async Task<HttpResponseMessage> Send( - HttpRequestMessage options, - bool enableRetry, - ListingsProviderInfo providerInfo, - CancellationToken cancellationToken, - HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead) - { - var response = await _httpClientFactory.CreateClient(NamedClient.Default) - .SendAsync(options, completionOption, cancellationToken).ConfigureAwait(false); - if (response.IsSuccessStatusCode) - { - return response; - } - - // Response is automatically disposed in the calling function, - // so dispose manually if not returning. - response.Dispose(); - if (!enableRetry || (int)response.StatusCode >= 500) - { - throw new HttpRequestException( - string.Format(CultureInfo.InvariantCulture, "Request failed: {0}", response.ReasonPhrase), - null, - response.StatusCode); - } - - _tokens.Clear(); - options.Headers.TryAddWithoutValidation("token", await GetToken(providerInfo, cancellationToken).ConfigureAwait(false)); - return await Send(options, false, providerInfo, cancellationToken).ConfigureAwait(false); - } - - private async Task<string> GetTokenInternal( - string username, - string password, - CancellationToken cancellationToken) - { - using var options = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/token"); -#pragma warning disable CA5350 // SchedulesDirect is always SHA1. - var hashedPasswordBytes = SHA1.HashData(Encoding.ASCII.GetBytes(password)); -#pragma warning restore CA5350 - // TODO: remove ToLower when Convert.ToHexString supports lowercase - // Schedules Direct requires the hex to be lowercase - string hashedPassword = Convert.ToHexString(hashedPasswordBytes).ToLowerInvariant(); - options.Content = new StringContent("{\"username\":\"" + username + "\",\"password\":\"" + hashedPassword + "\"}", Encoding.UTF8, MediaTypeNames.Application.Json); - - using var response = await Send(options, false, null, cancellationToken).ConfigureAwait(false); - response.EnsureSuccessStatusCode(); - var root = await response.Content.ReadFromJsonAsync<TokenDto>(_jsonOptions, cancellationToken).ConfigureAwait(false); - if (string.Equals(root?.Message, "OK", StringComparison.Ordinal)) - { - _logger.LogInformation("Authenticated with Schedules Direct token: {Token}", root.Token); - return root.Token; - } - - throw new AuthenticationException("Could not authenticate with Schedules Direct Error: " + root.Message); - } - - private async Task AddLineupToAccount(ListingsProviderInfo info, CancellationToken cancellationToken) - { - var token = await GetToken(info, cancellationToken).ConfigureAwait(false); - - ArgumentException.ThrowIfNullOrEmpty(token); - ArgumentException.ThrowIfNullOrEmpty(info.ListingsId); - - _logger.LogInformation("Adding new LineUp "); - - using var options = new HttpRequestMessage(HttpMethod.Put, ApiUrl + "/lineups/" + info.ListingsId); - options.Headers.TryAddWithoutValidation("token", token); - using var response = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - } - - private async Task<bool> HasLineup(ListingsProviderInfo info, CancellationToken cancellationToken) - { - ArgumentException.ThrowIfNullOrEmpty(info.ListingsId); - - var token = await GetToken(info, cancellationToken).ConfigureAwait(false); - - ArgumentException.ThrowIfNullOrEmpty(token); - - _logger.LogInformation("Headends on account "); - - using var options = new HttpRequestMessage(HttpMethod.Get, ApiUrl + "/lineups"); - options.Headers.TryAddWithoutValidation("token", token); - - try - { - using var httpResponse = await Send(options, false, null, cancellationToken).ConfigureAwait(false); - httpResponse.EnsureSuccessStatusCode(); - var root = await httpResponse.Content.ReadFromJsonAsync<LineupsDto>(_jsonOptions, cancellationToken).ConfigureAwait(false); - return root?.Lineups.Any(i => string.Equals(info.ListingsId, i.Lineup, StringComparison.OrdinalIgnoreCase)) ?? false; - } - catch (HttpRequestException ex) - { - // SchedulesDirect returns 400 if no lineups are configured. - if (ex.StatusCode is HttpStatusCode.BadRequest) - { - return false; - } - - throw; - } - } - - public async Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings) - { - if (validateLogin) - { - ArgumentException.ThrowIfNullOrEmpty(info.Username); - ArgumentException.ThrowIfNullOrEmpty(info.Password); - } - - if (validateListings) - { - ArgumentException.ThrowIfNullOrEmpty(info.ListingsId); - - var hasLineup = await HasLineup(info, CancellationToken.None).ConfigureAwait(false); - - if (!hasLineup) - { - await AddLineupToAccount(info, CancellationToken.None).ConfigureAwait(false); - } - } - } - - public Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location) - { - return GetHeadends(info, country, location, CancellationToken.None); - } - - public async Task<List<ChannelInfo>> GetChannels(ListingsProviderInfo info, CancellationToken cancellationToken) - { - var listingsId = info.ListingsId; - ArgumentException.ThrowIfNullOrEmpty(listingsId); - - var token = await GetToken(info, cancellationToken).ConfigureAwait(false); - - ArgumentException.ThrowIfNullOrEmpty(token); - - using var options = new HttpRequestMessage(HttpMethod.Get, ApiUrl + "/lineups/" + listingsId); - options.Headers.TryAddWithoutValidation("token", token); - - using var httpResponse = await Send(options, true, info, cancellationToken).ConfigureAwait(false); - var root = await httpResponse.Content.ReadFromJsonAsync<ChannelDto>(_jsonOptions, cancellationToken).ConfigureAwait(false); - if (root is null) - { - return new List<ChannelInfo>(); - } - - _logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.Map.Count); - _logger.LogInformation("Mapping Stations to Channel"); - - var allStations = root.Stations; - - var map = root.Map; - var list = new List<ChannelInfo>(map.Count); - foreach (var channel in map) - { - var channelNumber = GetChannelNumber(channel); - - var stationIndex = allStations.FindIndex(item => string.Equals(item.StationId, channel.StationId, StringComparison.OrdinalIgnoreCase)); - var station = stationIndex == -1 - ? new StationDto { StationId = channel.StationId } - : allStations[stationIndex]; - - var channelInfo = new ChannelInfo - { - Id = station.StationId, - CallSign = station.Callsign, - Number = channelNumber, - Name = string.IsNullOrWhiteSpace(station.Name) ? channelNumber : station.Name - }; - - if (station.Logo is not null) - { - channelInfo.ImageUrl = station.Logo.Url; - } - - list.Add(channelInfo); - } - - return list; - } - - /// <inheritdoc /> - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// <summary> - /// Releases unmanaged and optionally managed resources. - /// </summary> - /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> - protected virtual void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - if (disposing) - { - _tokenSemaphore?.Dispose(); - } - - _disposed = true; - } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs deleted file mode 100644 index 95ac996e01..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Broadcaster dto. - /// </summary> - public class BroadcasterDto - { - /// <summary> - /// Gets or sets the city. - /// </summary> - [JsonPropertyName("city")] - public string? City { get; set; } - - /// <summary> - /// Gets or sets the state. - /// </summary> - [JsonPropertyName("state")] - public string? State { get; set; } - - /// <summary> - /// Gets or sets the postal code. - /// </summary> - [JsonPropertyName("postalCode")] - public string? Postalcode { get; set; } - - /// <summary> - /// Gets or sets the country. - /// </summary> - [JsonPropertyName("country")] - public string? Country { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs deleted file mode 100644 index f6251b9ad8..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Caption dto. - /// </summary> - public class CaptionDto - { - /// <summary> - /// Gets or sets the content. - /// </summary> - [JsonPropertyName("content")] - public string? Content { get; set; } - - /// <summary> - /// Gets or sets the lang. - /// </summary> - [JsonPropertyName("lang")] - public string? Lang { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CastDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CastDto.cs deleted file mode 100644 index 0b7a2c63ad..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CastDto.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Cast dto. - /// </summary> - public class CastDto - { - /// <summary> - /// Gets or sets the billing order. - /// </summary> - [JsonPropertyName("billingOrder")] - public string? BillingOrder { get; set; } - - /// <summary> - /// Gets or sets the role. - /// </summary> - [JsonPropertyName("role")] - public string? Role { get; set; } - - /// <summary> - /// Gets or sets the name id. - /// </summary> - [JsonPropertyName("nameId")] - public string? NameId { get; set; } - - /// <summary> - /// Gets or sets the person id. - /// </summary> - [JsonPropertyName("personId")] - public string? PersonId { get; set; } - - /// <summary> - /// Gets or sets the name. - /// </summary> - [JsonPropertyName("name")] - public string? Name { get; set; } - - /// <summary> - /// Gets or sets the character name. - /// </summary> - [JsonPropertyName("characterName")] - public string? CharacterName { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs deleted file mode 100644 index 87c327ed82..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Channel dto. - /// </summary> - public class ChannelDto - { - /// <summary> - /// Gets or sets the list of maps. - /// </summary> - [JsonPropertyName("map")] - public IReadOnlyList<MapDto> Map { get; set; } = Array.Empty<MapDto>(); - - /// <summary> - /// Gets or sets the list of stations. - /// </summary> - [JsonPropertyName("stations")] - public IReadOnlyList<StationDto> Stations { get; set; } = Array.Empty<StationDto>(); - - /// <summary> - /// Gets or sets the metadata. - /// </summary> - [JsonPropertyName("metadata")] - public MetadataDto? Metadata { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs deleted file mode 100644 index c19cd2e48d..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Content rating dto. - /// </summary> - public class ContentRatingDto - { - /// <summary> - /// Gets or sets the body. - /// </summary> - [JsonPropertyName("body")] - public string? Body { get; set; } - - /// <summary> - /// Gets or sets the code. - /// </summary> - [JsonPropertyName("code")] - public string? Code { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs deleted file mode 100644 index f00c9accdd..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Crew dto. - /// </summary> - public class CrewDto - { - /// <summary> - /// Gets or sets the billing order. - /// </summary> - [JsonPropertyName("billingOrder")] - public string? BillingOrder { get; set; } - - /// <summary> - /// Gets or sets the role. - /// </summary> - [JsonPropertyName("role")] - public string? Role { get; set; } - - /// <summary> - /// Gets or sets the name id. - /// </summary> - [JsonPropertyName("nameId")] - public string? NameId { get; set; } - - /// <summary> - /// Gets or sets the person id. - /// </summary> - [JsonPropertyName("personId")] - public string? PersonId { get; set; } - - /// <summary> - /// Gets or sets the name. - /// </summary> - [JsonPropertyName("name")] - public string? Name { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DayDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DayDto.cs deleted file mode 100644 index 1a371965cf..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DayDto.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Day dto. - /// </summary> - public class DayDto - { - /// <summary> - /// Gets or sets the station id. - /// </summary> - [JsonPropertyName("stationID")] - public string? StationId { get; set; } - - /// <summary> - /// Gets or sets the list of programs. - /// </summary> - [JsonPropertyName("programs")] - public IReadOnlyList<ProgramDto> Programs { get; set; } = Array.Empty<ProgramDto>(); - - /// <summary> - /// Gets or sets the metadata schedule. - /// </summary> - [JsonPropertyName("metadata")] - public MetadataScheduleDto? Metadata { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs deleted file mode 100644 index ca6ae7fb13..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Description 1_000 dto. - /// </summary> - public class Description1000Dto - { - /// <summary> - /// Gets or sets the description language. - /// </summary> - [JsonPropertyName("descriptionLanguage")] - public string? DescriptionLanguage { get; set; } - - /// <summary> - /// Gets or sets the description. - /// </summary> - [JsonPropertyName("description")] - public string? Description { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs deleted file mode 100644 index 1577219ed2..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Description 100 dto. - /// </summary> - public class Description100Dto - { - /// <summary> - /// Gets or sets the description language. - /// </summary> - [JsonPropertyName("descriptionLanguage")] - public string? DescriptionLanguage { get; set; } - - /// <summary> - /// Gets or sets the description. - /// </summary> - [JsonPropertyName("description")] - public string? Description { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs deleted file mode 100644 index eaf4a340bd..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Descriptions program dto. - /// </summary> - public class DescriptionsProgramDto - { - /// <summary> - /// Gets or sets the list of description 100. - /// </summary> - [JsonPropertyName("description100")] - public IReadOnlyList<Description100Dto> Description100 { get; set; } = Array.Empty<Description100Dto>(); - - /// <summary> - /// Gets or sets the list of description1000. - /// </summary> - [JsonPropertyName("description1000")] - public IReadOnlyList<Description1000Dto> Description1000 { get; set; } = Array.Empty<Description1000Dto>(); - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs deleted file mode 100644 index fbdfb1f716..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Event details dto. - /// </summary> - public class EventDetailsDto - { - /// <summary> - /// Gets or sets the sub type. - /// </summary> - [JsonPropertyName("subType")] - public string? SubType { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs deleted file mode 100644 index 6852d89d71..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Gracenote dto. - /// </summary> - public class GracenoteDto - { - /// <summary> - /// Gets or sets the season. - /// </summary> - [JsonPropertyName("season")] - public int Season { get; set; } - - /// <summary> - /// Gets or sets the episode. - /// </summary> - [JsonPropertyName("episode")] - public int Episode { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs deleted file mode 100644 index b9844562f3..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Headends dto. - /// </summary> - public class HeadendsDto - { - /// <summary> - /// Gets or sets the headend. - /// </summary> - [JsonPropertyName("headend")] - public string? Headend { get; set; } - - /// <summary> - /// Gets or sets the transport. - /// </summary> - [JsonPropertyName("transport")] - public string? Transport { get; set; } - - /// <summary> - /// Gets or sets the location. - /// </summary> - [JsonPropertyName("location")] - public string? Location { get; set; } - - /// <summary> - /// Gets or sets the list of lineups. - /// </summary> - [JsonPropertyName("lineups")] - public IReadOnlyList<LineupDto> Lineups { get; set; } = Array.Empty<LineupDto>(); - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs deleted file mode 100644 index a1ae3ca6d4..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Image data dto. - /// </summary> - public class ImageDataDto - { - /// <summary> - /// Gets or sets the width. - /// </summary> - [JsonPropertyName("width")] - public string? Width { get; set; } - - /// <summary> - /// Gets or sets the height. - /// </summary> - [JsonPropertyName("height")] - public string? Height { get; set; } - - /// <summary> - /// Gets or sets the uri. - /// </summary> - [JsonPropertyName("uri")] - public string? Uri { get; set; } - - /// <summary> - /// Gets or sets the size. - /// </summary> - [JsonPropertyName("size")] - public string? Size { get; set; } - - /// <summary> - /// Gets or sets the aspect. - /// </summary> - [JsonPropertyName("aspect")] - public string? Aspect { get; set; } - - /// <summary> - /// Gets or sets the category. - /// </summary> - [JsonPropertyName("category")] - public string? Category { get; set; } - - /// <summary> - /// Gets or sets the text. - /// </summary> - [JsonPropertyName("text")] - public string? Text { get; set; } - - /// <summary> - /// Gets or sets the primary. - /// </summary> - [JsonPropertyName("primary")] - public string? Primary { get; set; } - - /// <summary> - /// Gets or sets the tier. - /// </summary> - [JsonPropertyName("tier")] - public string? Tier { get; set; } - - /// <summary> - /// Gets or sets the caption. - /// </summary> - [JsonPropertyName("caption")] - public CaptionDto? Caption { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs deleted file mode 100644 index 3dc64e5d8a..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// The lineup dto. - /// </summary> - public class LineupDto - { - /// <summary> - /// Gets or sets the linup. - /// </summary> - [JsonPropertyName("lineup")] - public string? Lineup { get; set; } - - /// <summary> - /// Gets or sets the lineup name. - /// </summary> - [JsonPropertyName("name")] - public string? Name { get; set; } - - /// <summary> - /// Gets or sets the transport. - /// </summary> - [JsonPropertyName("transport")] - public string? Transport { get; set; } - - /// <summary> - /// Gets or sets the location. - /// </summary> - [JsonPropertyName("location")] - public string? Location { get; set; } - - /// <summary> - /// Gets or sets the uri. - /// </summary> - [JsonPropertyName("uri")] - public string? Uri { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether this lineup was deleted. - /// </summary> - [JsonPropertyName("isDeleted")] - public bool? IsDeleted { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs deleted file mode 100644 index f190817813..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Lineups dto. - /// </summary> - public class LineupsDto - { - /// <summary> - /// Gets or sets the response code. - /// </summary> - [JsonPropertyName("code")] - public int Code { get; set; } - - /// <summary> - /// Gets or sets the server id. - /// </summary> - [JsonPropertyName("serverID")] - public string? ServerId { get; set; } - - /// <summary> - /// Gets or sets the datetime. - /// </summary> - [JsonPropertyName("datetime")] - public DateTime? LineupTimestamp { get; set; } - - /// <summary> - /// Gets or sets the list of lineups. - /// </summary> - [JsonPropertyName("lineups")] - public IReadOnlyList<LineupDto> Lineups { get; set; } = Array.Empty<LineupDto>(); - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs deleted file mode 100644 index fecc55e037..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Logo dto. - /// </summary> - public class LogoDto - { - /// <summary> - /// Gets or sets the url. - /// </summary> - [JsonPropertyName("URL")] - public string? Url { get; set; } - - /// <summary> - /// Gets or sets the height. - /// </summary> - [JsonPropertyName("height")] - public int Height { get; set; } - - /// <summary> - /// Gets or sets the width. - /// </summary> - [JsonPropertyName("width")] - public int Width { get; set; } - - /// <summary> - /// Gets or sets the md5. - /// </summary> - [JsonPropertyName("md5")] - public string? Md5 { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs deleted file mode 100644 index ffd02d474b..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Map dto. - /// </summary> - public class MapDto - { - /// <summary> - /// Gets or sets the station id. - /// </summary> - [JsonPropertyName("stationID")] - public string? StationId { get; set; } - - /// <summary> - /// Gets or sets the channel. - /// </summary> - [JsonPropertyName("channel")] - public string? Channel { get; set; } - - /// <summary> - /// Gets or sets the provider callsign. - /// </summary> - [JsonPropertyName("providerCallsign")] - public string? ProvderCallsign { get; set; } - - /// <summary> - /// Gets or sets the logical channel number. - /// </summary> - [JsonPropertyName("logicalChannelNumber")] - public string? LogicalChannelNumber { get; set; } - - /// <summary> - /// Gets or sets the uhfvhf. - /// </summary> - [JsonPropertyName("uhfVhf")] - public int UhfVhf { get; set; } - - /// <summary> - /// Gets or sets the atsc major. - /// </summary> - [JsonPropertyName("atscMajor")] - public int AtscMajor { get; set; } - - /// <summary> - /// Gets or sets the atsc minor. - /// </summary> - [JsonPropertyName("atscMinor")] - public int AtscMinor { get; set; } - - /// <summary> - /// Gets or sets the match type. - /// </summary> - [JsonPropertyName("matchType")] - public string? MatchType { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs deleted file mode 100644 index 40faa493c5..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Metadata dto. - /// </summary> - public class MetadataDto - { - /// <summary> - /// Gets or sets the linup. - /// </summary> - [JsonPropertyName("lineup")] - public string? Lineup { get; set; } - - /// <summary> - /// Gets or sets the modified timestamp. - /// </summary> - [JsonPropertyName("modified")] - public string? Modified { get; set; } - - /// <summary> - /// Gets or sets the transport. - /// </summary> - [JsonPropertyName("transport")] - public string? Transport { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs deleted file mode 100644 index 43f2901566..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Metadata programs dto. - /// </summary> - public class MetadataProgramsDto - { - /// <summary> - /// Gets or sets the gracenote object. - /// </summary> - [JsonPropertyName("Gracenote")] - public GracenoteDto? Gracenote { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs deleted file mode 100644 index 04560ab55d..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Metadata schedule dto. - /// </summary> - public class MetadataScheduleDto - { - /// <summary> - /// Gets or sets the modified timestamp. - /// </summary> - [JsonPropertyName("modified")] - public string? Modified { get; set; } - - /// <summary> - /// Gets or sets the md5. - /// </summary> - [JsonPropertyName("md5")] - public string? Md5 { get; set; } - - /// <summary> - /// Gets or sets the start date. - /// </summary> - [JsonPropertyName("startDate")] - public DateTime? StartDate { get; set; } - - /// <summary> - /// Gets or sets the end date. - /// </summary> - [JsonPropertyName("endDate")] - public DateTime? EndDate { get; set; } - - /// <summary> - /// Gets or sets the days count. - /// </summary> - [JsonPropertyName("days")] - public int Days { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs deleted file mode 100644 index 31bef423b2..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Movie dto. - /// </summary> - public class MovieDto - { - /// <summary> - /// Gets or sets the year. - /// </summary> - [JsonPropertyName("year")] - public string? Year { get; set; } - - /// <summary> - /// Gets or sets the duration. - /// </summary> - [JsonPropertyName("duration")] - public int Duration { get; set; } - - /// <summary> - /// Gets or sets the list of quality rating. - /// </summary> - [JsonPropertyName("qualityRating")] - public IReadOnlyList<QualityRatingDto> QualityRating { get; set; } = Array.Empty<QualityRatingDto>(); - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs deleted file mode 100644 index e8b15dc07c..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Multipart dto. - /// </summary> - public class MultipartDto - { - /// <summary> - /// Gets or sets the part number. - /// </summary> - [JsonPropertyName("partNumber")] - public int PartNumber { get; set; } - - /// <summary> - /// Gets or sets the total parts. - /// </summary> - [JsonPropertyName("totalParts")] - public int TotalParts { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs deleted file mode 100644 index 84c48f67f3..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Program details dto. - /// </summary> - public class ProgramDetailsDto - { - /// <summary> - /// Gets or sets the audience. - /// </summary> - [JsonPropertyName("audience")] - public string? Audience { get; set; } - - /// <summary> - /// Gets or sets the program id. - /// </summary> - [JsonPropertyName("programID")] - public string? ProgramId { get; set; } - - /// <summary> - /// Gets or sets the list of titles. - /// </summary> - [JsonPropertyName("titles")] - public IReadOnlyList<TitleDto> Titles { get; set; } = Array.Empty<TitleDto>(); - - /// <summary> - /// Gets or sets the event details object. - /// </summary> - [JsonPropertyName("eventDetails")] - public EventDetailsDto? EventDetails { get; set; } - - /// <summary> - /// Gets or sets the descriptions. - /// </summary> - [JsonPropertyName("descriptions")] - public DescriptionsProgramDto? Descriptions { get; set; } - - /// <summary> - /// Gets or sets the original air date. - /// </summary> - [JsonPropertyName("originalAirDate")] - public DateTime? OriginalAirDate { get; set; } - - /// <summary> - /// Gets or sets the list of genres. - /// </summary> - [JsonPropertyName("genres")] - public IReadOnlyList<string> Genres { get; set; } = Array.Empty<string>(); - - /// <summary> - /// Gets or sets the episode title. - /// </summary> - [JsonPropertyName("episodeTitle150")] - public string? EpisodeTitle150 { get; set; } - - /// <summary> - /// Gets or sets the list of metadata. - /// </summary> - [JsonPropertyName("metadata")] - public IReadOnlyList<MetadataProgramsDto> Metadata { get; set; } = Array.Empty<MetadataProgramsDto>(); - - /// <summary> - /// Gets or sets the list of content raitings. - /// </summary> - [JsonPropertyName("contentRating")] - public IReadOnlyList<ContentRatingDto> ContentRating { get; set; } = Array.Empty<ContentRatingDto>(); - - /// <summary> - /// Gets or sets the list of cast. - /// </summary> - [JsonPropertyName("cast")] - public IReadOnlyList<CastDto> Cast { get; set; } = Array.Empty<CastDto>(); - - /// <summary> - /// Gets or sets the list of crew. - /// </summary> - [JsonPropertyName("crew")] - public IReadOnlyList<CrewDto> Crew { get; set; } = Array.Empty<CrewDto>(); - - /// <summary> - /// Gets or sets the entity type. - /// </summary> - [JsonPropertyName("entityType")] - public string? EntityType { get; set; } - - /// <summary> - /// Gets or sets the show type. - /// </summary> - [JsonPropertyName("showType")] - public string? ShowType { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether there is image artwork. - /// </summary> - [JsonPropertyName("hasImageArtwork")] - public bool HasImageArtwork { get; set; } - - /// <summary> - /// Gets or sets the primary image. - /// </summary> - [JsonPropertyName("primaryImage")] - public string? PrimaryImage { get; set; } - - /// <summary> - /// Gets or sets the thumb image. - /// </summary> - [JsonPropertyName("thumbImage")] - public string? ThumbImage { get; set; } - - /// <summary> - /// Gets or sets the backdrop image. - /// </summary> - [JsonPropertyName("backdropImage")] - public string? BackdropImage { get; set; } - - /// <summary> - /// Gets or sets the banner image. - /// </summary> - [JsonPropertyName("bannerImage")] - public string? BannerImage { get; set; } - - /// <summary> - /// Gets or sets the image id. - /// </summary> - [JsonPropertyName("imageID")] - public string? ImageId { get; set; } - - /// <summary> - /// Gets or sets the md5. - /// </summary> - [JsonPropertyName("md5")] - public string? Md5 { get; set; } - - /// <summary> - /// Gets or sets the list of content advisory. - /// </summary> - [JsonPropertyName("contentAdvisory")] - public IReadOnlyList<string> ContentAdvisory { get; set; } = Array.Empty<string>(); - - /// <summary> - /// Gets or sets the movie object. - /// </summary> - [JsonPropertyName("movie")] - public MovieDto? Movie { get; set; } - - /// <summary> - /// Gets or sets the list of recommendations. - /// </summary> - [JsonPropertyName("recommendations")] - public IReadOnlyList<RecommendationDto> Recommendations { get; set; } = Array.Empty<RecommendationDto>(); - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs deleted file mode 100644 index 60389b45bf..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Program dto. - /// </summary> - public class ProgramDto - { - /// <summary> - /// Gets or sets the program id. - /// </summary> - [JsonPropertyName("programID")] - public string? ProgramId { get; set; } - - /// <summary> - /// Gets or sets the air date time. - /// </summary> - [JsonPropertyName("airDateTime")] - public DateTime? AirDateTime { get; set; } - - /// <summary> - /// Gets or sets the duration. - /// </summary> - [JsonPropertyName("duration")] - public int Duration { get; set; } - - /// <summary> - /// Gets or sets the md5. - /// </summary> - [JsonPropertyName("md5")] - public string? Md5 { get; set; } - - /// <summary> - /// Gets or sets the list of audio properties. - /// </summary> - [JsonPropertyName("audioProperties")] - public IReadOnlyList<string> AudioProperties { get; set; } = Array.Empty<string>(); - - /// <summary> - /// Gets or sets the list of video properties. - /// </summary> - [JsonPropertyName("videoProperties")] - public IReadOnlyList<string> VideoProperties { get; set; } = Array.Empty<string>(); - - /// <summary> - /// Gets or sets the list of ratings. - /// </summary> - [JsonPropertyName("ratings")] - public IReadOnlyList<RatingDto> Ratings { get; set; } = Array.Empty<RatingDto>(); - - /// <summary> - /// Gets or sets a value indicating whether this program is new. - /// </summary> - [JsonPropertyName("new")] - public bool? New { get; set; } - - /// <summary> - /// Gets or sets the multipart object. - /// </summary> - [JsonPropertyName("multipart")] - public MultipartDto? Multipart { get; set; } - - /// <summary> - /// Gets or sets the live tape delay. - /// </summary> - [JsonPropertyName("liveTapeDelay")] - public string? LiveTapeDelay { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether this is the premiere. - /// </summary> - [JsonPropertyName("premiere")] - public bool Premiere { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether this is a repeat. - /// </summary> - [JsonPropertyName("repeat")] - public bool Repeat { get; set; } - - /// <summary> - /// Gets or sets the premiere or finale. - /// </summary> - [JsonPropertyName("isPremiereOrFinale")] - public string? IsPremiereOrFinale { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs deleted file mode 100644 index c5ddcf7c51..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Quality rating dto. - /// </summary> - public class QualityRatingDto - { - /// <summary> - /// Gets or sets the ratings body. - /// </summary> - [JsonPropertyName("ratingsBody")] - public string? RatingsBody { get; set; } - - /// <summary> - /// Gets or sets the rating. - /// </summary> - [JsonPropertyName("rating")] - public string? Rating { get; set; } - - /// <summary> - /// Gets or sets the min rating. - /// </summary> - [JsonPropertyName("minRating")] - public string? MinRating { get; set; } - - /// <summary> - /// Gets or sets the max rating. - /// </summary> - [JsonPropertyName("maxRating")] - public string? MaxRating { get; set; } - - /// <summary> - /// Gets or sets the increment. - /// </summary> - [JsonPropertyName("increment")] - public string? Increment { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs deleted file mode 100644 index e04b619a4d..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Rating dto. - /// </summary> - public class RatingDto - { - /// <summary> - /// Gets or sets the body. - /// </summary> - [JsonPropertyName("body")] - public string? Body { get; set; } - - /// <summary> - /// Gets or sets the code. - /// </summary> - [JsonPropertyName("code")] - public string? Code { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs deleted file mode 100644 index c8f79fd1c2..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Recommendation dto. - /// </summary> - public class RecommendationDto - { - /// <summary> - /// Gets or sets the program id. - /// </summary> - [JsonPropertyName("programID")] - public string? ProgramId { get; set; } - - /// <summary> - /// Gets or sets the title. - /// </summary> - [JsonPropertyName("title120")] - public string? Title120 { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs deleted file mode 100644 index 0cd05709b3..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Request schedule for channel dto. - /// </summary> - public class RequestScheduleForChannelDto - { - /// <summary> - /// Gets or sets the station id. - /// </summary> - [JsonPropertyName("stationID")] - public string? StationId { get; set; } - - /// <summary> - /// Gets or sets the list of dates. - /// </summary> - [JsonPropertyName("date")] - public IReadOnlyList<string> Date { get; set; } = Array.Empty<string>(); - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs deleted file mode 100644 index 84e224b71e..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Show image dto. - /// </summary> - public class ShowImagesDto - { - /// <summary> - /// Gets or sets the program id. - /// </summary> - [JsonPropertyName("programID")] - public string? ProgramId { get; set; } - - /// <summary> - /// Gets or sets the list of data. - /// </summary> - [JsonPropertyName("data")] - public IReadOnlyList<ImageDataDto> Data { get; set; } = Array.Empty<ImageDataDto>(); - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/StationDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/StationDto.cs deleted file mode 100644 index d797fd49b1..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/StationDto.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Station dto. - /// </summary> - public class StationDto - { - /// <summary> - /// Gets or sets the station id. - /// </summary> - [JsonPropertyName("stationID")] - public string? StationId { get; set; } - - /// <summary> - /// Gets or sets the name. - /// </summary> - [JsonPropertyName("name")] - public string? Name { get; set; } - - /// <summary> - /// Gets or sets the callsign. - /// </summary> - [JsonPropertyName("callsign")] - public string? Callsign { get; set; } - - /// <summary> - /// Gets or sets the broadcast language. - /// </summary> - [JsonPropertyName("broadcastLanguage")] - public IReadOnlyList<string> BroadcastLanguage { get; set; } = Array.Empty<string>(); - - /// <summary> - /// Gets or sets the description language. - /// </summary> - [JsonPropertyName("descriptionLanguage")] - public IReadOnlyList<string> DescriptionLanguage { get; set; } = Array.Empty<string>(); - - /// <summary> - /// Gets or sets the broadcaster. - /// </summary> - [JsonPropertyName("broadcaster")] - public BroadcasterDto? Broadcaster { get; set; } - - /// <summary> - /// Gets or sets the affiliate. - /// </summary> - [JsonPropertyName("affiliate")] - public string? Affiliate { get; set; } - - /// <summary> - /// Gets or sets the logo. - /// </summary> - [JsonPropertyName("logo")] - public LogoDto? Logo { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether it is commercial free. - /// </summary> - [JsonPropertyName("isCommercialFree")] - public bool? IsCommercialFree { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs deleted file mode 100644 index 61cd4a9b00..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// Title dto. - /// </summary> - public class TitleDto - { - /// <summary> - /// Gets or sets the title. - /// </summary> - [JsonPropertyName("title120")] - public string? Title120 { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs deleted file mode 100644 index afb9994869..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos -{ - /// <summary> - /// The token dto. - /// </summary> - public class TokenDto - { - /// <summary> - /// Gets or sets the response code. - /// </summary> - [JsonPropertyName("code")] - public int Code { get; set; } - - /// <summary> - /// Gets or sets the response message. - /// </summary> - [JsonPropertyName("message")] - public string? Message { get; set; } - - /// <summary> - /// Gets or sets the server id. - /// </summary> - [JsonPropertyName("serverID")] - public string? ServerId { get; set; } - - /// <summary> - /// Gets or sets the token. - /// </summary> - [JsonPropertyName("token")] - public string? Token { get; set; } - - /// <summary> - /// Gets or sets the current datetime. - /// </summary> - [JsonPropertyName("datetime")] - public DateTime? TokenTimestamp { get; set; } - - /// <summary> - /// Gets or sets the response message. - /// </summary> - [JsonPropertyName("response")] - public string? Response { get; set; } - } -} diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs deleted file mode 100644 index e60e9dcc1c..0000000000 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ /dev/null @@ -1,267 +0,0 @@ -#nullable disable - -#pragma warning disable CS1591 - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Jellyfin.Extensions; -using Jellyfin.XmlTv; -using Jellyfin.XmlTv.Entities; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.LiveTv; -using Microsoft.Extensions.Logging; - -namespace Emby.Server.Implementations.LiveTv.Listings -{ - public class XmlTvListingsProvider : IListingsProvider - { - private static readonly TimeSpan _maxCacheAge = TimeSpan.FromHours(1); - - private readonly IServerConfigurationManager _config; - private readonly IHttpClientFactory _httpClientFactory; - private readonly ILogger<XmlTvListingsProvider> _logger; - - public XmlTvListingsProvider( - IServerConfigurationManager config, - IHttpClientFactory httpClientFactory, - ILogger<XmlTvListingsProvider> logger) - { - _config = config; - _httpClientFactory = httpClientFactory; - _logger = logger; - } - - public string Name => "XmlTV"; - - public string Type => "xmltv"; - - private string GetLanguage(ListingsProviderInfo info) - { - if (!string.IsNullOrWhiteSpace(info.PreferredLanguage)) - { - return info.PreferredLanguage; - } - - return _config.Configuration.PreferredMetadataLanguage; - } - - private async Task<string> GetXml(ListingsProviderInfo info, CancellationToken cancellationToken) - { - _logger.LogInformation("xmltv path: {Path}", info.Path); - - string cacheFilename = info.Id + ".xml"; - string cacheFile = Path.Combine(_config.ApplicationPaths.CachePath, "xmltv", cacheFilename); - - if (File.Exists(cacheFile) && File.GetLastWriteTimeUtc(cacheFile) >= DateTime.UtcNow.Subtract(_maxCacheAge)) - { - return cacheFile; - } - - // Must check if file exists as parent directory may not exist. - if (File.Exists(cacheFile)) - { - File.Delete(cacheFile); - } - else - { - Directory.CreateDirectory(Path.GetDirectoryName(cacheFile)); - } - - if (info.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) - { - _logger.LogInformation("Downloading xmltv listings from {Path}", info.Path); - - using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(info.Path, cancellationToken).ConfigureAwait(false); - var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - await using (stream.ConfigureAwait(false)) - { - return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false); - } - } - else - { - var stream = AsyncFile.OpenRead(info.Path); - await using (stream.ConfigureAwait(false)) - { - return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false); - } - } - } - - private async Task<string> UnzipIfNeededAndCopy(string originalUrl, Stream stream, string file, CancellationToken cancellationToken) - { - var fileStream = new FileStream( - file, - FileMode.CreateNew, - FileAccess.Write, - FileShare.None, - IODefaults.FileStreamBufferSize, - FileOptions.Asynchronous); - - await using (fileStream.ConfigureAwait(false)) - { - if (Path.GetExtension(originalUrl.AsSpan().LeftPart('?')).Equals(".gz", StringComparison.OrdinalIgnoreCase)) - { - try - { - using var reader = new GZipStream(stream, CompressionMode.Decompress); - await reader.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error extracting from gz file {File}", originalUrl); - } - } - else - { - await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); - } - - return file; - } - } - - public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(channelId)) - { - throw new ArgumentNullException(nameof(channelId)); - } - - _logger.LogDebug("Getting xmltv programs for channel {Id}", channelId); - - string path = await GetXml(info, cancellationToken).ConfigureAwait(false); - _logger.LogDebug("Opening XmlTvReader for {Path}", path); - var reader = new XmlTvReader(path, GetLanguage(info)); - - return reader.GetProgrammes(channelId, startDateUtc, endDateUtc, cancellationToken) - .Select(p => GetProgramInfo(p, info)); - } - - private static ProgramInfo GetProgramInfo(XmlTvProgram program, ListingsProviderInfo info) - { - string episodeTitle = program.Episode.Title; - var programCategories = program.Categories.Where(c => !string.IsNullOrWhiteSpace(c)).ToList(); - - var programInfo = new ProgramInfo - { - ChannelId = program.ChannelId, - EndDate = program.EndDate.UtcDateTime, - EpisodeNumber = program.Episode.Episode, - EpisodeTitle = episodeTitle, - Genres = programCategories, - StartDate = program.StartDate.UtcDateTime, - Name = program.Title, - Overview = program.Description, - ProductionYear = program.CopyrightDate?.Year, - SeasonNumber = program.Episode.Series, - IsSeries = program.Episode.Series is not null, - IsRepeat = program.IsPreviouslyShown && !program.IsNew, - IsPremiere = program.Premiere is not null, - IsKids = programCategories.Any(c => info.KidsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), - IsMovie = programCategories.Any(c => info.MovieCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), - IsNews = programCategories.Any(c => info.NewsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), - IsSports = programCategories.Any(c => info.SportsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), - ImageUrl = string.IsNullOrEmpty(program.Icon?.Source) ? null : program.Icon.Source, - HasImage = !string.IsNullOrEmpty(program.Icon?.Source), - OfficialRating = string.IsNullOrEmpty(program.Rating?.Value) ? null : program.Rating.Value, - CommunityRating = program.StarRating, - SeriesId = program.Episode.Episode is null ? null : program.Title?.GetMD5().ToString("N", CultureInfo.InvariantCulture) - }; - - if (string.IsNullOrWhiteSpace(program.ProgramId)) - { - string uniqueString = (program.Title ?? string.Empty) + (episodeTitle ?? string.Empty); - - if (programInfo.SeasonNumber.HasValue) - { - uniqueString = "-" + programInfo.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture); - } - - if (programInfo.EpisodeNumber.HasValue) - { - uniqueString = "-" + programInfo.EpisodeNumber.Value.ToString(CultureInfo.InvariantCulture); - } - - programInfo.ShowId = uniqueString.GetMD5().ToString("N", CultureInfo.InvariantCulture); - - // If we don't have valid episode info, assume it's a unique program, otherwise recordings might be skipped - if (programInfo.IsSeries - && !programInfo.IsRepeat - && (programInfo.EpisodeNumber ?? 0) == 0) - { - programInfo.ShowId += programInfo.StartDate.Ticks.ToString(CultureInfo.InvariantCulture); - } - } - else - { - programInfo.ShowId = program.ProgramId; - } - - // Construct an id from the channel and start date - programInfo.Id = string.Format(CultureInfo.InvariantCulture, "{0}_{1:O}", program.ChannelId, program.StartDate); - - if (programInfo.IsMovie) - { - programInfo.IsSeries = false; - programInfo.EpisodeNumber = null; - programInfo.EpisodeTitle = null; - } - - return programInfo; - } - - public Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings) - { - // Assume all urls are valid. check files for existence - if (!info.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase) && !File.Exists(info.Path)) - { - throw new FileNotFoundException("Could not find the XmlTv file specified:", info.Path); - } - - return Task.CompletedTask; - } - - public async Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location) - { - // In theory this should never be called because there is always only one lineup - string path = await GetXml(info, CancellationToken.None).ConfigureAwait(false); - _logger.LogDebug("Opening XmlTvReader for {Path}", path); - var reader = new XmlTvReader(path, GetLanguage(info)); - IEnumerable<XmlTvChannel> results = reader.GetChannels(); - - // Should this method be async? - return results.Select(c => new NameIdPair() { Id = c.Id, Name = c.DisplayName }).ToList(); - } - - public async Task<List<ChannelInfo>> GetChannels(ListingsProviderInfo info, CancellationToken cancellationToken) - { - // In theory this should never be called because there is always only one lineup - string path = await GetXml(info, cancellationToken).ConfigureAwait(false); - _logger.LogDebug("Opening XmlTvReader for {Path}", path); - var reader = new XmlTvReader(path, GetLanguage(info)); - var results = reader.GetChannels(); - - // Should this method be async? - return results.Select(c => new ChannelInfo - { - Id = c.Id, - Name = c.DisplayName, - ImageUrl = string.IsNullOrEmpty(c.Icon?.Source) ? null : c.Icon.Source, - Number = string.IsNullOrWhiteSpace(c.Number) ? c.Id : c.Number - }).ToList(); - } - } -} |
