-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathskip.php
207 lines (171 loc) · 5.67 KB
/
skip.php
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
<?php
require_once 'config.php';
// Set up Redis connection
$redis = getRedisInstance();
// Check if it's a stats request
if (isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] === 'stats') {
handleStatsRequest($redis);
exit;
}
// Get client IP and user agent
$clientIP = $_SERVER['REMOTE_ADDR'];
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown';
// Get UUID and song ID from POST data
$uuid = $_POST['uuid'] ?? null;
$songId = $_POST['songid'] ?? null;
// Get client IP
$clientIP = $_SERVER['REMOTE_ADDR'];
// Check Redis connection
if (!$redis) {
http_response_code(500);
exit(json_encode(['error' => 'Internal server error: Redis connection failed']));
}
// Function to get the count of active listeners
function getActiveListenersCount($redis)
{
$now = time();
$allListeners = $redis->sMembers('active_listeners');
$activeCount = 0;
foreach ($allListeners as $uuid) {
$listenerKey = "listener:$uuid";
$lastSeen = $redis->hGet($listenerKey, 'last_seen');
if ($lastSeen !== false && $now - $lastSeen <= 60) {
$activeCount++;
}
}
return $activeCount;
}
// Check if the UUID is in the active listeners set
if (!$uuid) {
http_response_code(400);
exit(json_encode(['error' => 'UUID not provided']));
}
if (!$redis->sIsMember('active_listeners', $uuid)) {
http_response_code(400);
exit(json_encode(['error' => 'Invalid or inactive listener']));
}
// Validate song ID
if (!$songId) {
http_response_code(400);
exit(json_encode(['error' => 'Invalid song ID']));
}
function fetchData($url, $method = 'GET')
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
}
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception(curl_error($ch));
}
curl_close($ch);
return $response;
}
function fetchCurrentSongId($redis)
{
$metadataUrl = LIBRESPOT_API_URL . "/metadata";
$cacheKey = 'spotify_metadata_' . md5($metadataUrl);
$cachedData = $redis->get($cacheKey);
if ($cachedData !== false) {
$data = json_decode($cachedData, true);
if (isset($data['current']['songid']) && !empty($data['current']['songid'])) {
return $data['current']['songid'];
}
}
// If we couldn't get the song ID from Redis, fetch from the API
try {
$rawData = fetchData($metadataUrl);
$metadata = json_decode($rawData, true);
if (!isset($metadata['current']['songid']) || empty($metadata['current']['songid'])) {
throw new Exception("No current song ID in metadata");
}
$songId = $metadata['current']['songid'];
// Cache the metadata
$redis->setex($cacheKey, 10, $rawData);
return $songId;
} catch (Exception $e) {
throw new Exception('Failed to fetch or process metadata: ' . $e->getMessage());
}
}
// Fetch current song ID
try {
$currentSongId = fetchCurrentSongId($redis);
} catch (Exception $e) {
http_response_code(500);
exit(json_encode(['error' => $e->getMessage()]));
}
// Check if the voted song is currently playing
if ($songId !== $currentSongId) {
http_response_code(400);
exit(json_encode(['error' => 'Voted song does not match currently playing song']));
}
// Get total number of active listeners
$totalListeners = getActiveListenersCount($redis);
if ($totalListeners === 0) {
http_response_code(400);
exit(json_encode(['error' => 'No active listeners']));
}
// Implement rate limiting for votes
$rateLimitKey = "rate_limit:vote:{$clientIP}";
$voteAttempts = $redis->incr($rateLimitKey);
$redis->expire($rateLimitKey, 60); // Reset after 1 minute
if ($voteAttempts > 5) { // Allow 5 votes per minute
http_response_code(429);
exit(json_encode(['error' => 'Too many vote attempts. Please wait before trying again.']));
}
// Record the vote using both UUID and IP
$voteKey = "vote_skip:{$songId}";
$combinedIdentifier = "{$uuid}:{$clientIP}";
$redis->sAdd($voteKey, $combinedIdentifier);
$redis->expire($voteKey, 300); // Expire votes after 5 minutes
// Count unique votes
$voteCount = $redis->sCard($voteKey);
// Calculate if skip conditions are met
$skipConditionsMet = ($voteCount >= 2) && ($voteCount >= ceil($totalListeners / 2));
if ($skipConditionsMet) {
// Perform the skip action
$skipUrl = LIBRESPOT_API_URL . "/skip";
try {
$response = fetchData($skipUrl, 'GET');
// Clear the votes for this song
$redis->del($voteKey);
echo json_encode([
'success' => true,
'message' => 'Song skipped successfully'
]);
} catch (Exception $e) {
http_response_code(500);
exit(json_encode(['error' => 'Failed to skip the song']));
}
} else {
echo json_encode([
'success' => true,
'message' => 'Vote recorded'
]);
}
function handleStatsRequest($redis)
{
try {
$currentSongId = fetchCurrentSongId($redis);
} catch (Exception $e) {
http_response_code(500);
exit(json_encode(['error' => $e->getMessage()]));
}
// Get vote count for the current song
$voteKey = "vote_skip:{$currentSongId}";
$voteCount = $redis->sCard($voteKey);
// Get total number of active listeners
$totalListeners = getActiveListenersCount($redis);
// Calculate total votes needed (not remaining votes)
$votesNeeded = max(2, ceil($totalListeners / 2));
// Prepare and send the response
$response = [
'song' => $currentSongId,
'count' => $voteCount,
'needed' => $votesNeeded
];
header('Content-Type: application/json');
echo json_encode($response);
}