TvdbPrescanTask.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. using MediaBrowser.Common.Configuration;
  2. using MediaBrowser.Common.Net;
  3. using MediaBrowser.Controller.Extensions;
  4. using MediaBrowser.Controller.Library;
  5. using MediaBrowser.Model.Logging;
  6. using MediaBrowser.Model.Net;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.IO;
  10. using System.Linq;
  11. using System.Text;
  12. using System.Threading;
  13. using System.Threading.Tasks;
  14. using System.Xml;
  15. namespace MediaBrowser.Controller.Providers.TV
  16. {
  17. /// <summary>
  18. /// Class TvdbPrescanTask
  19. /// </summary>
  20. public class TvdbPrescanTask : ILibraryPrescanTask
  21. {
  22. /// <summary>
  23. /// The server time URL
  24. /// </summary>
  25. private const string ServerTimeUrl = "http://thetvdb.com/api/Updates.php?type=none";
  26. /// <summary>
  27. /// The updates URL
  28. /// </summary>
  29. private const string UpdatesUrl = "http://thetvdb.com/api/Updates.php?type=all&time={0}";
  30. /// <summary>
  31. /// The _HTTP client
  32. /// </summary>
  33. private readonly IHttpClient _httpClient;
  34. /// <summary>
  35. /// The _logger
  36. /// </summary>
  37. private readonly ILogger _logger;
  38. /// <summary>
  39. /// The _config
  40. /// </summary>
  41. private readonly IConfigurationManager _config;
  42. /// <summary>
  43. /// Initializes a new instance of the <see cref="TvdbPrescanTask"/> class.
  44. /// </summary>
  45. /// <param name="logger">The logger.</param>
  46. /// <param name="httpClient">The HTTP client.</param>
  47. /// <param name="config">The config.</param>
  48. public TvdbPrescanTask(ILogger logger, IHttpClient httpClient, IConfigurationManager config)
  49. {
  50. _logger = logger;
  51. _httpClient = httpClient;
  52. _config = config;
  53. }
  54. /// <summary>
  55. /// Runs the specified progress.
  56. /// </summary>
  57. /// <param name="progress">The progress.</param>
  58. /// <param name="cancellationToken">The cancellation token.</param>
  59. /// <returns>Task.</returns>
  60. public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
  61. {
  62. var path = RemoteSeriesProvider.GetSeriesDataPath(_config.CommonApplicationPaths);
  63. var timestampFile = Path.Combine(path, "time.txt");
  64. var timestampFileInfo = new FileInfo(timestampFile);
  65. // Don't check for tvdb updates anymore frequently than 24 hours
  66. if (timestampFileInfo.Exists && (DateTime.UtcNow - timestampFileInfo.LastWriteTimeUtc).TotalDays < 1)
  67. {
  68. return;
  69. }
  70. // Find out the last time we queried tvdb for updates
  71. var lastUpdateTime = timestampFileInfo.Exists ? File.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty;
  72. string newUpdateTime;
  73. var existingDirectories = Directory.EnumerateDirectories(path).Select(Path.GetFileName).ToList();
  74. // If this is our first time, update all series
  75. if (string.IsNullOrEmpty(lastUpdateTime))
  76. {
  77. // First get tvdb server time
  78. using (var stream = await _httpClient.Get(new HttpRequestOptions
  79. {
  80. Url = ServerTimeUrl,
  81. CancellationToken = cancellationToken,
  82. EnableHttpCompression = true,
  83. ResourcePool = RemoteSeriesProvider.Current.TvDbResourcePool
  84. }).ConfigureAwait(false))
  85. {
  86. var doc = new XmlDocument();
  87. doc.Load(stream);
  88. newUpdateTime = doc.SafeGetString("//Time");
  89. }
  90. await UpdateSeries(existingDirectories, path, cancellationToken).ConfigureAwait(false);
  91. }
  92. else
  93. {
  94. var seriesToUpdate = await GetSeriesIdsToUpdate(existingDirectories, lastUpdateTime, cancellationToken).ConfigureAwait(false);
  95. newUpdateTime = seriesToUpdate.Item2;
  96. await UpdateSeries(seriesToUpdate.Item1, path, cancellationToken).ConfigureAwait(false);
  97. }
  98. File.WriteAllText(timestampFile, newUpdateTime, Encoding.UTF8);
  99. }
  100. /// <summary>
  101. /// Gets the series ids to update.
  102. /// </summary>
  103. /// <param name="existingSeriesIds">The existing series ids.</param>
  104. /// <param name="lastUpdateTime">The last update time.</param>
  105. /// <param name="cancellationToken">The cancellation token.</param>
  106. /// <returns>Task{IEnumerable{System.String}}.</returns>
  107. private async Task<Tuple<IEnumerable<string>, string>> GetSeriesIdsToUpdate(IEnumerable<string> existingSeriesIds, string lastUpdateTime, CancellationToken cancellationToken)
  108. {
  109. // First get last time
  110. using (var stream = await _httpClient.Get(new HttpRequestOptions
  111. {
  112. Url = string.Format(UpdatesUrl, lastUpdateTime),
  113. CancellationToken = cancellationToken,
  114. EnableHttpCompression = true,
  115. ResourcePool = RemoteSeriesProvider.Current.TvDbResourcePool
  116. }).ConfigureAwait(false))
  117. {
  118. var doc = new XmlDocument();
  119. doc.Load(stream);
  120. var newUpdateTime = doc.SafeGetString("//Time");
  121. var seriesNodes = doc.SelectNodes("//Series");
  122. var seriesList = seriesNodes == null ? new string[] { } :
  123. seriesNodes.Cast<XmlNode>()
  124. .Select(i => i.InnerText)
  125. .Where(i => !string.IsNullOrWhiteSpace(i) && existingSeriesIds.Contains(i, StringComparer.OrdinalIgnoreCase));
  126. return new Tuple<IEnumerable<string>, string>(seriesList, newUpdateTime);
  127. }
  128. }
  129. /// <summary>
  130. /// Updates the series.
  131. /// </summary>
  132. /// <param name="seriesIds">The series ids.</param>
  133. /// <param name="seriesDataPath">The series data path.</param>
  134. /// <param name="cancellationToken">The cancellation token.</param>
  135. /// <returns>Task.</returns>
  136. private async Task UpdateSeries(IEnumerable<string> seriesIds, string seriesDataPath, CancellationToken cancellationToken)
  137. {
  138. foreach (var seriesId in seriesIds)
  139. {
  140. try
  141. {
  142. await UpdateSeries(seriesId, seriesDataPath, cancellationToken).ConfigureAwait(false);
  143. }
  144. catch (HttpException ex)
  145. {
  146. // Already logged at lower levels, but don't fail the whole operation, unless timed out
  147. if (ex.IsTimedOut)
  148. {
  149. throw;
  150. }
  151. }
  152. }
  153. }
  154. /// <summary>
  155. /// Updates the series.
  156. /// </summary>
  157. /// <param name="id">The id.</param>
  158. /// <param name="seriesDataPath">The series data path.</param>
  159. /// <param name="cancellationToken">The cancellation token.</param>
  160. /// <returns>Task.</returns>
  161. private Task UpdateSeries(string id, string seriesDataPath, CancellationToken cancellationToken)
  162. {
  163. _logger.Info("Updating series " + id);
  164. seriesDataPath = Path.Combine(seriesDataPath, id);
  165. if (!Directory.Exists(seriesDataPath))
  166. {
  167. Directory.CreateDirectory(seriesDataPath);
  168. }
  169. return RemoteSeriesProvider.Current.DownloadSeriesZip(id, seriesDataPath, cancellationToken);
  170. }
  171. }
  172. }