AI prompts
base on Play simultaneously music/audio from assets/network/file directly from Flutter, compatible with android / ios / web / macos, displays notifications # ๐ง assets_audio_player ๐
[](https://pub.dartlang.org/packages/assets_audio_player)
<a href="https://github.com/Solido/awesome-flutter">
<img alt="Awesome Flutter" src="https://img.shields.io/badge/Awesome-Flutter-blue.svg?longCache=true&style=flat" />
</a>
<img src="https://img.shields.io/badge/platform-android%20%7C%20ios%20%7C%20macos%20%7C%20web%20-%23989898" />
[](https://codemagic.io/apps/5ed8002fe1907b001c67db52/5ed8002fe1907b001c67db51/latest_build)
[](https://www.codefactor.io/repository/github/florent37/flutter-assetsaudioplayer)
Play music/audio stored in assets files (simultaneously) directly from Flutter (android / ios / web / macos).
You can also use play audio files from **network** using their url, **radios/livestream** and **local files**
**Notification can be displayed on Android & iOS, and bluetooth actions are handled**
```yaml
flutter:
assets:
- assets/audios/
```
```Dart
AssetsAudioPlayer.newPlayer().open(
Audio("assets/audios/song1.mp3"),
autoStart: true,
showNotification: true,
);
```
[](https://github.com/florent37/Flutter-AssetsAudioPlayer)
[](https://github.com/florent37/Flutter-AssetsAudioPlayer)
# ๐ฅ Import
```yaml
dependencies:
assets_audio_player: ^3.0.8
```
**Works with `flutter: ">=3.3.0"`, be sure to upgrade your sdk**
You like the package ? buy me a kofi :)
<a href='https://ko-fi.com/A160LCC' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://az743702.vo.msecnd.net/cdn/kofi1.png?v=0' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
<table>
<thead>
<tr>
<th>Audio Source</th>
<th>Android</th>
<th>iOS</th>
<th>Web</th>
<th>MacOS</th>
</tr>
</thead>
<tbody>
<tr>
<td>๐๏ธ Asset file (asset path)</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>๐ Network file (url)</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>๐ Local file (path)</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>๐ป Network LiveStream / radio (url) <br/> (<b>Default, HLS, Dash, SmoothStream</b>)</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Android</th>
<th>iOS</th>
<th>Web</th>
<th>MacOS</th>
</tr>
</thead>
<tbody>
<tr>
<td>๐ถ Multiple players</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>๐ฝ Open Playlist</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>๐ฌSystem notification</td>
<td>โ
</td>
<td>โ
</td>
<td>๐ซ</td>
<td>๐ซ</td>
</tr>
<tr>
<td>๐ง Bluetooth actions</td>
<td>โ
</td>
<td>โ
</td>
<td>๐ซ</td>
<td>๐ซ</td>
</tr>
<tr>
<td>๐ Respect System silent mode</td>
<td>โ
</td>
<td>โ
</td>
<td>๐ซ</td>
<td>๐ซ</td>
</tr>
<tr>
<td>๐ Pause on phone call</td>
<td>โ
</td>
<td>โ
</td>
<td>๐ซ</td>
<td>๐ซ</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>Commands</th>
<th>Android</th>
<th>iOS</th>
<th>Web</th>
<th>MacOS</th>
</tr>
</thead>
<tbody>
<tr>
<td>โถ Play</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>โธ Pause</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>โน Stop</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>โฉ Seek(position)</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>โชโฉ SeekBy(position)</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>โฉ Forward(speed)</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>โช Rewind(speed)</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>โญ Next</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>โฎ Prev</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>Widgets</th>
<th>Android</th>
<th>iOS</th>
<th>Web</th>
<th>MacOS</th>
</tr>
</thead>
<tbody>
<tr>
<td>๐ฆ Audio Widget</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>๐ฆ Widget Builders</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>๐ฆ AudioPlayer Builders Extension</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>Properties</th>
<th>Android</th>
<th>iOS</th>
<th>Web</th>
<th>MacOS</th>
</tr>
</thead>
<tbody>
<tr>
<td>๐ Loop</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>๐ Shuffle</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>๐ get/set Volume</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>โฉ get/set Play Speed</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>โฉ get/set Pitch</td>
<td>โ
</td>
<td>๐ซ</td>
<td>๐ซ</td>
<td>๐ซ</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>Listeners</th>
<th>Android</th>
<th>iOS</th>
<th>Web</th>
<th>MacOS</th>
</tr>
</thead>
<tbody>
<tr>
<td>๐ฆป Listener onReady(completeDuration)</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>๐ฆป Listener currentPosition</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>๐ฆป Listener finished</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>๐ฆป Listener buffering</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>๐ฆป Listener volume</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>๐ฆปListener Play Speed</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
<td>โ
</td>
</tr>
<tr>
<td>๐ฆปListener Pitch</td>
<td>โ
</td>
<td>๐ซ</td>
<td>๐ซ</td>
<td>๐ซ</td>
</tr>
</tbody>
</table>
# ๐ Import assets files
No needed to copy songs to a media cache, with assets_audio_player you can open them directly from the assets.
1. Create an audio directory in your assets (not necessary named "audios")
2. Declare it inside your pubspec.yaml
```yaml
flutter:
assets:
- assets/audios/
```
## ๐ ๏ธ Getting Started
```Dart
final assetsAudioPlayer = AssetsAudioPlayer();
assetsAudioPlayer.open(
Audio("assets/audios/song1.mp3"),
);
```
You can also play _network songs_ from _url_
```Dart
final assetsAudioPlayer = AssetsAudioPlayer();
try {
await assetsAudioPlayer.open(
Audio.network("http://www.mysite.com/myMp3file.mp3"),
);
} catch (t) {
//mp3 unreachable
}
```
_LiveStream / Radio_ from _url_
**The main difference with network, if you pause/play, on livestream it will resume to present duration**
```Dart
final assetsAudioPlayer = AssetsAudioPlayer();
try {
await assetsAudioPlayer.open(
Audio.liveStream(MY_LIVESTREAM_URL),
);
} catch (t) {
//stream unreachable
}
```
And play _songs from file_
```Dart
//create a new player
final assetsAudioPlayer = AssetsAudioPlayer();
assetsAudioPlayer.open(
Audio.file(FILE_URI),
);
```
for file uri, please look at https://pub.dev/packages/path_provider
```Dart
assetsAudioPlayer.playOrPause();
assetsAudioPlayer.play();
assetsAudioPlayer.pause();
```
```Dart
assetsAudioPlayer.seek(Duration to);
assetsAudioPlayer.seekBy(Duration by);
```
```Dart
assetsAudioPlayer.forwardRewind(double speed);
//if positive, forward, if negative, rewind
```
```Dart
assetsAudioPlayer.stop();
```
# Notifications
[](https://github.com/florent37/Flutter-AssetsAudioPlayer)
[](https://github.com/florent37/Flutter-AssetsAudioPlayer)
on iOS, it will use `MPNowPlayingInfoCenter`
1. Add metas inside your audio
```dart
final audio = Audio.network("/assets/audio/country.mp3",
metas: Metas(
title: "Country",
artist: "Florent Champigny",
album: "CountryAlbum",
image: MetasImage.asset("assets/images/country.jpg"), //can be MetasImage.network
),
);
```
2. open with `showNotification: true`
```dart
_player.open(audio, showNotification: true)
```
## Custom notification
Custom icon (android only)
### By ResourceName
Make sure you added those icons inside your `android/res/drawable` **!!! not on flutter assets !!!!**
```dart
await _assetsAudioPlayer.open(
myAudio,
showNotification: true,
notificationSettings: NotificationSettings(
customStopIcon: AndroidResDrawable(name: "ic_stop_custom"),
customPauseIcon: AndroidResDrawable(name:"ic_pause_custom"),
customPlayIcon: AndroidResDrawable(name:"ic_play_custom"),
customPrevIcon: AndroidResDrawable(name:"ic_prev_custom"),
customNextIcon: AndroidResDrawable(name:"ic_next_custom"),
)
```
And don't forget tell proguard to keep those resources for release mode
(part Keeping Resources)
https://sites.google.com/a/android.com/tools/tech-docs/new-build-system/resource-shrinking
```xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@drawable/ic_next_custom, @drawable/ic_prev_custom, @drawable/ic_pause_custom, @drawable/ic_play_custom, @drawable/ic_stop_custom"/>
```
### By Manifest
1. Add your icon into your android's `res` folder (android/app/src/main/res)
2. Reference this icon into your AndroidManifest (android/app/src/main/AndroidManifest.xml)
```xml
<meta-data
android:name="assets.audio.player.notification.icon"
android:resource="@drawable/ic_music_custom"/>
```
You can also change actions icons
```
<meta-data
android:name="assets.audio.player.notification.icon.play"
android:resource="@drawable/ic_play_custom"/>
<meta-data
android:name="assets.audio.player.notification.icon.pause"
android:resource="@drawable/ic_pause_custom"/>
<meta-data
android:name="assets.audio.player.notification.icon.stop"
android:resource="@drawable/ic_stop_custom"/>
<meta-data
android:name="assets.audio.player.notification.icon.next"
android:resource="@drawable/ic_next_custom"/>
<meta-data
android:name="assets.audio.player.notification.icon.prev"
android:resource="@drawable/ic_prev_custom"/>
```
## Handle notification click (android)
Add in main
```dart
AssetsAudioPlayer.setupNotificationsOpenAction((notification) {
//custom action
return true; //true : handled, does not notify others listeners
//false : enable others listeners to handle it
});
```
Then if you want a custom action on widget
```dart
AssetsAudioPlayer.addNotificationOpenAction((notification) {
//custom action
return false; //true : handled, does not notify others listeners
//false : enable others listeners to handle it
});
```
## Custom actions
You can enable/disable a notification action
```dart
open(AUDIO,
showNotification: true,
notificationSettings: NotificationSettings(
prevEnabled: false, //disable the previous button
//and have a custom next action (will disable the default action)
customNextAction: (player) {
print("next");
}
)
)
```
## Update audio's metas / notification content
After your audio creation, just call
```dart
audio.updateMetas(
player: _assetsAudioPlayer, //add the player if the audio is actually played
title: "My new title",
artist: "My new artist",
//if I not provide a new album, it keep the old one
image: MetasImage.network(
//my new image url
),
);
```
## Bluetooth Actions
You have to enable notification to make them work
Available remote commands :
- Play / Pause
- Next
- Prev
- Stop
## HeadPhone Strategy
(Only for Android for now)
while opening a song/playlist, add a strategy
```dart
assetsAudioPlayer.open(
...
headPhoneStrategy: HeadPhoneStrategy.pauseOnUnplug,
//headPhoneStrategy: HeadPhoneStrategy.none, //default
//headPhoneStrategy: HeadPhoneStrategy.pauseOnUnplugPlayOnPlug,
)
```
If you want to make it work on bluetooth too, you'll have to add the BLUETOOTH permission inside your AndroidManifest.xml
```xml
<uses-permission android:name="android.permission.BLUETOOTH" />
```
# โ Play in parallel / simultaneously
You can create new AssetsAudioPlayer using AssetsAudioPlayer.newPlayer(),
which will play songs in a different native Media Player
This will enable to play two songs simultaneously
You can have as many player as you want !
```dart
///play 3 songs in parallel
AssetsAudioPlayer.newPlayer().open(
Audio("assets/audios/song1.mp3")
);
AssetsAudioPlayer.newPlayer().open(
Audio("assets/audios/song2.mp3")
);
//another way, with create, open, play & dispose the player on finish
AssetsAudioPlayer.playAndForget(
Audio("assets/audios/song3.mp3")
);
```
Each player has an unique generated `id`, you can retrieve or create them manually using
```dart
final player = AssetsAudioPlayer.withId(id: "MY_UNIQUE_ID");
```
# ๐๏ธ Playlist
```Dart
assetsAudioPlayer.open(
Playlist(
audios: [
Audio("assets/audios/song1.mp3"),
Audio("assets/audios/song2.mp3")
]
),
loopMode: LoopMode.playlist //loop the full playlist
);
assetsAudioPlayer.next();
assetsAudioPlayer.prev();
assetsAudioPlayer.playlistPlayAtIndex(1);
```
## Audio Widget
If you want a more flutter way to play audio, try the `AudioWidget` !
[](https://github.com/florent37/Flutter-AssetsAudioPlayer)
```dart
//inside a stateful widget
bool _play = false;
@override
Widget build(BuildContext context) {
return AudioWidget.assets(
path: "assets/audios/country.mp3",
play: _play,
child: RaisedButton(
child: Text(
_play ? "pause" : "play",
),
onPressed: () {
setState(() {
_play = !_play;
});
}
),
onReadyToPlay: (duration) {
//onReadyToPlay
},
onPositionChanged: (current, duration) {
//onPositionChanged
},
);
}
```
How to ๐ stop ๐ the AudioWidget ?
Just remove the Audio from the tree !
Or simply keep `play: false`
## ๐ง Listeners
All listeners exposes Streams
Using RxDart, AssetsAudioPlayer exposes some listeners as ValueObservable (Observable that provides synchronous access to the last emitted item);
### ๐ต Current song
```Dart
//The current playing audio, filled with the total song duration
assetsAudioPlayer.current //ValueObservable<PlayingAudio>
//Retrieve directly the current played asset
final PlayingAudio playing = assetsAudioPlayer.current.value;
//Listen to the current playing song
assetsAudioPlayer.current.listen((playingAudio){
final asset = playingAudio.assetAudio;
final songDuration = playingAudio.duration;
})
```
### โ Current song duration
```Dart
//Listen to the current playing song
final duration = assetsAudioPlayer.current.value.duration;
```
### โณ Current position (in seconds)
```Dart
assetsAudioPlayer.currentPosition //ValueObservable<Duration>
//retrieve directly the current song position
final Duration position = assetsAudioPlayer.currentPosition.value;
return StreamBuilder(
stream: assetsAudioPlayer.currentPosition,
builder: (context, asyncSnapshot) {
final Duration duration = asyncSnapshot.data;
return Text(duration.toString());
}),
```
or use a PlayerBuilder !
```dart
PlayerBuilder.currentPosition(
player: _assetsAudioPlayer,
builder: (context, duration) {
return Text(duration.toString());
}
)
```
or Player Builder Extension
```dart
_assetsAudioPlayer.builderCurrentPosition(
builder: (context, duration) {
return Text(duration.toString());
}
)
```
### โถ IsPlaying
boolean observable representing the current mediaplayer playing state
```Dart
assetsAudioPlayer.isPlaying // ValueObservable<bool>
//retrieve directly the current player state
final bool playing = assetsAudioPlayer.isPlaying.value;
//will follow the AssetsAudioPlayer playing state
return StreamBuilder(
stream: assetsAudioPlayer.isPlaying,
builder: (context, asyncSnapshot) {
final bool isPlaying = asyncSnapshot.data;
return Text(isPlaying ? "Pause" : "Play");
}),
```
or use a PlayerBuilder !
```dart
PlayerBuilder.isPlaying(
player: _assetsAudioPlayer,
builder: (context, isPlaying) {
return Text(isPlaying ? "Pause" : "Play");
}
)
```
or Player Builder Extension
```dart
_assetsAudioPlayer.builderIsPlaying(
builder: (context, isPlaying) {
return Text(isPlaying ? "Pause" : "Play");
}
)
```
### ๐ Volume
Change the volume (between 0.0 & 1.0)
```Dart
assetsAudioPlayer.setVolume(0.5);
```
The media player can follow the system "volume mode" (vibrate, muted, normal)
Simply set the `respectSilentMode` optional parameter as `true`
```dart
_player.open(PLAYABLE, respectSilentMode: true);
```
https://developer.android.com/reference/android/media/AudioManager.html?hl=fr#getRingerMode()
https://developer.apple.com/documentation/avfoundation/avaudiosessioncategorysoloambient
Listen the volume
```dart
return StreamBuilder(
stream: assetsAudioPlayer.volume,
builder: (context, asyncSnapshot) {
final double volume = asyncSnapshot.data;
return Text("volume : $volume");
}),
```
or use a PlayerBuilder !
```dart
PlayerBuilder.volume(
player: _assetsAudioPlayer,
builder: (context, volume) {
return Text("volume : $volume");
}
)
```
### โ Finished
Called when the current song has finished to play,
it gives the Playing audio that just finished
```Dart
assetsAudioPlayer.playlistAudioFinished //ValueObservable<Playing>
assetsAudioPlayer.playlistAudioFinished.listen((Playing playing){
})
```
Called when the complete playlist has finished to play
```Dart
assetsAudioPlayer.playlistFinished //ValueObservable<bool>
assetsAudioPlayer.playlistFinished.listen((finished){
})
```
### ๐ Looping
```Dart
final LoopMode loopMode = assetsAudioPlayer.loop;
// possible values
// LoopMode.none : not looping
// LoopMode.single : looping a single audio
// LoopMode.playlist : looping the fyll playlist
assetsAudioPlayer.setLoopMode(LoopMode.single);
assetsAudioPlayer.loopMode.listen((loopMode){
//listen to loop
})
assetsAudioPlayer.toggleLoop(); //toggle the value of looping
```
### ๐ Play Speed
```Dart
assetsAudioPlayer.setPlaySpeed(1.5);
assetsAudioPlayer.playSpeed.listen((playSpeed){
//listen to playSpeed
})
//change play speed for a particular Audio
Audio audio = Audio.network(
url,
playSpeed: 1.5
);
assetsAudioPlayer.open(audio);
```
### ๐๏ธ Pitch
```Dart
assetsAudioPlayer.setPitch(1.2);
assetsAudioPlayer.pitch.listen((pitch){
//listen to pitch
})
//change pitch for a particular Audio
Audio audio = Audio.network(
url,
pitch: 1.2
);
assetsAudioPlayer.open(audio);
```
# Error Handling
By default, on playing error, it stop the audio
BUT you can add a custom behavior
```dart
_player.onErrorDo = (handler){
handler.player.stop();
};
```
Open another audio
```dart
_player.onErrorDo = (handler){
handler.player.open(ANOTHER_AUDIO);
};
```
Try to open again on same position
```dart
_player.onErrorDo = (handler){
handler.player.open(
handler.playlist.copyWith(
startIndex: handler.playlistIndex
),
seek: handler.currentPosition
);
};
```
# Network Policies (android/iOS/macOS)
Android only allow HTTPS calls, you will have an error if you're using HTTP,
don't forget to add INTERNET permission and seet `usesCleartextTraffic="true"` in your **AndroidManifest.xml**
```
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
<uses-permission android:name="android.permission.INTERNET" />
<application
...
android:usesCleartextTraffic="true"
...>
...
</application>
</manifest>
```
iOS only allow HTTPS calls, you will have an error if you're using HTTP,
don't forget to edit your **info.plist** and set `NSAppTransportSecurity` to `NSAllowsArbitraryLoads`
```
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
```
To enable http calls on macOs, you have to add input/output calls capabilities into `info.plist`
```
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>fetch</string>
</array>
<key>com.apple.security.network.client</key>
<true/>
```
and in your
`Runner/DebugProfile.entitlements`
add
```
<key>com.apple.security.network.client</key>
<true/>
```
Complete `Runner/DebugProfile.entitlements`
```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
```
# ๐ถ Musics
All musics used in the samples came from https://www.freemusicarchive.org/
", Assign "at most 3 tags" to the expected json: {"id":"13703","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"