-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathAttendee.cs
152 lines (127 loc) · 4.71 KB
/
Attendee.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.System;
using System;
using System.Collections.Generic;
using System.Text;
using WebSocketSharp;
namespace EmbyParty
{
public enum AttendeeState
{
Idle, //Not playing anything.
WaitForPlay, //This Guest has been sent a playback order but hasn't reported initiation of playback yet.
WaitForSeek, //This Guest has been sent a seek order but hasn't delivered a time update yet.
Syncing, //This Attendee is ready to continue but one or more Guests aren't yet.
Ready //Synced (stable) playback in progress.
}
public class Attendee
{
private const long ACCURACY_START = 20000000; //2s
private const long ACCURACY_DECAY = 10000000; //1s
public const long ACCURACY_WORST = 70000000; //7s
public string Id { get; set; }
public Party Party { get; set; }
public string RemoteControl { get; set; } //Session this attendee is controlling
public string TargetId { get => SafeTargetId(new List<string>() {Id}); }
public bool IsBeingRemoteControlled { get; set; }
public long Ping; //ms
public List<long> PendingPing { get; set; }
public string UserId { get; set; }
/* Fields used for client-side display */
public string UserName { get; set; }
public string DeviceName { get; set; }
public string DeviceType { get; set; }
public string DisplayName { get; set; }
/* --- */
private DateTimeOffset _lastStateChange;
private AttendeeState _state;
public AttendeeState State {
get => _state;
set {
_state = value;
_lastStateChange = DateTimeOffset.UtcNow;
}
}
public long MsSinceStateChange { get => (long)Math.Floor((DateTimeOffset.UtcNow - _lastStateChange).TotalMilliseconds); }
public string ReportedDeviceId { get; set; }
public string PlaySessionId { get; set; }
public long? CurrentMediaItemId { get; set; }
public long RunTimeTicks { get; set; }
public bool IsPaused { get; set; }
public bool IsHost { get; set; }
private long _positionTicks;
private DateTimeOffset _lastUpdate;
public long PositionTicks {
get => _positionTicks;
set {
_positionTicks = value;
_lastUpdate = DateTimeOffset.UtcNow;
}
}
public DateTimeOffset LastUpdate { get => _lastUpdate; }
public long TicksSinceUpdate { get => (long)Math.Floor((DateTimeOffset.UtcNow - _lastUpdate).TotalMilliseconds) * 10000; }
public long EstimatedPositionTicks { get => IsPaused ? PositionTicks : PositionTicks + TicksSinceUpdate; }
private long _accuracy = ACCURACY_START;
private HashSet<ProgressEvent> _ignores = new HashSet<ProgressEvent>();
public Attendee()
{
State = AttendeeState.Idle;
PendingPing = new List<long>();
}
public void IgnoreNext(ProgressEvent command)
{
_ignores.Add(command);
}
public bool ShouldIgnore(ProgressEvent command)
{
if (_ignores.Contains(command))
{
_ignores.Remove(command);
return true;
}
return false;
}
public void ClearIgnores()
{
_ignores.Clear();
}
public bool IsOutOfSync(long ticks)
{
return ticks < EstimatedPositionTicks - _accuracy || ticks > EstimatedPositionTicks + _accuracy;
}
public long UpdatePositionTicksFromEstimate()
{
PositionTicks = EstimatedPositionTicks;
return PositionTicks;
}
public long LowerAccuracy()
{
if (_accuracy < ACCURACY_WORST)
{
_accuracy += ACCURACY_DECAY;
}
return _accuracy;
}
public void ResetAccuracy()
{
_accuracy = ACCURACY_START;
}
public string SafeTargetId(List<string> except)
{
//Prevents endless recursion
if (RemoteControl != null)
{
Attendee target = Party.GetAttendee(RemoteControl);
if (target != null && !except.Contains(target.Id))
{
except.Add(target.Id);
return target.SafeTargetId(except);
}
return RemoteControl;
}
return Id;
}
}
}