public ShellViewModel(Library library, ViewSettings viewSettings, CoreSettings coreSettings, IWindowManager windowManager, MobileApiInfo mobileApiInfo)
        {
            this.library = library;
            this.ViewSettings = viewSettings;
            this.coreSettings = coreSettings;

            this.disposable = new CompositeDisposable();
            this.UpdateViewModel = new UpdateViewModel(viewSettings);

            this.library.Initialize();
            this.accessToken = this.library.LocalAccessControl.RegisterLocalAccessToken();

            this.library.WhenAnyValue(x => x.CurrentPlaylist).Subscribe(x => this.RaisePropertyChanged("CurrentPlaylist"));

            this.canChangeTime = this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockTime), this.accessToken)
                .ToProperty(this, x => x.CanChangeTime);
            this.canChangeVolume = this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockVolume), this.accessToken)
                .ToProperty(this, x => x.CanChangeVolume);
            this.canAlterPlaylist = this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlaylist), this.accessToken)
                .ToProperty(this, x => x.CanAlterPlaylist);

            this.showVotes = this.library.RemoteAccessControl.WhenAnyValue(x => x.IsGuestSystemReallyEnabled)
                .CombineLatest(mobileApiInfo.ConnectedClientCount, (enableGuestSystem, connectedClients) => enableGuestSystem && connectedClients > 0)
                .ToProperty(this, x => x.ShowVotes);

            mobileApiInfo.VideoPlayerToggleRequest.Subscribe(_ => this.ShowVideoPlayer = !this.ShowVideoPlayer);

            this.isAdmin = this.library.LocalAccessControl.ObserveAccessPermission(this.accessToken)
                .Select(x => x == AccessPermission.Admin)
                .ToProperty(this, x => x.IsAdmin);

            this.NextSongCommand = ReactiveCommand.CreateAsyncTask(this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlayPause), this.accessToken)
                    .CombineLatest(this.library.WhenAnyValue(x => x.CurrentPlaylist.CanPlayNextSong), (x1, x2) => x1 && x2)
                    .ObserveOn(RxApp.MainThreadScheduler),
                _ => this.library.PlayNextSongAsync(this.accessToken));

            this.PreviousSongCommand = ReactiveCommand.CreateAsyncTask(this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlayPause), this.accessToken)
                    .CombineLatest(this.library.WhenAnyValue(x => x.CurrentPlaylist.CanPlayPreviousSong), (x1, x2) => x1 && x2)
                    .ObserveOn(RxApp.MainThreadScheduler),
                _ => this.library.PlayPreviousSongAsync(this.accessToken));

            if (!this.library.Playlists.Any())
            {
                this.library.AddAndSwitchToPlaylist(this.GetNewPlaylistName(), this.accessToken);
            }

            else
            {
                this.library.SwitchToPlaylist(this.library.Playlists.First(), this.accessToken);
            }

            this.SettingsViewModel = new SettingsViewModel(this.library, this.ViewSettings, this.coreSettings, windowManager, this.accessToken, mobileApiInfo);

            this.LocalViewModel = new LocalViewModel(this.library, this.ViewSettings, this.coreSettings, accessToken);
            this.YoutubeViewModel = new YoutubeViewModel(this.library, this.ViewSettings, this.coreSettings, accessToken);
            this.SoundCloudViewModel = new SoundCloudViewModel(this.library, accessToken, this.coreSettings, this.ViewSettings);
            this.DirectYoutubeViewModel = new DirectYoutubeViewModel(this.library, this.coreSettings, accessToken);

            this.currentSongSource = this.WhenAnyValue(x => x.IsLocal, x => x.IsYoutube, x => x.IsSoundCloud,
                (local, youtube, soundcloud) =>
                {
                    if (local)
                    {
                        return (ISongSourceViewModel)this.LocalViewModel;
                    }

                    if (youtube)
                    {
                        return this.YoutubeViewModel;
                    }

                    if (soundcloud)
                    {
                        return this.SoundCloudViewModel;
                    }

                    return this.LocalViewModel;
                })
                .ToProperty(this, x => x.CurrentSongSource, null, ImmediateScheduler.Instance);

            this.MuteCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.IsAdmin));
            this.MuteCommand.Subscribe(x => this.Volume = 0);

            this.UnMuteCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.IsAdmin));
            this.UnMuteCommand.Subscribe(x => this.Volume = 1);

            this.canModifyWindow = this.library.LocalAccessControl.HasAccess(this.ViewSettings.WhenAnyValue(x => x.LockWindow), this.accessToken)
                .ToProperty(this, x => x.CanModifyWindow);

            this.isPlaying = this.library.PlaybackState
                .Select(x => x == AudioPlayerState.Playing)
                .ObserveOn(RxApp.MainThreadScheduler)
                .ToProperty(this, x => x.IsPlaying);

            this.currentTime = this.library.CurrentPlaybackTime
                .StartWith(TimeSpan.Zero)
                .Select(x => x.FormatAdaptive())
                .ToProperty(this, x => x.CurrentTime);

            this.currentSeconds = this.library.CurrentPlaybackTime
                .Select(x => (int)x.TotalSeconds)
                .ToProperty(this, x => x.CurrentSeconds);

            this.totalTime = this.library.TotalTime
                .Select(x => x.FormatAdaptive())
                .ToProperty(this, x => x.TotalTime);

            this.totalSeconds = this.library.TotalTime
                .Select(x => (int)x.TotalSeconds)
                .ToProperty(this, x => x.TotalSeconds);

            this.volume = this.library.WhenAnyValue(x => x.Volume, x => (double)x)
                .ToProperty(this, x => x.Volume);

            this.AddPlaylistCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.CanAlterPlaylist));
            this.AddPlaylistCommand.Subscribe(x => this.AddPlaylist());

            this.Playlists = this.library.Playlists.CreateDerivedCollection(this.CreatePlaylistViewModel, x => x.Dispose());

            this.ShowSettingsCommand = ReactiveCommand.Create();
            this.ShowSettingsCommand.Subscribe(x => this.SettingsViewModel.HandleSettings());

            this.ShufflePlaylistCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.CanAlterPlaylist));
            this.ShufflePlaylistCommand.Subscribe(x => this.library.ShufflePlaylist(this.accessToken));

            IObservable<bool> canPlay = this.WhenAnyValue(x => x.CurrentPlaylist.SelectedEntries)
                .CombineLatest(this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlayPause), this.accessToken), this.library.LoadedSong, this.library.PlaybackState,
                    (selectedPlaylistEntries, hasPlayAccess, loadedSong, playBackState) =>

                        // The admin can always play, but if we are in party mode, we have to check
                        // whether it is allowed to play
                        hasPlayAccess &&

                        // If exactly one song is selected, the command can be executed
                        (selectedPlaylistEntries != null && selectedPlaylistEntries.Count() == 1 ||

                        // If the current song is paused, the command can be executed
                        (loadedSong != null || playBackState == AudioPlayerState.Paused)));
            this.PlayCommand = ReactiveCommand.CreateAsyncTask(canPlay, async _ =>
            {
                if (await this.library.PlaybackState.FirstAsync() == AudioPlayerState.Paused || await this.library.LoadedSong.FirstAsync() != null)
                {
                    await this.library.ContinueSongAsync(this.accessToken);
                }

                else
                {
                    await this.library.PlaySongAsync(this.CurrentPlaylist.SelectedEntries.First().Index, this.accessToken);
                }
            });

            this.PlayOverrideCommand = ReactiveCommand.CreateAsyncTask(this.WhenAnyValue(x => x.CurrentPlaylist.SelectedEntries)
                .CombineLatest(this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlayPause), this.accessToken),
                    (selectedPlaylistEntries, hasAccess) => hasAccess && (selectedPlaylistEntries != null && selectedPlaylistEntries.Count() == 1)),
                _ => this.library.PlaySongAsync(this.CurrentPlaylist.SelectedEntries.First().Index, this.accessToken));

            this.PauseCommand = ReactiveCommand.CreateAsyncTask(this.library.LocalAccessControl.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlayPause), this.accessToken)
                .CombineLatest(this.WhenAnyValue(x => x.IsPlaying), (hasAccess, isPlaying) => hasAccess && isPlaying),
                _ => this.library.PauseSongAsync(this.accessToken));

            var pauseOrContinueCommand = this.WhenAnyValue(x => x.IsPlaying)
                .Select(x => x ? this.PauseCommand : this.PlayCommand).Publish(null);
            pauseOrContinueCommand.Connect();

            this.PauseContinueCommand = ReactiveCommand.CreateAsyncTask(
                pauseOrContinueCommand.Select(x => x.CanExecuteObservable).Switch().ObserveOn(RxApp.MainThreadScheduler),
                async _ =>
                {
                    IReactiveCommand<Unit> command = await pauseOrContinueCommand.FirstAsync();
                    await command.ExecuteAsync();
                });

            this.EditPlaylistNameCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.CanAlterPlaylist, x => x.CurrentPlaylist, (x1, x2) => x1 && !x2.Model.IsTemporary));
            this.EditPlaylistNameCommand.Subscribe(x => this.CurrentPlaylist.EditName = true);

            this.RemovePlaylistCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.CurrentEditedPlaylist, x => x.CurrentPlaylist, x => x.CanAlterPlaylist,
                    (currentEditedPlaylist, currentPlaylist, canAlterPlaylist) => (currentEditedPlaylist != null || currentPlaylist != null) && canAlterPlaylist));
            this.RemovePlaylistCommand.Subscribe(x => this.RemoveCurrentPlaylist());

            this.IsLocal = true;
        }
        private void SetupMobileApi()
        {
            var library = Locator.Current.GetService<Library>();

            this.Log().Info("Remote control is {0}", this.coreSettings.EnableRemoteControl ? "enabled" : "disabled");
            this.Log().Info("Port is set to {0}", this.coreSettings.Port);

            IObservable<MobileApi> apiChanged = this.coreSettings.WhenAnyValue(x => x.Port).DistinctUntilChanged()
                .CombineLatest(this.coreSettings.WhenAnyValue(x => x.EnableRemoteControl), Tuple.Create)
                .Do(_ =>
                {
                    if (this.mobileApi != null)
                    {
                        this.mobileApi.Dispose();
                        this.mobileApi = null;
                    }
                })
                .Where(x => x.Item2)
                .Select(x => x.Item1)
                .Select(x => new MobileApi(x, library)).Publish(null).RefCount().Where(x => x != null);

            apiChanged.Subscribe(x =>
            {
                this.mobileApi = x;
                x.SendBroadcastAsync();
                x.StartClientDiscovery();
            });

            IConnectableObservable<IReadOnlyList<MobileClient>> connectedClients = apiChanged.Select(x => x.ConnectedClients).Switch().Publish(new List<MobileClient>());
            connectedClients.Connect();

            IConnectableObservable<bool> isPortOccupied = apiChanged.Select(x => x.IsPortOccupied).Switch().Publish(false);
            isPortOccupied.Connect();

            var apiStats = new MobileApiInfo(connectedClients, isPortOccupied);

            Locator.CurrentMutable.RegisterConstant(apiStats, typeof(MobileApiInfo));
        }
