define(['playbackManager', 'events', 'serverNotifications', 'connectionManager'], function (playbackManager, events, serverNotifications, connectionManager) { 'use strict'; function getActivePlayerId() { var info = playbackManager.getPlayerInfo(); return info ? info.id : null; } function sendPlayCommand(apiClient, options, playType) { var sessionId = getActivePlayerId(); var ids = options.ids || options.items.map(function (i) { return i.Id; }); var remoteOptions = { ItemIds: ids.join(','), PlayCommand: playType }; if (options.startPositionTicks) { remoteOptions.StartPositionTicks = options.startPositionTicks; } if (options.mediaSourceId) { remoteOptions.MediaSourceId = options.mediaSourceId; } if (options.audioStreamIndex != null) { remoteOptions.AudioStreamIndex = options.audioStreamIndex; } if (options.subtitleStreamIndex != null) { remoteOptions.SubtitleStreamIndex = options.subtitleStreamIndex; } if (options.startIndex != null) { remoteOptions.StartIndex = options.startIndex; } return apiClient.sendPlayCommand(sessionId, remoteOptions); } function sendPlayStateCommand(apiClient, command, options) { var sessionId = getActivePlayerId(); apiClient.sendPlayStateCommand(sessionId, command, options); } function getCurrentApiClient(instance) { var currentServerId = instance.currentServerId; if (currentServerId) { return connectionManager.getApiClient(currentServerId); } return connectionManager.currentApiClient(); } function sendCommandByName(instance, name, options) { var command = { Name: name }; if (options) { command.Arguments = options; } instance.sendCommand(command); } function unsubscribeFromPlayerUpdates(instance) { instance.isUpdating = true; var apiClient = getCurrentApiClient(instance); apiClient.sendMessage('SessionsStop'); if (instance.pollInterval) { clearInterval(instance.pollInterval); instance.pollInterval = null; } } function processUpdatedSessions(instance, sessions, apiClient) { var serverId = apiClient.serverId(); sessions.map(function (s) { if (s.NowPlayingItem) { s.NowPlayingItem.ServerId = serverId; } }); var currentTargetId = getActivePlayerId(); var session = sessions.filter(function (s) { return s.Id === currentTargetId; })[0]; if (session) { normalizeImages(session, apiClient); var eventNames = getChangedEvents(instance.lastPlayerData, session); instance.lastPlayerData = session; for (var i = 0, length = eventNames.length; i < length; i++) { events.trigger(instance, eventNames[i], [session]); } } else { instance.lastPlayerData = session; playbackManager.setDefaultPlayerActive(); } } function getChangedEvents(state1, state2) { var names = []; if (!state1) { names.push('statechange'); names.push('timeupdate'); names.push('pause'); return names; } // TODO: Trim these down to prevent the UI from over-refreshing names.push('statechange'); names.push('timeupdate'); names.push('pause'); return names; } function onPollIntervalFired() { var instance = this; var apiClient = getCurrentApiClient(instance); if (!apiClient.isMessageChannelOpen()) { apiClient.getSessions().then(function (sessions) { processUpdatedSessions(instance, sessions, apiClient); }); } } function subscribeToPlayerUpdates(instance) { instance.isUpdating = true; var apiClient = getCurrentApiClient(instance); apiClient.sendMessage('SessionsStart', '100,800'); if (instance.pollInterval) { clearInterval(instance.pollInterval); instance.pollInterval = null; } instance.pollInterval = setInterval(onPollIntervalFired.bind(instance), 5000); } function normalizeImages(state, apiClient) { if (state && state.NowPlayingItem) { var item = state.NowPlayingItem; if (!item.ImageTags || !item.ImageTags.Primary) { if (item.PrimaryImageTag) { item.ImageTags = item.ImageTags || {}; item.ImageTags.Primary = item.PrimaryImageTag; } } if (item.BackdropImageTag && item.BackdropItemId === item.Id) { item.BackdropImageTags = [item.BackdropImageTag]; } if (item.BackdropImageTag && item.BackdropItemId !== item.Id) { item.ParentBackdropImageTags = [item.BackdropImageTag]; item.ParentBackdropItemId = item.BackdropItemId; } if (!item.ServerId) { item.ServerId = apiClient.serverId(); } } } function SessionPlayer() { var self = this; this.name = 'Remote Control'; this.type = 'mediaplayer'; this.isLocalPlayer = false; this.id = 'remoteplayer'; events.on(serverNotifications, 'Sessions', function (e, apiClient, data) { processUpdatedSessions(self, data, apiClient); }); } SessionPlayer.prototype.beginPlayerUpdates = function () { this.playerListenerCount = this.playerListenerCount || 0; if (this.playerListenerCount <= 0) { this.playerListenerCount = 0; subscribeToPlayerUpdates(this); } this.playerListenerCount++; }; SessionPlayer.prototype.endPlayerUpdates = function () { this.playerListenerCount = this.playerListenerCount || 0; this.playerListenerCount--; if (this.playerListenerCount <= 0) { unsubscribeFromPlayerUpdates(this); this.playerListenerCount = 0; } }; SessionPlayer.prototype.getPlayerState = function () { return this.lastPlayerData || {}; }; SessionPlayer.prototype.getTargets = function () { var apiClient = getCurrentApiClient(this); var sessionQuery = { ControllableByUserId: apiClient.getCurrentUserId() }; if (apiClient) { var name = this.name; return apiClient.getSessions(sessionQuery).then(function (sessions) { return sessions.filter(function (s) { return s.DeviceId !== apiClient.deviceId(); }).map(function (s) { return { name: s.DeviceName, deviceName: s.DeviceName, deviceType: s.DeviceType, id: s.Id, playerName: name, appName: s.Client, playableMediaTypes: s.PlayableMediaTypes, isLocalPlayer: false, supportedCommands: s.SupportedCommands, user: s.UserId ? { Id: s.UserId, Name: s.UserName, PrimaryImageTag: s.UserPrimaryImageTag } : null }; }); }); } else { return Promise.resolve([]); } }; SessionPlayer.prototype.sendCommand = function (command) { var sessionId = getActivePlayerId(); var apiClient = getCurrentApiClient(this); apiClient.sendCommand(sessionId, command); }; SessionPlayer.prototype.play = function (options) { options = Object.assign({}, options); if (options.items) { options.ids = options.items.map(function (i) { return i.Id; }); options.items = null; } return sendPlayCommand(getCurrentApiClient(this), options, 'PlayNow'); }; SessionPlayer.prototype.shuffle = function (item) { sendPlayCommand(getCurrentApiClient(this), { ids: [item.Id] }, 'PlayShuffle'); }; SessionPlayer.prototype.instantMix = function (item) { sendPlayCommand(getCurrentApiClient(this), { ids: [item.Id] }, 'PlayInstantMix'); }; SessionPlayer.prototype.queue = function (options) { sendPlayCommand(getCurrentApiClient(this), options, 'PlayNext'); }; SessionPlayer.prototype.queueNext = function (options) { sendPlayCommand(getCurrentApiClient(this), options, 'PlayLast'); }; SessionPlayer.prototype.canPlayMediaType = function (mediaType) { mediaType = (mediaType || '').toLowerCase(); return mediaType === 'audio' || mediaType === 'video'; }; SessionPlayer.prototype.canQueueMediaType = function (mediaType) { return this.canPlayMediaType(mediaType); }; SessionPlayer.prototype.stop = function () { sendPlayStateCommand(getCurrentApiClient(this), 'stop'); }; SessionPlayer.prototype.nextTrack = function () { sendPlayStateCommand(getCurrentApiClient(this), 'nextTrack'); }; SessionPlayer.prototype.previousTrack = function () { sendPlayStateCommand(getCurrentApiClient(this), 'previousTrack'); }; SessionPlayer.prototype.seek = function (positionTicks) { sendPlayStateCommand(getCurrentApiClient(this), 'seek', { SeekPositionTicks: positionTicks }); }; SessionPlayer.prototype.currentTime = function (val) { if (val != null) { return this.seek(val); } var state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.PositionTicks; }; SessionPlayer.prototype.duration = function () { var state = this.lastPlayerData || {}; state = state.NowPlayingItem || {}; return state.RunTimeTicks; }; SessionPlayer.prototype.paused = function () { var state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.IsPaused; }; SessionPlayer.prototype.getVolume = function () { var state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.VolumeLevel; }; SessionPlayer.prototype.isMuted = function () { var state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.IsMuted; }; SessionPlayer.prototype.pause = function () { sendPlayStateCommand(getCurrentApiClient(this), 'Pause'); }; SessionPlayer.prototype.unpause = function () { sendPlayStateCommand(getCurrentApiClient(this), 'Unpause'); }; SessionPlayer.prototype.playPause = function () { sendPlayStateCommand(getCurrentApiClient(this), 'PlayPause'); }; SessionPlayer.prototype.setMute = function (isMuted) { if (isMuted) { sendCommandByName(this, 'Mute'); } else { sendCommandByName(this, 'Unmute'); } }; SessionPlayer.prototype.toggleMute = function () { sendCommandByName(this, 'ToggleMute'); }; SessionPlayer.prototype.setVolume = function (vol) { sendCommandByName(this, 'SetVolume', { Volume: vol }); }; SessionPlayer.prototype.volumeUp = function () { sendCommandByName(this, 'VolumeUp'); }; SessionPlayer.prototype.volumeDown = function () { sendCommandByName(this, 'VolumeDown'); }; SessionPlayer.prototype.toggleFullscreen = function () { sendCommandByName(this, 'ToggleFullscreen'); }; SessionPlayer.prototype.audioTracks = function () { var state = this.lastPlayerData || {}; state = state.NowPlayingItem || {}; var streams = state.MediaStreams || []; return streams.filter(function (s) { return s.Type === 'Audio'; }); }; SessionPlayer.prototype.getAudioStreamIndex = function () { var state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.AudioStreamIndex; }; SessionPlayer.prototype.playTrailers = function (item) { sendCommandByName(this, 'PlayTrailers', { ItemId: item.Id }); }; SessionPlayer.prototype.setAudioStreamIndex = function (index) { sendCommandByName(this, 'SetAudioStreamIndex', { Index: index }); }; SessionPlayer.prototype.subtitleTracks = function () { var state = this.lastPlayerData || {}; state = state.NowPlayingItem || {}; var streams = state.MediaStreams || []; return streams.filter(function (s) { return s.Type === 'Subtitle'; }); }; SessionPlayer.prototype.getSubtitleStreamIndex = function () { var state = this.lastPlayerData || {}; state = state.PlayState || {}; return state.SubtitleStreamIndex; }; SessionPlayer.prototype.setSubtitleStreamIndex = function (index) { sendCommandByName(this, 'SetSubtitleStreamIndex', { Index: index }); }; SessionPlayer.prototype.getMaxStreamingBitrate = function () { }; SessionPlayer.prototype.setMaxStreamingBitrate = function (options) { }; SessionPlayer.prototype.isFullscreen = function () { }; SessionPlayer.prototype.toggleFullscreen = function () { }; SessionPlayer.prototype.getRepeatMode = function () { }; SessionPlayer.prototype.setRepeatMode = function (mode) { sendCommandByName(this, 'SetRepeatMode', { RepeatMode: mode }); }; SessionPlayer.prototype.setPlaylistShuffleMode = function (mode) { sendCommandByName(this, 'SetPlaylistShuffleMode', { ShuffleMode: mode }); }; SessionPlayer.prototype.getPlaylistShuffleMode = function () { }; SessionPlayer.prototype.displayContent = function (options) { sendCommandByName(this, 'DisplayContent', options); }; SessionPlayer.prototype.isPlaying = function () { var state = this.lastPlayerData || {}; return state.NowPlayingItem != null; }; SessionPlayer.prototype.isPlayingVideo = function () { var state = this.lastPlayerData || {}; state = state.NowPlayingItem || {}; return state.MediaType === 'Video'; }; SessionPlayer.prototype.isPlayingAudio = function () { var state = this.lastPlayerData || {}; state = state.NowPlayingItem || {}; return state.MediaType === 'Audio'; }; SessionPlayer.prototype.getPlaylist = function () { return Promise.resolve([]); }; SessionPlayer.prototype.getCurrentPlaylistItemId = function () { }; SessionPlayer.prototype.setCurrentPlaylistItem = function (playlistItemId) { return Promise.resolve(); }; SessionPlayer.prototype.removeFromPlaylist = function (playlistItemIds) { return Promise.resolve(); }; SessionPlayer.prototype.tryPair = function (target) { return Promise.resolve(); }; return SessionPlayer; });