SyncPlayController.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  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 Jellyfin.Api.Models.SyncPlayDtos;
  8. using MediaBrowser.Controller.Net;
  9. using MediaBrowser.Controller.Session;
  10. using MediaBrowser.Controller.SyncPlay;
  11. using MediaBrowser.Controller.SyncPlay.PlaybackRequests;
  12. using MediaBrowser.Controller.SyncPlay.Requests;
  13. using MediaBrowser.Model.SyncPlay;
  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] NewGroupRequestDto 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. [Authorize(Policy = Policies.SyncPlayAccess)]
  69. public ActionResult SyncPlayJoinGroup(
  70. [FromBody, Required] JoinGroupRequestDto requestData)
  71. {
  72. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  73. var syncPlayRequest = new JoinGroupRequest(requestData.GroupId);
  74. _syncPlayManager.JoinGroup(currentSession, syncPlayRequest, CancellationToken.None);
  75. return NoContent();
  76. }
  77. /// <summary>
  78. /// Leave the joined SyncPlay group.
  79. /// </summary>
  80. /// <response code="204">Group leave successful.</response>
  81. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  82. [HttpPost("Leave")]
  83. [ProducesResponseType(StatusCodes.Status204NoContent)]
  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.SyncPlayAccess)]
  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. public ActionResult SyncPlaySetNewQueue(
  114. [FromBody, Required] PlayRequestDto requestData)
  115. {
  116. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  117. var syncPlayRequest = new PlayGroupRequest(
  118. requestData.PlayingQueue,
  119. requestData.PlayingItemPosition,
  120. requestData.StartPositionTicks);
  121. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  122. return NoContent();
  123. }
  124. /// <summary>
  125. /// Request to change playlist item in SyncPlay group.
  126. /// </summary>
  127. /// <param name="requestData">The new item to play.</param>
  128. /// <response code="204">Queue update sent to all group members.</response>
  129. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  130. [HttpPost("SetPlaylistItem")]
  131. [ProducesResponseType(StatusCodes.Status204NoContent)]
  132. public ActionResult SyncPlaySetPlaylistItem(
  133. [FromBody, Required] SetPlaylistItemRequestDto requestData)
  134. {
  135. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  136. var syncPlayRequest = new SetPlaylistItemGroupRequest(requestData.PlaylistItemId);
  137. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  138. return NoContent();
  139. }
  140. /// <summary>
  141. /// Request to remove items from the playlist in SyncPlay group.
  142. /// </summary>
  143. /// <param name="requestData">The items to remove.</param>
  144. /// <response code="204">Queue update sent to all group members.</response>
  145. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  146. [HttpPost("RemoveFromPlaylist")]
  147. [ProducesResponseType(StatusCodes.Status204NoContent)]
  148. public ActionResult SyncPlayRemoveFromPlaylist(
  149. [FromBody, Required] RemoveFromPlaylistRequestDto requestData)
  150. {
  151. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  152. var syncPlayRequest = new RemoveFromPlaylistGroupRequest(requestData.PlaylistItemIds);
  153. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  154. return NoContent();
  155. }
  156. /// <summary>
  157. /// Request to move an item in the playlist in SyncPlay group.
  158. /// </summary>
  159. /// <param name="requestData">The new position for the item.</param>
  160. /// <response code="204">Queue update sent to all group members.</response>
  161. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  162. [HttpPost("MovePlaylistItem")]
  163. [ProducesResponseType(StatusCodes.Status204NoContent)]
  164. public ActionResult SyncPlayMovePlaylistItem(
  165. [FromBody, Required] MovePlaylistItemRequestDto requestData)
  166. {
  167. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  168. var syncPlayRequest = new MovePlaylistItemGroupRequest(requestData.PlaylistItemId, requestData.NewIndex);
  169. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  170. return NoContent();
  171. }
  172. /// <summary>
  173. /// Request to queue items to the playlist of a SyncPlay group.
  174. /// </summary>
  175. /// <param name="requestData">The items to add.</param>
  176. /// <response code="204">Queue update sent to all group members.</response>
  177. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  178. [HttpPost("Queue")]
  179. [ProducesResponseType(StatusCodes.Status204NoContent)]
  180. public ActionResult SyncPlayQueue(
  181. [FromBody, Required] QueueRequestDto requestData)
  182. {
  183. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  184. var syncPlayRequest = new QueueGroupRequest(requestData.ItemIds, requestData.Mode);
  185. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  186. return NoContent();
  187. }
  188. /// <summary>
  189. /// Request unpause in SyncPlay group.
  190. /// </summary>
  191. /// <response code="204">Unpause update sent to all group members.</response>
  192. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  193. [HttpPost("Unpause")]
  194. [ProducesResponseType(StatusCodes.Status204NoContent)]
  195. public ActionResult SyncPlayUnpause()
  196. {
  197. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  198. var syncPlayRequest = new UnpauseGroupRequest();
  199. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  200. return NoContent();
  201. }
  202. /// <summary>
  203. /// Request pause in SyncPlay group.
  204. /// </summary>
  205. /// <response code="204">Pause update sent to all group members.</response>
  206. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  207. [HttpPost("Pause")]
  208. [ProducesResponseType(StatusCodes.Status204NoContent)]
  209. public ActionResult SyncPlayPause()
  210. {
  211. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  212. var syncPlayRequest = new PauseGroupRequest();
  213. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  214. return NoContent();
  215. }
  216. /// <summary>
  217. /// Request stop in SyncPlay group.
  218. /// </summary>
  219. /// <response code="204">Stop update sent to all group members.</response>
  220. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  221. [HttpPost("Stop")]
  222. [ProducesResponseType(StatusCodes.Status204NoContent)]
  223. public ActionResult SyncPlayStop()
  224. {
  225. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  226. var syncPlayRequest = new StopGroupRequest();
  227. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  228. return NoContent();
  229. }
  230. /// <summary>
  231. /// Request seek in SyncPlay group.
  232. /// </summary>
  233. /// <param name="requestData">The new playback position.</param>
  234. /// <response code="204">Seek update sent to all group members.</response>
  235. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  236. [HttpPost("Seek")]
  237. [ProducesResponseType(StatusCodes.Status204NoContent)]
  238. public ActionResult SyncPlaySeek(
  239. [FromBody, Required] SeekRequestDto requestData)
  240. {
  241. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  242. var syncPlayRequest = new SeekGroupRequest(requestData.PositionTicks);
  243. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  244. return NoContent();
  245. }
  246. /// <summary>
  247. /// Notify SyncPlay group that member is buffering.
  248. /// </summary>
  249. /// <param name="requestData">The player status.</param>
  250. /// <response code="204">Group state update sent to all group members.</response>
  251. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  252. [HttpPost("Buffering")]
  253. [ProducesResponseType(StatusCodes.Status204NoContent)]
  254. public ActionResult SyncPlayBuffering(
  255. [FromBody, Required] BufferRequestDto requestData)
  256. {
  257. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  258. var syncPlayRequest = new BufferGroupRequest(
  259. requestData.When,
  260. requestData.PositionTicks,
  261. requestData.IsPlaying,
  262. requestData.PlaylistItemId);
  263. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  264. return NoContent();
  265. }
  266. /// <summary>
  267. /// Notify SyncPlay group that member is ready for playback.
  268. /// </summary>
  269. /// <param name="requestData">The player status.</param>
  270. /// <response code="204">Group state update sent to all group members.</response>
  271. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  272. [HttpPost("Ready")]
  273. [ProducesResponseType(StatusCodes.Status204NoContent)]
  274. public ActionResult SyncPlayReady(
  275. [FromBody, Required] ReadyRequestDto requestData)
  276. {
  277. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  278. var syncPlayRequest = new ReadyGroupRequest(
  279. requestData.When,
  280. requestData.PositionTicks,
  281. requestData.IsPlaying,
  282. requestData.PlaylistItemId);
  283. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  284. return NoContent();
  285. }
  286. /// <summary>
  287. /// Request SyncPlay group to ignore member during group-wait.
  288. /// </summary>
  289. /// <param name="requestData">The settings to set.</param>
  290. /// <response code="204">Member state updated.</response>
  291. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  292. [HttpPost("SetIgnoreWait")]
  293. [ProducesResponseType(StatusCodes.Status204NoContent)]
  294. public ActionResult SyncPlaySetIgnoreWait(
  295. [FromBody, Required] IgnoreWaitRequestDto requestData)
  296. {
  297. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  298. var syncPlayRequest = new IgnoreWaitGroupRequest(requestData.IgnoreWait);
  299. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  300. return NoContent();
  301. }
  302. /// <summary>
  303. /// Request next item in SyncPlay group.
  304. /// </summary>
  305. /// <param name="requestData">The current item information.</param>
  306. /// <response code="204">Next item update sent to all group members.</response>
  307. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  308. [HttpPost("NextItem")]
  309. [ProducesResponseType(StatusCodes.Status204NoContent)]
  310. public ActionResult SyncPlayNextItem(
  311. [FromBody, Required] NextItemRequestDto requestData)
  312. {
  313. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  314. var syncPlayRequest = new NextItemGroupRequest(requestData.PlaylistItemId);
  315. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  316. return NoContent();
  317. }
  318. /// <summary>
  319. /// Request previous item in SyncPlay group.
  320. /// </summary>
  321. /// <param name="requestData">The current item information.</param>
  322. /// <response code="204">Previous item update sent to all group members.</response>
  323. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  324. [HttpPost("PreviousItem")]
  325. [ProducesResponseType(StatusCodes.Status204NoContent)]
  326. public ActionResult SyncPlayPreviousItem(
  327. [FromBody, Required] PreviousItemRequestDto requestData)
  328. {
  329. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  330. var syncPlayRequest = new PreviousItemGroupRequest(requestData.PlaylistItemId);
  331. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  332. return NoContent();
  333. }
  334. /// <summary>
  335. /// Request to set repeat mode in SyncPlay group.
  336. /// </summary>
  337. /// <param name="requestData">The new repeat mode.</param>
  338. /// <response code="204">Play queue update sent to all group members.</response>
  339. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  340. [HttpPost("SetRepeatMode")]
  341. [ProducesResponseType(StatusCodes.Status204NoContent)]
  342. public ActionResult SyncPlaySetRepeatMode(
  343. [FromBody, Required] SetRepeatModeRequestDto requestData)
  344. {
  345. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  346. var syncPlayRequest = new SetRepeatModeGroupRequest(requestData.Mode);
  347. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  348. return NoContent();
  349. }
  350. /// <summary>
  351. /// Request to set shuffle mode in SyncPlay group.
  352. /// </summary>
  353. /// <param name="requestData">The new shuffle mode.</param>
  354. /// <response code="204">Play queue update sent to all group members.</response>
  355. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  356. [HttpPost("SetShuffleMode")]
  357. [ProducesResponseType(StatusCodes.Status204NoContent)]
  358. public ActionResult SyncPlaySetShuffleMode(
  359. [FromBody, Required] SetShuffleModeRequestDto requestData)
  360. {
  361. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  362. var syncPlayRequest = new SetShuffleModeGroupRequest(requestData.Mode);
  363. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  364. return NoContent();
  365. }
  366. /// <summary>
  367. /// Update session ping.
  368. /// </summary>
  369. /// <param name="requestData">The new ping.</param>
  370. /// <response code="204">Ping updated.</response>
  371. /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
  372. [HttpPost("Ping")]
  373. [ProducesResponseType(StatusCodes.Status204NoContent)]
  374. public ActionResult SyncPlayPing(
  375. [FromBody, Required] PingRequestDto requestData)
  376. {
  377. var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
  378. var syncPlayRequest = new PingGroupRequest(requestData.Ping);
  379. _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
  380. return NoContent();
  381. }
  382. }
  383. }