Example #3
0
        public ShellViewModel(Library library, ViewSettings viewSettings, CoreSettings coreSettings, IWindowManager windowManager, MobileApiInfo mobileApiInfo)
        {
            this.library = library;
            this.ViewSettings = viewSettings;
            this.coreSettings = coreSettings;

            this.disposable = new CompositeDisposable();
            this.UpdateViewModel = new UpdateViewModel(viewSettings);

            this.library.Initialize();
            this.accessToken = this.library.LocalAccessControl.RegisterLocalAccessToken();

            this.library.CurrentPlaylistChanged.Subscribe(x => this.RaisePropertyChanged("CurrentPlaylist"));

            this.canChangeTime = this.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockTime))
                .ToProperty(this, x => x.CanChangeTime);
            this.canChangeVolume = this.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockVolume))
                .ToProperty(this, x => x.CanChangeVolume);
            this.canAlterPlaylist = this.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlaylist))
                .ToProperty(this, x => x.CanAlterPlaylist);

            this.showVotes = this.coreSettings.WhenAnyValue(x => x.EnableVotingSystem)
                .CombineLatest(mobileApiInfo.ConnectedClientCount, (enableVoting, connectedClients) => enableVoting && connectedClients > 0)
                .ToProperty(this, x => x.ShowVotes);

            this.isAdmin = this.library.LocalAccessControl.ObserveAccessPermission(this.accessToken)
                .Select(x => x == AccessPermission.Admin)
                .ToProperty(this, x => x.IsAdmin);

            this.NextSongCommand = this.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlayPause))
                .CombineLatest(this.library.CanPlayNextSong, (x1, x2) => x1 && x2)
                .ToCommand();
            this.NextSongCommand.RegisterAsyncTask(_ => this.library.PlayNextSongAsync(this.accessToken));

            this.PreviousSongCommand = this.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlayPause))
                .CombineLatest(this.library.CanPlayPreviousSong, (x1, x2) => x1 && x2)
                .ToCommand();
            this.PreviousSongCommand.RegisterAsyncTask(_ => this.library.PlayPreviousSongAsync(this.accessToken));

            if (!this.library.Playlists.Any())
            {
                this.library.AddAndSwitchToPlaylist(this.GetNewPlaylistName(), this.accessToken);
            }

            else
            {
                this.library.SwitchToPlaylist(this.library.Playlists.First(), this.accessToken);
            }

            this.SettingsViewModel = new SettingsViewModel(this.library, this.ViewSettings, this.coreSettings, windowManager, this.accessToken, mobileApiInfo);

            this.LocalViewModel = new LocalViewModel(this.library, this.ViewSettings, this.coreSettings, accessToken);
            this.YoutubeViewModel = new YoutubeViewModel(this.library, this.ViewSettings, this.coreSettings, accessToken);
            this.SoundCloudViewModel = new SoundCloudViewModel(this.library, accessToken, this.coreSettings, this.ViewSettings);
            this.DirectYoutubeViewModel = new DirectYoutubeViewModel(this.library, accessToken);

            Observable.Interval(TimeSpan.FromMilliseconds(300), RxApp.TaskpoolScheduler)
                .Where(_ => this.RemainingPlaylistTimeout > TimeSpan.Zero)
                .Subscribe(x => this.RaisePropertyChanged("RemainingPlaylistTimeout"))
                .DisposeWith(this.disposable);

            this.currentSongSource = this.WhenAnyValue(x => x.IsLocal, x => x.IsYoutube, x => x.IsSoundCloud,
                (local, youtube, soundcloud) =>
                {
                    if (local)
                    {
                        return (ISongSourceViewModel)this.LocalViewModel;
                    }

                    if (youtube)
                    {
                        return this.YoutubeViewModel;
                    }

                    if (soundcloud)
                    {
                        return this.SoundCloudViewModel;
                    }

                    return this.LocalViewModel;
                })
                .ToProperty(this, x => x.CurrentSongSource, null, ImmediateScheduler.Instance);

            this.displayTimeoutWarning = Observable.Merge(this.LocalViewModel.TimeoutWarning, this.YoutubeViewModel.TimeoutWarning, this.DirectYoutubeViewModel.TimeoutWarning, this.SoundCloudViewModel.TimeoutWarning)
                .SelectMany(x => new[] { true, false }.ToObservable())
                .ToProperty(this, x => x.DisplayTimeoutWarning);

            this.showPlaylistTimeout = this.WhenAnyValue(x => x.IsAdmin)
                .CombineLatest(this.WhenAnyValue(x => x.SettingsViewModel.EnablePlaylistTimeout), (isAdmin, enableTimeout) => !isAdmin && enableTimeout)
                .ToProperty(this, x => x.ShowPlaylistTimeout);

            this.MuteCommand = new ReactiveCommand(this.WhenAnyValue(x => x.IsAdmin));
            this.MuteCommand.Subscribe(x => this.Volume = 0);

            this.UnMuteCommand = new ReactiveCommand(this.WhenAnyValue(x => x.IsAdmin));
            this.UnMuteCommand.Subscribe(x => this.Volume = 1);

            this.canModifyWindow = this.HasAccess(this.ViewSettings.WhenAnyValue(x => x.LockWindow))
                .ToProperty(this, x => x.CanModifyWindow);

            this.isPlaying = this.library.PlaybackState
                .Select(x => x == AudioPlayerState.Playing)
                .ToProperty(this, x => x.IsPlaying);

            this.currentTime = this.library.CurrentPlaybackTime
                .StartWith(TimeSpan.Zero)
                .Select(x => x.FormatAdaptive())
                .ToProperty(this, x => x.CurrentTime);

            this.currentSeconds = this.library.CurrentPlaybackTime
                .Select(x => (int)x.TotalSeconds)
                .ToProperty(this, x => x.CurrentSeconds);

            this.totalTime = this.library.TotalTime
                .Select(x => x.FormatAdaptive())
                .ToProperty(this, x => x.TotalTime);

            this.totalSeconds = this.library.TotalTime
                .Select(x => (int)x.TotalSeconds)
                .ToProperty(this, x => x.TotalSeconds);

            this.volume = this.library.WhenAnyValue(x => x.Volume, x => (double)x)
                .ToProperty(this, x => x.Volume);

            this.AddPlaylistCommand = new ReactiveCommand(this.WhenAnyValue(x => x.CanAlterPlaylist));
            this.AddPlaylistCommand.Subscribe(x => this.AddPlaylist());

            this.Playlists = this.library.Playlists.CreateDerivedCollection(this.CreatePlaylistViewModel);
            this.Playlists.ItemsRemoved.Subscribe(x => x.Dispose());

            this.ShowSettingsCommand = new ReactiveCommand();
            this.ShowSettingsCommand.Subscribe(x => this.SettingsViewModel.HandleSettings());

            this.ShufflePlaylistCommand = new ReactiveCommand(this.WhenAnyValue(x => x.CanAlterPlaylist));
            this.ShufflePlaylistCommand.Subscribe(x => this.library.ShufflePlaylist(this.accessToken));

            this.PlayCommand = new ReactiveCommand(this.WhenAnyValue(x => x.SelectedPlaylistEntries)
                .CombineLatest(this.WhenAnyValue(x => x.IsAdmin), this.coreSettings.WhenAnyValue(x => x.LockPlayPause), this.library.LoadedSong, this.library.PlaybackState,
                    (selectedPlaylistEntries, isAdmin, lockPlayPause, loadedSong, playBackState) =>

                        // The admin can always play, but if we are in party mode, we have to check
                        // whether it is allowed to play
                        (isAdmin || !lockPlayPause) &&

                        // If exactly one song is selected, the command can be executed
                        (selectedPlaylistEntries != null && selectedPlaylistEntries.Count() == 1 ||

                        // If the current song is paused, the command can be executed
                        (loadedSong != null || playBackState == AudioPlayerState.Paused))));
            this.PlayCommand.SelectMany(async x =>
            {
                if (await this.library.PlaybackState.FirstAsync() == AudioPlayerState.Paused || await this.library.LoadedSong.FirstAsync() != null)
                {
                    await this.library.ContinueSongAsync(this.accessToken);
                }

                else
                {
                    await this.library.PlaySongAsync(this.SelectedPlaylistEntries.First().Index, this.accessToken);
                }

                return Unit.Default;
            }).Subscribe();

            this.PlayOverrideCommand = this.WhenAnyValue(x => x.SelectedPlaylistEntries)
                .CombineLatest(this.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlayPause)), (selectedPlaylistEntries, hasAccess) =>
                    hasAccess && (selectedPlaylistEntries != null && selectedPlaylistEntries.Count() == 1))
                .ToCommand();
            this.PlayOverrideCommand.RegisterAsyncTask(_ => this.library.PlaySongAsync(this.SelectedPlaylistEntries.First().Index, this.accessToken));

            // The default play command differs whether we are in party mode or not and depends on
            // the selected setting in administrator mode and the song source.
            //
            // In party mode, it is always "Add To Playlist", in administrator mode we look at the
            // value that the song source returns
            this.defaultPlaybackCommand = this.WhenAnyValue(x => x.CurrentSongSource, x => x.IsAdmin,
                    (songSource, isAdmin) => !isAdmin || songSource.DefaultPlaybackAction == DefaultPlaybackAction.AddToPlaylist ?
                        songSource.AddToPlaylistCommand : songSource.PlayNowCommand)
                .ToProperty(this, x => x.DefaultPlaybackCommand);

            this.PauseCommand = this.HasAccess(this.coreSettings.WhenAnyValue(x => x.LockPlayPause))
                .CombineLatest(this.isPlaying, (hasAccess, isPlaying) => hasAccess && isPlaying)
                .ToCommand();
            this.PauseCommand.RegisterAsyncTask(_ => this.library.PauseSongAsync(this.accessToken));

            var pauseOrContinueCommand = this.WhenAnyValue(x => x.IsPlaying)
                .Select(x => x ? this.PauseCommand : this.PlayCommand).Publish(null);
            pauseOrContinueCommand.Connect();

            this.PauseContinueCommand = pauseOrContinueCommand.Select(x => x.CanExecuteObservable).Switch().ToCommand();
            this.PauseContinueCommand.SelectMany(async _ => await pauseOrContinueCommand.FirstAsync()).Subscribe(x => x.Execute(null));

            this.EditPlaylistNameCommand = this.WhenAnyValue(x => x.CanAlterPlaylist, x => x.CurrentPlaylist, (x1, x2) => x1 && !x2.Model.IsTemporary)
                .ToCommand();
            this.EditPlaylistNameCommand.Subscribe(x => this.CurrentPlaylist.EditName = true);

            this.RemovePlaylistCommand = this.WhenAnyValue(x => x.CurrentEditedPlaylist, x => x.CurrentPlaylist, x => x.CanAlterPlaylist,
                    (currentEditedPlaylist, currentPlaylist, canAlterPlaylist) => (currentEditedPlaylist != null || currentPlaylist != null) && canAlterPlaylist)
                .ToCommand();
            this.RemovePlaylistCommand.Subscribe(x => this.RemoveCurrentPlaylist());

            this.RemoveSelectedPlaylistEntriesCommand = this.WhenAnyValue(x => x.SelectedPlaylistEntries, x => x.CanAlterPlaylist,
                    (selectedPlaylistEntries, canAlterPlaylist) => selectedPlaylistEntries != null && selectedPlaylistEntries.Any() && canAlterPlaylist)
                .ToCommand();
            this.RemoveSelectedPlaylistEntriesCommand.Subscribe(x => this.library.RemoveFromPlaylist(this.SelectedPlaylistEntries.Select(entry => entry.Index), this.accessToken));

            // We re-evaluate the selected entries after each up or down move here, because WPF
            // doesn't send us proper updates about the selection
            var reEvaluateSelectedPlaylistEntry = new Subject<Unit>();
            this.MovePlaylistSongUpCommand = this.WhenAnyValue(x => x.SelectedPlaylistEntries)
                .Merge(reEvaluateSelectedPlaylistEntry.Select(_ => this.SelectedPlaylistEntries))
                .Select(x => x != null && x.Count() == 1 && x.First().Index > 0)
                .CombineLatest(this.WhenAnyValue(x => x.CanAlterPlaylist), (canMoveUp, canAlterPlaylist) => canMoveUp && canAlterPlaylist)
                .ToCommand();
            this.MovePlaylistSongUpCommand.Subscribe(_ =>
            {
                int index = this.SelectedPlaylistEntries.First().Index;
                this.library.MovePlaylistSong(index, index - 1, this.accessToken);
                reEvaluateSelectedPlaylistEntry.OnNext(Unit.Default);
            });

            this.MovePlaylistSongDownCommand = this.WhenAnyValue(x => x.SelectedPlaylistEntries)
                .Merge(reEvaluateSelectedPlaylistEntry.Select(_ => this.SelectedPlaylistEntries))
                .Select(x => x != null && x.Count() == 1 && x.First().Index < this.CurrentPlaylist.Songs.Count - 1)
                .CombineLatest(this.WhenAnyValue(x => x.CanAlterPlaylist), (canMoveDown, canAlterPlaylist) => canMoveDown && canAlterPlaylist)
                .ToCommand();
            this.MovePlaylistSongDownCommand.Subscribe(_ =>
            {
                int index = this.SelectedPlaylistEntries.First().Index;
                this.library.MovePlaylistSong(index, index + 1, this.accessToken);
                reEvaluateSelectedPlaylistEntry.OnNext(Unit.Default);
            });

            this.MovePlaylistSongCommand = this.WhenAnyValue(x => x.SelectedPlaylistEntries)
                .Merge(reEvaluateSelectedPlaylistEntry.Select(_ => this.SelectedPlaylistEntries))
                .Select(x => x != null && x.Count() == 1)
                .CombineLatest(this.WhenAnyValue(x => x.CanAlterPlaylist), (canMoveUp, canAlterPlaylist) => canMoveUp && canAlterPlaylist)
                .ToCommand();
            this.MovePlaylistSongCommand.Subscribe(x =>
            {
                int fromIndex = this.SelectedPlaylistEntries.First().Index;
                int toIndex = (int?)x ?? this.CurrentPlaylist.Songs.Last().Index + 1;

                // If we move a song from the front of the playlist to the back, we want it move be
                // in front of the target song
                if (fromIndex < toIndex)
                {
                    toIndex--;
                }

                this.library.MovePlaylistSong(fromIndex, toIndex, this.accessToken);
                reEvaluateSelectedPlaylistEntry.OnNext(Unit.Default);
            });

            this.IsLocal = true;
        }
        public SettingsViewModel(Library library, ViewSettings viewSettings, CoreSettings coreSettings, IWindowManager windowManager, Guid accessToken, MobileApiInfo mobileApiInfo)
        {
            if (library == null)
                Throw.ArgumentNullException(() => library);

            if (viewSettings == null)
                Throw.ArgumentNullException(() => viewSettings);

            if (coreSettings == null)
                Throw.ArgumentNullException(() => coreSettings);

            if (mobileApiInfo == null)
                throw new ArgumentNullException("mobileApiInfo");

            this.library = library;
            this.viewSettings = viewSettings;
            this.coreSettings = coreSettings;
            this.windowManager = windowManager;
            this.accessToken = accessToken;

            this.canCreateAdmin = this
                .WhenAnyValue(x => x.CreationPassword, x => !string.IsNullOrWhiteSpace(x) && !this.isAdminCreated)
                .ToProperty(this, x => x.CanCreateAdmin);

            this.CreateAdminCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.CanCreateAdmin),
                ImmediateScheduler.Instance); // Immediate execution, because we set the password to an empty string afterwards
            this.CreateAdminCommand.Subscribe(p =>
            {
                this.library.LocalAccessControl.SetLocalPassword(this.accessToken, this.CreationPassword);
                this.isAdminCreated = true;
            });

            this.ChangeToPartyCommand = ReactiveCommand.Create(this.CreateAdminCommand.Select(x => true).StartWith(false));
            this.ChangeToPartyCommand.Subscribe(p =>
            {
                this.library.LocalAccessControl.DowngradeLocalAccess(this.accessToken);
                this.ShowSettings = false;
            });

            this.canLogin = this.WhenAnyValue(x => x.LoginPassword, x => !string.IsNullOrWhiteSpace(x))
                .ToProperty(this, x => x.CanLogin);

            this.LoginCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.CanLogin),
                ImmediateScheduler.Instance); // Immediate execution, because we set the password to an empty string afterwards
            this.LoginCommand.Subscribe(p =>
            {
                try
                {
                    this.library.LocalAccessControl.UpgradeLocalAccess(this.accessToken, this.LoginPassword);
                    this.IsWrongPassword = false;

                    this.ShowLogin = false;
                    this.ShowSettings = true;
                }

                catch (WrongPasswordException)
                {
                    this.IsWrongPassword = true;
                }
            });

            this.OpenLinkCommand = ReactiveCommand.Create();
            this.OpenLinkCommand.Cast<string>().Subscribe(x =>
            {
                try
                {
                    Process.Start(x);
                }

                catch (Win32Exception ex)
                {
                    this.Log().ErrorException(String.Format("Could not open link {0}", x), ex);
                }
            });

            this.ReportBugCommand = ReactiveCommand.Create();
            this.ReportBugCommand.Subscribe(p => this.windowManager.ShowWindow(new BugReportViewModel()));

            this.ChangeAccentColorCommand = ReactiveCommand.Create();
            this.ChangeAccentColorCommand.Subscribe(x => this.viewSettings.AccentColor = (string)x);

            this.ChangeAppThemeCommand = ReactiveCommand.Create();
            this.ChangeAppThemeCommand.Subscribe(x => this.viewSettings.AppTheme = (string)x);

            this.UpdateLibraryCommand = ReactiveCommand.Create(this.library.WhenAnyValue(x => x.IsUpdating, x => !x)
                .ObserveOn(RxApp.MainThreadScheduler)
                .CombineLatest(this.library.WhenAnyValue(x => x.SongSourcePath).Select(x => !String.IsNullOrEmpty(x)), (x1, x2) => x1 && x2));
            this.UpdateLibraryCommand.Subscribe(_ => this.library.UpdateNow());

            this.librarySource = this.library.WhenAnyValue(x => x.SongSourcePath)
                .ToProperty(this, x => x.LibrarySource);

            this.port = this.coreSettings.Port;
            this.ChangePortCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.Port, NetworkHelpers.IsPortValid));
            this.ChangePortCommand.Subscribe(_ => this.coreSettings.Port = this.Port);

            this.remoteControlPassword = this.coreSettings.RemoteControlPassword;
            this.ChangeRemoteControlPasswordCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.RemoteControlPassword)
                .Select(x => !String.IsNullOrWhiteSpace(x)));
            this.ChangeRemoteControlPasswordCommand.Subscribe(x =>
                this.library.RemoteAccessControl.SetRemotePassword(this.accessToken, this.RemoteControlPassword));
            this.showRemoteControlPasswordError = this.WhenAnyValue(x => x.RemoteControlPassword, x => x.LockRemoteControl,
                    (password, lockRemoteControl) => String.IsNullOrWhiteSpace(password) && lockRemoteControl)
                .ToProperty(this, x => x.ShowRemoteControlPasswordError);

            this.isRemoteAccessReallyLocked = this.library.RemoteAccessControl.WhenAnyValue(x => x.IsRemoteAccessReallyLocked)
                .ToProperty(this, x => x.IsRemoteAccessReallyLocked);

            this.isPortOccupied = mobileApiInfo.IsPortOccupied.ToProperty(this, x => x.IsPortOccupied);

            this.enableChangelog = this.viewSettings.WhenAnyValue(x => x.EnableChangelog)
                .ToProperty(this, x => x.EnableChangelog);

            this.defaultPlaybackEngine = this.coreSettings.WhenAnyValue(x => x.DefaultPlaybackEngine)
                .ToProperty(this, x => x.DefaultPlaybackEngine);
        }
