写一个想听啥听啥的听歌软件

前言

前面我发了一篇文章,向大家推荐了一个听歌软件:Listen(原文链接),我一般是在Edge浏览器里安装插件使用的,但是由于我是用的iPhone手机,然后Listen的源码我又一直编译不过去(吐槽下ReactNative)所以想自己撸一个听歌软件自己用.

技术选型

本人原本是做ios开发的,但是奈何objective-C比较难用,swift又一言难尽,swiftUI坑又比较多.在加上最近flutter比较火,而我之前也在demo上试了写了一下,感觉还不错,所以就打算使用flutter来写app了.

而且flutter还是跨平台的,还可以把android windows的一起给搞定了,嘿嘿~ 美滋滋

api接口

巧妇难为无米之炊,没有接口也没法进行下去啊. 我第一时间想到的是抓Listen1的包. 但是咪咕渠道的有个sid字段的值不知道是什么规则生成的,每次都不一样.导致我咪咕渠道的api迟迟搞不定.

不过我在github上找到了其它可用的api(链接地址),谢谢这位大哥了嘿嘿.

开始撸代码

先撸一个公共类,作为解析各平台的解析协议

class SongModel {  Map<String, dynamic> originMap = {}; //原始数据  String channel = ""; //渠道  //资源id  String resourceId() {    return "";  }  //歌曲名  String songName() {    return "";  }  //歌曲图片  Future<String> songImg() {    return Future.value("");  }  //歌手名  String singer() {    return '';  }  //专辑名  String album() {    return "";  }  //播放地址  Future<String> playUrl() async {    return "";  }  //获取歌词  Future<String> lyric() async {    return "";  }}

特定平台的api实现

import 'package:dio/dio.dart';import 'package:listen_flutter/api/migu/model/models/migu_models.dart';class MiguApi {  //搜索歌曲  static Future<MiguPageListModel> search(String? text) async {    var searchText = "jay";    if (text?.isNotEmpty == true) {      searchText = text!;    }    var path = 'http://iecoxe.top:5000/v1/migu/search?key=$searchText';    var response = await Dio().get(path);    return MiguPageListModel(response.data);  }  //获取播放地址  static Future<String> getPlayUrl(MiguSongModel song) async {    var copyrightId = song.originMap['copyrightId'];    var path = 'http://iecoxe.top:5000/v1/migu/song?cid=$copyrightId';    var response = await Dio().get(path);    var map = response.data as Map;    return map['lyric'];  }  //获取播放地址  static Future<String> getLyric(MiguSongModel song) async {    var copyrightId = song.originMap['copyrightId'];    var path = 'http://iecoxe.top:5000/v1/migu/lyric?cid=$copyrightId';    var response = await Dio().get(path);    var map = response.data as Map;    return map['lyric'];  }}

model类做好解析

import 'package:hive/hive.dart';import 'package:listen_flutter/api/migu/apis/migu_api.dart';import 'package:listen_flutter/models/page_response.dart';import 'package:listen_flutter/models/song_model.dart';part 'migu_models.g.dart';@HiveType(typeId: 0)class MiguSongModel extends HiveObject implements SongModel {  @override  @HiveField(1)  Map<String, dynamic> originMap; //原始数据  @override  @HiveField(2)  String channel = "migu";  MiguSongModel(this.originMap);  //资源id  @override  String resourceId() {    return "migu" + originMap['id'];  }  //歌曲名  @override  String songName() {    return originMap['songName'];  }  //歌曲图片  @override  Future<String> songImg() {    return Future.value(originMap['cover']);  }  //歌手名  @override  String singer() {    return originMap['singerName'];  }  //专辑名  @override  String album() {    return originMap['albumName'];  }  @override  Future<String> playUrl() {    return Future.value(originMap['mp3']);  }  @override  Future<String> lyric() {    return MiguApi.getLyric(this);  }}class MiguPageListModel implements PageResponse {  late Map<String, dynamic> originMap; //原始数据  MiguPageListModel(this.originMap);  @override  List<MiguSongModel> list() {    var list = originMap["musics"] as List;    return list.map((e) => MiguSongModel(e)).toList();  }  @override  bool hasNextPage() {    return true;  }  @override  int nextPage() {    return 1;  }}

最后就是构建UI,解析歌词啥的了

// import 'package:just_audio/just_audio.dart';import 'dart:math';import 'package:audio_service/audio_service.dart';import 'package:audioplayers/audioplayers.dart';import 'package:get/get.dart';import 'package:get_storage/get_storage.dart';// import 'package:just_audio/just_audio.dart';import 'package:listen_flutter/models/song_model.dart';import 'package:listen_flutter/songlist/songlist.dart';import 'audio_handle.dart';class PlayerManager {  var currentSong = Rx<SongModel?>(null); //当前播放歌曲  var playerState = Rx<PlayerState>(PlayerState.PAUSED); //当前播放状态  var position = Duration.zero.obs;  var duration = Duration.zero.obs;  var isInit = false.obs;  var queueState = 0.obs; //0 顺序播放 1:随机播放 2:单曲循环  AudioPlayer audioPlayer = AudioPlayer(); //播放器  var audioHandler = Get.find<MyAudioHandler>();  factory PlayerManager() => _getInstance();  static PlayerManager get instance => _getInstance();  static PlayerManager? _instance;  PlayerManager._internal() {    audioPlayer.onPlayerStateChanged.listen((event) {      playerState.value = event;      audioHandler.playbackState.add(audioHandler.playbackState.value          .copyWith(playing: event == PlayerState.PLAYING));    });    audioPlayer.onAudioPositionChanged.listen((event) {      position.value = event;      audioHandler.playbackState.add(          audioHandler.playbackState.value.copyWith(updatePosition: event));    });    audioPlayer.onDurationChanged.listen((event) {      duration.value = event;    });    audioPlayer.onPlayerCompletion.listen((event) {      next();    });    queueState.listen((value) {      GetStorage().write("queueState", value);    });    queueState.value = GetStorage().read<int>("queueState") ?? 0;    var resourceId = GetStorage().read<String>("currentSong");    if (resourceId == null) {      SongListManager.playList()          .then((value) => currentSong.value = value.first);    } else {      SongListManager.getSong(resourceId)          .then((value) => currentSong.value = value);    }  }  static PlayerManager _getInstance() {    _instance = _instance ?? PlayerManager._internal();    return _instance!;  }  play(SongModel songModel) async {    SongListManager.addSong(songModel);    try {      var url = await songModel.playUrl();      int state = await audioPlayer.play(url);      audioPlayer.getDuration();      currentSong.value = songModel;      GetStorage().write("currentSong", currentSong.value!.resourceId());      addSong();      isInit.value = true;    } catch (e) {      next();    }  }  pause() {    if (currentSong.value == null) {      return;    }    audioPlayer.pause();  }  resume() async {    if (currentSong.value == null) {      return;    }    if (isInit.value) {      audioPlayer.resume();    } else {      play(currentSong.value!);    }  }  next() async {    if (currentSong.value == null) {      return;    }    var list = await SongListManager.playList();    if (queueState.value == 0) {      var index = list.indexOf(currentSong.value!);      var i = index + 1;      if (index >= list.length - 1) {        i = 0;      }      var song = list.elementAt(i);      currentSong.value = song;      play(song);    } else if (queueState.value == 1) {      var random = Random();      var index = random.nextInt(list.length);      var song = list.elementAt(index);      currentSong.value = song;      play(song);    } else {      play(currentSong.value!);    }  }  previous() async {    if (currentSong.value == null) {      return;    }    var list = await SongListManager.playList();    if (queueState.value == 0) {      var index = list.indexOf(currentSong.value!);      var i = index - 1;      if (index <= 0) {        i = list.length - 1;      }      var song = list.elementAt(i);      currentSong.value = song;      play(song);    } else if (queueState.value == 1) {      var random = Random();      var index = random.nextInt(list.length);      var song = list.elementAt(index);      currentSong.value = song;      play(song);    } else {      play(currentSong.value!);    }  }  seek(Duration duration) async {    if (currentSong.value == null) {      return;    }    await audioPlayer.seek(duration);  }  addSong() async {    var songModel = currentSong.value;    if (songModel == null) {      return;    }    var img = await songModel.songImg();    var album = songModel.album();    var title = songModel.songName();    // var duration = await audioPlayer.getDuration();    var duration = 200000;    var item = MediaItem(      id: songModel.resourceId(),      album: album,      title: title,      artist: '',      duration: Duration(milliseconds: duration),      artUri: Uri.parse(img),    );    // audioHandler.playMediaItem(item);    audioHandler.mediaItem.add(item);    var playbackState = PlaybackState(      // Which buttons should appear in the notification now      controls: [        MediaControl.skipToPrevious,        MediaControl.play,        MediaControl.pause,        MediaControl.stop,        MediaControl.skipToNext,      ],      // Which other actions should be enabled in the notification      systemActions: const {        MediaAction.seek,        MediaAction.seekForward,        MediaAction.seekBackward,      },      // Which controls to show in Android's compact view.      androidCompactActionIndices: const [0, 1, 3],      // Whether audio is ready, buffering, ...      processingState: AudioProcessingState.ready,      // Whether audio is playing      speed: 1.0,      // The current queue position      queueIndex: 0,      playing: true,    );    audioHandler.playbackState.add(playbackState);  }}

哎,我为啥要在头条上发这种东西呢?这种东西真的有人看吗?

不管了,发几张成品图:

顺便搞了个mac版的,上班好好用 嘿嘿

虽然界面比较简陋,但是听歌的搞这么花里胡哨的干啥呢,是吧?

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2021年9月18日
下一篇 2021年9月18日

相关推荐