LastfmBaseProvider.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. using System.Collections.Generic;
  2. using System.Net;
  3. using MediaBrowser.Common.Net;
  4. using MediaBrowser.Common.Extensions;
  5. using MediaBrowser.Controller.Configuration;
  6. using MediaBrowser.Controller.Entities;
  7. using MediaBrowser.Model.Entities;
  8. using MediaBrowser.Model.Logging;
  9. using System;
  10. using System.Threading;
  11. using System.Threading.Tasks;
  12. using MediaBrowser.Model.Serialization;
  13. namespace MediaBrowser.Controller.Providers.Music
  14. {
  15. class LastfmProviderException : ApplicationException
  16. {
  17. public LastfmProviderException(string msg)
  18. : base(msg)
  19. {
  20. }
  21. }
  22. /// <summary>
  23. /// Class MovieDbProvider
  24. /// </summary>
  25. public abstract class LastfmBaseProvider : BaseMetadataProvider
  26. {
  27. protected static readonly SemaphoreSlim LastfmResourcePool = new SemaphoreSlim(5, 5);
  28. /// <summary>
  29. /// Initializes a new instance of the <see cref="LastfmBaseProvider" /> class.
  30. /// </summary>
  31. /// <param name="jsonSerializer">The json serializer.</param>
  32. /// <param name="httpClient">The HTTP client.</param>
  33. /// <param name="logManager">The log manager.</param>
  34. /// <param name="configurationManager">The configuration manager.</param>
  35. /// <exception cref="System.ArgumentNullException">jsonSerializer</exception>
  36. protected LastfmBaseProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager)
  37. : base(logManager, configurationManager)
  38. {
  39. if (jsonSerializer == null)
  40. {
  41. throw new ArgumentNullException("jsonSerializer");
  42. }
  43. if (httpClient == null)
  44. {
  45. throw new ArgumentNullException("httpClient");
  46. }
  47. JsonSerializer = jsonSerializer;
  48. HttpClient = httpClient;
  49. }
  50. protected override string ProviderVersion
  51. {
  52. get
  53. {
  54. return "3-12-13.3";
  55. }
  56. }
  57. protected override bool RefreshOnVersionChange
  58. {
  59. get
  60. {
  61. return true;
  62. }
  63. }
  64. /// <summary>
  65. /// Gets the json serializer.
  66. /// </summary>
  67. /// <value>The json serializer.</value>
  68. protected IJsonSerializer JsonSerializer { get; private set; }
  69. /// <summary>
  70. /// Gets the HTTP client.
  71. /// </summary>
  72. /// <value>The HTTP client.</value>
  73. protected IHttpClient HttpClient { get; private set; }
  74. /// <summary>
  75. /// The name of the local json meta file for this item type
  76. /// </summary>
  77. protected string LocalMetaFileName { get; set; }
  78. /// <summary>
  79. /// If we save locally, refresh if they delete something
  80. /// </summary>
  81. protected override bool RefreshOnFileSystemStampChange
  82. {
  83. get
  84. {
  85. return ConfigurationManager.Configuration.SaveLocalMeta;
  86. }
  87. }
  88. /// <summary>
  89. /// Gets the priority.
  90. /// </summary>
  91. /// <value>The priority.</value>
  92. public override MetadataProviderPriority Priority
  93. {
  94. get { return MetadataProviderPriority.Second; }
  95. }
  96. /// <summary>
  97. /// Gets a value indicating whether [requires internet].
  98. /// </summary>
  99. /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value>
  100. public override bool RequiresInternet
  101. {
  102. get
  103. {
  104. return true;
  105. }
  106. }
  107. protected const string RootUrl = @"http://ws.audioscrobbler.com/2.0/?";
  108. protected static string ApiKey = "7b76553c3eb1d341d642755aecc40a33";
  109. /// <summary>
  110. /// Determines whether [has local meta] [the specified item].
  111. /// </summary>
  112. /// <param name="item">The item.</param>
  113. /// <returns><c>true</c> if [has local meta] [the specified item]; otherwise, <c>false</c>.</returns>
  114. protected bool HasLocalMeta(BaseItem item)
  115. {
  116. return item.ResolveArgs.ContainsMetaFileByName(LocalMetaFileName);
  117. }
  118. /// <summary>
  119. /// Fetches the items data.
  120. /// </summary>
  121. /// <param name="item">The item.</param>
  122. /// <param name="cancellationToken"></param>
  123. /// <returns>Task.</returns>
  124. protected virtual async Task FetchData(BaseItem item, CancellationToken cancellationToken)
  125. {
  126. var id = item.GetProviderId(MetadataProviders.Musicbrainz) ?? await FindId(item, cancellationToken).ConfigureAwait(false);
  127. if (!string.IsNullOrWhiteSpace(id))
  128. {
  129. Logger.Debug("LastfmProvider - getting info for {0}", item.Name);
  130. cancellationToken.ThrowIfCancellationRequested();
  131. item.SetProviderId(MetadataProviders.Musicbrainz, id);
  132. await FetchLastfmData(item, id, cancellationToken).ConfigureAwait(false);
  133. }
  134. else
  135. {
  136. Logger.Info("LastfmProvider could not find " + item.Name + ". Check name on Last.fm.");
  137. }
  138. }
  139. protected abstract Task<string> FindId(BaseItem item, CancellationToken cancellationToken);
  140. protected abstract Task FetchLastfmData(BaseItem item, string id, CancellationToken cancellationToken);
  141. /// <summary>
  142. /// Encodes an URL.
  143. /// </summary>
  144. /// <param name="name">The name.</param>
  145. /// <returns>System.String.</returns>
  146. protected static string UrlEncode(string name)
  147. {
  148. return WebUtility.UrlEncode(name);
  149. }
  150. protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
  151. {
  152. if (item.DontFetchMeta) return false;
  153. if (ConfigurationManager.Configuration.SaveLocalMeta && HasFileSystemStampChanged(item, providerInfo))
  154. {
  155. //If they deleted something from file system, chances are, this item was mis-identified the first time
  156. item.SetProviderId(MetadataProviders.Musicbrainz, null);
  157. Logger.Debug("LastfmProvider reports file system stamp change...");
  158. return true;
  159. }
  160. if (providerInfo.LastRefreshStatus == ProviderRefreshStatus.CompletedWithErrors)
  161. {
  162. Logger.Debug("LastfmProvider for {0} - last attempt had errors. Will try again.", item.Path);
  163. return true;
  164. }
  165. if (RefreshOnVersionChange && ProviderVersion != providerInfo.ProviderVersion)
  166. {
  167. Logger.Debug("LastfmProvider version change re-running for {0}", item.Path);
  168. return true;
  169. }
  170. var downloadDate = providerInfo.LastRefreshed;
  171. if (ConfigurationManager.Configuration.MetadataRefreshDays == -1 && downloadDate != DateTime.MinValue)
  172. {
  173. return false;
  174. }
  175. if (DateTime.Today.Subtract(item.DateCreated).TotalDays > 180 && downloadDate != DateTime.MinValue)
  176. return false; // don't trigger a refresh data for item that are more than 6 months old and have been refreshed before
  177. if (DateTime.Today.Subtract(downloadDate).TotalDays < ConfigurationManager.Configuration.MetadataRefreshDays) // only refresh every n days
  178. return false;
  179. Logger.Debug("LastfmProvider - " + item.Name + " needs refresh. Download date: " + downloadDate + " item created date: " + item.DateCreated + " Check for Update age: " + ConfigurationManager.Configuration.MetadataRefreshDays);
  180. return true;
  181. }
  182. /// <summary>
  183. /// Fetches metadata and returns true or false indicating if any work that requires persistence was done
  184. /// </summary>
  185. /// <param name="item">The item.</param>
  186. /// <param name="force">if set to <c>true</c> [force].</param>
  187. /// <param name="cancellationToken">The cancellation token</param>
  188. /// <returns>Task{System.Boolean}.</returns>
  189. public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
  190. {
  191. if (item.DontFetchMeta)
  192. {
  193. Logger.Info("LastfmProvider - Not fetching because requested to ignore " + item.Name);
  194. return false;
  195. }
  196. cancellationToken.ThrowIfCancellationRequested();
  197. var providerData = item.ProviderData.GetValueOrDefault(Id, new BaseProviderInfo { ProviderId = Id });
  198. if (!ConfigurationManager.Configuration.SaveLocalMeta || !HasLocalMeta(item) || (force && !HasLocalMeta(item)) || (RefreshOnVersionChange && providerData.ProviderVersion != ProviderVersion))
  199. {
  200. try
  201. {
  202. await FetchData(item, cancellationToken).ConfigureAwait(false);
  203. SetLastRefreshed(item, DateTime.UtcNow);
  204. }
  205. catch (LastfmProviderException)
  206. {
  207. SetLastRefreshed(item, DateTime.UtcNow, ProviderRefreshStatus.CompletedWithErrors);
  208. }
  209. return true;
  210. }
  211. Logger.Debug("LastfmProvider not fetching because local meta exists for " + item.Name);
  212. SetLastRefreshed(item, DateTime.UtcNow);
  213. return true;
  214. }
  215. }
  216. #region Result Objects
  217. public class LastfmStats
  218. {
  219. public string listeners { get; set; }
  220. public string playcount { get; set; }
  221. }
  222. public class LastfmTag
  223. {
  224. public string name { get; set; }
  225. public string url { get; set; }
  226. }
  227. public class LastfmTags
  228. {
  229. public List<LastfmTag> tag { get; set; }
  230. }
  231. public class LastfmFormationInfo
  232. {
  233. public string yearfrom { get; set; }
  234. public string yearto { get; set; }
  235. }
  236. public class LastFmBio
  237. {
  238. public string published { get; set; }
  239. public string summary { get; set; }
  240. public string content { get; set; }
  241. public string placeformed { get; set; }
  242. public string yearformed { get; set; }
  243. public List<LastfmFormationInfo> formationlist { get; set; }
  244. }
  245. public class LastfmArtist
  246. {
  247. public string name { get; set; }
  248. public string mbid { get; set; }
  249. public string url { get; set; }
  250. public string streamable { get; set; }
  251. public string ontour { get; set; }
  252. public LastfmStats stats { get; set; }
  253. public List<LastfmArtist> similar { get; set; }
  254. public LastfmTags tags { get; set; }
  255. public LastFmBio bio { get; set; }
  256. }
  257. public class LastfmAlbum
  258. {
  259. public string name { get; set; }
  260. public string artist { get; set; }
  261. public string id { get; set; }
  262. public string mbid { get; set; }
  263. public string releasedate { get; set; }
  264. public int listeners { get; set; }
  265. public int playcount { get; set; }
  266. public LastfmTags toptags { get; set; }
  267. public LastFmBio wiki { get; set; }
  268. }
  269. public class LastfmGetAlbumResult
  270. {
  271. public LastfmAlbum album { get; set; }
  272. }
  273. public class LastfmGetArtistResult
  274. {
  275. public LastfmArtist artist { get; set; }
  276. }
  277. public class Artistmatches
  278. {
  279. public List<LastfmArtist> artist { get; set; }
  280. }
  281. public class LastfmArtistSearchResult
  282. {
  283. public Artistmatches artistmatches { get; set; }
  284. }
  285. public class LastfmArtistSearchResults
  286. {
  287. public LastfmArtistSearchResult results { get; set; }
  288. }
  289. #endregion
  290. }