Example #5
0
        private void SetupMobileApi()
        {
            var coreSettings = this.kernel.Get<CoreSettings>();
            var library = this.kernel.Get<Library>();

            this.Log().Info("Remote control is {0}", coreSettings.EnableRemoteControl ? "enabled" : "disabled");
            this.Log().Info("Port is set to {0}", coreSettings.Port);

            IObservable<MobileApi> apiChanged = coreSettings.WhenAnyValue(x => x.Port).DistinctUntilChanged()
                .CombineLatest(coreSettings.WhenAnyValue(x => x.EnableRemoteControl), Tuple.Create)
                .Where(x => x.Item2)
                .Select(x => x.Item1)
                .Do(_ =>
                {
                    if (this.mobileApi != null)
                    {
                        this.mobileApi.Dispose();
                    }
                })
                .Select(x => new MobileApi(x, library)).Publish(null).RefCount().Where(x => x != null);

            apiChanged.Subscribe(x =>
            {
                this.mobileApi = x;
                x.SendBroadcastAsync();
                x.StartClientDiscovery();
            });

            IConnectableObservable<int> connectedClients = apiChanged.Select(x => x.ConnectedClients).Switch().Publish(0);
            connectedClients.Connect();

            IConnectableObservable<bool> isPortOccupied = apiChanged.Select(x => x.IsPortOccupied).Switch().Publish(false);
            isPortOccupied.Connect();

            var apiStats = new MobileApiInfo(connectedClients, isPortOccupied);

            this.kernel.Bind<MobileApiInfo>().ToConstant(apiStats);

            coreSettings.WhenAnyValue(x => x.EnableRemoteControl)
                .Where(x => !x && this.mobileApi != null)
                .Subscribe(x => this.mobileApi.Dispose());
        }