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