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

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

时间: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版的,上班好好用 嘿嘿

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

    推荐阅读
  • 哪款核桃油好用(全网都在找的一款核桃油)

    食用核桃油可益智补脑,促进视网膜发育,促进骨骼、牙齿和头发生长,补肾护肝,增强免疫力,调节人体胆固醇,润肠通便,改善睡眠等,可谓是好处多多。近期,一款品牌名为“好好核”的核桃油在市场上异军突起,赢得了众多消费者的亲睐和口碑。“好好核”拥有行业内从青果到出油的完善生产解决方案,专家组经过认真评估后,一致认为:总体技术达到国际先进水平!因此,相对于橄榄油,核桃油更适合孕妇和宝宝食用。

  • 白癜风能彻底治好吗

    如今,随着天气环境的恶劣,患上白癜风的人越来越多,皮肤受到刺激后导致黑色素慢慢消退,形成白斑。专家表示,对于初期白癜风患者来说,一定要重视治疗,这样才能更好的得到控制,那么,白癜风能彻底治好吗?生活中治疗白癜风的方法有很多,患者要根据自身的情况对症治疗。

  • 武汉六大汽车客运站恢复运营(武汉六大汽车客运站恢复省内客运班线)

    8月17日,湖北公路客运集团股份有限公司通报,随着全国新冠肺炎疫情逐步得到控制,中高风险地区持续减少,省客集团旗下宏基客运站、傅家坡客运站、杨春湖换乘中心、金家墩青年路客运站、古田客运站、汉口北客运中心将于8月18日零时起逐步恢复省内班线运营。所有进站购票乘车的旅客须出具“健康码”及行程码,佩戴好口罩,并积极配合工作人员开展查验工作。

  • 十大女性穿搭风格(女人该如何打扮自己)

    半身裙搭配灰色系的针织半身裙修身显瘦,修饰腿型,也修饰身材,灰色的菱形格纹设计经典又时尚,是古典元素与现代美感的结合。上衣搭配是一件灰蓝色的卫衣,舒适轻盈,尽显休闲之感。配一双中筒的短靴,穿出一种女人的帅气感。配一件黑色的短裙,也有一种俏佳人的感觉。上衣叠穿搭配,内搭竖条纹的衬衫,外搭一件藏青色的针织马甲,休闲之中带有成熟女人的大气。

  • 产后脱发正常吗(产后脱发是什么情况)

    产后为什么会脱发产后脱发与妈妈怀孕前后体内雌激素水平的变化有关,女性怀孕后,由于体内雌激素增多,脱发的速度减慢,头发的寿命延长,而分娩后,其体内雌激素水平下降至正常,那些超期服役的头发便脱落,所以导致产后妈妈脱发严重。如何预防产后脱发1、注意合理饮食,多吃新鲜蔬菜、水果及海产品、豆类、蛋类,避免太腻太辣摄取过量的油脂性食物。

  • 1-3岁幼儿识水果英语(看图识水果fruit启蒙英语so)

    1-3岁幼儿识水果英语?接下来我们就一起去研究一下吧!家长们在锻炼孩子启蒙时,参照该文标准发音将单词与实物水果相结合,加深孩子感知认知。同时,循序渐进使用语句加深孩子语境,形成连贯性的意识。)pear(梨子)Ilovepears!(西瓜是我最爱吃的水果!

  • 玄彬被问合作女演员谁最漂亮(韩国去年最受欢迎的演员出炉)

    韩国国际文化交流振兴院发布2022年国际韩流趋势调查结果,2021年韩国14名最受欢迎的演员出炉,前三名都是男演员,宋慧乔登顶最受欢迎的女演员,前夫宋仲基紧随其后成为第四名最受欢迎的男演员。目前孙艺珍和玄彬结婚,孙艺珍的热度很高,两人今年3月份结婚。看来民众对男演员的喜爱程度超过女演员,估计参加投票的可能很多都是女粉丝,所以男演员上榜的很多,十四名最受欢迎的演员,女演员只有五位,而其他的九位都是男演员。

  • 卫星导航系统分析与仿真技术(综述与述评北斗卫星导航系统专项工程副总设计师)

    1962年美国空军启动名为“621B”的项目,1964—1966年期间,科学家首次提出四星距离交会测量定位体制。同期,苏联也研制部署了GLONASS卫星导航系统。在功能融合、特色服务方面,北斗系统进行一系列开创性探索,引领了卫星导航领域的发展。北斗二号系统在北斗一号系统基础上保留RDSS体制,增加四星测量的无源定位体制,短报文通信性能进一步增强,于2012年建成并正式运行,提供连续稳定的服务,已在各行各业广泛应用。

  • 微博热搜榜是什么时候出现的(微博热搜回归登上热搜榜)

    6月17日15:00,微博热搜榜、热门话题榜恢复更新。对于热搜榜和话题榜在接下来运营中会否有规则调整,微博方面暂无回应。6月10日,国家互联网信息办公室指导北京市互联网信息办公室,谈新浪微博负责人,责令其立即整改,暂停更新微博热搜榜和热门话题榜一周。对此微博管理员回应称,微博将严格落实管理要求,切实履行主体责任,加强违法违规信息管控,健全完善内部审核处置流程,积极维护网上传播秩序,构建微博社区的良好生态。

  • 淡水鲈鱼怎么做好吃(淡水鲈鱼的组佛啊)

    以下内容大家不妨参考一二希望能帮到您!淡水鲈鱼怎么做好吃准备食材:淡水鲈鱼一条、青红椒、葱、姜、十三香、八角、花椒、盐、香菜。洗净淡水鲈鱼改刀,切掉鱼骨。挑去腌制的料,上锅蒸十五分钟左右。出锅摆姜丝、葱丝、辣椒丝,少许酱油,淋上热油,最后加入香菜即可。