肥宅钓鱼网
当前位置: 首页 钓鱼百科

有啥听歌的好软件(写一个想听啥听啥的听歌软件)

时间:2023-06-11 作者: 小编 阅读量: 1 栏目名: 钓鱼百科

key=$searchText';varresponse=awaitDio().get;returnMiguPageListModel;}//获取播放地址staticFuture<String>getPlayUrlasync{varcopyrightId=song.originMap['copyrightId'];varpath='http://iecoxe.top:5000/v1/migu/song?

前言

前面我发了一篇文章,向大家推荐了一个听歌软件: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 = ""; //渠道//资源idString 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@overrideString resourceId() {return "migu"originMap['id'];}//歌曲名@overrideString songName() {return originMap['songName'];}//歌曲图片@overrideFuture<String> songImg() {return Future.value(originMap['cover']);}//歌手名@overrideString singer() {return originMap['singerName'];}//专辑名@overrideString album() {return originMap['albumName'];}@overrideFuture<String> playUrl() {return Future.value(originMap['mp3']);}@overrideFuture<String> lyric() {return MiguApi.getLyric(this);}}class MiguPageListModel implements PageResponse {late Map<String, dynamic> originMap; //原始数据MiguPageListModel(this.originMap);@overrideList<MiguSongModel> list() {var list = originMap["musics"] as List;return list.map((e) => MiguSongModel(e)).toList();}@overridebool hasNextPage() {return true;}@overrideint 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 = index1;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 nowcontrols: [MediaControl.skipToPrevious,MediaControl.play,MediaControl.pause,MediaControl.stop,MediaControl.skipToNext,],// Which other actions should be enabled in the notificationsystemActions: 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 playingspeed: 1.0,// The current queue positionqueueIndex: 0,playing: true,);audioHandler.playbackState.add(playbackState);}}

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

不管了,发几张成品图:

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

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

    推荐阅读
  • 有什么比较好用的云盘(我们还有哪些云盘选择)

    因为使用OneDrive需要微软账号登陆,登陆后至少能获得5GB免费容量。坚果云还能实现共享办公,邀请成员进入同一坚果云项目,修改的文件能够实时同步共享给项目成员,大大提高了工作效率。FirefoxSend这是由火狐提供的云盘,最突出的特点就是安全,上传至火狐的文件,每次分享链接最多仅支持20次下载或者24小时后链接将过期失效。FirefoxSend同样没有限制存储空间,上传下载也不限速,但上传文件是有大小限制的,最大只支持1GB大小的文件。

  • (一种远程控制自己电脑的方法)

    RdViewer是一款实用的远程电脑管理的工具软件。这款软件有很多优于同类软件的地方,这里给大家介绍下如何使用,首先在RdViewer官网下载和安装软件。

  • 用黄油可以做哪些面食(黄油做面食起什么作用)

    8、发酵好的面团拿出来,在表面均匀的刷上蛋液。

  • 甲亢患者饮食注意事项 甲亢患者的饮食指导

    甲状腺功能亢进症简称甲亢,是由于甲状腺激素过多所致的内分泌疾病。还应该多吃含钙和铁丰富的食物,如动物肝脏等。即使是吃含碘盐,也应向医生说明情况。以免升阳助热,化燥伤阴,加重病情。禁烟酒及刺激性饮料甲亢病人的饮食应该清淡,烟酒都属于热性、刺激性的东西,所以应该禁烟酒。所以,应禁止饮用,以免加重病情,影响身体康复。

  • 务德镇中心幼儿园(务德镇中心幼儿园)

    幼儿园大课间舞蹈操。孩子们脸上洋溢着快乐的笑容,欢呼声此起彼伏。务德镇中心幼儿园本着“自然育人,育自然人”的办园理念,努力为幼儿提供亲近自然、了解自然的机会。大课间室外运动既增强了幼儿的身体素质,又展示了孩子们健康、活泼、积极向上的风采。这是幼儿园全体老师的初心和愿望。

  • 梦到老公带着别的女人回家不要我(我每天晚上都做同样的梦)

    梦里这个家里还有另外一个女人,我总是迷迷糊糊的,听见她的娇喘声,而故事的男主竟然是我的丈夫林墨。可是,早上醒过来的时候,就什么都没有发生,我的老公林墨他就躺在我的身边。林墨揉着惺忪的睡眼问我。我目不转睛地盯着林墨,然后说道:“老公,我做了一个梦,梦里和别的女人上床了。”我从镜子里看见林墨和我的身影,觉得我真的是太幸福了,我轻轻的掰开他的手,将牙刷滴到他的嘴里,他顺势张开嘴巴,然后开始刷牙。

  • 淘气包埃米尔读后感(淘气包埃米尔读后感推荐)

    淘气包埃米尔读后感?,现在小编就来说说关于淘气包埃米尔读后感?书中一共有三个大段,一个大段大约有三四个小段,我觉的好的是埃米尔把猪血扣再爸爸的头上,不得不削第100个市头老儿。我读完《淘气包埃米尔》心感到了埃米尔是一个勇敢的孩子,因他很淘气,淘气看出勇敢;也通过勇敢来帮别人;通过帮别人感到自己高兴。范文二:这几天,我终于读完了共有238页的《淘气包埃米尔》,这本书是瑞典的民族英雄——阿斯特丽德。

  • 卤鲜鱿鱼(卤鲜鱿鱼的制作过程)

    以下内容大家不妨参考一二希望能帮到您!卤鲜鱿鱼原料白糖5克,生抽10克,酱油60克,葱段10克,姜片8克,蒜泥5克,植物油20克,香料包1个。做法将鲜鱿鱼去掉须爪,撕去外膜,洗净,放入沸水中焯烫,捞出沥干。锅内放入植物油烧热,放入葱段、姜片、蒜片爆香,添汤加调料和香料包、红曲粉,烧开后煮10分钟,放入鱿鱼卤2—3分钟即可。食用时取出切成条,码入盘内。

  • 简短520表白感人情话(简短520表白感人情话有哪些?)

    简短520表白感人情话见到你的微笑,我的世界是如此美妙。看到你的美貌,让我心里蹦蹦乱跳。与你相识相知到老,是今生较大的荣耀。情真真,意切切,一片相思几时休,月明人倚楼。情悠悠,思悠悠,爱到何时方始休,除非水倒流。520表白日,此生为你。亲爱的,520我爱你。我爱你三个字,说出来只要三秒钟,解释要三小时,证明却要一辈子。今天5月20日,我要大声说:520,我爱你!