SyncPlayController.cs 20 KB

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