ApiClient.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029
  1. using MediaBrowser.Model.Configuration;
  2. using MediaBrowser.Model.Dto;
  3. using MediaBrowser.Model.Entities;
  4. using MediaBrowser.Model.Globalization;
  5. using MediaBrowser.Model.Logging;
  6. using MediaBrowser.Model.Plugins;
  7. using MediaBrowser.Model.System;
  8. using MediaBrowser.Model.Tasks;
  9. using MediaBrowser.Model.Weather;
  10. using MediaBrowser.Model.Web;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.IO;
  14. using System.Linq;
  15. using System.Threading;
  16. using System.Threading.Tasks;
  17. namespace MediaBrowser.ApiInteraction
  18. {
  19. /// <summary>
  20. /// Provides api methods centered around an HttpClient
  21. /// </summary>
  22. public class ApiClient : BaseApiClient
  23. {
  24. /// <summary>
  25. /// Gets the HTTP client.
  26. /// </summary>
  27. /// <value>The HTTP client.</value>
  28. protected IAsyncHttpClient HttpClient { get; private set; }
  29. /// <summary>
  30. /// Initializes a new instance of the <see cref="ApiClient" /> class.
  31. /// </summary>
  32. /// <param name="logger">The logger.</param>
  33. /// <param name="httpClient">The HTTP client.</param>
  34. /// <exception cref="System.ArgumentNullException">httpClient</exception>
  35. public ApiClient(ILogger logger, IAsyncHttpClient httpClient)
  36. : base(logger)
  37. {
  38. if (httpClient == null)
  39. {
  40. throw new ArgumentNullException("httpClient");
  41. }
  42. HttpClient = httpClient;
  43. }
  44. /// <summary>
  45. /// Initializes a new instance of the <see cref="ApiClient" /> class.
  46. /// </summary>
  47. /// <param name="logger">The logger.</param>
  48. public ApiClient(ILogger logger)
  49. : this(logger, new AsyncHttpClient())
  50. {
  51. }
  52. /// <summary>
  53. /// Sets the authorization header.
  54. /// </summary>
  55. /// <param name="header">The header.</param>
  56. protected override void SetAuthorizationHeader(string header)
  57. {
  58. HttpClient.SetAuthorizationHeader(header);
  59. }
  60. /// <summary>
  61. /// Gets an image stream based on a url
  62. /// </summary>
  63. /// <param name="url">The URL.</param>
  64. /// <returns>Task{Stream}.</returns>
  65. /// <exception cref="System.ArgumentNullException">url</exception>
  66. public Task<Stream> GetImageStreamAsync(string url)
  67. {
  68. if (string.IsNullOrEmpty(url))
  69. {
  70. throw new ArgumentNullException("url");
  71. }
  72. return HttpClient.GetAsync(url, Logger, CancellationToken.None);
  73. }
  74. /// <summary>
  75. /// Gets a BaseItem
  76. /// </summary>
  77. /// <param name="id">The id.</param>
  78. /// <param name="userId">The user id.</param>
  79. /// <returns>Task{BaseItemDto}.</returns>
  80. /// <exception cref="System.ArgumentNullException">id</exception>
  81. public async Task<BaseItemDto> GetItemAsync(string id, Guid userId)
  82. {
  83. if (string.IsNullOrEmpty(id))
  84. {
  85. throw new ArgumentNullException("id");
  86. }
  87. if (userId == Guid.Empty)
  88. {
  89. throw new ArgumentNullException("userId");
  90. }
  91. var url = GetApiUrl("Users/" + userId + "/Items/" + id);
  92. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  93. {
  94. return DeserializeFromStream<BaseItemDto>(stream);
  95. }
  96. }
  97. /// <summary>
  98. /// Gets the intros async.
  99. /// </summary>
  100. /// <param name="itemId">The item id.</param>
  101. /// <param name="userId">The user id.</param>
  102. /// <returns>Task{System.String[]}.</returns>
  103. /// <exception cref="System.ArgumentNullException">id</exception>
  104. public async Task<string[]> GetIntrosAsync(string itemId, Guid userId)
  105. {
  106. if (string.IsNullOrEmpty(itemId))
  107. {
  108. throw new ArgumentNullException("itemId");
  109. }
  110. if (userId == Guid.Empty)
  111. {
  112. throw new ArgumentNullException("userId");
  113. }
  114. var url = GetApiUrl("Users/" + userId + "/Items/" + itemId + "/Intros");
  115. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  116. {
  117. return DeserializeFromStream<string[]>(stream);
  118. }
  119. }
  120. /// <summary>
  121. /// Gets a BaseItem
  122. /// </summary>
  123. /// <param name="userId">The user id.</param>
  124. /// <returns>Task{BaseItemDto}.</returns>
  125. /// <exception cref="System.ArgumentNullException">userId</exception>
  126. public async Task<BaseItemDto> GetRootFolderAsync(Guid userId)
  127. {
  128. if (userId == Guid.Empty)
  129. {
  130. throw new ArgumentNullException("userId");
  131. }
  132. var url = GetApiUrl("Users/" + userId + "/Items/Root");
  133. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  134. {
  135. return DeserializeFromStream<BaseItemDto>(stream);
  136. }
  137. }
  138. /// <summary>
  139. /// Gets all Users
  140. /// </summary>
  141. /// <returns>Task{UserDto[]}.</returns>
  142. public async Task<UserDto[]> GetAllUsersAsync()
  143. {
  144. var url = GetApiUrl("Users");
  145. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  146. {
  147. return DeserializeFromStream<UserDto[]>(stream);
  148. }
  149. }
  150. /// <summary>
  151. /// Queries for items
  152. /// </summary>
  153. /// <param name="query">The query.</param>
  154. /// <returns>Task{ItemsResult}.</returns>
  155. /// <exception cref="System.ArgumentNullException">query</exception>
  156. public async Task<ItemsResult> GetItemsAsync(ItemQuery query)
  157. {
  158. if (query == null)
  159. {
  160. throw new ArgumentNullException("query");
  161. }
  162. var url = GetItemListUrl(query);
  163. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  164. {
  165. return DeserializeFromStream<ItemsResult>(stream);
  166. }
  167. }
  168. /// <summary>
  169. /// Gets all People
  170. /// </summary>
  171. /// <param name="userId">The user id.</param>
  172. /// <param name="itemId">Optional itemId, to localize the search to a specific item or folder</param>
  173. /// <param name="personTypes">Use this to limit results to specific person types</param>
  174. /// <param name="startIndex">Used to skip over a given number of items. Use if paging.</param>
  175. /// <param name="limit">The maximum number of items to return</param>
  176. /// <param name="sortOrder">The sort order</param>
  177. /// <param name="recursive">if set to true items will be searched recursively.</param>
  178. /// <returns>Task{IbnItemsResult}.</returns>
  179. /// <exception cref="System.ArgumentNullException">userId</exception>
  180. public async Task<ItemsResult> GetAllPeopleAsync(
  181. Guid userId,
  182. string itemId = null,
  183. IEnumerable<string> personTypes = null,
  184. int? startIndex = null,
  185. int? limit = null,
  186. SortOrder? sortOrder = null,
  187. bool recursive = false)
  188. {
  189. if (userId == Guid.Empty)
  190. {
  191. throw new ArgumentNullException("userId");
  192. }
  193. var dict = new QueryStringDictionary();
  194. dict.AddIfNotNull("startIndex", startIndex);
  195. dict.AddIfNotNull("limit", limit);
  196. dict.Add("recursive", recursive);
  197. if (sortOrder.HasValue)
  198. {
  199. dict["sortOrder"] = sortOrder.Value.ToString();
  200. }
  201. dict.AddIfNotNull("personTypes", personTypes);
  202. var url = string.IsNullOrEmpty(itemId) ? "Users/" + userId + "/Items/Root/Persons" : "Users/" + userId + "/Items/" + itemId + "/Persons";
  203. url = GetApiUrl(url, dict);
  204. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  205. {
  206. return DeserializeFromStream<ItemsResult>(stream);
  207. }
  208. }
  209. /// <summary>
  210. /// Gets a studio
  211. /// </summary>
  212. /// <param name="name">The name.</param>
  213. /// <returns>Task{BaseItemDto}.</returns>
  214. /// <exception cref="System.ArgumentNullException">userId</exception>
  215. public async Task<BaseItemDto> GetStudioAsync(string name)
  216. {
  217. if (string.IsNullOrEmpty(name))
  218. {
  219. throw new ArgumentNullException("name");
  220. }
  221. var url = GetApiUrl("Library/Studios/" + name);
  222. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  223. {
  224. return DeserializeFromStream<BaseItemDto>(stream);
  225. }
  226. }
  227. /// <summary>
  228. /// Gets a genre
  229. /// </summary>
  230. /// <param name="name">The name.</param>
  231. /// <returns>Task{BaseItemDto}.</returns>
  232. /// <exception cref="System.ArgumentNullException">userId</exception>
  233. public async Task<BaseItemDto> GetGenreAsync(string name)
  234. {
  235. if (string.IsNullOrEmpty(name))
  236. {
  237. throw new ArgumentNullException("name");
  238. }
  239. var url = GetApiUrl("Library/Genres/" + name);
  240. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  241. {
  242. return DeserializeFromStream<BaseItemDto>(stream);
  243. }
  244. }
  245. /// <summary>
  246. /// Restarts the kernel or the entire server if necessary
  247. /// If the server application is restarting this request will fail to return, even if
  248. /// the operation is successful.
  249. /// </summary>
  250. /// <returns>Task.</returns>
  251. public Task PerformPendingRestartAsync()
  252. {
  253. var url = GetApiUrl("System/Restart");
  254. return PostAsync<EmptyRequestResult>(url, new QueryStringDictionary());
  255. }
  256. /// <summary>
  257. /// Gets the system status async.
  258. /// </summary>
  259. /// <returns>Task{SystemInfo}.</returns>
  260. public async Task<SystemInfo> GetSystemInfoAsync()
  261. {
  262. var url = GetApiUrl("System/Info");
  263. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  264. {
  265. return DeserializeFromStream<SystemInfo>(stream);
  266. }
  267. }
  268. /// <summary>
  269. /// Gets a person
  270. /// </summary>
  271. /// <param name="name">The name.</param>
  272. /// <returns>Task{BaseItemDto}.</returns>
  273. /// <exception cref="System.ArgumentNullException">userId</exception>
  274. public async Task<BaseItemDto> GetPersonAsync(string name)
  275. {
  276. if (string.IsNullOrEmpty(name))
  277. {
  278. throw new ArgumentNullException("name");
  279. }
  280. var url = GetApiUrl("Library/Persons/" + name);
  281. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  282. {
  283. return DeserializeFromStream<BaseItemDto>(stream);
  284. }
  285. }
  286. /// <summary>
  287. /// Gets a year
  288. /// </summary>
  289. /// <param name="year">The year.</param>
  290. /// <returns>Task{BaseItemDto}.</returns>
  291. /// <exception cref="System.ArgumentNullException">userId</exception>
  292. public async Task<BaseItemDto> GetYearAsync(int year)
  293. {
  294. var url = GetApiUrl("Library/Years/" + year);
  295. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  296. {
  297. return DeserializeFromStream<BaseItemDto>(stream);
  298. }
  299. }
  300. /// <summary>
  301. /// Gets a list of plugins installed on the server
  302. /// </summary>
  303. /// <returns>Task{PluginInfo[]}.</returns>
  304. public async Task<PluginInfo[]> GetInstalledPluginsAsync()
  305. {
  306. var url = GetApiUrl("Plugins");
  307. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  308. {
  309. return DeserializeFromStream<PluginInfo[]>(stream);
  310. }
  311. }
  312. /// <summary>
  313. /// Gets a list of plugins installed on the server
  314. /// </summary>
  315. /// <param name="plugin">The plugin.</param>
  316. /// <returns>Task{Stream}.</returns>
  317. /// <exception cref="System.ArgumentNullException">plugin</exception>
  318. public Task<Stream> GetPluginAssemblyAsync(PluginInfo plugin)
  319. {
  320. if (plugin == null)
  321. {
  322. throw new ArgumentNullException("plugin");
  323. }
  324. var url = GetApiUrl("Plugins/" + plugin.Id + "/Assembly");
  325. return HttpClient.GetAsync(url, Logger, CancellationToken.None);
  326. }
  327. /// <summary>
  328. /// Gets the current server configuration
  329. /// </summary>
  330. /// <returns>Task{ServerConfiguration}.</returns>
  331. public async Task<ServerConfiguration> GetServerConfigurationAsync()
  332. {
  333. var url = GetApiUrl("System/Configuration");
  334. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  335. {
  336. return DeserializeFromStream<ServerConfiguration>(stream);
  337. }
  338. }
  339. /// <summary>
  340. /// Gets the scheduled tasks.
  341. /// </summary>
  342. /// <returns>Task{TaskInfo[]}.</returns>
  343. public async Task<TaskInfo[]> GetScheduledTasksAsync()
  344. {
  345. var url = GetApiUrl("ScheduledTasks");
  346. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  347. {
  348. return DeserializeFromStream<TaskInfo[]>(stream);
  349. }
  350. }
  351. /// <summary>
  352. /// Gets the scheduled task async.
  353. /// </summary>
  354. /// <param name="id">The id.</param>
  355. /// <returns>Task{TaskInfo}.</returns>
  356. /// <exception cref="System.ArgumentNullException">id</exception>
  357. public async Task<TaskInfo> GetScheduledTaskAsync(Guid id)
  358. {
  359. if (id == Guid.Empty)
  360. {
  361. throw new ArgumentNullException("id");
  362. }
  363. var url = GetApiUrl("ScheduledTasks/" + id);
  364. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  365. {
  366. return DeserializeFromStream<TaskInfo>(stream);
  367. }
  368. }
  369. /// <summary>
  370. /// Gets the plugin configuration file in plain text.
  371. /// </summary>
  372. /// <param name="pluginId">The plugin id.</param>
  373. /// <returns>Task{Stream}.</returns>
  374. /// <exception cref="System.ArgumentNullException">assemblyFileName</exception>
  375. public async Task<Stream> GetPluginConfigurationFileAsync(Guid pluginId)
  376. {
  377. if (pluginId == Guid.Empty)
  378. {
  379. throw new ArgumentNullException("pluginId");
  380. }
  381. var url = GetApiUrl("Plugins/" + pluginId + "/ConfigurationFile");
  382. return await HttpClient.GetAsync(url, Logger, CancellationToken.None).ConfigureAwait(false);
  383. }
  384. /// <summary>
  385. /// Gets a user by id
  386. /// </summary>
  387. /// <param name="id">The id.</param>
  388. /// <returns>Task{UserDto}.</returns>
  389. /// <exception cref="System.ArgumentNullException">id</exception>
  390. public async Task<UserDto> GetUserAsync(Guid id)
  391. {
  392. if (id == Guid.Empty)
  393. {
  394. throw new ArgumentNullException("id");
  395. }
  396. var url = GetApiUrl("Users/" + id);
  397. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  398. {
  399. return DeserializeFromStream<UserDto>(stream);
  400. }
  401. }
  402. /// <summary>
  403. /// Gets the parental ratings async.
  404. /// </summary>
  405. /// <returns>Task{List{ParentalRating}}.</returns>
  406. public async Task<List<ParentalRating>> GetParentalRatingsAsync()
  407. {
  408. var url = GetApiUrl("Localization/ParentalRatings");
  409. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  410. {
  411. return DeserializeFromStream<List<ParentalRating>>(stream);
  412. }
  413. }
  414. /// <summary>
  415. /// Gets weather information for the default location as set in configuration
  416. /// </summary>
  417. /// <returns>Task{WeatherInfo}.</returns>
  418. public async Task<WeatherInfo> GetWeatherInfoAsync()
  419. {
  420. var url = GetApiUrl("Weather");
  421. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  422. {
  423. return DeserializeFromStream<WeatherInfo>(stream);
  424. }
  425. }
  426. /// <summary>
  427. /// Gets weather information for a specific location
  428. /// Location can be a US zipcode, or "city,state", "city,state,country", "city,country"
  429. /// It can also be an ip address, or "latitude,longitude"
  430. /// </summary>
  431. /// <param name="location">The location.</param>
  432. /// <returns>Task{WeatherInfo}.</returns>
  433. /// <exception cref="System.ArgumentNullException">location</exception>
  434. public async Task<WeatherInfo> GetWeatherInfoAsync(string location)
  435. {
  436. if (string.IsNullOrEmpty(location))
  437. {
  438. throw new ArgumentNullException("location");
  439. }
  440. var dict = new QueryStringDictionary();
  441. dict.Add("location", location);
  442. var url = GetApiUrl("Weather", dict);
  443. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  444. {
  445. return DeserializeFromStream<WeatherInfo>(stream);
  446. }
  447. }
  448. /// <summary>
  449. /// Gets local trailers for an item
  450. /// </summary>
  451. /// <param name="userId">The user id.</param>
  452. /// <param name="itemId">The item id.</param>
  453. /// <returns>Task{ItemsResult}.</returns>
  454. /// <exception cref="System.ArgumentNullException">query</exception>
  455. public async Task<BaseItemDto[]> GetLocalTrailersAsync(Guid userId, string itemId)
  456. {
  457. if (userId == Guid.Empty)
  458. {
  459. throw new ArgumentNullException("userId");
  460. }
  461. if (string.IsNullOrEmpty(itemId))
  462. {
  463. throw new ArgumentNullException("itemId");
  464. }
  465. var url = GetApiUrl("Users/" + userId + "/Items/" + itemId + "/LocalTrailers");
  466. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  467. {
  468. return DeserializeFromStream<BaseItemDto[]>(stream);
  469. }
  470. }
  471. /// <summary>
  472. /// Gets special features for an item
  473. /// </summary>
  474. /// <param name="userId">The user id.</param>
  475. /// <param name="itemId">The item id.</param>
  476. /// <returns>Task{BaseItemDto[]}.</returns>
  477. /// <exception cref="System.ArgumentNullException">userId</exception>
  478. public async Task<BaseItemDto[]> GetSpecialFeaturesAsync(Guid userId, string itemId)
  479. {
  480. if (userId == Guid.Empty)
  481. {
  482. throw new ArgumentNullException("userId");
  483. }
  484. if (string.IsNullOrEmpty(itemId))
  485. {
  486. throw new ArgumentNullException("itemId");
  487. }
  488. var url = GetApiUrl("Users/" + userId + "/Items/" + itemId + "/SpecialFeatures");
  489. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  490. {
  491. return DeserializeFromStream<BaseItemDto[]>(stream);
  492. }
  493. }
  494. /// <summary>
  495. /// Gets the cultures async.
  496. /// </summary>
  497. /// <returns>Task{CultureDto[]}.</returns>
  498. public async Task<CultureDto[]> GetCulturesAsync()
  499. {
  500. var url = GetApiUrl("Localization/Cultures");
  501. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  502. {
  503. return DeserializeFromStream<CultureDto[]>(stream);
  504. }
  505. }
  506. /// <summary>
  507. /// Gets the countries async.
  508. /// </summary>
  509. /// <returns>Task{CountryInfo[]}.</returns>
  510. public async Task<CountryInfo[]> GetCountriesAsync()
  511. {
  512. var url = GetApiUrl("Localization/Countries");
  513. using (var stream = await GetSerializedStreamAsync(url).ConfigureAwait(false))
  514. {
  515. return DeserializeFromStream<CountryInfo[]>(stream);
  516. }
  517. }
  518. /// <summary>
  519. /// Marks an item as played or unplayed.
  520. /// This should not be used to update playstate following playback.
  521. /// There are separate playstate check-in methods for that. This should be used for a
  522. /// separate option to reset playstate.
  523. /// </summary>
  524. /// <param name="itemId">The item id.</param>
  525. /// <param name="userId">The user id.</param>
  526. /// <param name="wasPlayed">if set to <c>true</c> [was played].</param>
  527. /// <returns>Task.</returns>
  528. /// <exception cref="System.ArgumentNullException">itemId</exception>
  529. public Task UpdatePlayedStatusAsync(string itemId, Guid userId, bool wasPlayed)
  530. {
  531. if (string.IsNullOrEmpty(itemId))
  532. {
  533. throw new ArgumentNullException("itemId");
  534. }
  535. if (userId == Guid.Empty)
  536. {
  537. throw new ArgumentNullException("userId");
  538. }
  539. var url = GetApiUrl("Users/" + userId + "/PlayedItems/" + itemId);
  540. if (wasPlayed)
  541. {
  542. return PostAsync<EmptyRequestResult>(url, new Dictionary<string, string>());
  543. }
  544. return HttpClient.DeleteAsync(url, Logger, CancellationToken.None);
  545. }
  546. /// <summary>
  547. /// Updates the favorite status async.
  548. /// </summary>
  549. /// <param name="itemId">The item id.</param>
  550. /// <param name="userId">The user id.</param>
  551. /// <param name="isFavorite">if set to <c>true</c> [is favorite].</param>
  552. /// <returns>Task.</returns>
  553. /// <exception cref="System.ArgumentNullException">itemId</exception>
  554. public Task UpdateFavoriteStatusAsync(string itemId, Guid userId, bool isFavorite)
  555. {
  556. if (string.IsNullOrEmpty(itemId))
  557. {
  558. throw new ArgumentNullException("itemId");
  559. }
  560. if (userId == Guid.Empty)
  561. {
  562. throw new ArgumentNullException("userId");
  563. }
  564. var url = GetApiUrl("Users/" + userId + "/FavoriteItems/" + itemId);
  565. if (isFavorite)
  566. {
  567. return PostAsync<EmptyRequestResult>(url, new Dictionary<string, string>());
  568. }
  569. return HttpClient.DeleteAsync(url, Logger, CancellationToken.None);
  570. }
  571. /// <summary>
  572. /// Reports to the server that the user has begun playing an item
  573. /// </summary>
  574. /// <param name="itemId">The item id.</param>
  575. /// <param name="userId">The user id.</param>
  576. /// <returns>Task{UserItemDataDto}.</returns>
  577. /// <exception cref="System.ArgumentNullException">itemId</exception>
  578. public Task ReportPlaybackStartAsync(string itemId, Guid userId)
  579. {
  580. if (string.IsNullOrEmpty(itemId))
  581. {
  582. throw new ArgumentNullException("itemId");
  583. }
  584. if (userId == Guid.Empty)
  585. {
  586. throw new ArgumentNullException("userId");
  587. }
  588. var url = GetApiUrl("Users/" + userId + "/PlayingItems/" + itemId);
  589. return PostAsync<EmptyRequestResult>(url, new Dictionary<string, string>());
  590. }
  591. /// <summary>
  592. /// Reports playback progress to the server
  593. /// </summary>
  594. /// <param name="itemId">The item id.</param>
  595. /// <param name="userId">The user id.</param>
  596. /// <param name="positionTicks">The position ticks.</param>
  597. /// <returns>Task{UserItemDataDto}.</returns>
  598. /// <exception cref="System.ArgumentNullException">itemId</exception>
  599. public Task ReportPlaybackProgressAsync(string itemId, Guid userId, long? positionTicks)
  600. {
  601. if (string.IsNullOrEmpty(itemId))
  602. {
  603. throw new ArgumentNullException("itemId");
  604. }
  605. if (userId == Guid.Empty)
  606. {
  607. throw new ArgumentNullException("userId");
  608. }
  609. var dict = new QueryStringDictionary();
  610. dict.AddIfNotNull("positionTicks", positionTicks);
  611. var url = GetApiUrl("Users/" + userId + "/PlayingItems/" + itemId + "/Progress", dict);
  612. return PostAsync<EmptyRequestResult>(url, new Dictionary<string, string>());
  613. }
  614. /// <summary>
  615. /// Reports to the server that the user has stopped playing an item
  616. /// </summary>
  617. /// <param name="itemId">The item id.</param>
  618. /// <param name="userId">The user id.</param>
  619. /// <param name="positionTicks">The position ticks.</param>
  620. /// <returns>Task{UserItemDataDto}.</returns>
  621. /// <exception cref="System.ArgumentNullException">itemId</exception>
  622. public Task ReportPlaybackStoppedAsync(string itemId, Guid userId, long? positionTicks)
  623. {
  624. if (string.IsNullOrEmpty(itemId))
  625. {
  626. throw new ArgumentNullException("itemId");
  627. }
  628. if (userId == Guid.Empty)
  629. {
  630. throw new ArgumentNullException("userId");
  631. }
  632. var dict = new QueryStringDictionary();
  633. dict.AddIfNotNull("positionTicks", positionTicks);
  634. var url = GetApiUrl("Users/" + userId + "/PlayingItems/" + itemId, dict);
  635. return HttpClient.DeleteAsync(url, Logger, CancellationToken.None);
  636. }
  637. /// <summary>
  638. /// Clears a user's rating for an item
  639. /// </summary>
  640. /// <param name="itemId">The item id.</param>
  641. /// <param name="userId">The user id.</param>
  642. /// <returns>Task{UserItemDataDto}.</returns>
  643. /// <exception cref="System.ArgumentNullException">itemId</exception>
  644. public Task ClearUserItemRatingAsync(string itemId, Guid userId)
  645. {
  646. if (string.IsNullOrEmpty(itemId))
  647. {
  648. throw new ArgumentNullException("itemId");
  649. }
  650. if (userId == Guid.Empty)
  651. {
  652. throw new ArgumentNullException("userId");
  653. }
  654. var url = GetApiUrl("Users/" + userId + "/Items/" + itemId + "/Rating");
  655. return HttpClient.DeleteAsync(url, Logger, CancellationToken.None);
  656. }
  657. /// <summary>
  658. /// Updates a user's rating for an item, based on likes or dislikes
  659. /// </summary>
  660. /// <param name="itemId">The item id.</param>
  661. /// <param name="userId">The user id.</param>
  662. /// <param name="likes">if set to <c>true</c> [likes].</param>
  663. /// <returns>Task{UserItemDataDto}.</returns>
  664. /// <exception cref="System.ArgumentNullException">itemId</exception>
  665. public Task<UserItemDataDto> UpdateUserItemRatingAsync(string itemId, Guid userId, bool likes)
  666. {
  667. if (string.IsNullOrEmpty(itemId))
  668. {
  669. throw new ArgumentNullException("itemId");
  670. }
  671. if (userId == Guid.Empty)
  672. {
  673. throw new ArgumentNullException("userId");
  674. }
  675. var dict = new QueryStringDictionary { };
  676. dict.Add("likes", likes);
  677. var url = GetApiUrl("Users/" + userId + "/Items/" + itemId + "/Rating", dict);
  678. return PostAsync<UserItemDataDto>(url, new Dictionary<string, string>());
  679. }
  680. /// <summary>
  681. /// Authenticates a user and returns the result
  682. /// </summary>
  683. /// <param name="userId">The user id.</param>
  684. /// <param name="password">The password.</param>
  685. /// <exception cref="System.ArgumentNullException">userId</exception>
  686. public Task AuthenticateUserAsync(Guid userId, string password)
  687. {
  688. if (userId == Guid.Empty)
  689. {
  690. throw new ArgumentNullException("userId");
  691. }
  692. var url = GetApiUrl("Users/" + userId + "/Authenticate");
  693. var args = new Dictionary<string, string>();
  694. if (!string.IsNullOrEmpty(password))
  695. {
  696. args["password"] = password;
  697. }
  698. return PostAsync<EmptyRequestResult>(url, args);
  699. }
  700. /// <summary>
  701. /// Uploads the user image async.
  702. /// </summary>
  703. /// <param name="userId">The user id.</param>
  704. /// <param name="imageType">Type of the image.</param>
  705. /// <param name="image">The image.</param>
  706. /// <returns>Task{RequestResult}.</returns>
  707. /// <exception cref="System.NotImplementedException"></exception>
  708. public Task UploadUserImageAsync(Guid userId, ImageType imageType, Stream image)
  709. {
  710. // Implement when needed
  711. throw new NotImplementedException();
  712. }
  713. /// <summary>
  714. /// Updates the server configuration async.
  715. /// </summary>
  716. /// <param name="configuration">The configuration.</param>
  717. /// <returns>Task.</returns>
  718. /// <exception cref="System.ArgumentNullException">configuration</exception>
  719. public Task UpdateServerConfigurationAsync(ServerConfiguration configuration)
  720. {
  721. if (configuration == null)
  722. {
  723. throw new ArgumentNullException("configuration");
  724. }
  725. var url = GetApiUrl("System/Configuration");
  726. return PostAsync<ServerConfiguration, EmptyRequestResult>(url, configuration);
  727. }
  728. /// <summary>
  729. /// Updates the scheduled task triggers.
  730. /// </summary>
  731. /// <param name="id">The id.</param>
  732. /// <param name="triggers">The triggers.</param>
  733. /// <returns>Task{RequestResult}.</returns>
  734. /// <exception cref="System.ArgumentNullException">id</exception>
  735. public Task UpdateScheduledTaskTriggersAsync(Guid id, TaskTriggerInfo[] triggers)
  736. {
  737. if (id == Guid.Empty)
  738. {
  739. throw new ArgumentNullException("id");
  740. }
  741. if (triggers == null)
  742. {
  743. throw new ArgumentNullException("triggers");
  744. }
  745. var url = GetApiUrl("ScheduledTasks/" + id + "/Triggers");
  746. return PostAsync<TaskTriggerInfo[], EmptyRequestResult>(url, triggers);
  747. }
  748. /// <summary>
  749. /// Updates display preferences for a user
  750. /// </summary>
  751. /// <param name="userId">The user id.</param>
  752. /// <param name="itemId">The item id.</param>
  753. /// <param name="displayPreferences">The display preferences.</param>
  754. /// <returns>Task{DisplayPreferences}.</returns>
  755. /// <exception cref="System.ArgumentNullException">userId</exception>
  756. public Task UpdateDisplayPreferencesAsync(Guid userId, string itemId, DisplayPreferences displayPreferences)
  757. {
  758. if (userId == Guid.Empty)
  759. {
  760. throw new ArgumentNullException("userId");
  761. }
  762. if (string.IsNullOrEmpty(itemId))
  763. {
  764. throw new ArgumentNullException("itemId");
  765. }
  766. if (displayPreferences == null)
  767. {
  768. throw new ArgumentNullException("displayPreferences");
  769. }
  770. var url = GetApiUrl("Users/" + userId + "/Items/" + itemId + "/DisplayPreferences");
  771. return PostAsync<DisplayPreferences, EmptyRequestResult>(url, displayPreferences);
  772. }
  773. /// <summary>
  774. /// Posts a set of data to a url, and deserializes the return stream into T
  775. /// </summary>
  776. /// <typeparam name="T"></typeparam>
  777. /// <param name="url">The URL.</param>
  778. /// <param name="args">The args.</param>
  779. /// <returns>Task{``0}.</returns>
  780. private Task<T> PostAsync<T>(string url, Dictionary<string, string> args)
  781. where T : class
  782. {
  783. return PostAsync<T>(url, args, SerializationFormat);
  784. }
  785. /// <summary>
  786. /// Posts a set of data to a url, and deserializes the return stream into T
  787. /// </summary>
  788. /// <typeparam name="T"></typeparam>
  789. /// <param name="url">The URL.</param>
  790. /// <param name="args">The args.</param>
  791. /// <param name="serializationFormat">The serialization format.</param>
  792. /// <returns>Task{``0}.</returns>
  793. private async Task<T> PostAsync<T>(string url, Dictionary<string, string> args, SerializationFormats serializationFormat)
  794. where T : class
  795. {
  796. url = AddDataFormat(url, serializationFormat);
  797. // Create the post body
  798. var strings = args.Keys.Select(key => string.Format("{0}={1}", key, args[key]));
  799. var postContent = string.Join("&", strings.ToArray());
  800. const string contentType = "application/x-www-form-urlencoded";
  801. using (var stream = await HttpClient.PostAsync(url, contentType, postContent, Logger, CancellationToken.None).ConfigureAwait(false))
  802. {
  803. return DeserializeFromStream<T>(stream);
  804. }
  805. }
  806. /// <summary>
  807. /// Posts an object of type TInputType to a given url, and deserializes the response into an object of type TOutputType
  808. /// </summary>
  809. /// <typeparam name="TInputType">The type of the T input type.</typeparam>
  810. /// <typeparam name="TOutputType">The type of the T output type.</typeparam>
  811. /// <param name="url">The URL.</param>
  812. /// <param name="obj">The obj.</param>
  813. /// <returns>Task{``1}.</returns>
  814. private Task<TOutputType> PostAsync<TInputType, TOutputType>(string url, TInputType obj)
  815. where TOutputType : class
  816. {
  817. return PostAsync<TInputType, TOutputType>(url, obj, SerializationFormat);
  818. }
  819. /// <summary>
  820. /// Posts an object of type TInputType to a given url, and deserializes the response into an object of type TOutputType
  821. /// </summary>
  822. /// <typeparam name="TInputType">The type of the T input type.</typeparam>
  823. /// <typeparam name="TOutputType">The type of the T output type.</typeparam>
  824. /// <param name="url">The URL.</param>
  825. /// <param name="obj">The obj.</param>
  826. /// <param name="serializationFormat">The serialization format.</param>
  827. /// <returns>Task{``1}.</returns>
  828. private async Task<TOutputType> PostAsync<TInputType, TOutputType>(string url, TInputType obj, SerializationFormats serializationFormat)
  829. where TOutputType : class
  830. {
  831. url = AddDataFormat(url, serializationFormat);
  832. const string contentType = "application/x-www-form-urlencoded";
  833. var postContent = SerializeToJson(obj);
  834. using (var stream = await HttpClient.PostAsync(url, contentType, postContent, Logger, CancellationToken.None).ConfigureAwait(false))
  835. {
  836. return DeserializeFromStream<TOutputType>(stream);
  837. }
  838. }
  839. /// <summary>
  840. /// This is a helper around getting a stream from the server that contains serialized data
  841. /// </summary>
  842. /// <param name="url">The URL.</param>
  843. /// <returns>Task{Stream}.</returns>
  844. public Task<Stream> GetSerializedStreamAsync(string url)
  845. {
  846. return GetSerializedStreamAsync(url, SerializationFormat);
  847. }
  848. /// <summary>
  849. /// This is a helper around getting a stream from the server that contains serialized data
  850. /// </summary>
  851. /// <param name="url">The URL.</param>
  852. /// <param name="serializationFormat">The serialization format.</param>
  853. /// <returns>Task{Stream}.</returns>
  854. public Task<Stream> GetSerializedStreamAsync(string url, SerializationFormats serializationFormat)
  855. {
  856. url = AddDataFormat(url, serializationFormat);
  857. return HttpClient.GetAsync(url, Logger, CancellationToken.None);
  858. }
  859. /// <summary>
  860. /// Adds the data format.
  861. /// </summary>
  862. /// <param name="url">The URL.</param>
  863. /// <param name="serializationFormat">The serialization format.</param>
  864. /// <returns>System.String.</returns>
  865. private string AddDataFormat(string url, SerializationFormats serializationFormat)
  866. {
  867. var format = serializationFormat == SerializationFormats.Protobuf ? "x-protobuf" : serializationFormat.ToString();
  868. if (url.IndexOf('?') == -1)
  869. {
  870. url += "?format=" + format;
  871. }
  872. else
  873. {
  874. url += "&format=" + format;
  875. }
  876. return url;
  877. }
  878. }
  879. }