SyncPlayController.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. using System.Collections.Generic;
  2. using System.ComponentModel.DataAnnotations;
  3. using System.Threading;
  4. using Jellyfin.Api.Constants;
  5. using Jellyfin.Api.Helpers;
  6. using Jellyfin.Api.Models.SyncPlayDtos;
  7. using MediaBrowser.Controller.Net;
  8. using MediaBrowser.Controller.Session;
  9. using MediaBrowser.Controller.SyncPlay;
  10. using MediaBrowser.Controller.SyncPlay.PlaybackRequests;
  11. using MediaBrowser.Controller.SyncPlay.Requests;
  12. using MediaBrowser.Model.SyncPlay;
  13. using Microsoft.AspNetCore.Authorization;
  14. using Microsoft.AspNetCore.Http;
  15. using Microsoft.AspNetCore.Mvc;
  16. namespace Jellyfin.Api.Controllers
  17. {
  18. /// <summary>
  19. /// The sync play controller.
  20. /// </summary>
  21. [Authorize(Policy = Policies.SyncPlayHasAccess)]
  22. public class SyncPlayController : BaseJellyfinApiController
  23. {
  24. private readonly ISessionManager _sessionManager;
  25. private readonly IAuthorizationContext _authorizationContext;
  26. private readonly ISyncPlayManager _syncPlayManager;
  27. /// <summary>
  28. /// Initializes a new instance of the <see cref="SyncPlayController"/> class.
  29. /// </summary>
  30. /// <param name="sessionManager">Instance of the <see cref="ISessionManager"/> interface.</param>
  31. /// <param name="authorizationContext">Instance of the <see cref="IAuthorizationContext"/> interface.</param>
  32. /// <param name="syncPlayManager">Instance of the <see cref="ISyncPlayManager"/> interface.</param>
  33. public SyncPlayController(
  34. ISessionManager sessionManager,
  35. IAuthorizationContext authorizationContext,
  36. ISyncPlayManager syncPlayManager)
  37. {
  38. _sessionManager = sessionManager;
  39. _authorizationContext = authorizationContext;
  40. _syncPlayManager = syncPlayManager;
  41. }
  42. /// <summary>
  43. /// Create a new SyncPlay group.
  44. /// </summary>
  45. /// <param name="requestData">The settings of the new group.</param>
  46. /// <response code="204">New group created.</response>
  47. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  48. [HttpPost("New")]
  49. [ProducesResponseType(StatusCodes.Status204NoContent)]
  50. [Authorize(Policy = Policies.SyncPlayCreateGroup)]
  51. public ActionResult SyncPlayCreateGroup(
  52. [FromBody, Required] NewGroupRequestDto requestData)
  53. {
  54. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  55. var syncPlayRequest = new NewGroupRequest(requestData.GroupName);
  56. _syncPlayManager.NewGroup(currentSession, syncPlayRequest, CancellationToken.None);
  57. return NoContent();
  58. }
  59. /// <summary>
  60. /// Join an existing SyncPlay group.
  61. /// </summary>
  62. /// <param name="requestData">The group to join.</param>
  63. /// <response code="204">Group join successful.</response>
  64. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  65. [HttpPost("Join")]
  66. [ProducesResponseType(StatusCodes.Status204NoContent)]
  67. [Authorize(Policy = Policies.SyncPlayJoinGroup)]
  68. public ActionResult SyncPlayJoinGroup(
  69. [FromBody, Required] JoinGroupRequestDto requestData)
  70. {
  71. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  72. var syncPlayRequest = new JoinGroupRequest(requestData.GroupId);
  73. _syncPlayManager.JoinGroup(currentSession, syncPlayRequest, CancellationToken.None);
  74. return NoContent();
  75. }
  76. /// <summary>
  77. /// Leave the joined SyncPlay group.
  78. /// </summary>
  79. /// <response code="204">Group leave successful.</response>
  80. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  81. [HttpPost("Leave")]
  82. [ProducesResponseType(StatusCodes.Status204NoContent)]
  83. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  84. public ActionResult SyncPlayLeaveGroup()
  85. {
  86. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  87. var syncPlayRequest = new LeaveGroupRequest();
  88. _syncPlayManager.LeaveGroup(currentSession, syncPlayRequest, CancellationToken.None);
  89. return NoContent();
  90. }
  91. /// <summary>
  92. /// Gets all SyncPlay groups.
  93. /// </summary>
  94. /// <response code="200">Groups returned.</response>
  95. /// <returns>An <see cref="IEnumerable{GroupInfoView}"/> containing the available SyncPlay groups.</returns>
  96. [HttpGet("List")]
  97. [ProducesResponseType(StatusCodes.Status200OK)]
  98. [Authorize(Policy = Policies.SyncPlayJoinGroup)]
  99. public ActionResult<IEnumerable<GroupInfoDto>> SyncPlayGetGroups()
  100. {
  101. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  102. var syncPlayRequest = new ListGroupsRequest();
  103. return Ok(_syncPlayManager.ListGroups(currentSession, syncPlayRequest));
  104. }
  105. /// <summary>
  106. /// Request to set new playlist in SyncPlay group.
  107. /// </summary>
  108. /// <param name="requestData">The new playlist to play in the group.</param>
  109. /// <response code="204">Queue update sent to all group members.</response>
  110. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  111. [HttpPost("SetNewQueue")]
  112. [ProducesResponseType(StatusCodes.Status204NoContent)]
  113. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  114. public ActionResult SyncPlaySetNewQueue(
  115. [FromBody, Required] PlayRequestDto requestData)
  116. {
  117. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  118. var syncPlayRequest = new PlayGroupRequest(
  119. requestData.PlayingQueue,
  120. requestData.PlayingItemPosition,
  121. requestData.StartPositionTicks);
  122. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  123. return NoContent();
  124. }
  125. /// <summary>
  126. /// Request to change playlist item in SyncPlay group.
  127. /// </summary>
  128. /// <param name="requestData">The new item to play.</param>
  129. /// <response code="204">Queue update sent to all group members.</response>
  130. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  131. [HttpPost("SetPlaylistItem")]
  132. [ProducesResponseType(StatusCodes.Status204NoContent)]
  133. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  134. public ActionResult SyncPlaySetPlaylistItem(
  135. [FromBody, Required] SetPlaylistItemRequestDto requestData)
  136. {
  137. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  138. var syncPlayRequest = new SetPlaylistItemGroupRequest(requestData.PlaylistItemId);
  139. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  140. return NoContent();
  141. }
  142. /// <summary>
  143. /// Request to remove items from the playlist in SyncPlay group.
  144. /// </summary>
  145. /// <param name="requestData">The items to remove.</param>
  146. /// <response code="204">Queue update sent to all group members.</response>
  147. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  148. [HttpPost("RemoveFromPlaylist")]
  149. [ProducesResponseType(StatusCodes.Status204NoContent)]
  150. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  151. public ActionResult SyncPlayRemoveFromPlaylist(
  152. [FromBody, Required] RemoveFromPlaylistRequestDto requestData)
  153. {
  154. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  155. var syncPlayRequest = new RemoveFromPlaylistGroupRequest(requestData.PlaylistItemIds);
  156. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  157. return NoContent();
  158. }
  159. /// <summary>
  160. /// Request to move an item in the playlist in SyncPlay group.
  161. /// </summary>
  162. /// <param name="requestData">The new position for the item.</param>
  163. /// <response code="204">Queue update sent to all group members.</response>
  164. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  165. [HttpPost("MovePlaylistItem")]
  166. [ProducesResponseType(StatusCodes.Status204NoContent)]
  167. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  168. public ActionResult SyncPlayMovePlaylistItem(
  169. [FromBody, Required] MovePlaylistItemRequestDto requestData)
  170. {
  171. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  172. var syncPlayRequest = new MovePlaylistItemGroupRequest(requestData.PlaylistItemId, requestData.NewIndex);
  173. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  174. return NoContent();
  175. }
  176. /// <summary>
  177. /// Request to queue items to the playlist of a SyncPlay group.
  178. /// </summary>
  179. /// <param name="requestData">The items to add.</param>
  180. /// <response code="204">Queue update sent to all group members.</response>
  181. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  182. [HttpPost("Queue")]
  183. [ProducesResponseType(StatusCodes.Status204NoContent)]
  184. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  185. public ActionResult SyncPlayQueue(
  186. [FromBody, Required] QueueRequestDto requestData)
  187. {
  188. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  189. var syncPlayRequest = new QueueGroupRequest(requestData.ItemIds, requestData.Mode);
  190. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  191. return NoContent();
  192. }
  193. /// <summary>
  194. /// Request unpause in SyncPlay group.
  195. /// </summary>
  196. /// <response code="204">Unpause update sent to all group members.</response>
  197. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  198. [HttpPost("Unpause")]
  199. [ProducesResponseType(StatusCodes.Status204NoContent)]
  200. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  201. public ActionResult SyncPlayUnpause()
  202. {
  203. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  204. var syncPlayRequest = new UnpauseGroupRequest();
  205. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  206. return NoContent();
  207. }
  208. /// <summary>
  209. /// Request pause in SyncPlay group.
  210. /// </summary>
  211. /// <response code="204">Pause update sent to all group members.</response>
  212. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  213. [HttpPost("Pause")]
  214. [ProducesResponseType(StatusCodes.Status204NoContent)]
  215. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  216. public ActionResult SyncPlayPause()
  217. {
  218. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  219. var syncPlayRequest = new PauseGroupRequest();
  220. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  221. return NoContent();
  222. }
  223. /// <summary>
  224. /// Request stop in SyncPlay group.
  225. /// </summary>
  226. /// <response code="204">Stop update sent to all group members.</response>
  227. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  228. [HttpPost("Stop")]
  229. [ProducesResponseType(StatusCodes.Status204NoContent)]
  230. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  231. public ActionResult SyncPlayStop()
  232. {
  233. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  234. var syncPlayRequest = new StopGroupRequest();
  235. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  236. return NoContent();
  237. }
  238. /// <summary>
  239. /// Request seek in SyncPlay group.
  240. /// </summary>
  241. /// <param name="requestData">The new playback position.</param>
  242. /// <response code="204">Seek update sent to all group members.</response>
  243. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  244. [HttpPost("Seek")]
  245. [ProducesResponseType(StatusCodes.Status204NoContent)]
  246. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  247. public ActionResult SyncPlaySeek(
  248. [FromBody, Required] SeekRequestDto requestData)
  249. {
  250. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  251. var syncPlayRequest = new SeekGroupRequest(requestData.PositionTicks);
  252. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  253. return NoContent();
  254. }
  255. /// <summary>
  256. /// Notify SyncPlay group that member is buffering.
  257. /// </summary>
  258. /// <param name="requestData">The player status.</param>
  259. /// <response code="204">Group state update sent to all group members.</response>
  260. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  261. [HttpPost("Buffering")]
  262. [ProducesResponseType(StatusCodes.Status204NoContent)]
  263. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  264. public ActionResult SyncPlayBuffering(
  265. [FromBody, Required] BufferRequestDto requestData)
  266. {
  267. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  268. var syncPlayRequest = new BufferGroupRequest(
  269. requestData.When,
  270. requestData.PositionTicks,
  271. requestData.IsPlaying,
  272. requestData.PlaylistItemId);
  273. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  274. return NoContent();
  275. }
  276. /// <summary>
  277. /// Notify SyncPlay group that member is ready for playback.
  278. /// </summary>
  279. /// <param name="requestData">The player status.</param>
  280. /// <response code="204">Group state update sent to all group members.</response>
  281. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  282. [HttpPost("Ready")]
  283. [ProducesResponseType(StatusCodes.Status204NoContent)]
  284. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  285. public ActionResult SyncPlayReady(
  286. [FromBody, Required] ReadyRequestDto requestData)
  287. {
  288. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  289. var syncPlayRequest = new ReadyGroupRequest(
  290. requestData.When,
  291. requestData.PositionTicks,
  292. requestData.IsPlaying,
  293. requestData.PlaylistItemId);
  294. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  295. return NoContent();
  296. }
  297. /// <summary>
  298. /// Request SyncPlay group to ignore member during group-wait.
  299. /// </summary>
  300. /// <param name="requestData">The settings to set.</param>
  301. /// <response code="204">Member state updated.</response>
  302. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  303. [HttpPost("SetIgnoreWait")]
  304. [ProducesResponseType(StatusCodes.Status204NoContent)]
  305. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  306. public ActionResult SyncPlaySetIgnoreWait(
  307. [FromBody, Required] IgnoreWaitRequestDto requestData)
  308. {
  309. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  310. var syncPlayRequest = new IgnoreWaitGroupRequest(requestData.IgnoreWait);
  311. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  312. return NoContent();
  313. }
  314. /// <summary>
  315. /// Request next item in SyncPlay group.
  316. /// </summary>
  317. /// <param name="requestData">The current item information.</param>
  318. /// <response code="204">Next item update sent to all group members.</response>
  319. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  320. [HttpPost("NextItem")]
  321. [ProducesResponseType(StatusCodes.Status204NoContent)]
  322. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  323. public ActionResult SyncPlayNextItem(
  324. [FromBody, Required] NextItemRequestDto requestData)
  325. {
  326. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  327. var syncPlayRequest = new NextItemGroupRequest(requestData.PlaylistItemId);
  328. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  329. return NoContent();
  330. }
  331. /// <summary>
  332. /// Request previous item in SyncPlay group.
  333. /// </summary>
  334. /// <param name="requestData">The current item information.</param>
  335. /// <response code="204">Previous item update sent to all group members.</response>
  336. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  337. [HttpPost("PreviousItem")]
  338. [ProducesResponseType(StatusCodes.Status204NoContent)]
  339. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  340. public ActionResult SyncPlayPreviousItem(
  341. [FromBody, Required] PreviousItemRequestDto requestData)
  342. {
  343. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  344. var syncPlayRequest = new PreviousItemGroupRequest(requestData.PlaylistItemId);
  345. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  346. return NoContent();
  347. }
  348. /// <summary>
  349. /// Request to set repeat mode in SyncPlay group.
  350. /// </summary>
  351. /// <param name="requestData">The new repeat mode.</param>
  352. /// <response code="204">Play queue update sent to all group members.</response>
  353. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  354. [HttpPost("SetRepeatMode")]
  355. [ProducesResponseType(StatusCodes.Status204NoContent)]
  356. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  357. public ActionResult SyncPlaySetRepeatMode(
  358. [FromBody, Required] SetRepeatModeRequestDto requestData)
  359. {
  360. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  361. var syncPlayRequest = new SetRepeatModeGroupRequest(requestData.Mode);
  362. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  363. return NoContent();
  364. }
  365. /// <summary>
  366. /// Request to set shuffle mode in SyncPlay group.
  367. /// </summary>
  368. /// <param name="requestData">The new shuffle mode.</param>
  369. /// <response code="204">Play queue update sent to all group members.</response>
  370. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  371. [HttpPost("SetShuffleMode")]
  372. [ProducesResponseType(StatusCodes.Status204NoContent)]
  373. [Authorize(Policy = Policies.SyncPlayIsInGroup)]
  374. public ActionResult SyncPlaySetShuffleMode(
  375. [FromBody, Required] SetShuffleModeRequestDto requestData)
  376. {
  377. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  378. var syncPlayRequest = new SetShuffleModeGroupRequest(requestData.Mode);
  379. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  380. return NoContent();
  381. }
  382. /// <summary>
  383. /// Update session ping.
  384. /// </summary>
  385. /// <param name="requestData">The new ping.</param>
  386. /// <response code="204">Ping updated.</response>
  387. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  388. [HttpPost("Ping")]
  389. [ProducesResponseType(StatusCodes.Status204NoContent)]
  390. public ActionResult SyncPlayPing(
  391. [FromBody, Required] PingRequestDto requestData)
  392. {
  393. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  394. var syncPlayRequest = new PingGroupRequest(requestData.Ping);
  395. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  396. return NoContent();
  397. }
  398. }
  399. }