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

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

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

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

    推荐阅读
  • 麻黄豆怎么做(麻黄豆的制作方法)

    再重新起锅放油加热,把切好的红辣椒入锅,放入豆瓣酱炒香,放入切好的蒜末继续翻炒,随后放入炖好的鱼汤,再放料酒,白糖和陈醋以及胡椒粉煮开。

  • 梦见鬼魂 梦见鬼魂和我说话是什么意思

    梦见鬼魂,会遇到危险,你的人身安全可能会受到威胁。

  • 接待顾客的礼仪和技巧(如何接待顾客)

    根据基本礼仪规范:握手时,距对方约一步远,上身稍向前倾,两足立正,伸出右手,四指并拢,虎口相交,拇指张开下滑,向受礼者握手。根据有无司机的情况合理安排客户的位置,一般临窗的位置为佳,有前后排的前排位置较好。进入会议室之后,在席位安排上遵循一个原则,靠近门口的不是客户的位置,因而客户的位置是内里不易被打扰的位置,也是视野较为宽阔的位置。

  • 李一桐将不再出演武庚纪

    可是制作方的宣传方式令人失望,主角官宣男主任嘉伦,却没有告知李一桐。让人生气的是有网友询问制作方“魔改神女什么的从武庚纪消失”,制作方回应“大男主,五圣女”,这是直接否认李一桐的存在,李一桐忍无可忍,发布声明辞演《武庚纪》。《武庚纪》漫画是大男主,网上有很多关于魔改的消息,这些流言蜚语对李一桐造成伤害,制作方不仅没有解决问题,还让李一桐陷入尴尬的境地。

  • 怎么和甲方沟通(好好沟通为什么这么难)

    赵洱岽表示,甲方和乙方的沟通,更多是合同约束,非程序决策较多,即一次性的、偶然的、特殊物性的非结构性问题多。萌三只好再三向对方道歉。赵洱岽认为,目标不清晰是造成沟通困境的重要原因。赵洱岽认为,始终牢记目标,对事不对人,保持客观理性,注意换位思考,是沟通中应遵循的原则。与此同时,赵洱岽强调,每个人都有可能成为生活中的甲方、乙方,都应该树立甲方和乙方的意识和心态,进行自我教育和主动反省。

  • 故病在五脏什么意思(是你的五脏六腑生病了)

    02测肝功能肝功能异常,人往往伴随易怒、生气等不良情绪。03测脾功能脾功能异常,人会表现为疲倦乏力、没有食欲、腹胀、腹泻或大便稀溏等。04测肺功能肺受外邪干扰可表现为感冒、咳嗽等症状,肚脐右侧边缘将有明显的压痛。05测肾功能肾虚常常表现为腰膝酸软、健忘、四肢发冷等症状。如果您自我“体检”发现了五脏的疾患,除了就医诊治外,还可以通过捏四边来调理身心。

  • 陈奕迅中文咬字什么水平(陈奕迅竖中指事件还原)

    [闽南网]此前世界杯开幕赛陈奕迅竖中指照片引发网友热议,近日陈奕迅竖中指真相曝光,网友纷纷表示理解陈奕迅。近日世界杯开幕赛陈奕迅惊喜现身,虽然膝盖上有伤,但是他还是在现场秀了一番球技表达自己对足球的爱。据在场的中国歌迷爆料表示,陈奕迅在当时还跟外国球迷和粉丝互爆粗口,情绪非常的差,让不少喜欢他的人感到失望,还称没想到陈奕迅是这样的人。所以很多时候竖中指,就是对此行为的暗示,达到激怒对方的目的。

  • 不矫情不做作的句子(不矫情不做作的句子推荐)

    生活坏到一定程度就会好起来,因为它无法更坏。许多事情,坚持坚持,就过来了。一个萝卜一个坑,说的是婚姻情况。婚姻是键盘,太多秩序和规则;爱情是鼠标,一点就通。男人自比主机,内存最重要;女人好似显示器,一切都看得出来。我从不觉得善良是件好事,因为你给了别人伤害你的资本,可谁不是从一个心地善良的孩子,被现实折磨成一个心机深重的疯子。

  • 有哪些看漫画免费的漫画软件(如果喜欢看漫画)

    如果喜欢看漫画每天分享一个小技巧,不妨关注一下,说不定哪天能用到呢!我头条号什么也没有,只有一些免费的手机电脑实用软件,以及一些解决实际问题的小技巧今天推一款软件,毫不夸张地说,你想看的漫画,这里都有酷克漫画酷克漫。

  • 4月6日北京千灵山公园景区索道临时停运公告

    4月6日北京千灵山公园景区索道临时停运公告尊敬的市民游客朋友:4月6日因丰台区灾害防御中心发布大风蓝色预警,索道临时停运一天。已经提前网络预订门票的游客,可在原渠道进行退订,由此给您带来的不便,敬请谅解。北京千灵山公园2023年4月6日景区地址:北京市丰台区王佐镇西庄店村北1交通路线:356区间;321路、339路、983路、951路、458路、459路公交云岗下车,换乘550路、356路区间;