Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

http-api: update HTTP API and add documentation

http-api: update OpenAPI Spec

http-api: update endpoints

http-api: update OpenAPI Spec

http-api: update OpenAPI Spec

http-api: update OpenAPI Spec

+2382 -41
+2
Cargo.lock
··· 5729 5729 "queryst", 5730 5730 "reqwest", 5731 5731 "rockbox-graphql", 5732 + "rockbox-library", 5732 5733 "rockbox-rpc", 5733 5734 "rockbox-sys", 5734 5735 "serde_json", 5736 + "sqlx", 5735 5737 "threadpool", 5736 5738 "tokio", 5737 5739 ]
+3 -3
crates/graphql/src/schema/playback.rs
··· 23 23 24 24 async fn current_track(&self, ctx: &Context<'_>) -> Result<Option<Track>, Error> { 25 25 let client = ctx.data::<reqwest::Client>().unwrap(); 26 - let url = format!("{}/current_track", rockbox_url()); 26 + let url = format!("{}/player/current-track", rockbox_url()); 27 27 let response = client.get(&url).send().await?; 28 28 let track = response.json::<Option<Mp3Entry>>().await?; 29 29 Ok(track.map(|t| t.into())) ··· 31 31 32 32 async fn next_track(&self, ctx: &Context<'_>) -> Result<Option<Track>, Error> { 33 33 let client = ctx.data::<reqwest::Client>().unwrap(); 34 - let url = format!("{}/next_track", rockbox_url()); 34 + let url = format!("{}/player/next-track", rockbox_url()); 35 35 let response = client.get(&url).send().await?; 36 36 let track = response.json::<Option<Mp3Entry>>().await?; 37 37 Ok(track.map(|t| t.into())) ··· 39 39 40 40 async fn get_file_position(&self, ctx: &Context<'_>) -> Result<i32, Error> { 41 41 let client = ctx.data::<reqwest::Client>().unwrap(); 42 - let url = format!("{}/file_position", rockbox_url()); 42 + let url = format!("{}/player/file-position", rockbox_url()); 43 43 let response = client.get(&url).send().await?; 44 44 let response = response.json::<FilePosition>().await?; 45 45 Ok(response.position)
+1
crates/library/src/entity/album.rs
··· 7 7 pub artist: String, 8 8 pub year: u32, 9 9 pub year_string: String, 10 + #[serde(skip_serializing_if = "Option::is_none")] 10 11 pub album_art: Option<String>, 11 12 pub md5: String, 12 13 }
+2
crates/library/src/entity/artist.rs
··· 4 4 pub struct Artist { 5 5 pub id: String, 6 6 pub name: String, 7 + #[serde(skip_serializing_if = "Option::is_none")] 7 8 pub bio: Option<String>, 9 + #[serde(skip_serializing_if = "Option::is_none")] 8 10 pub image: Option<String>, 9 11 }
+5
crates/library/src/entity/track.rs
··· 15 15 pub filesize: u32, 16 16 pub frequency: u32, 17 17 pub length: u32, 18 + #[serde(skip_serializing_if = "Option::is_none")] 18 19 pub track_number: Option<u32>, 20 + #[serde(skip_serializing_if = "Option::is_none")] 19 21 pub year: Option<u32>, 22 + #[serde(skip_serializing_if = "Option::is_none")] 20 23 pub year_string: Option<String>, 24 + #[serde(skip_serializing_if = "Option::is_none")] 21 25 pub genre: Option<String>, 22 26 pub md5: String, 27 + #[serde(skip_serializing_if = "Option::is_none")] 23 28 pub album_art: Option<String>, 24 29 pub artist_id: String, 25 30 pub album_id: String,
+1 -1
crates/rpc/src/browse.rs
··· 22 22 request: tonic::Request<TreeGetEntriesRequest>, 23 23 ) -> Result<tonic::Response<TreeGetEntriesResponse>, tonic::Status> { 24 24 let path = request.into_inner().path; 25 - let url = format!("{}/tree_entries?q={}", rockbox_url(), path); 25 + let url = format!("{}/browse/tree-entries?q={}", rockbox_url(), path); 26 26 let response = self 27 27 .client 28 28 .get(url)
+3 -3
crates/rpc/src/playback.rs
··· 117 117 ) -> Result<tonic::Response<CurrentTrackResponse>, tonic::Status> { 118 118 let track = self 119 119 .client 120 - .get(&format!("{}/current_track", rockbox_url())) 120 + .get(&format!("{}/player/current-track", rockbox_url())) 121 121 .send() 122 122 .await 123 123 .map_err(|e| tonic::Status::internal(e.to_string()))? ··· 133 133 ) -> Result<tonic::Response<NextTrackResponse>, tonic::Status> { 134 134 let track = self 135 135 .client 136 - .get(&format!("{}/next_track", rockbox_url())) 136 + .get(&format!("{}/player/next-track", rockbox_url())) 137 137 .send() 138 138 .await 139 139 .map_err(|e| tonic::Status::internal(e.to_string()))? ··· 161 161 ) -> Result<tonic::Response<GetFilePositionResponse>, tonic::Status> { 162 162 let position = self 163 163 .client 164 - .get(&format!("{}/file_position", rockbox_url())) 164 + .get(&format!("{}/player/file-position", rockbox_url())) 165 165 .send() 166 166 .await 167 167 .map_err(|e| tonic::Status::internal(e.to_string()))?
+2
crates/server/Cargo.toml
··· 16 16 serde_json = "1.0.128" 17 17 threadpool = "1.8.1" 18 18 tokio = {version = "1.36.0", features = ["full"]} 19 + sqlx = {version = "0.8.2", features = ["runtime-tokio", "tls-rustls", "sqlite", "chrono", "derive", "macros"]} 20 + rockbox-library = {path = "../library"}
+27
crates/server/docs/index.html
··· 1 + <!DOCTYPE html> 2 + 3 + <html lang="en"> 4 + <head> 5 + <meta charset="utf-8" /> 6 + 7 + <title>Rockbox API</title> 8 + <meta name="description" content="Rockbox HTTP API Docs" /> 9 + 10 + <script src="https://cdn.jsdelivr.net/npm/@stoplight/elements@7.13.11/web-components.min.js"></script> 11 + <link 12 + rel="stylesheet" 13 + href="https://cdn.jsdelivr.net/npm/@stoplight/elements@7.13.11/styles.min.css" 14 + /> 15 + 16 + <style> 17 + body { 18 + height: 100vh; 19 + } 20 + </style> 21 + </head> 22 + <body> 23 + <elements-api 24 + apiDescriptionUrl="http://localhost:6063/openapi.json" 25 + ></elements-api> 26 + </body> 27 + </html>
+2159
crates/server/openapi.json
··· 1 + { 2 + "openapi": "3.1.0", 3 + "x-stoplight": { 4 + "id": "pekyaa98hprrk" 5 + }, 6 + "info": { 7 + "title": "Rockbox API v1", 8 + "version": "1.0", 9 + "contact": { 10 + "name": "Tsiry Sandratraina", 11 + "email": "tsiry.sndr@fluentci.io" 12 + }, 13 + "summary": "Retrieve metadata from Rockbox Library, control playback.", 14 + "description": "", 15 + "x-logo": { 16 + "url": "https://avatars.githubusercontent.com/u/1327269?s=200&v=4" 17 + } 18 + }, 19 + "servers": [ 20 + { 21 + "url": "http://localhost:6063" 22 + } 23 + ], 24 + "paths": { 25 + "/albums": { 26 + "get": { 27 + "summary": "Get Several Albums", 28 + "tags": [ 29 + "Albums" 30 + ], 31 + "responses": { 32 + "200": { 33 + "description": "OK", 34 + "content": { 35 + "application/json": { 36 + "schema": { 37 + "type": "array", 38 + "items": { 39 + "$ref": "#/components/schemas/Album.v1", 40 + "x-stoplight": { 41 + "id": "g3x93ebnemzva" 42 + } 43 + } 44 + } 45 + } 46 + } 47 + } 48 + }, 49 + "operationId": "get-albums", 50 + "parameters": [ 51 + { 52 + "schema": { 53 + "type": "integer" 54 + }, 55 + "in": "query", 56 + "name": "limit", 57 + "description": "The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50." 58 + }, 59 + { 60 + "schema": { 61 + "type": "integer" 62 + }, 63 + "in": "query", 64 + "name": "offset", 65 + "description": "The index of the first item to return. Default: 0 (the first item). Use with limit to get the next set of items." 66 + } 67 + ] 68 + } 69 + }, 70 + "/albums/{id}": { 71 + "parameters": [ 72 + { 73 + "schema": { 74 + "type": "string" 75 + }, 76 + "name": "id", 77 + "in": "path", 78 + "required": true 79 + } 80 + ], 81 + "get": { 82 + "summary": "Get Album", 83 + "tags": [ 84 + "Albums" 85 + ], 86 + "responses": { 87 + "200": { 88 + "description": "OK", 89 + "content": { 90 + "application/json": { 91 + "schema": { 92 + "$ref": "#/components/schemas/Album.v1" 93 + } 94 + } 95 + } 96 + }, 97 + "404": { 98 + "description": "Not Found" 99 + } 100 + }, 101 + "operationId": "get-albums-id" 102 + } 103 + }, 104 + "/albums/{id}/tracks": { 105 + "parameters": [ 106 + { 107 + "schema": { 108 + "type": "string" 109 + }, 110 + "name": "id", 111 + "in": "path", 112 + "required": true 113 + } 114 + ], 115 + "get": { 116 + "summary": "Get Album Tracks", 117 + "responses": { 118 + "200": { 119 + "description": "OK", 120 + "content": { 121 + "application/json": { 122 + "schema": { 123 + "type": "array", 124 + "items": { 125 + "$ref": "#/components/schemas/Track.v1", 126 + "x-stoplight": { 127 + "id": "hi439roy03b8p" 128 + } 129 + } 130 + } 131 + } 132 + } 133 + } 134 + }, 135 + "operationId": "get-albums-id-tracks", 136 + "parameters": [ 137 + { 138 + "schema": { 139 + "type": "integer" 140 + }, 141 + "in": "query", 142 + "name": "limit", 143 + "description": "The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50." 144 + }, 145 + { 146 + "schema": { 147 + "type": "integer" 148 + }, 149 + "in": "query", 150 + "name": "offset", 151 + "description": "The index of the first item to return. Default: 0 (the first item). Use with limit to get the next set of items." 152 + } 153 + ], 154 + "tags": [ 155 + "Albums" 156 + ] 157 + } 158 + }, 159 + "/artists": { 160 + "get": { 161 + "summary": "Get Several Artists", 162 + "tags": [ 163 + "Artists" 164 + ], 165 + "responses": { 166 + "200": { 167 + "description": "OK", 168 + "content": { 169 + "application/json": { 170 + "schema": { 171 + "type": "array", 172 + "items": { 173 + "$ref": "#/components/schemas/Artist.v1", 174 + "x-stoplight": { 175 + "id": "03rsibvu1zlxz" 176 + } 177 + } 178 + } 179 + } 180 + } 181 + } 182 + }, 183 + "operationId": "get-artists", 184 + "parameters": [ 185 + { 186 + "schema": { 187 + "type": "integer" 188 + }, 189 + "in": "query", 190 + "name": "limit", 191 + "description": "The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50." 192 + }, 193 + { 194 + "schema": { 195 + "type": "integer" 196 + }, 197 + "in": "query", 198 + "name": "offset", 199 + "description": "The index of the first item to return. Default: 0 (the first item). Use with limit to get the next set of items." 200 + } 201 + ] 202 + } 203 + }, 204 + "/artists/{id}": { 205 + "parameters": [ 206 + { 207 + "schema": { 208 + "type": "string" 209 + }, 210 + "name": "id", 211 + "in": "path", 212 + "required": true 213 + } 214 + ], 215 + "get": { 216 + "summary": "Get Artist", 217 + "tags": [ 218 + "Artists" 219 + ], 220 + "responses": { 221 + "200": { 222 + "description": "OK", 223 + "content": { 224 + "application/json": { 225 + "schema": { 226 + "type": "array", 227 + "items": { 228 + "$ref": "#/components/schemas/Artist.v1", 229 + "x-stoplight": { 230 + "id": "mq2z0jvupvbjs" 231 + } 232 + } 233 + } 234 + } 235 + } 236 + }, 237 + "404": { 238 + "description": "Not Found" 239 + } 240 + }, 241 + "operationId": "get-artists-id" 242 + } 243 + }, 244 + "/tracks": { 245 + "get": { 246 + "summary": "Get Several Tracks", 247 + "responses": { 248 + "200": { 249 + "description": "OK", 250 + "headers": {}, 251 + "content": { 252 + "application/json": { 253 + "schema": { 254 + "type": "array", 255 + "items": { 256 + "$ref": "#/components/schemas/Track.v1", 257 + "x-stoplight": { 258 + "id": "1onvs6nmuxc8z" 259 + } 260 + } 261 + } 262 + } 263 + } 264 + } 265 + }, 266 + "operationId": "get-tracks", 267 + "requestBody": { 268 + "content": {}, 269 + "description": "" 270 + }, 271 + "x-internal": false, 272 + "parameters": [ 273 + { 274 + "schema": { 275 + "type": "integer" 276 + }, 277 + "in": "query", 278 + "name": "limit", 279 + "description": "The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50." 280 + }, 281 + { 282 + "schema": { 283 + "type": "integer" 284 + }, 285 + "in": "query", 286 + "name": "offset", 287 + "description": "The index of the first item to return. Default: 0 (the first item). Use with limit to get the next set of items." 288 + } 289 + ], 290 + "tags": [ 291 + "Tracks" 292 + ] 293 + } 294 + }, 295 + "/tracks/{id}": { 296 + "parameters": [ 297 + { 298 + "schema": { 299 + "type": "string" 300 + }, 301 + "name": "id", 302 + "in": "path", 303 + "required": true 304 + } 305 + ], 306 + "get": { 307 + "summary": "Get Track", 308 + "responses": { 309 + "200": { 310 + "description": "OK", 311 + "content": { 312 + "application/json": { 313 + "schema": { 314 + "$ref": "#/components/schemas/Track.v1" 315 + }, 316 + "examples": { 317 + "Track": { 318 + "value": { 319 + "album": "2014 Forest Hills Drive", 320 + "album_artist": "J. Cole", 321 + "album_id": "cm1ibibnn00043fj7ppakq9w9", 322 + "artist": "J. Cole", 323 + "artist_id": "cm1ibibnn00033fj7hy019na5", 324 + "bitrate": 96, 325 + "composer": "Roy Hammond, Jermaine Cole, CHARLES SIMMONS", 326 + "created_at": 1727296114, 327 + "disc_number": 1, 328 + "filesize": 2872773, 329 + "frequency": 44100, 330 + "genre_id": "", 331 + "id": "cm1ibibnn00053fj79ghe6b2d", 332 + "length": 239381, 333 + "md5": "b5a0d86e156e6d02c90f647bf5cc6fc2", 334 + "path": "/home/coder/Music/03 - J. Cole - Wet Dreamz(Explicit).m4a", 335 + "title": "Wet Dreamz", 336 + "track_number": 3, 337 + "updated_at": 1727296114, 338 + "year": 2014, 339 + "year_string": "2014-12-09" 340 + } 341 + } 342 + } 343 + } 344 + }, 345 + "headers": {} 346 + }, 347 + "404": { 348 + "description": "Not Found" 349 + } 350 + }, 351 + "operationId": "get-tracks-trackid", 352 + "tags": [ 353 + "Tracks" 354 + ] 355 + } 356 + }, 357 + "/artists/{id}/albums": { 358 + "parameters": [ 359 + { 360 + "schema": { 361 + "type": "string" 362 + }, 363 + "name": "id", 364 + "in": "path", 365 + "required": true 366 + } 367 + ], 368 + "get": { 369 + "summary": "Get Artist's Albums", 370 + "tags": [ 371 + "artists" 372 + ], 373 + "responses": {}, 374 + "operationId": "get-artists-id-albums", 375 + "parameters": [ 376 + { 377 + "schema": { 378 + "type": "integer" 379 + }, 380 + "in": "query", 381 + "name": "limit", 382 + "description": "The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50." 383 + }, 384 + { 385 + "schema": { 386 + "type": "integer" 387 + }, 388 + "in": "query", 389 + "name": "offset", 390 + "description": "The index of the first item to return. Default: 0 (the first item). Use with limit to get the next set of items." 391 + } 392 + ] 393 + } 394 + }, 395 + "/browse/tree-entries": { 396 + "get": { 397 + "summary": "Get Entries", 398 + "tags": [ 399 + "Browse" 400 + ], 401 + "responses": {}, 402 + "operationId": "get-tree_entries" 403 + }, 404 + "parameters": [] 405 + }, 406 + "/player/play": { 407 + "parameters": [], 408 + "put": { 409 + "summary": "Play", 410 + "operationId": "put-player-play", 411 + "responses": { 412 + "200": { 413 + "description": "OK" 414 + } 415 + }, 416 + "tags": [ 417 + "Player" 418 + ], 419 + "parameters": [ 420 + { 421 + "schema": { 422 + "type": "integer" 423 + }, 424 + "in": "query", 425 + "name": "elapsed" 426 + }, 427 + { 428 + "schema": { 429 + "type": "integer" 430 + }, 431 + "in": "query", 432 + "name": "offset" 433 + } 434 + ] 435 + } 436 + }, 437 + "/player/ff-rewind": { 438 + "put": { 439 + "summary": "Fast Forward Rewind", 440 + "operationId": "put-player-ff_rewind", 441 + "responses": { 442 + "200": { 443 + "description": "OK" 444 + } 445 + }, 446 + "tags": [ 447 + "Player" 448 + ] 449 + }, 450 + "parameters": [] 451 + }, 452 + "/player/status": { 453 + "get": { 454 + "summary": "Get Playback Status", 455 + "tags": [ 456 + "Player" 457 + ], 458 + "responses": {}, 459 + "operationId": "get-player-status" 460 + } 461 + }, 462 + "/player/current-track": { 463 + "get": { 464 + "summary": "Get Currently Playing Track", 465 + "tags": [ 466 + "Player" 467 + ], 468 + "responses": { 469 + "200": { 470 + "description": "OK", 471 + "content": { 472 + "application/json": { 473 + "schema": { 474 + "$ref": "#/components/schemas/Track.v1" 475 + } 476 + } 477 + } 478 + } 479 + }, 480 + "operationId": "get-player-current-track", 481 + "description": "Get the object currently being played " 482 + } 483 + }, 484 + "/player/next-track": { 485 + "get": { 486 + "summary": "Get Next Track", 487 + "tags": [ 488 + "Player" 489 + ], 490 + "responses": { 491 + "200": { 492 + "description": "OK", 493 + "content": { 494 + "application/json": { 495 + "schema": { 496 + "$ref": "#/components/schemas/Track.v1" 497 + } 498 + } 499 + } 500 + } 501 + }, 502 + "operationId": "get-player-next-track", 503 + "description": "Get the next track" 504 + } 505 + }, 506 + "/player/flush-and-reload-tracks": { 507 + "put": { 508 + "summary": "Flush And Reload Tracks", 509 + "operationId": "put-player-flush-and-reload-tracks", 510 + "responses": { 511 + "200": { 512 + "description": "OK" 513 + } 514 + }, 515 + "tags": [ 516 + "Player" 517 + ] 518 + } 519 + }, 520 + "/settings": { 521 + "get": { 522 + "summary": "Get Global Settings", 523 + "tags": [ 524 + "Settings" 525 + ], 526 + "responses": { 527 + "200": { 528 + "description": "OK", 529 + "content": { 530 + "application/json": { 531 + "schema": { 532 + "$ref": "#/components/schemas/Settings.v1" 533 + } 534 + } 535 + } 536 + } 537 + }, 538 + "operationId": "get-settings" 539 + } 540 + }, 541 + "/status": { 542 + "get": { 543 + "summary": "Get System Status", 544 + "tags": [ 545 + "System" 546 + ], 547 + "responses": { 548 + "200": { 549 + "description": "OK", 550 + "content": { 551 + "application/json": { 552 + "schema": { 553 + "type": "object", 554 + "properties": { 555 + "dircache_size": { 556 + "type": "integer" 557 + }, 558 + "last_screen": { 559 + "type": "integer" 560 + }, 561 + "last_volume_change": { 562 + "type": "integer" 563 + }, 564 + "resume_crc32": { 565 + "type": "integer" 566 + }, 567 + "resume_elapsed": { 568 + "type": "integer" 569 + }, 570 + "resume_index": { 571 + "type": "integer" 572 + }, 573 + "resume_offset": { 574 + "type": "integer" 575 + }, 576 + "runtime": { 577 + "type": "integer" 578 + }, 579 + "topruntime": { 580 + "type": "integer" 581 + }, 582 + "viewer_icon_count": { 583 + "type": "integer" 584 + } 585 + }, 586 + "x-examples": { 587 + "Example 1": { 588 + "dircache_size": -1, 589 + "last_screen": 0, 590 + "last_volume_change": -1, 591 + "resume_crc32": 4294967295, 592 + "resume_elapsed": 4294967295, 593 + "resume_index": -1, 594 + "resume_offset": 4294967295, 595 + "runtime": 40599, 596 + "topruntime": 40599, 597 + "viewer_icon_count": 0 598 + } 599 + } 600 + }, 601 + "examples": { 602 + "Example 1": { 603 + "value": { 604 + "dircache_size": -1, 605 + "last_screen": 0, 606 + "last_volume_change": -1, 607 + "resume_crc32": 4294967295, 608 + "resume_elapsed": 4294967295, 609 + "resume_index": -1, 610 + "resume_offset": 4294967295, 611 + "runtime": 40599, 612 + "topruntime": 40599, 613 + "viewer_icon_count": 0 614 + } 615 + } 616 + } 617 + } 618 + } 619 + } 620 + }, 621 + "operationId": "get-status" 622 + } 623 + }, 624 + "/version": { 625 + "get": { 626 + "summary": "Get Rockbox Version", 627 + "tags": [ 628 + "System" 629 + ], 630 + "responses": { 631 + "200": { 632 + "description": "OK", 633 + "content": { 634 + "application/json": { 635 + "schema": { 636 + "type": "object", 637 + "properties": { 638 + "version": { 639 + "type": "string" 640 + } 641 + }, 642 + "x-examples": { 643 + "Example 1": { 644 + "version": "7ca0a9895d-240918" 645 + } 646 + } 647 + }, 648 + "examples": { 649 + "Example 1": { 650 + "value": { 651 + "version": "7ca0a9895d-240918" 652 + } 653 + } 654 + } 655 + } 656 + } 657 + } 658 + }, 659 + "operationId": "get-version" 660 + } 661 + }, 662 + "/player/resume": { 663 + "put": { 664 + "summary": "Resume Playback", 665 + "operationId": "put-player-resume", 666 + "responses": { 667 + "200": { 668 + "description": "OK" 669 + } 670 + }, 671 + "tags": [ 672 + "Player" 673 + ] 674 + } 675 + }, 676 + "/player/pause": { 677 + "put": { 678 + "summary": "Pause Playback", 679 + "operationId": "put-player-pause", 680 + "responses": { 681 + "200": { 682 + "description": "OK" 683 + } 684 + }, 685 + "tags": [ 686 + "Player" 687 + ] 688 + } 689 + }, 690 + "/player/next": { 691 + "put": { 692 + "summary": "Play Next Track", 693 + "operationId": "put-player-next", 694 + "responses": { 695 + "200": { 696 + "description": "OK" 697 + } 698 + }, 699 + "tags": [ 700 + "Player" 701 + ] 702 + } 703 + }, 704 + "/player/prev": { 705 + "parameters": [], 706 + "put": { 707 + "summary": "Play Previous Track", 708 + "operationId": "put-player-prev", 709 + "responses": { 710 + "200": { 711 + "description": "OK" 712 + } 713 + }, 714 + "tags": [ 715 + "Player" 716 + ] 717 + } 718 + }, 719 + "/player/stop": { 720 + "put": { 721 + "summary": "Stop Playback", 722 + "operationId": "put-player-stop", 723 + "responses": { 724 + "200": { 725 + "description": "OK" 726 + } 727 + }, 728 + "tags": [ 729 + "Player" 730 + ] 731 + } 732 + } 733 + }, 734 + "components": { 735 + "schemas": { 736 + "Album.v1": { 737 + "type": "object", 738 + "properties": { 739 + "album_art": { 740 + "type": "string" 741 + }, 742 + "artist": { 743 + "type": "string" 744 + }, 745 + "id": { 746 + "type": "string" 747 + }, 748 + "md5": { 749 + "type": "string" 750 + }, 751 + "title": { 752 + "type": "string" 753 + }, 754 + "year": { 755 + "type": "integer" 756 + }, 757 + "year_string": { 758 + "type": "string" 759 + } 760 + }, 761 + "x-examples": { 762 + "Example 1": { 763 + "album_art": "216ccc791352fbbffc11268b984db19a.jpg", 764 + "artist": "J. Cole", 765 + "id": "cm1ibibn600013fj7d96nsaeh", 766 + "md5": "d55c5a429200828bc798d9f10253c1af", 767 + "title": "2014 Forest Hills Drive", 768 + "year": 2014, 769 + "year_string": "2014-12-09" 770 + } 771 + }, 772 + "examples": [ 773 + { 774 + "album_art": "216ccc791352fbbffc11268b984db19a.jpg", 775 + "artist": "J. Cole", 776 + "id": "cm1ibibn600013fj7d96nsaeh", 777 + "md5": "d55c5a429200828bc798d9f10253c1af", 778 + "title": "2014 Forest Hills Drive", 779 + "year": 2014, 780 + "year_string": "2014-12-09" 781 + } 782 + ], 783 + "title": "Album" 784 + }, 785 + "Artist.v1": { 786 + "x-stoplight": { 787 + "id": "03rsibvu1zlxz" 788 + }, 789 + "type": "object", 790 + "x-examples": { 791 + "Example 1": { 792 + "id": "cm1ibibn600003fj70egpmcbc", 793 + "name": "J. Cole" 794 + } 795 + }, 796 + "examples": [ 797 + { 798 + "id": "cm1ibibn600003fj70egpmcbc", 799 + "name": "J. Cole" 800 + } 801 + ], 802 + "title": "Artist", 803 + "properties": { 804 + "id": { 805 + "type": "string" 806 + }, 807 + "name": { 808 + "type": "string" 809 + }, 810 + "bio": { 811 + "type": "string", 812 + "x-stoplight": { 813 + "id": "f2du13h4su3yn" 814 + } 815 + }, 816 + "image": { 817 + "type": "string", 818 + "x-stoplight": { 819 + "id": "69j27eohqstp7" 820 + } 821 + } 822 + } 823 + }, 824 + "Settings.v1": { 825 + "type": "object", 826 + "properties": { 827 + "afr_enabled": { 828 + "type": "integer" 829 + }, 830 + "album_art": { 831 + "type": "integer" 832 + }, 833 + "autocreatebookmark": { 834 + "type": "integer" 835 + }, 836 + "autoloadbookmark": { 837 + "type": "integer" 838 + }, 839 + "autoresume_automatic": { 840 + "type": "integer" 841 + }, 842 + "autoresume_enable": { 843 + "type": "boolean" 844 + }, 845 + "autoresume_paths": { 846 + "type": "string" 847 + }, 848 + "autoupdatebookmark": { 849 + "type": "boolean" 850 + }, 851 + "backdrop_file": { 852 + "type": "string" 853 + }, 854 + "backlight_on_button_hold": { 855 + "type": "integer" 856 + }, 857 + "backlight_timeout": { 858 + "type": "integer" 859 + }, 860 + "backlight_timeout_plugged": { 861 + "type": "integer" 862 + }, 863 + "balance": { 864 + "type": "integer" 865 + }, 866 + "bass": { 867 + "type": "integer" 868 + }, 869 + "bass_cutoff": { 870 + "type": "integer" 871 + }, 872 + "battery_capacity": { 873 + "type": "integer" 874 + }, 875 + "battery_display": { 876 + "type": "integer" 877 + }, 878 + "battery_type": { 879 + "type": "integer" 880 + }, 881 + "beep": { 882 + "type": "integer" 883 + }, 884 + "bg_color": { 885 + "type": "integer" 886 + }, 887 + "bidir_limit": { 888 + "type": "integer" 889 + }, 890 + "bl_filter_first_keypress": { 891 + "type": "boolean" 892 + }, 893 + "bl_selective_actions": { 894 + "type": "boolean" 895 + }, 896 + "bl_selective_actions_mask": { 897 + "type": "integer" 898 + }, 899 + "brightness": { 900 + "type": "integer" 901 + }, 902 + "browse_current": { 903 + "type": "boolean" 904 + }, 905 + "browser_default": { 906 + "type": "integer" 907 + }, 908 + "bt_selective_softlock_actions": { 909 + "type": "boolean" 910 + }, 911 + "bt_selective_softlock_actions_mask": { 912 + "type": "integer" 913 + }, 914 + "buffer_margin": { 915 + "type": "integer" 916 + }, 917 + "caption_backlight": { 918 + "type": "boolean" 919 + }, 920 + "car_adapter_mode": { 921 + "type": "boolean" 922 + }, 923 + "car_adapter_mode_delay": { 924 + "type": "integer" 925 + }, 926 + "channel_config": { 927 + "type": "integer" 928 + }, 929 + "colors_file": { 930 + "type": "string" 931 + }, 932 + "compressor_settings": { 933 + "type": "object", 934 + "properties": { 935 + "attack_time": { 936 + "type": "integer" 937 + }, 938 + "knee": { 939 + "type": "integer" 940 + }, 941 + "makeup_gain": { 942 + "type": "integer" 943 + }, 944 + "ratio": { 945 + "type": "integer" 946 + }, 947 + "release_time": { 948 + "type": "integer" 949 + }, 950 + "threshold": { 951 + "type": "integer" 952 + } 953 + } 954 + }, 955 + "constrain_next_folder": { 956 + "type": "boolean" 957 + }, 958 + "contrast": { 959 + "type": "integer" 960 + }, 961 + "crossfade": { 962 + "type": "integer" 963 + }, 964 + "crossfade_fade_in_delay": { 965 + "type": "integer" 966 + }, 967 + "crossfade_fade_in_duration": { 968 + "type": "integer" 969 + }, 970 + "crossfade_fade_out_delay": { 971 + "type": "integer" 972 + }, 973 + "crossfade_fade_out_duration": { 974 + "type": "integer" 975 + }, 976 + "crossfade_fade_out_mixmode": { 977 + "type": "integer" 978 + }, 979 + "crossfeed": { 980 + "type": "integer" 981 + }, 982 + "crossfeed_cross_gain": { 983 + "type": "integer" 984 + }, 985 + "crossfeed_direct_gain": { 986 + "type": "integer" 987 + }, 988 + "crossfeed_hf_attenuation": { 989 + "type": "integer" 990 + }, 991 + "crossfeed_hf_cutoff": { 992 + "type": "integer" 993 + }, 994 + "cuesheet": { 995 + "type": "boolean" 996 + }, 997 + "cursor_style": { 998 + "type": "integer" 999 + }, 1000 + "default_codepage": { 1001 + "type": "integer" 1002 + }, 1003 + "depth_3d": { 1004 + "type": "integer" 1005 + }, 1006 + "dircache": { 1007 + "type": "boolean" 1008 + }, 1009 + "dirfilter": { 1010 + "type": "integer" 1011 + }, 1012 + "disable_mainmenu_scrolling": { 1013 + "type": "boolean" 1014 + }, 1015 + "disk_spindown": { 1016 + "type": "integer" 1017 + }, 1018 + "dithering_enabled": { 1019 + "type": "boolean" 1020 + }, 1021 + "eq_band_settings": { 1022 + "type": "array", 1023 + "items": { 1024 + "type": "object", 1025 + "properties": { 1026 + "cutoff": { 1027 + "type": "integer" 1028 + }, 1029 + "gain": { 1030 + "type": "integer" 1031 + }, 1032 + "q": { 1033 + "type": "integer" 1034 + } 1035 + } 1036 + } 1037 + }, 1038 + "eq_enabled": { 1039 + "type": "boolean" 1040 + }, 1041 + "eq_precut": { 1042 + "type": "integer" 1043 + }, 1044 + "fade_on_stop": { 1045 + "type": "boolean" 1046 + }, 1047 + "ff_rewind_accel": { 1048 + "type": "integer" 1049 + }, 1050 + "ff_rewind_min_step": { 1051 + "type": "integer" 1052 + }, 1053 + "fg_color": { 1054 + "type": "integer" 1055 + }, 1056 + "flip_display": { 1057 + "type": "boolean" 1058 + }, 1059 + "font_file": { 1060 + "type": "string" 1061 + }, 1062 + "glyphs_to_cache": { 1063 + "type": "integer" 1064 + }, 1065 + "governor": { 1066 + "type": "integer" 1067 + }, 1068 + "hold_lr_for_scroll_in_list": { 1069 + "type": "boolean" 1070 + }, 1071 + "hotkey_tree": { 1072 + "type": "integer" 1073 + }, 1074 + "hotkey_wps": { 1075 + "type": "integer" 1076 + }, 1077 + "icon_file": { 1078 + "type": "string" 1079 + }, 1080 + "interpret_numbers": { 1081 + "type": "integer" 1082 + }, 1083 + "invert": { 1084 + "type": "boolean" 1085 + }, 1086 + "kbd_file": { 1087 + "type": "string" 1088 + }, 1089 + "keep_current_track_on_replace_playlist": { 1090 + "type": "boolean" 1091 + }, 1092 + "keyclick": { 1093 + "type": "integer" 1094 + }, 1095 + "keyclick_hardware": { 1096 + "type": "boolean" 1097 + }, 1098 + "keyclick_repeats": { 1099 + "type": "integer" 1100 + }, 1101 + "keypress_restarts_sleeptimer": { 1102 + "type": "boolean" 1103 + }, 1104 + "lang_file": { 1105 + "type": "string" 1106 + }, 1107 + "lcd_sleep_after_backlight_off": { 1108 + "type": "integer" 1109 + }, 1110 + "list_accel_start_delay": { 1111 + "type": "integer" 1112 + }, 1113 + "list_accel_wait": { 1114 + "type": "integer" 1115 + }, 1116 + "list_line_padding": { 1117 + "type": "integer" 1118 + }, 1119 + "list_order": { 1120 + "type": "integer" 1121 + }, 1122 + "list_separator_color": { 1123 + "type": "integer" 1124 + }, 1125 + "list_separator_height": { 1126 + "type": "integer" 1127 + }, 1128 + "list_wraparound": { 1129 + "type": "boolean" 1130 + }, 1131 + "lse_color": { 1132 + "type": "integer" 1133 + }, 1134 + "lss_color": { 1135 + "type": "integer" 1136 + }, 1137 + "lst_color": { 1138 + "type": "integer" 1139 + }, 1140 + "max_files_in_dir": { 1141 + "type": "integer" 1142 + }, 1143 + "max_files_in_playlist": { 1144 + "type": "integer" 1145 + }, 1146 + "next_folder": { 1147 + "type": "integer" 1148 + }, 1149 + "offset_out_of_view": { 1150 + "type": "boolean" 1151 + }, 1152 + "party_mode": { 1153 + "type": "boolean" 1154 + }, 1155 + "pause_rewind": { 1156 + "type": "integer" 1157 + }, 1158 + "pbe": { 1159 + "type": "integer" 1160 + }, 1161 + "pbe_precut": { 1162 + "type": "integer" 1163 + }, 1164 + "peak_meter_clip_hold": { 1165 + "type": "integer" 1166 + }, 1167 + "peak_meter_dbfs": { 1168 + "type": "boolean" 1169 + }, 1170 + "peak_meter_hold": { 1171 + "type": "integer" 1172 + }, 1173 + "peak_meter_max": { 1174 + "type": "integer" 1175 + }, 1176 + "peak_meter_min": { 1177 + "type": "integer" 1178 + }, 1179 + "peak_meter_release": { 1180 + "type": "integer" 1181 + }, 1182 + "pitch_mode_semitone": { 1183 + "type": "boolean" 1184 + }, 1185 + "pitch_mode_timestretch": { 1186 + "type": "boolean" 1187 + }, 1188 + "play_frequency": { 1189 + "type": "integer" 1190 + }, 1191 + "play_selected": { 1192 + "type": "boolean" 1193 + }, 1194 + "player_name": { 1195 + "type": "string" 1196 + }, 1197 + "playlist_catalog_dir": { 1198 + "type": "string" 1199 + }, 1200 + "playlist_shuffle": { 1201 + "type": "boolean" 1202 + }, 1203 + "playlist_viewer_icons": { 1204 + "type": "boolean" 1205 + }, 1206 + "playlist_viewer_indices": { 1207 + "type": "boolean" 1208 + }, 1209 + "playlist_viewer_track_display": { 1210 + "type": "integer" 1211 + }, 1212 + "power_mode": { 1213 + "type": "integer" 1214 + }, 1215 + "poweroff": { 1216 + "type": "integer" 1217 + }, 1218 + "prevent_skip": { 1219 + "type": "boolean" 1220 + }, 1221 + "recursive_dir_insert": { 1222 + "type": "integer" 1223 + }, 1224 + "repeat_mode": { 1225 + "type": "integer" 1226 + }, 1227 + "replaygain_settings": { 1228 + "type": "object", 1229 + "properties": { 1230 + "noclip": { 1231 + "type": "boolean" 1232 + }, 1233 + "preamp": { 1234 + "type": "integer" 1235 + }, 1236 + "type": { 1237 + "type": "integer" 1238 + } 1239 + } 1240 + }, 1241 + "resume_rewind": { 1242 + "type": "integer" 1243 + }, 1244 + "rewind_across_tracks": { 1245 + "type": "boolean" 1246 + }, 1247 + "roll_off": { 1248 + "type": "integer" 1249 + }, 1250 + "root_menu_customized": { 1251 + "type": "boolean" 1252 + }, 1253 + "runtimedb": { 1254 + "type": "boolean" 1255 + }, 1256 + "sbs_file": { 1257 + "type": "string" 1258 + }, 1259 + "screen_scroll_step": { 1260 + "type": "integer" 1261 + }, 1262 + "scroll_delay": { 1263 + "type": "integer" 1264 + }, 1265 + "scroll_paginated": { 1266 + "type": "boolean" 1267 + }, 1268 + "scroll_speed": { 1269 + "type": "integer" 1270 + }, 1271 + "scroll_step": { 1272 + "type": "integer" 1273 + }, 1274 + "scrollbar": { 1275 + "type": "integer" 1276 + }, 1277 + "scrollbar_width": { 1278 + "type": "integer" 1279 + }, 1280 + "shortcuts_replaces_qs": { 1281 + "type": "boolean" 1282 + }, 1283 + "show_filename_ext": { 1284 + "type": "integer" 1285 + }, 1286 + "show_icons": { 1287 + "type": "boolean" 1288 + }, 1289 + "show_path_in_browser": { 1290 + "type": "integer" 1291 + }, 1292 + "show_queue_options": { 1293 + "type": "integer" 1294 + }, 1295 + "show_shuffled_adding_options": { 1296 + "type": "boolean" 1297 + }, 1298 + "show_shutdown_message": { 1299 + "type": "boolean" 1300 + }, 1301 + "single_mode": { 1302 + "type": "integer" 1303 + }, 1304 + "skip_length": { 1305 + "type": "integer" 1306 + }, 1307 + "sleeptimer_duration": { 1308 + "type": "integer" 1309 + }, 1310 + "sleeptimer_on_startup": { 1311 + "type": "boolean" 1312 + }, 1313 + "sort_case": { 1314 + "type": "boolean" 1315 + }, 1316 + "sort_dir": { 1317 + "type": "integer" 1318 + }, 1319 + "sort_file": { 1320 + "type": "integer" 1321 + }, 1322 + "spdif_enable": { 1323 + "type": "boolean" 1324 + }, 1325 + "speaker_mode": { 1326 + "type": "integer" 1327 + }, 1328 + "start_directory": { 1329 + "type": "string" 1330 + }, 1331 + "start_in_screen": { 1332 + "type": "integer" 1333 + }, 1334 + "statusbar": { 1335 + "type": "integer" 1336 + }, 1337 + "stereo_width": { 1338 + "type": "integer" 1339 + }, 1340 + "stereosw_mode": { 1341 + "type": "integer" 1342 + }, 1343 + "surround_balance": { 1344 + "type": "integer" 1345 + }, 1346 + "surround_enabled": { 1347 + "type": "integer" 1348 + }, 1349 + "surround_fx1": { 1350 + "type": "integer" 1351 + }, 1352 + "surround_fx2": { 1353 + "type": "boolean" 1354 + }, 1355 + "surround_method2": { 1356 + "type": "boolean" 1357 + }, 1358 + "surround_mix": { 1359 + "type": "integer" 1360 + }, 1361 + "tagcache_autoupdate": { 1362 + "type": "boolean" 1363 + }, 1364 + "tagcache_db_path": { 1365 + "type": "string" 1366 + }, 1367 + "tagcache_ram": { 1368 + "type": "integer" 1369 + }, 1370 + "tagcache_scan_paths": { 1371 + "type": "string" 1372 + }, 1373 + "talk_battery_level": { 1374 + "type": "boolean" 1375 + }, 1376 + "talk_dir": { 1377 + "type": "integer" 1378 + }, 1379 + "talk_dir_clip": { 1380 + "type": "boolean" 1381 + }, 1382 + "talk_file": { 1383 + "type": "integer" 1384 + }, 1385 + "talk_file_clip": { 1386 + "type": "boolean" 1387 + }, 1388 + "talk_filetype": { 1389 + "type": "boolean" 1390 + }, 1391 + "talk_menu": { 1392 + "type": "boolean" 1393 + }, 1394 + "talk_mixer_amp": { 1395 + "type": "integer" 1396 + }, 1397 + "timeformat": { 1398 + "type": "integer" 1399 + }, 1400 + "timestretch_enabled": { 1401 + "type": "boolean" 1402 + }, 1403 + "touch_mode": { 1404 + "type": "integer" 1405 + }, 1406 + "touchpad_deadzone": { 1407 + "type": "integer" 1408 + }, 1409 + "touchpad_sensitivity": { 1410 + "type": "integer" 1411 + }, 1412 + "treble": { 1413 + "type": "integer" 1414 + }, 1415 + "treble_cutoff": { 1416 + "type": "integer" 1417 + }, 1418 + "ts_calibration_data": { 1419 + "type": "object", 1420 + "properties": { 1421 + "A": { 1422 + "type": "integer" 1423 + }, 1424 + "B": { 1425 + "type": "integer" 1426 + }, 1427 + "C": { 1428 + "type": "integer" 1429 + }, 1430 + "D": { 1431 + "type": "integer" 1432 + }, 1433 + "E": { 1434 + "type": "integer" 1435 + }, 1436 + "F": { 1437 + "type": "integer" 1438 + }, 1439 + "divider": { 1440 + "type": "integer" 1441 + } 1442 + } 1443 + }, 1444 + "unplug_autoresume": { 1445 + "type": "boolean" 1446 + }, 1447 + "unplug_mode": { 1448 + "type": "integer" 1449 + }, 1450 + "usb_charging": { 1451 + "type": "integer" 1452 + }, 1453 + "usb_hid": { 1454 + "type": "boolean" 1455 + }, 1456 + "usb_keypad_mode": { 1457 + "type": "integer" 1458 + }, 1459 + "usb_skip_first_drive": { 1460 + "type": "boolean" 1461 + }, 1462 + "usemrb": { 1463 + "type": "integer" 1464 + }, 1465 + "viewers_icon_file": { 1466 + "type": "string" 1467 + }, 1468 + "volume": { 1469 + "type": "integer" 1470 + }, 1471 + "volume_adjust_mode": { 1472 + "type": "integer" 1473 + }, 1474 + "volume_adjust_norm_steps": { 1475 + "type": "integer" 1476 + }, 1477 + "volume_limit": { 1478 + "type": "integer" 1479 + }, 1480 + "volume_type": { 1481 + "type": "integer" 1482 + }, 1483 + "warnon_erase_dynplaylist": { 1484 + "type": "boolean" 1485 + }, 1486 + "wps_file": { 1487 + "type": "string" 1488 + } 1489 + }, 1490 + "x-examples": { 1491 + "Example 1": { 1492 + "afr_enabled": 0, 1493 + "album_art": 0, 1494 + "autocreatebookmark": 1935762276, 1495 + "autoloadbookmark": 1869623098, 1496 + "autoresume_automatic": 0, 1497 + "autoresume_enable": false, 1498 + "autoresume_paths": "", 1499 + "autoupdatebookmark": true, 1500 + "backdrop_file": "", 1501 + "backlight_on_button_hold": 0, 1502 + "backlight_timeout": 0, 1503 + "backlight_timeout_plugged": 0, 1504 + "balance": 0, 1505 + "bass": 0, 1506 + "bass_cutoff": 0, 1507 + "battery_capacity": 0, 1508 + "battery_display": 65536, 1509 + "battery_type": 1946157056, 1510 + "beep": 0, 1511 + "bg_color": 0, 1512 + "bidir_limit": 0, 1513 + "bl_filter_first_keypress": false, 1514 + "bl_selective_actions": false, 1515 + "bl_selective_actions_mask": 0, 1516 + "brightness": 0, 1517 + "browse_current": false, 1518 + "browser_default": 0, 1519 + "bt_selective_softlock_actions": false, 1520 + "bt_selective_softlock_actions_mask": 0, 1521 + "buffer_margin": 1, 1522 + "caption_backlight": false, 1523 + "car_adapter_mode": false, 1524 + "car_adapter_mode_delay": 60, 1525 + "channel_config": 0, 1526 + "colors_file": "", 1527 + "compressor_settings": { 1528 + "attack_time": 0, 1529 + "knee": 0, 1530 + "makeup_gain": 0, 1531 + "ratio": 0, 1532 + "release_time": 0, 1533 + "threshold": 0 1534 + }, 1535 + "constrain_next_folder": false, 1536 + "contrast": 1985966958, 1537 + "crossfade": 0, 1538 + "crossfade_fade_in_delay": 2, 1539 + "crossfade_fade_in_duration": 0, 1540 + "crossfade_fade_out_delay": 2, 1541 + "crossfade_fade_out_duration": 0, 1542 + "crossfade_fade_out_mixmode": 2, 1543 + "crossfeed": -60, 1544 + "crossfeed_cross_gain": 700, 1545 + "crossfeed_direct_gain": 4294967136, 1546 + "crossfeed_hf_attenuation": 0, 1547 + "crossfeed_hf_cutoff": 0, 1548 + "cuesheet": false, 1549 + "cursor_style": 825127794, 1550 + "default_codepage": 8, 1551 + "depth_3d": 3400, 1552 + "dircache": false, 1553 + "dirfilter": 1, 1554 + "disable_mainmenu_scrolling": false, 1555 + "disk_spindown": 0, 1556 + "dithering_enabled": true, 1557 + "eq_band_settings": [ 1558 + { 1559 + "cutoff": 0, 1560 + "gain": 10, 1561 + "q": 64 1562 + }, 1563 + { 1564 + "cutoff": 0, 1565 + "gain": 10, 1566 + "q": 125 1567 + }, 1568 + { 1569 + "cutoff": 0, 1570 + "gain": 10, 1571 + "q": 250 1572 + }, 1573 + { 1574 + "cutoff": 0, 1575 + "gain": 10, 1576 + "q": 500 1577 + }, 1578 + { 1579 + "cutoff": 0, 1580 + "gain": 10, 1581 + "q": 1000 1582 + }, 1583 + { 1584 + "cutoff": 0, 1585 + "gain": 10, 1586 + "q": 2000 1587 + }, 1588 + { 1589 + "cutoff": 0, 1590 + "gain": 10, 1591 + "q": 4000 1592 + }, 1593 + { 1594 + "cutoff": 0, 1595 + "gain": 10, 1596 + "q": 8000 1597 + }, 1598 + { 1599 + "cutoff": 0, 1600 + "gain": 7, 1601 + "q": 16000 1602 + }, 1603 + { 1604 + "cutoff": 0, 1605 + "gain": 0, 1606 + "q": 0 1607 + } 1608 + ], 1609 + "eq_enabled": true, 1610 + "eq_precut": 7, 1611 + "fade_on_stop": false, 1612 + "ff_rewind_accel": 846620009, 1613 + "ff_rewind_min_step": 1650614627, 1614 + "fg_color": 0, 1615 + "flip_display": true, 1616 + "font_file": "", 1617 + "glyphs_to_cache": 0, 1618 + "governor": 0, 1619 + "hold_lr_for_scroll_in_list": true, 1620 + "hotkey_tree": 0, 1621 + "hotkey_wps": 50, 1622 + "icon_file": "", 1623 + "interpret_numbers": 0, 1624 + "invert": true, 1625 + "kbd_file": "", 1626 + "keep_current_track_on_replace_playlist": false, 1627 + "keyclick": 0, 1628 + "keyclick_hardware": true, 1629 + "keyclick_repeats": 2, 1630 + "keypress_restarts_sleeptimer": false, 1631 + "lang_file": "", 1632 + "lcd_sleep_after_backlight_off": 0, 1633 + "list_accel_start_delay": 0, 1634 + "list_accel_wait": 0, 1635 + "list_line_padding": 6, 1636 + "list_order": 0, 1637 + "list_separator_color": 0, 1638 + "list_separator_height": 0, 1639 + "list_wraparound": false, 1640 + "lse_color": 0, 1641 + "lss_color": 0, 1642 + "lst_color": 0, 1643 + "max_files_in_dir": -1, 1644 + "max_files_in_playlist": 0, 1645 + "next_folder": 0, 1646 + "offset_out_of_view": false, 1647 + "party_mode": true, 1648 + "pause_rewind": 54710920, 1649 + "pbe": 0, 1650 + "pbe_precut": 0, 1651 + "peak_meter_clip_hold": 0, 1652 + "peak_meter_dbfs": false, 1653 + "peak_meter_hold": 0, 1654 + "peak_meter_max": 0, 1655 + "peak_meter_min": 0, 1656 + "peak_meter_release": 0, 1657 + "pitch_mode_semitone": false, 1658 + "pitch_mode_timestretch": false, 1659 + "play_frequency": 0, 1660 + "play_selected": true, 1661 + "player_name": "", 1662 + "playlist_catalog_dir": "", 1663 + "playlist_shuffle": false, 1664 + "playlist_viewer_icons": false, 1665 + "playlist_viewer_indices": false, 1666 + "playlist_viewer_track_display": 1, 1667 + "power_mode": 0, 1668 + "poweroff": 0, 1669 + "prevent_skip": false, 1670 + "recursive_dir_insert": 0, 1671 + "repeat_mode": 1, 1672 + "replaygain_settings": { 1673 + "noclip": false, 1674 + "preamp": -15, 1675 + "type": 0 1676 + }, 1677 + "resume_rewind": 35, 1678 + "rewind_across_tracks": false, 1679 + "roll_off": 320, 1680 + "root_menu_customized": false, 1681 + "runtimedb": false, 1682 + "sbs_file": "", 1683 + "screen_scroll_step": 909211702, 1684 + "scroll_delay": 1685024815, 1685 + "scroll_paginated": false, 1686 + "scroll_speed": 0, 1687 + "scroll_step": 1953718627, 1688 + "scrollbar": 50, 1689 + "scrollbar_width": 1000, 1690 + "shortcuts_replaces_qs": false, 1691 + "show_filename_ext": 2, 1692 + "show_icons": true, 1693 + "show_path_in_browser": 0, 1694 + "show_queue_options": 0, 1695 + "show_shuffled_adding_options": true, 1696 + "show_shutdown_message": false, 1697 + "single_mode": 60, 1698 + "skip_length": 6, 1699 + "sleeptimer_duration": 0, 1700 + "sleeptimer_on_startup": false, 1701 + "sort_case": true, 1702 + "sort_dir": 909192819, 1703 + "sort_file": 3551608, 1704 + "spdif_enable": true, 1705 + "speaker_mode": 0, 1706 + "start_directory": "", 1707 + "start_in_screen": 0, 1708 + "statusbar": 9, 1709 + "stereo_width": 100, 1710 + "stereosw_mode": 0, 1711 + "surround_balance": 0, 1712 + "surround_enabled": 0, 1713 + "surround_fx1": 0, 1714 + "surround_fx2": false, 1715 + "surround_method2": false, 1716 + "surround_mix": 1, 1717 + "tagcache_autoupdate": false, 1718 + "tagcache_db_path": "", 1719 + "tagcache_ram": 0, 1720 + "tagcache_scan_paths": "", 1721 + "talk_battery_level": true, 1722 + "talk_dir": 3, 1723 + "talk_dir_clip": true, 1724 + "talk_file": 1, 1725 + "talk_file_clip": false, 1726 + "talk_filetype": false, 1727 + "talk_menu": true, 1728 + "talk_mixer_amp": 1601136494, 1729 + "timeformat": 0, 1730 + "timestretch_enabled": false, 1731 + "touch_mode": 0, 1732 + "touchpad_deadzone": 0, 1733 + "touchpad_sensitivity": 0, 1734 + "treble": 0, 1735 + "treble_cutoff": 0, 1736 + "ts_calibration_data": { 1737 + "A": 0, 1738 + "B": 0, 1739 + "C": 0, 1740 + "D": 0, 1741 + "E": 0, 1742 + "F": 0, 1743 + "divider": 0 1744 + }, 1745 + "unplug_autoresume": true, 1746 + "unplug_mode": 0, 1747 + "usb_charging": 1868786015, 1748 + "usb_hid": false, 1749 + "usb_keypad_mode": 0, 1750 + "usb_skip_first_drive": false, 1751 + "usemrb": 0, 1752 + "viewers_icon_file": "", 1753 + "volume": 0, 1754 + "volume_adjust_mode": 0, 1755 + "volume_adjust_norm_steps": 0, 1756 + "volume_limit": 32, 1757 + "volume_type": 8421504, 1758 + "warnon_erase_dynplaylist": false, 1759 + "wps_file": "" 1760 + } 1761 + }, 1762 + "examples": [ 1763 + { 1764 + "afr_enabled": 0, 1765 + "album_art": 0, 1766 + "autocreatebookmark": 1935762276, 1767 + "autoloadbookmark": 1869623098, 1768 + "autoresume_automatic": 0, 1769 + "autoresume_enable": false, 1770 + "autoresume_paths": "", 1771 + "autoupdatebookmark": true, 1772 + "backdrop_file": "", 1773 + "backlight_on_button_hold": 0, 1774 + "backlight_timeout": 0, 1775 + "backlight_timeout_plugged": 0, 1776 + "balance": 0, 1777 + "bass": 0, 1778 + "bass_cutoff": 0, 1779 + "battery_capacity": 0, 1780 + "battery_display": 65536, 1781 + "battery_type": 1946157056, 1782 + "beep": 0, 1783 + "bg_color": 0, 1784 + "bidir_limit": 0, 1785 + "bl_filter_first_keypress": false, 1786 + "bl_selective_actions": false, 1787 + "bl_selective_actions_mask": 0, 1788 + "brightness": 0, 1789 + "browse_current": false, 1790 + "browser_default": 0, 1791 + "bt_selective_softlock_actions": false, 1792 + "bt_selective_softlock_actions_mask": 0, 1793 + "buffer_margin": 1, 1794 + "caption_backlight": false, 1795 + "car_adapter_mode": false, 1796 + "car_adapter_mode_delay": 60, 1797 + "channel_config": 0, 1798 + "colors_file": "", 1799 + "compressor_settings": { 1800 + "attack_time": 0, 1801 + "knee": 0, 1802 + "makeup_gain": 0, 1803 + "ratio": 0, 1804 + "release_time": 0, 1805 + "threshold": 0 1806 + }, 1807 + "constrain_next_folder": false, 1808 + "contrast": 1985966958, 1809 + "crossfade": 0, 1810 + "crossfade_fade_in_delay": 2, 1811 + "crossfade_fade_in_duration": 0, 1812 + "crossfade_fade_out_delay": 2, 1813 + "crossfade_fade_out_duration": 0, 1814 + "crossfade_fade_out_mixmode": 2, 1815 + "crossfeed": -60, 1816 + "crossfeed_cross_gain": 700, 1817 + "crossfeed_direct_gain": 4294967136, 1818 + "crossfeed_hf_attenuation": 0, 1819 + "crossfeed_hf_cutoff": 0, 1820 + "cuesheet": false, 1821 + "cursor_style": 825127794, 1822 + "default_codepage": 8, 1823 + "depth_3d": 3400, 1824 + "dircache": false, 1825 + "dirfilter": 1, 1826 + "disable_mainmenu_scrolling": false, 1827 + "disk_spindown": 0, 1828 + "dithering_enabled": true, 1829 + "eq_band_settings": [ 1830 + { 1831 + "cutoff": 0, 1832 + "gain": 10, 1833 + "q": 64 1834 + }, 1835 + { 1836 + "cutoff": 0, 1837 + "gain": 10, 1838 + "q": 125 1839 + }, 1840 + { 1841 + "cutoff": 0, 1842 + "gain": 10, 1843 + "q": 250 1844 + }, 1845 + { 1846 + "cutoff": 0, 1847 + "gain": 10, 1848 + "q": 500 1849 + }, 1850 + { 1851 + "cutoff": 0, 1852 + "gain": 10, 1853 + "q": 1000 1854 + }, 1855 + { 1856 + "cutoff": 0, 1857 + "gain": 10, 1858 + "q": 2000 1859 + }, 1860 + { 1861 + "cutoff": 0, 1862 + "gain": 10, 1863 + "q": 4000 1864 + }, 1865 + { 1866 + "cutoff": 0, 1867 + "gain": 10, 1868 + "q": 8000 1869 + }, 1870 + { 1871 + "cutoff": 0, 1872 + "gain": 7, 1873 + "q": 16000 1874 + }, 1875 + { 1876 + "cutoff": 0, 1877 + "gain": 0, 1878 + "q": 0 1879 + } 1880 + ], 1881 + "eq_enabled": true, 1882 + "eq_precut": 7, 1883 + "fade_on_stop": false, 1884 + "ff_rewind_accel": 846620009, 1885 + "ff_rewind_min_step": 1650614627, 1886 + "fg_color": 0, 1887 + "flip_display": true, 1888 + "font_file": "", 1889 + "glyphs_to_cache": 0, 1890 + "governor": 0, 1891 + "hold_lr_for_scroll_in_list": true, 1892 + "hotkey_tree": 0, 1893 + "hotkey_wps": 50, 1894 + "icon_file": "", 1895 + "interpret_numbers": 0, 1896 + "invert": true, 1897 + "kbd_file": "", 1898 + "keep_current_track_on_replace_playlist": false, 1899 + "keyclick": 0, 1900 + "keyclick_hardware": true, 1901 + "keyclick_repeats": 2, 1902 + "keypress_restarts_sleeptimer": false, 1903 + "lang_file": "", 1904 + "lcd_sleep_after_backlight_off": 0, 1905 + "list_accel_start_delay": 0, 1906 + "list_accel_wait": 0, 1907 + "list_line_padding": 6, 1908 + "list_order": 0, 1909 + "list_separator_color": 0, 1910 + "list_separator_height": 0, 1911 + "list_wraparound": false, 1912 + "lse_color": 0, 1913 + "lss_color": 0, 1914 + "lst_color": 0, 1915 + "max_files_in_dir": -1, 1916 + "max_files_in_playlist": 0, 1917 + "next_folder": 0, 1918 + "offset_out_of_view": false, 1919 + "party_mode": true, 1920 + "pause_rewind": 54710920, 1921 + "pbe": 0, 1922 + "pbe_precut": 0, 1923 + "peak_meter_clip_hold": 0, 1924 + "peak_meter_dbfs": false, 1925 + "peak_meter_hold": 0, 1926 + "peak_meter_max": 0, 1927 + "peak_meter_min": 0, 1928 + "peak_meter_release": 0, 1929 + "pitch_mode_semitone": false, 1930 + "pitch_mode_timestretch": false, 1931 + "play_frequency": 0, 1932 + "play_selected": true, 1933 + "player_name": "", 1934 + "playlist_catalog_dir": "", 1935 + "playlist_shuffle": false, 1936 + "playlist_viewer_icons": false, 1937 + "playlist_viewer_indices": false, 1938 + "playlist_viewer_track_display": 1, 1939 + "power_mode": 0, 1940 + "poweroff": 0, 1941 + "prevent_skip": false, 1942 + "recursive_dir_insert": 0, 1943 + "repeat_mode": 1, 1944 + "replaygain_settings": { 1945 + "noclip": false, 1946 + "preamp": -15, 1947 + "type": 0 1948 + }, 1949 + "resume_rewind": 35, 1950 + "rewind_across_tracks": false, 1951 + "roll_off": 320, 1952 + "root_menu_customized": false, 1953 + "runtimedb": false, 1954 + "sbs_file": "", 1955 + "screen_scroll_step": 909211702, 1956 + "scroll_delay": 1685024815, 1957 + "scroll_paginated": false, 1958 + "scroll_speed": 0, 1959 + "scroll_step": 1953718627, 1960 + "scrollbar": 50, 1961 + "scrollbar_width": 1000, 1962 + "shortcuts_replaces_qs": false, 1963 + "show_filename_ext": 2, 1964 + "show_icons": true, 1965 + "show_path_in_browser": 0, 1966 + "show_queue_options": 0, 1967 + "show_shuffled_adding_options": true, 1968 + "show_shutdown_message": false, 1969 + "single_mode": 60, 1970 + "skip_length": 6, 1971 + "sleeptimer_duration": 0, 1972 + "sleeptimer_on_startup": false, 1973 + "sort_case": true, 1974 + "sort_dir": 909192819, 1975 + "sort_file": 3551608, 1976 + "spdif_enable": true, 1977 + "speaker_mode": 0, 1978 + "start_directory": "", 1979 + "start_in_screen": 0, 1980 + "statusbar": 9, 1981 + "stereo_width": 100, 1982 + "stereosw_mode": 0, 1983 + "surround_balance": 0, 1984 + "surround_enabled": 0, 1985 + "surround_fx1": 0, 1986 + "surround_fx2": false, 1987 + "surround_method2": false, 1988 + "surround_mix": 1, 1989 + "tagcache_autoupdate": false, 1990 + "tagcache_db_path": "", 1991 + "tagcache_ram": 0, 1992 + "tagcache_scan_paths": "", 1993 + "talk_battery_level": true, 1994 + "talk_dir": 3, 1995 + "talk_dir_clip": true, 1996 + "talk_file": 1, 1997 + "talk_file_clip": false, 1998 + "talk_filetype": false, 1999 + "talk_menu": true, 2000 + "talk_mixer_amp": 1601136494, 2001 + "timeformat": 0, 2002 + "timestretch_enabled": false, 2003 + "touch_mode": 0, 2004 + "touchpad_deadzone": 0, 2005 + "touchpad_sensitivity": 0, 2006 + "treble": 0, 2007 + "treble_cutoff": 0, 2008 + "ts_calibration_data": { 2009 + "A": 0, 2010 + "B": 0, 2011 + "C": 0, 2012 + "D": 0, 2013 + "E": 0, 2014 + "F": 0, 2015 + "divider": 0 2016 + }, 2017 + "unplug_autoresume": true, 2018 + "unplug_mode": 0, 2019 + "usb_charging": 1868786015, 2020 + "usb_hid": false, 2021 + "usb_keypad_mode": 0, 2022 + "usb_skip_first_drive": false, 2023 + "usemrb": 0, 2024 + "viewers_icon_file": "", 2025 + "volume": 0, 2026 + "volume_adjust_mode": 0, 2027 + "volume_adjust_norm_steps": 0, 2028 + "volume_limit": 32, 2029 + "volume_type": 8421504, 2030 + "warnon_erase_dynplaylist": false, 2031 + "wps_file": "" 2032 + } 2033 + ], 2034 + "title": "Settings" 2035 + }, 2036 + "Track.v1": { 2037 + "type": "object", 2038 + "properties": { 2039 + "album": { 2040 + "type": "string" 2041 + }, 2042 + "album_artist": { 2043 + "type": "string" 2044 + }, 2045 + "album_id": { 2046 + "type": "string" 2047 + }, 2048 + "artist": { 2049 + "type": "string" 2050 + }, 2051 + "artist_id": { 2052 + "type": "string" 2053 + }, 2054 + "bitrate": { 2055 + "type": "integer" 2056 + }, 2057 + "composer": { 2058 + "type": "string" 2059 + }, 2060 + "created_at": { 2061 + "type": "integer" 2062 + }, 2063 + "disc_number": { 2064 + "type": "integer" 2065 + }, 2066 + "filesize": { 2067 + "type": "integer" 2068 + }, 2069 + "frequency": { 2070 + "type": "integer" 2071 + }, 2072 + "genre_id": { 2073 + "type": "string" 2074 + }, 2075 + "id": { 2076 + "type": "string" 2077 + }, 2078 + "length": { 2079 + "type": "integer" 2080 + }, 2081 + "md5": { 2082 + "type": "string" 2083 + }, 2084 + "path": { 2085 + "type": "string" 2086 + }, 2087 + "title": { 2088 + "type": "string" 2089 + }, 2090 + "track_number": { 2091 + "type": "integer" 2092 + }, 2093 + "updated_at": { 2094 + "type": "integer" 2095 + }, 2096 + "year": { 2097 + "type": "integer" 2098 + }, 2099 + "year_string": { 2100 + "type": "string" 2101 + } 2102 + }, 2103 + "x-examples": { 2104 + "Example 1": { 2105 + "album": "2014 Forest Hills Drive", 2106 + "album_artist": "J. Cole", 2107 + "album_id": "cm1ibibnn00043fj7ppakq9w9", 2108 + "artist": "J. Cole", 2109 + "artist_id": "cm1ibibnn00033fj7hy019na5", 2110 + "bitrate": 96, 2111 + "composer": "Roy Hammond, Jermaine Cole, CHARLES SIMMONS", 2112 + "created_at": 1727296114, 2113 + "disc_number": 1, 2114 + "filesize": 2872773, 2115 + "frequency": 44100, 2116 + "genre_id": "", 2117 + "id": "cm1ibibnn00053fj79ghe6b2d", 2118 + "length": 239381, 2119 + "md5": "b5a0d86e156e6d02c90f647bf5cc6fc2", 2120 + "path": "/home/coder/Music/03 - J. Cole - Wet Dreamz(Explicit).m4a", 2121 + "title": "Wet Dreamz", 2122 + "track_number": 3, 2123 + "updated_at": 1727296114, 2124 + "year": 2014, 2125 + "year_string": "2014-12-09" 2126 + } 2127 + }, 2128 + "examples": [ 2129 + { 2130 + "album": "2014 Forest Hills Drive", 2131 + "album_artist": "J. Cole", 2132 + "album_id": "cm1ibibnn00043fj7ppakq9w9", 2133 + "artist": "J. Cole", 2134 + "artist_id": "cm1ibibnn00033fj7hy019na5", 2135 + "bitrate": 96, 2136 + "composer": "Roy Hammond, Jermaine Cole, CHARLES SIMMONS", 2137 + "created_at": 1727296114, 2138 + "disc_number": 1, 2139 + "filesize": 2872773, 2140 + "frequency": 44100, 2141 + "genre_id": "", 2142 + "id": "cm1ibibnn00053fj79ghe6b2d", 2143 + "length": 239381, 2144 + "md5": "b5a0d86e156e6d02c90f647bf5cc6fc2", 2145 + "path": "/home/coder/Music/03 - J. Cole - Wet Dreamz(Explicit).m4a", 2146 + "title": "Wet Dreamz", 2147 + "track_number": 3, 2148 + "updated_at": 1727296114, 2149 + "year": 2014, 2150 + "year_string": "2014-12-09" 2151 + } 2152 + ], 2153 + "description": "", 2154 + "title": "Track" 2155 + } 2156 + } 2157 + }, 2158 + "x-internal": true 2159 + }
+177 -34
crates/server/src/lib.rs
··· 1 1 use owo_colors::OwoColorize; 2 + use rockbox_library::repo; 2 3 use rockbox_sys::{self as rb, events::RockboxCommand, types::playlist_amount::PlaylistAmount}; 4 + use sqlx::Sqlite; 3 5 use std::{ 4 6 ffi::c_char, 5 7 io::{BufRead, BufReader, Write}, ··· 31 33 println!("{}", BANNER.yellow()); 32 34 33 35 let port = std::env::var("ROCKBOX_TCP_PORT").unwrap_or_else(|_| "6063".to_string()); 34 - let addr = format!("127.0.0.1:{}", port); 36 + let addr = format!("0.0.0.0:{}", port); 35 37 let listener = TcpListener::bind(&addr).unwrap(); 36 38 listener.set_nonblocking(true).unwrap(); 37 39 ··· 43 45 44 46 let pool = ThreadPool::new(4); 45 47 let active_connections = Arc::new(Mutex::new(0)); 48 + let rt = tokio::runtime::Runtime::new().unwrap(); 49 + let db_pool = rt 50 + .block_on(rockbox_library::create_connection_pool()) 51 + .unwrap(); 46 52 47 53 loop { 48 54 match listener.accept() { 49 55 Ok((stream, _)) => { 56 + let db_pool = db_pool.clone(); 50 57 let active_connections = Arc::clone(&active_connections); 51 58 { 52 59 let mut active_connections = active_connections.lock().unwrap(); 53 60 *active_connections += 1; 54 61 } 55 62 pool.execute(move || { 56 - handle_connection(stream); 63 + handle_connection(stream, db_pool); 57 64 { 58 65 let mut active_connections = active_connections.lock().unwrap(); 59 66 *active_connections -= 1; ··· 81 88 } 82 89 } 83 90 84 - fn handle_connection(mut stream: TcpStream) { 91 + fn handle_connection(mut stream: TcpStream, pool: sqlx::Pool<Sqlite>) { 92 + let rt = tokio::runtime::Runtime::new().unwrap(); 85 93 let buf_reader = BufReader::new(&mut stream); 86 94 let http_request: Vec<_> = buf_reader 87 95 .lines() ··· 96 104 97 105 println!("{} {}", method.bright_cyan(), path); 98 106 99 - if method != "GET" { 107 + if method != "GET" && method != "PUT" { 100 108 let response = "HTTP/1.1 405 Method Not Allowed\r\n\r\n"; 101 109 stream.write_all(response.as_bytes()).unwrap(); 102 110 return; 103 111 } 104 112 105 113 match path { 106 - "/pause" => { 114 + "/player/pause" => { 107 115 rb::playback::pause(); 108 116 } 109 - "/resume" => { 117 + "/player/resume" => { 110 118 rb::playback::resume(); 111 119 } 112 - "/next" => { 120 + "/player/next" => { 113 121 rb::playback::next(); 114 122 } 115 - "/prev" => { 123 + "/player/prev" => { 116 124 rb::playback::prev(); 117 125 } 118 - "/stop" => { 126 + "/player/stop" => { 119 127 rb::playback::hard_stop(); 120 128 } 121 129 "/playlist_amount" => { ··· 187 195 stream.write_all(response.as_bytes()).unwrap(); 188 196 return; 189 197 } 190 - "/flush_and_reload_tracks" => { 198 + "/player/flush-and-reload-tracks" => { 191 199 rb::playback::flush_and_reload_tracks(); 192 200 return; 193 201 } 194 - "/next_track" => { 202 + "/player/next-track" => { 195 203 let track = rb::playback::next_track(); 196 204 let response = format!( 197 205 "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{}", ··· 200 208 stream.write_all(response.as_bytes()).unwrap(); 201 209 return; 202 210 } 203 - "/current_track" => { 211 + "/player/current-track" => { 204 212 let track = rb::playback::current_track(); 205 213 let response = format!( 206 214 "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{}", ··· 218 226 stream.write_all(response.as_bytes()).unwrap(); 219 227 return; 220 228 } 221 - "/file_position" => { 229 + "/player/file-position" => { 222 230 let position = rb::playback::get_file_pos(); 223 231 let response = format!( 224 232 "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{}", ··· 236 244 stream.write_all(response.as_bytes()).unwrap(); 237 245 return; 238 246 } 247 + "/albums" => { 248 + let albums = rt.block_on(repo::album::all(pool)).unwrap(); 249 + let response = format!( 250 + "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{}", 251 + serde_json::to_string(&albums).unwrap() 252 + ); 253 + stream.write_all(response.as_bytes()).unwrap(); 254 + return; 255 + } 256 + "/artists" => { 257 + let artists = rt.block_on(repo::artist::all(pool)).unwrap(); 258 + let response = format!( 259 + "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{}", 260 + serde_json::to_string(&artists).unwrap() 261 + ); 262 + stream.write_all(response.as_bytes()).unwrap(); 263 + return; 264 + } 265 + "/tracks" => { 266 + let tracks = rt.block_on(repo::track::all(pool)).unwrap(); 267 + let response = format!( 268 + "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{}", 269 + serde_json::to_string(&tracks).unwrap() 270 + ); 271 + stream.write_all(response.as_bytes()).unwrap(); 272 + return; 273 + } 274 + "/openapi.json" => { 275 + let spec = include_str!("../openapi.json"); 276 + let response = format!( 277 + "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{}", 278 + spec 279 + ); 280 + stream.write_all(response.as_bytes()).unwrap(); 281 + return; 282 + } 283 + "/" => { 284 + let index = include_str!("../docs/index.html"); 285 + let response = format!( 286 + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n{}", 287 + index 288 + ); 289 + stream.write_all(response.as_bytes()).unwrap(); 290 + } 239 291 _ => { 240 - if path.starts_with("/play?") { 292 + if path.starts_with("/operations/") || path.starts_with("/schemas/") { 293 + let index = include_str!("../docs/index.html"); 294 + let response = format!( 295 + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n{}", 296 + index 297 + ); 298 + stream.write_all(response.as_bytes()).unwrap(); 299 + return; 300 + } 301 + 302 + if path.starts_with("/artists/") && path.ends_with("/tracks") { 303 + todo!("to be implemented"); 304 + } 305 + 306 + if path.starts_with("/albums/") && path.ends_with("/tracks") { 307 + todo!("to be implemented"); 308 + } 309 + 310 + if path.starts_with("/player/play?") { 241 311 let params: Vec<_> = path.split('?').collect(); 242 - let params: Vec<_> = params[1].split('&').collect(); 243 - let elapsed = params[0].split('=').collect::<Vec<_>>()[1].parse().unwrap(); 244 - let offset = params[1].split('=').collect::<Vec<_>>()[1].parse().unwrap(); 312 + let params = queryst::parse(params[1]).unwrap(); 313 + let elapsed = params.get("elapsed").unwrap().as_i64().unwrap(); 314 + let offset = params.get("offset").unwrap().as_i64().unwrap(); 245 315 rb::playback::play(elapsed, offset); 246 316 let response = "HTTP/1.1 200 OK\r\n\r\n"; 247 317 stream.write_all(response.as_bytes()).unwrap(); 248 318 return; 249 319 } 250 320 251 - if path.starts_with("/ff_rewind") { 321 + if path.starts_with("/player/ff_rewind") { 252 322 let params: Vec<_> = path.split('?').collect(); 253 - let newtime = params[1].split('=').collect::<Vec<_>>()[1].parse().unwrap(); 323 + let params = queryst::parse(params[1]).unwrap(); 324 + let newtime = params.get("newtime").unwrap().as_str().unwrap(); 325 + let newtime = newtime.parse().unwrap(); 254 326 rb::playback::ff_rewind(newtime); 255 327 let response = "HTTP/1.1 200 OK\r\n\r\n"; 256 328 stream.write_all(response.as_bytes()).unwrap(); 257 329 return; 258 330 } 259 331 260 - if path.starts_with("/tree_entries?") { 332 + if path.starts_with("/browse/tree-entries?") { 261 333 let params: Vec<_> = path.split('?').collect(); 262 334 let params = queryst::parse(params[1]).unwrap_or_default(); 263 335 let path = params.get("q").unwrap().as_str().unwrap(); ··· 289 361 return; 290 362 } 291 363 364 + if path.starts_with("/albums/") { 365 + let album_id = path.split('/').collect::<Vec<_>>()[2]; 366 + let album = rt.block_on(repo::album::find(pool, album_id)).unwrap(); 367 + 368 + if album.is_none() { 369 + let response = "HTTP/1.1 404 Not Found\r\n\r\n"; 370 + stream.write_all(response.as_bytes()).unwrap(); 371 + return; 372 + } 373 + 374 + let response = format!( 375 + "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{}", 376 + serde_json::to_string(&album).unwrap() 377 + ); 378 + stream.write_all(response.as_bytes()).unwrap(); 379 + return; 380 + } 381 + 382 + if path.starts_with("/artists/") { 383 + let artist_id = path.split('/').collect::<Vec<_>>()[2]; 384 + let artist = rt.block_on(repo::artist::find(pool, artist_id)).unwrap(); 385 + 386 + if artist.is_none() { 387 + let response = "HTTP/1.1 404 Not Found\r\n\r\n"; 388 + stream.write_all(response.as_bytes()).unwrap(); 389 + return; 390 + } 391 + 392 + let response = format!( 393 + "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{}", 394 + serde_json::to_string(&artist).unwrap() 395 + ); 396 + stream.write_all(response.as_bytes()).unwrap(); 397 + return; 398 + } 399 + 400 + if path.starts_with("/tracks/") { 401 + let track_id = path.split('/').collect::<Vec<_>>()[2]; 402 + let track = rt.block_on(repo::track::find(pool, track_id)).unwrap(); 403 + 404 + if track.is_none() { 405 + let response = "HTTP/1.1 404 Not Found\r\n\r\n"; 406 + stream.write_all(response.as_bytes()).unwrap(); 407 + return; 408 + } 409 + 410 + let response = format!( 411 + "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{}", 412 + serde_json::to_string(&track).unwrap() 413 + ); 414 + stream.write_all(response.as_bytes()).unwrap(); 415 + return; 416 + } 417 + 292 418 let response = "HTTP/1.1 404 Not Found\r\n\r\n"; 293 419 stream.write_all(response.as_bytes()).unwrap(); 294 420 return; ··· 308 434 thread::spawn(move || { 309 435 let port = std::env::var("ROCKBOX_TCP_PORT").unwrap_or_else(|_| "6063".to_string()); 310 436 let url = format!("http://127.0.0.1:{}", port); 437 + let client = reqwest::blocking::Client::new(); 311 438 312 439 while let Ok(event) = cmd_rx.recv() { 313 440 match event { 314 441 RockboxCommand::Play(elapsed, offset) => { 315 - reqwest::blocking::get(&format!( 316 - "{}/play?elapsed={}&offset={}", 317 - url, elapsed, offset 318 - )) 319 - .unwrap(); 442 + client 443 + .put(&format!( 444 + "{}/player/play?elapsed={}&offset={}", 445 + url, elapsed, offset 446 + )) 447 + .send() 448 + .unwrap(); 320 449 } 321 450 RockboxCommand::Pause => { 322 - reqwest::blocking::get(&format!("{}/pause", url)).unwrap(); 451 + client.put(&format!("{}/player/pause", url)).send().unwrap(); 323 452 } 324 453 RockboxCommand::Resume => { 325 - reqwest::blocking::get(&format!("{}/resume", url)).unwrap(); 454 + client 455 + .put(&format!("{}/player/resume", url)) 456 + .send() 457 + .unwrap(); 326 458 } 327 459 RockboxCommand::Next => { 328 - reqwest::blocking::get(&format!("{}/next", url)).unwrap(); 460 + client.put(&format!("{}/player/next", url)).send().unwrap(); 329 461 } 330 462 RockboxCommand::Prev => { 331 - reqwest::blocking::get(&format!("{}/prev", url)).unwrap(); 463 + client.put(&format!("{}/player/prev", url)).send().unwrap(); 332 464 } 333 465 RockboxCommand::FfRewind(newtime) => { 334 - reqwest::blocking::get(&format!("{}/ff_rewind?newtime={}", url, newtime)) 466 + client 467 + .put(&format!("{}/player/ff-rewind?newtime={}", url, newtime)) 468 + .send() 335 469 .unwrap(); 336 470 } 337 471 RockboxCommand::FlushAndReloadTracks => { 338 - reqwest::blocking::get(&format!("{}/flush_and_reload_tracks", url)).unwrap(); 472 + client 473 + .put(&format!("{}/player/flush-and-reload-tracks", url)) 474 + .send() 475 + .unwrap(); 339 476 } 340 477 RockboxCommand::Stop => { 341 - reqwest::blocking::get(&format!("{}/stop", url)).unwrap(); 478 + client.put(&format!("{}/player/stop", url)).send().unwrap(); 342 479 } 343 480 RockboxCommand::PlaylistResume => { 344 - reqwest::blocking::get(&format!("{}/playlist_resume", url)).unwrap(); 481 + client 482 + .put(&format!("{}/playlists/resume", url)) 483 + .send() 484 + .unwrap(); 345 485 } 346 486 RockboxCommand::PlaylistResumeTrack => { 347 - reqwest::blocking::get(&format!("{}/playlist_resume_track", url)).unwrap(); 487 + client 488 + .put(&format!("{}/playlists/resume-track", url)) 489 + .send() 490 + .unwrap(); 348 491 } 349 492 } 350 493 }