· 5 years ago · Dec 19, 2020, 12:08 PM
1/*
2 You ask what this is? I had the Idea of lerning how to work with different classes and help myself a bit better with the BeatSaver api. (DON'T SPAM ME THAT BEATSAVERSHARP EXISTS).
3 Btw nice that you are interested in my code.
4*/
5using System;
6using System.Collections.Generic;
7using System.Net;
8using System.Text.Json;
9using System.Text.Json.Serialization;
10using System.Threading.Tasks;
11
12namespace BeatSaverAPI
13{
14 public class BeatSaberSong
15 {
16 [JsonPropertyName("_songName")]
17 public string SongName { get; set; } = "N/A";
18 [JsonPropertyName("_songSubName")]
19 public string SubName { get; set; } = "N/A";
20 [JsonPropertyName("_songAuthorName")]
21 public string SongArtist { get; set; } = "N/A";
22 [JsonPropertyName("_levelAuthorName")]
23 public string Mapper { get; set; } = "N/A";
24 [JsonPropertyName("_beatsPerMinute")]
25 public decimal BPM { get; set; } = 0.0m;
26 public string Hash { get; set; } = "N/A";
27 public string Key { get; set; } = "N/A";
28 public bool RequestGood { get; set; } = false;
29 [JsonPropertyName("_difficultyBeatmapSets")]
30 public List<BeatSaberSongBeatMapCharacteristic> BeatMapCharacteristics { get; set; } = new List<BeatSaberSongBeatMapCharacteristic>();
31
32 public string _allDirectionsEnvironmentName { get; set; } = "N/A";
33 public string _environmentName { get; set; } = "N/A";
34 public string _coverImageFilename { get; set; } = "N/A";
35 public string _songFilename { get; set; } = "N/A";
36 public decimal _previewDuration { get; set; } = 0.0m;
37 public decimal _previewStartTime { get; set; } = 0.0m;
38 public decimal _shufflePeriod { get; set; } = 0.0m;
39 public decimal _shuffle { get; set; } = 0.0m;
40 public decimal _songTimeOffset { get; set; } = 0.0m;
41 public string _version { get; set; } = "N/A";
42
43 public string GetJSON()
44 {
45 return JsonSerializer.Serialize(this);
46 }
47 }
48
49 public class BeatSaverAPISearchResult
50 {
51 public List<BeatSaverAPISong> docs { get; set; } = new List<BeatSaverAPISong>();
52 public int totalDocs { get; set; } = 0;
53 public int lastPage { get; set; } = 0;
54 public bool RequestGood { get; set; } = false;
55
56 public bool HasResults()
57 {
58 if (docs.Count < 1) return false;
59 return true;
60 }
61 }
62
63 public class BeatSaverAPISong
64 {
65 public BeatSaverAPIMetadata metadata { get; set; } = new BeatSaverAPIMetadata();
66 public BeatSaverAPIStats stats { get; set; } = new BeatSaverAPIStats();
67 public string description { get; set; } = "N/A";
68 public string deletedAt { get; set; } = "N/A";
69 public string _id { get; set; } = "N/A";
70 public string key { get; set; } = "N/A";
71 public string name { get; set; } = "N/A";
72 public BeatSaverAPIUploader uploader { get; set; } = new BeatSaverAPIUploader();
73 public string hash { get; set; } = "N/A";
74 public string uploaded { get; set; } = "N/A";
75 public string directDownload { get; set; } = "N/A";
76 public string downloadURL { get; set; } = "N/A";
77 public string coverURL { get; set; } = "N/A";
78 public bool GoodRequest { get; set; } = false;
79
80 public BeatSaberSong ConvertToBeatSaberSong()
81 {
82 BeatSaberSong finished = new BeatSaberSong();
83 finished.BPM = metadata.bpm;
84 finished.Hash = hash;
85 finished.Key = key;
86 finished.Mapper = metadata.levelAuthorName;
87 finished.SongName = name;
88 finished.SongArtist = metadata.songAuthorName;
89 finished.RequestGood = true;
90 finished.SubName = metadata.songSubName;
91 finished.RequestGood = GoodRequest;
92 foreach (BeatSaverAPICharacteristic characteristic in metadata.characteristics)
93 {
94 BeatSaberSongBeatMapCharacteristic characteristic1 = new BeatSaberSongBeatMapCharacteristic();
95 characteristic1.BeatMapCharacteristicName = characteristic.name;
96 if (characteristic.difficulties.easy != null)
97 {
98 BeatSaberSongDifficulty easy = new BeatSaberSongDifficulty();
99 easy.Bombs = characteristic.difficulties.easy.bombs;
100 easy.DifficultyID = 1;
101 easy.DifficultyName = "easy";
102 easy.Duration = characteristic.difficulties.easy.duration;
103 easy.Length = characteristic.difficulties.easy.length;
104 easy.NJS = characteristic.difficulties.easy.njs;
105 easy.NJSOffset = characteristic.difficulties.easy.njsOffset;
106 easy.Notes = characteristic.difficulties.easy.notes;
107 easy.Obstacles = characteristic.difficulties.easy.obstacles;
108 characteristic1.Difficulties.Add(easy);
109 }
110 if (characteristic.difficulties.normal != null)
111 {
112 BeatSaberSongDifficulty normal = new BeatSaberSongDifficulty();
113 normal.Bombs = characteristic.difficulties.normal.bombs;
114 normal.DifficultyID = 3;
115 normal.DifficultyName = "normal";
116 normal.Duration = characteristic.difficulties.normal.duration;
117 normal.Length = characteristic.difficulties.normal.length;
118 normal.NJS = characteristic.difficulties.normal.njs;
119 normal.NJSOffset = characteristic.difficulties.normal.njsOffset;
120 normal.Notes = characteristic.difficulties.normal.notes;
121 normal.Obstacles = characteristic.difficulties.normal.obstacles;
122 characteristic1.Difficulties.Add(normal);
123 }
124 if (characteristic.difficulties.hard != null)
125 {
126 BeatSaberSongDifficulty hard = new BeatSaberSongDifficulty();
127 hard.Bombs = characteristic.difficulties.hard.bombs;
128 hard.DifficultyID = 5;
129 hard.DifficultyName = "hard";
130 hard.Duration = characteristic.difficulties.hard.duration;
131 hard.Length = characteristic.difficulties.hard.length;
132 hard.NJS = characteristic.difficulties.hard.njs;
133 hard.NJSOffset = characteristic.difficulties.hard.njsOffset;
134 hard.Notes = characteristic.difficulties.hard.notes;
135 hard.Obstacles = characteristic.difficulties.hard.obstacles;
136 characteristic1.Difficulties.Add(hard);
137 }
138 if (characteristic.difficulties.expert != null)
139 {
140 BeatSaberSongDifficulty expert = new BeatSaberSongDifficulty();
141 expert.Bombs = characteristic.difficulties.expert.bombs;
142 expert.DifficultyID = 7;
143 expert.DifficultyName = "expert";
144 expert.Duration = characteristic.difficulties.expert.duration;
145 expert.Length = characteristic.difficulties.expert.length;
146 expert.NJS = characteristic.difficulties.expert.njs;
147 expert.NJSOffset = characteristic.difficulties.expert.njsOffset;
148 expert.Notes = characteristic.difficulties.expert.notes;
149 expert.Obstacles = characteristic.difficulties.expert.obstacles;
150 characteristic1.Difficulties.Add(expert);
151 }
152 if (characteristic.difficulties.expertPlus != null)
153 {
154 BeatSaberSongDifficulty expertPlus = new BeatSaberSongDifficulty();
155 expertPlus.Bombs = characteristic.difficulties.expertPlus.bombs;
156 expertPlus.DifficultyID = 9;
157 expertPlus.DifficultyName = "expertPlus";
158 expertPlus.Duration = characteristic.difficulties.expertPlus.duration;
159 expertPlus.Length = characteristic.difficulties.expertPlus.length;
160 expertPlus.NJS = characteristic.difficulties.expertPlus.njs;
161 expertPlus.NJSOffset = characteristic.difficulties.expertPlus.njsOffset;
162 expertPlus.Notes = characteristic.difficulties.expertPlus.notes;
163 expertPlus.Obstacles = characteristic.difficulties.expertPlus.obstacles;
164 characteristic1.Difficulties.Add(expertPlus);
165 }
166 finished.BeatMapCharacteristics.Add(characteristic1);
167 }
168 return finished;
169 }
170 }
171
172 public class BeatSaverAPIStats
173 {
174 public int downloads { get; set; } = 0;
175 public int plays { get; set; } = 0;
176 public int downVotes { get; set; } = 0;
177 public int upVotes { get; set; } = 0;
178 public decimal heat { get; set; } = 0.0m;
179 public decimal rating { get; set; } = 0.0m;
180 }
181
182 public class BeatSaverAPIUploader
183 {
184 public string _id { get; set; } = "N/A";
185 public string username { get; set; } = "N/A";
186 }
187
188 public class BeatSaverAPIMetadata
189 {
190 public BeatSaverAPIDifficulties difficulties { get; set; } = new BeatSaverAPIDifficulties();
191 public decimal duration { get; set; } = 0.0m;
192 public string automapper { get; set; } = "N/A";
193 public List<BeatSaverAPICharacteristic> characteristics { get; set; } = new List<BeatSaverAPICharacteristic>();
194 public string levelAuthorName { get; set; } = "N/A";
195 public string songAuthorName { get; set; } = "N/A";
196 public string songName { get; set; } = "N/A";
197 public string songSubName { get; set; } = "N/A";
198 public decimal bpm { get; set; } = 0.0m;
199 }
200
201 public class BeatSaverAPICharacteristic
202 {
203 public BeatSaverAPICharacteristicDifficulties difficulties { get; set; } = new BeatSaverAPICharacteristicDifficulties();
204 public string name { get; set; } = "N/A";
205
206 public bool IsLawless()
207 {
208 if (name.ToLower() == "lawless") return true;
209 else return false;
210 }
211
212 public bool IsLightshow()
213 {
214 if (name.ToLower() == "lightshow") return true;
215 else return false;
216 }
217
218 public bool IsStandart()
219 {
220 if (name.ToLower() == "standard") return true;
221 else return false;
222 }
223
224 public bool IsNoArrows()
225 {
226 if (name.ToLower() == "noarrows") return true;
227 else return false;
228 }
229
230 public bool IsOneSaber()
231 {
232 if (name.ToLower() == "onesaber") return true;
233 else return false;
234 }
235
236 public bool IsNinetyDegree()
237 {
238 if (name.ToLower() == "90degree") return true;
239 else return false;
240 }
241
242 public bool IsThreeSixtyDegree()
243 {
244 if (name.ToLower() == "360Degree") return true;
245 else return false;
246 }
247 }
248
249 public class BeatSaverAPICharacteristicDifficulties
250 {
251 public BeatSaverAPICharacteristicDifficulty easy { get; set; } = new BeatSaverAPICharacteristicDifficulty();
252 public BeatSaverAPICharacteristicDifficulty expert { get; set; } = new BeatSaverAPICharacteristicDifficulty();
253 public BeatSaverAPICharacteristicDifficulty expertPlus { get; set; } = new BeatSaverAPICharacteristicDifficulty();
254 public BeatSaverAPICharacteristicDifficulty hard { get; set; } = new BeatSaverAPICharacteristicDifficulty();
255 public BeatSaverAPICharacteristicDifficulty normal { get; set; } = new BeatSaverAPICharacteristicDifficulty();
256 }
257
258 public class BeatSaverAPICharacteristicDifficulty
259 {
260 public decimal duration { get; set; } = 0.0m;
261 public decimal length { get; set; } = 0.0m;
262 public decimal njs { get; set; } = 0.0m;
263 public decimal njsOffset { get; set; } = 0.0m;
264 public int bombs { get; set; } = 0;
265 public int notes { get; set; } = 0;
266 public int obstacles { get; set; } = 0;
267 }
268
269 public class BeatSaverAPIDifficulties
270 {
271 public bool easy { get; set; } = false;
272 public bool expert { get; set; } = false;
273 public bool expertPlus { get; set; } = false;
274 public bool hard { get; set; } = false;
275 public bool normal { get; set; } = false;
276 }
277
278 public class BeatSaberSongDifficulty
279 {
280 public decimal Duration { get; set; } = 0.0m;
281 public decimal Length { get; set; } = 0.0m;
282 [JsonPropertyName("_noteJumpMovementSpeed")]
283 public decimal NJS { get; set; } = 0.0m;
284 [JsonPropertyName("_noteJumpStartBeatOffset")]
285 public decimal NJSOffset { get; set; } = 0.0m;
286 public int Bombs { get; set; } = 0;
287 public int Notes { get; set; } = 0;
288 public int Obstacles { get; set; } = 0;
289 [JsonPropertyName("_difficulty")]
290 public string DifficultyName { get; set; } = "N/A";
291 [JsonPropertyName("_difficultyRank")]
292 public int DifficultyID { get; set; } = 0;
293
294 public string _beatmapFilename { get; set; } = "N/A";
295
296 public bool IsEasy()
297 {
298 if (DifficultyName.ToLower() == "easy") return true;
299 else return false;
300 }
301
302 public bool IsNormal()
303 {
304 if (DifficultyName.ToLower() == "normal") return true;
305 else return false;
306 }
307
308 public bool IsHard()
309 {
310 if (DifficultyName.ToLower() == "hard") return true;
311 else return false;
312 }
313
314 public bool IsExpert()
315 {
316 if (DifficultyName.ToLower() == "expert") return true;
317 else return false;
318 }
319
320 public bool IsExpertPlus()
321 {
322 if (DifficultyName.ToLower() == "expertplus") return true;
323 else return false;
324 }
325 }
326
327 public class BeatSaberSongBeatMapCharacteristic
328 {
329 [JsonPropertyName("_beatmapCharacteristicName")]
330 public string BeatMapCharacteristicName { get; set; } = "N/A";
331 [JsonPropertyName("_difficultyBeatmaps")]
332 public List<BeatSaberSongDifficulty> Difficulties { get; set; } = new List<BeatSaberSongDifficulty>();
333 }
334
335 public class WebClientUtilities
336 {
337 public List<string> UAs = new List<string>() { "BeatSaverUtilities/1.0", "BeatSaverUtils/1.1", "BeatSaverBot/1.2", "MSEdge/5.1", "Firefox/59.2", "Something/5.1", "Random/4.3", "lmao/6.5", "Chrome/4.3", "NET/4.3" };
338 public string lastUA = "N/A";
339
340 public string GetRandomUserAgent()
341 {
342 while (true)
343 {
344 Random r = new Random();
345 int rand = r.Next(0, UAs.Count - 1);
346 if (lastUA != UAs[rand])
347 {
348 lastUA = UAs[rand];
349 return UAs[rand];
350 }
351 }
352 }
353 }
354
355 public class BeatSaverAPIInteractor
356 {
357 public readonly string BeatSaverAPIBaseLink = "https://beatsaver.com/api/";
358 public readonly string BeatSaverLink = "https://beatsaver.com";
359 WebClientUtilities WCU = new WebClientUtilities();
360
361 public BeatSaberSong LoadFromInfoDat(String json)
362 {
363 BeatSaberSong c = JsonSerializer.Deserialize<BeatSaberSong>(json);
364 return c;
365 }
366
367 internal async Task<BeatSaverAPISong> BeatSaverAPISongHash(string Hash)
368 {
369 WebClientUtilities WCU = new WebClientUtilities();
370
371 BeatSaverAPISong BeatSaverResult = new BeatSaverAPISong();
372
373 bool RateLimit = true;
374 while (RateLimit)
375 {
376 WebClient cl = new WebClient();
377 cl.Headers.Add("user-agent", WCU.GetRandomUserAgent());
378 try
379 {
380 String tmp = cl.DownloadString(BeatSaverAPIBaseLink + "maps/by-hash/" + Hash.ToLower());
381 BeatSaverResult = JsonSerializer.Deserialize<BeatSaverAPISong>(tmp);
382 BeatSaverResult.GoodRequest = true;
383 RateLimit = false;
384 }
385 catch (WebException e)
386 {
387 String response = ((HttpWebResponse)e.Response).StatusCode.ToString();
388 if (response.Contains("429"))
389 {
390 await Task.Delay(5000);
391 }
392 else if (response.Contains("404"))
393 {
394 BeatSaverResult.GoodRequest = false;
395 RateLimit = false;
396 }
397 else
398 {
399 BeatSaverResult.GoodRequest = false;
400 RateLimit = false;
401 }
402 }
403 }
404 return BeatSaverResult;
405 }
406
407 internal async Task<BeatSaverAPISong> BeatSaverAPISongKey(string Key)
408 {
409 WebClientUtilities WCU = new WebClientUtilities();
410
411 BeatSaverAPISong BeatSaverResult = new BeatSaverAPISong();
412
413 bool RateLimit = true;
414 while (RateLimit)
415 {
416 WebClient cl = new WebClient();
417 cl.Headers.Add("user-agent", WCU.GetRandomUserAgent());
418 try
419 {
420 String tmp = cl.DownloadString(BeatSaverAPIBaseLink + "maps/detail/" + Key.ToLower());
421 BeatSaverResult = JsonSerializer.Deserialize<BeatSaverAPISong>(tmp);
422 RateLimit = false;
423 BeatSaverResult.GoodRequest = true;
424 }
425 catch (WebException e)
426 {
427 String response = ((HttpWebResponse)e.Response).StatusCode.ToString();
428 if (response.Contains("429"))
429 {
430 await Task.Delay(5000);
431 }
432 else if (response.Contains("404"))
433 {
434 BeatSaverResult.GoodRequest = false;
435 RateLimit = false;
436 }
437 else
438 {
439 BeatSaverResult.GoodRequest = false;
440 RateLimit = false;
441 }
442 }
443 }
444 return BeatSaverResult;
445 }
446
447 public BeatSaverAPISong GetBeatSaverAPISongViaKey(String key)
448 {
449 return BeatSaverAPISongKey(key).Result;
450 }
451
452 public BeatSaverAPISong GetBeatSaverAPISongViaHash(String hash)
453 {
454 return BeatSaverAPISongHash(hash).Result;
455 }
456
457 public BeatSaverAPISong GetBeatSaverAPISong(String HashOrKey)
458 {
459 BeatSaverAPISong song = GetBeatSaverAPISongViaKey(HashOrKey);
460 if(!song.GoodRequest)
461 {
462 song = GetBeatSaverAPISongViaHash(HashOrKey);
463 }
464 return song;
465 }
466
467 public BeatSaberSong GetBeatSaberSong(String HashOrKey)
468 {
469 BeatSaberSong song = GetBeatSaberSongViaKey(HashOrKey);
470 if(!song.RequestGood)
471 {
472 song = GetBeatSaberSongViaHash(HashOrKey);
473 }
474 return song;
475 }
476
477 public BeatSaberSong GetBeatSaberSongViaHash(String Hash)
478 {
479 BeatSaverAPISong convert = GetBeatSaverAPISongViaHash(Hash);
480 return convert.ConvertToBeatSaberSong();
481 }
482
483 public BeatSaberSong GetBeatSaberSongViaKey(String Key)
484 {
485 BeatSaverAPISong convert = GetBeatSaverAPISongViaKey(Key);
486 return convert.ConvertToBeatSaberSong();
487 }
488
489 internal async Task<BeatSaverAPISearchResult> SearchTextAPI(String text)
490 {
491 WebClientUtilities WCU = new WebClientUtilities();
492
493 BeatSaverAPISearchResult BeatSaverResult = new BeatSaverAPISearchResult();
494
495 bool RateLimit = true;
496 while (RateLimit)
497 {
498 WebClient cl = new WebClient();
499 cl.Headers.Add("user-agent", WCU.GetRandomUserAgent());
500 try
501 {
502 String tmp = cl.DownloadString(BeatSaverAPIBaseLink + "search/text?q=%22" + text + "%22");
503 BeatSaverResult = JsonSerializer.Deserialize<BeatSaverAPISearchResult>(tmp);
504 RateLimit = false;
505 BeatSaverResult.RequestGood = true;
506 }
507 catch (WebException e)
508 {
509 String response = ((HttpWebResponse)e.Response).StatusCode.ToString();
510 if (response.Contains("429"))
511 {
512 await Task.Delay(5000);
513 }
514 else if (response.Contains("404"))
515 {
516 BeatSaverResult.RequestGood = false;
517 RateLimit = false;
518 }
519 else
520 {
521 BeatSaverResult.RequestGood = false;
522 RateLimit = false;
523 }
524 }
525 }
526 return BeatSaverResult;
527 }
528
529 public BeatSaverAPISearchResult SearchText(String text)
530 {
531 return SearchTextAPI(text).Result;
532 }
533 }
534}
535