2016-02-19 06:20:18 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
2016-08-30 18:17:37 +00:00
|
|
|
|
using System.Linq;
|
2016-02-21 17:22:13 +00:00
|
|
|
|
using System.Text.RegularExpressions;
|
2016-02-19 06:20:18 +00:00
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
2016-10-25 19:02:04 +00:00
|
|
|
|
using MediaBrowser.Model.IO;
|
2016-02-19 06:20:18 +00:00
|
|
|
|
using MediaBrowser.Common.Extensions;
|
2016-10-25 19:02:04 +00:00
|
|
|
|
using MediaBrowser.Common.IO;
|
2016-02-19 06:20:18 +00:00
|
|
|
|
using MediaBrowser.Common.Net;
|
2016-10-18 18:35:27 +00:00
|
|
|
|
using MediaBrowser.Controller;
|
2016-10-25 19:02:04 +00:00
|
|
|
|
using MediaBrowser.Controller.IO;
|
2016-02-19 06:20:18 +00:00
|
|
|
|
using MediaBrowser.Controller.LiveTv;
|
|
|
|
|
using MediaBrowser.Model.Logging;
|
|
|
|
|
|
|
|
|
|
namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
|
|
|
|
{
|
|
|
|
|
public class M3uParser
|
|
|
|
|
{
|
|
|
|
|
private readonly ILogger _logger;
|
|
|
|
|
private readonly IFileSystem _fileSystem;
|
|
|
|
|
private readonly IHttpClient _httpClient;
|
2016-10-18 18:35:27 +00:00
|
|
|
|
private readonly IServerApplicationHost _appHost;
|
2016-02-19 06:20:18 +00:00
|
|
|
|
|
2016-10-18 18:35:27 +00:00
|
|
|
|
public M3uParser(ILogger logger, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost)
|
2016-02-19 06:20:18 +00:00
|
|
|
|
{
|
|
|
|
|
_logger = logger;
|
|
|
|
|
_fileSystem = fileSystem;
|
|
|
|
|
_httpClient = httpClient;
|
2016-10-18 18:35:27 +00:00
|
|
|
|
_appHost = appHost;
|
2016-02-19 06:20:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-24 19:06:26 +00:00
|
|
|
|
public async Task<List<M3UChannel>> Parse(string url, string channelIdPrefix, string tunerHostId, CancellationToken cancellationToken)
|
2016-02-19 06:20:18 +00:00
|
|
|
|
{
|
|
|
|
|
var urlHash = url.GetMD5().ToString("N");
|
|
|
|
|
|
|
|
|
|
// Read the file and display it line by line.
|
|
|
|
|
using (var reader = new StreamReader(await GetListingsStream(url, cancellationToken).ConfigureAwait(false)))
|
|
|
|
|
{
|
2016-02-24 19:06:26 +00:00
|
|
|
|
return GetChannels(reader, urlHash, channelIdPrefix, tunerHostId);
|
2016-02-19 06:20:18 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task<Stream> GetListingsStream(string url, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
2016-10-18 18:35:27 +00:00
|
|
|
|
return _httpClient.Get(new HttpRequestOptions
|
|
|
|
|
{
|
|
|
|
|
Url = url,
|
|
|
|
|
CancellationToken = cancellationToken,
|
|
|
|
|
// Some data providers will require a user agent
|
|
|
|
|
UserAgent = _appHost.FriendlyName + "/" + _appHost.ApplicationVersion
|
|
|
|
|
});
|
2016-02-19 06:20:18 +00:00
|
|
|
|
}
|
|
|
|
|
return Task.FromResult(_fileSystem.OpenRead(url));
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-24 19:06:26 +00:00
|
|
|
|
private List<M3UChannel> GetChannels(StreamReader reader, string urlHash, string channelIdPrefix, string tunerHostId)
|
2016-02-19 06:20:18 +00:00
|
|
|
|
{
|
|
|
|
|
var channels = new List<M3UChannel>();
|
|
|
|
|
string line;
|
2016-02-23 00:48:30 +00:00
|
|
|
|
string extInf = "";
|
|
|
|
|
while ((line = reader.ReadLine()) != null)
|
2016-02-19 06:20:18 +00:00
|
|
|
|
{
|
|
|
|
|
line = line.Trim();
|
|
|
|
|
if (string.IsNullOrWhiteSpace(line))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (line.StartsWith("#EXTINF:", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
2016-02-23 00:48:30 +00:00
|
|
|
|
extInf = line.Substring(8).Trim();
|
|
|
|
|
_logger.Info("Found m3u channel: {0}", extInf);
|
2016-02-19 06:20:18 +00:00
|
|
|
|
}
|
2016-03-19 01:40:13 +00:00
|
|
|
|
else if (!string.IsNullOrWhiteSpace(extInf) && !line.StartsWith("#", StringComparison.OrdinalIgnoreCase))
|
2016-02-24 19:06:26 +00:00
|
|
|
|
{
|
2016-08-30 18:17:37 +00:00
|
|
|
|
var channel = GetChannelnfo(extInf, tunerHostId, line);
|
2016-02-23 01:09:08 +00:00
|
|
|
|
channel.Id = channelIdPrefix + urlHash + line.GetMD5().ToString("N");
|
|
|
|
|
channel.Path = line;
|
2016-02-23 00:48:30 +00:00
|
|
|
|
channels.Add(channel);
|
|
|
|
|
extInf = "";
|
2016-02-19 06:20:18 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return channels;
|
|
|
|
|
}
|
2016-08-30 18:17:37 +00:00
|
|
|
|
private M3UChannel GetChannelnfo(string extInf, string tunerHostId, string mediaUrl)
|
2016-02-23 00:48:30 +00:00
|
|
|
|
{
|
|
|
|
|
var titleIndex = extInf.LastIndexOf(',');
|
|
|
|
|
var channel = new M3UChannel();
|
2016-02-24 19:06:26 +00:00
|
|
|
|
channel.TunerHostId = tunerHostId;
|
2016-02-23 00:48:30 +00:00
|
|
|
|
|
|
|
|
|
channel.Number = extInf.Trim().Split(' ')[0] ?? "0";
|
|
|
|
|
channel.Name = extInf.Substring(titleIndex + 1);
|
|
|
|
|
|
|
|
|
|
//Check for channel number with the format from SatIp
|
|
|
|
|
int number;
|
|
|
|
|
var numberIndex = channel.Name.IndexOf('.');
|
|
|
|
|
if (numberIndex > 0)
|
|
|
|
|
{
|
|
|
|
|
if (int.TryParse(channel.Name.Substring(0, numberIndex), out number))
|
|
|
|
|
{
|
|
|
|
|
channel.Number = number.ToString();
|
|
|
|
|
channel.Name = channel.Name.Substring(numberIndex + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-08-30 18:17:37 +00:00
|
|
|
|
|
|
|
|
|
if (string.Equals(channel.Number, "-1", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(mediaUrl))
|
|
|
|
|
{
|
|
|
|
|
channel.Number = Path.GetFileNameWithoutExtension(mediaUrl.Split('/').Last());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (string.Equals(channel.Number, "-1", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
channel.Number = "0";
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-18 18:23:41 +00:00
|
|
|
|
channel.ImageUrl = FindProperty("tvg-logo", extInf);
|
|
|
|
|
|
|
|
|
|
var name = FindProperty("tvg-name", extInf);
|
|
|
|
|
if (string.IsNullOrWhiteSpace(name))
|
|
|
|
|
{
|
|
|
|
|
name = FindProperty("tvg-id", extInf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
channel.Name = name;
|
|
|
|
|
|
|
|
|
|
var numberString = FindProperty("tvg-id", extInf);
|
|
|
|
|
if (string.IsNullOrWhiteSpace(numberString))
|
|
|
|
|
{
|
|
|
|
|
numberString = FindProperty("channel-id", extInf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(numberString))
|
|
|
|
|
{
|
|
|
|
|
channel.Number = numberString;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-23 00:48:30 +00:00
|
|
|
|
return channel;
|
|
|
|
|
|
|
|
|
|
}
|
2016-10-18 18:23:41 +00:00
|
|
|
|
private string FindProperty(string property, string properties)
|
2016-02-21 17:22:13 +00:00
|
|
|
|
{
|
|
|
|
|
var reg = new Regex(@"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase);
|
|
|
|
|
var matches = reg.Matches(properties);
|
|
|
|
|
foreach (Match match in matches)
|
|
|
|
|
{
|
|
|
|
|
if (match.Groups[1].Value == property)
|
|
|
|
|
{
|
|
|
|
|
return match.Groups[2].Value;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-10-18 18:23:41 +00:00
|
|
|
|
return null;
|
2016-02-21 17:22:13 +00:00
|
|
|
|
}
|
2016-02-19 06:20:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-21 17:22:13 +00:00
|
|
|
|
|
2016-02-19 06:20:18 +00:00
|
|
|
|
public class M3UChannel : ChannelInfo
|
|
|
|
|
{
|
|
|
|
|
public string Path { get; set; }
|
|
|
|
|
}
|
2016-02-21 17:22:13 +00:00
|
|
|
|
}
|