-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add simple music support and wavlake support
- Loading branch information
1 parent
047dc04
commit 6e3d8ca
Showing
12 changed files
with
533 additions
and
1 deletion.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import 'package:crypto/crypto.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:nostrmo/component/cust_state.dart'; | ||
import 'package:nostrmo/component/music/music_component.dart'; | ||
import 'package:nostrmo/component/music/music_info_builder.dart'; | ||
import 'package:nostrmo/consts/base.dart'; | ||
import 'package:nostrmo/main.dart'; | ||
import 'package:nostrmo/provider/music_provider.dart'; | ||
import 'package:nostrmo/util/encrypt_util.dart'; | ||
import 'package:nostrmo/util/hash_util.dart'; | ||
|
||
class ContentMusicComponent extends StatefulWidget { | ||
String? eventId; | ||
|
||
String content; | ||
|
||
MusicInfoBuilder musicInfoBuilder; | ||
|
||
ContentMusicComponent(this.eventId, this.content, this.musicInfoBuilder); | ||
|
||
@override | ||
State<StatefulWidget> createState() { | ||
return _ContentMusicComponent(); | ||
} | ||
} | ||
|
||
class _ContentMusicComponent extends CustState<ContentMusicComponent> { | ||
MusicInfo? musicInfo; | ||
|
||
@override | ||
Widget doBuild(BuildContext context) { | ||
if (musicInfo == null) { | ||
return Container(); | ||
} | ||
|
||
return Container( | ||
margin: const EdgeInsets.only( | ||
top: Base.BASE_PADDING_HALF, | ||
bottom: Base.BASE_PADDING_HALF, | ||
), | ||
child: MusicComponent( | ||
musicInfo!, | ||
key: Key(HashUtil.md5(musicInfo!.sourceUrl!)), | ||
), | ||
); | ||
} | ||
|
||
@override | ||
Future<void> onReady(BuildContext context) async { | ||
musicInfo = musicInfoCache.get(widget.content); | ||
musicInfo ??= | ||
await widget.musicInfoBuilder.build(widget.content, widget.eventId); | ||
if (musicInfo != null) { | ||
musicInfoCache.put(widget.content, musicInfo!); | ||
} | ||
setState(() {}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter/widgets.dart'; | ||
import 'package:nostrmo/component/webview_router.dart'; | ||
import 'package:nostrmo/consts/base.dart'; | ||
import 'package:nostrmo/main.dart'; | ||
import 'package:nostrmo/util/duartion_tool.dart'; | ||
import 'package:provider/provider.dart'; | ||
|
||
import '../../provider/music_provider.dart'; | ||
import '../image_component.dart'; | ||
|
||
class MusicComponent extends StatefulWidget { | ||
MusicInfo musicInfo; | ||
|
||
bool clearAble; | ||
|
||
MusicComponent(this.musicInfo, {super.key, this.clearAble = false}); | ||
|
||
@override | ||
State<StatefulWidget> createState() { | ||
return _MusicComponent(); | ||
} | ||
} | ||
|
||
class _MusicComponent extends State<MusicComponent> { | ||
@override | ||
Widget build(BuildContext context) { | ||
var themeData = Theme.of(context); | ||
var cardColor = themeData.cardColor; | ||
var titleFontSize = themeData.textTheme.bodyMedium!.fontSize; | ||
var nameFontSize = themeData.textTheme.bodySmall!.fontSize; | ||
var hintColor = themeData.hintColor; | ||
|
||
var subInfoTextStyle = TextStyle( | ||
fontSize: nameFontSize, | ||
fontWeight: FontWeight.w500, | ||
color: hintColor, | ||
); | ||
|
||
var _musicProvider = Provider.of<MusicProvider>(context); | ||
var currentDuration = _musicProvider.currentDuration; | ||
var currentPosition = _musicProvider.currentPosition; | ||
bool isCurrent = false; | ||
if (_musicProvider.musicInfo != null && | ||
_musicProvider.musicInfo!.sourceUrl == widget.musicInfo.sourceUrl) { | ||
isCurrent = true; | ||
} | ||
|
||
var imageHeight = (titleFontSize! + nameFontSize!) * 1.7; | ||
|
||
var imageWidget = ImageComponent( | ||
imageUrl: widget.musicInfo.imageUrl, | ||
); | ||
|
||
var btnIcon = Icons.play_circle_outline; | ||
if (isCurrent && _musicProvider.isPlaying) { | ||
btnIcon = Icons.pause_circle_outline; | ||
} | ||
|
||
List<Widget> musicSubInfos = [ | ||
Container( | ||
margin: const EdgeInsets.only(right: Base.BASE_PADDING_HALF), | ||
child: Image.asset( | ||
widget.musicInfo.icon, | ||
width: 18, | ||
height: 18, | ||
), | ||
), | ||
Text( | ||
widget.musicInfo.name, | ||
style: subInfoTextStyle, | ||
), | ||
]; | ||
if (isCurrent && | ||
currentDuration != null && | ||
currentPosition != null && | ||
currentPosition.inSeconds > 0 && | ||
currentDuration.inSeconds > 0) { | ||
musicSubInfos.add(Text( | ||
" • ${currentPosition.prettyDuration()} / ${currentDuration.prettyDuration()}", | ||
style: subInfoTextStyle, | ||
)); | ||
} | ||
|
||
List<Widget> topList = [ | ||
Container( | ||
width: imageHeight, | ||
height: imageHeight, | ||
child: imageWidget, | ||
), | ||
Expanded( | ||
child: Container( | ||
padding: const EdgeInsets.only( | ||
left: Base.BASE_PADDING, | ||
right: Base.BASE_PADDING, | ||
), | ||
child: Column( | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
mainAxisSize: MainAxisSize.min, | ||
children: [ | ||
Text( | ||
widget.musicInfo.title, | ||
style: TextStyle( | ||
fontSize: titleFontSize, | ||
fontWeight: FontWeight.w500, | ||
), | ||
maxLines: 1, | ||
overflow: TextOverflow.ellipsis, | ||
), | ||
Row( | ||
children: musicSubInfos, | ||
), | ||
], | ||
), | ||
)), | ||
Container( | ||
width: imageHeight, | ||
height: imageHeight, | ||
child: GestureDetector( | ||
onTap: () { | ||
if (isCurrent) { | ||
musicProvider.playOrPause(); | ||
} else { | ||
musicProvider.play(widget.musicInfo); | ||
} | ||
}, | ||
child: Icon(btnIcon), | ||
), | ||
), | ||
]; | ||
if (widget.clearAble) { | ||
topList.add(Container( | ||
width: imageHeight, | ||
height: imageHeight, | ||
child: GestureDetector( | ||
onTap: () { | ||
musicProvider.stop(); | ||
}, | ||
child: Icon(Icons.clear), | ||
), | ||
)); | ||
} | ||
|
||
var topWidget = Row( | ||
children: topList, | ||
); | ||
|
||
Widget progressBar = Container(); | ||
if (_musicProvider.isPlaying && isCurrent) { | ||
double? value; | ||
if (currentDuration != null && | ||
currentPosition != null && | ||
currentPosition.inSeconds > 0 && | ||
currentDuration.inSeconds > 0) { | ||
value = currentPosition.inSeconds / currentDuration.inSeconds; | ||
} | ||
progressBar = LinearProgressIndicator( | ||
value: value, | ||
); | ||
} | ||
|
||
return GestureDetector( | ||
onTap: () { | ||
if (widget.musicInfo.sourceUrl != null && | ||
widget.musicInfo.sourceUrl!.indexOf("http") == 0) { | ||
WebViewRouter.open(context, widget.musicInfo.sourceUrl!); | ||
} | ||
}, | ||
child: Container( | ||
color: cardColor, | ||
child: Column( | ||
mainAxisSize: MainAxisSize.min, | ||
children: [ | ||
Container( | ||
height: imageHeight, | ||
child: topWidget, | ||
), | ||
progressBar, | ||
], | ||
), | ||
), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import 'package:nostrmo/provider/music_provider.dart'; | ||
|
||
abstract class MusicInfoBuilder { | ||
bool check(String content); | ||
|
||
Future<MusicInfo?> build(String content, String? eventId); | ||
} |
82 changes: 82 additions & 0 deletions
82
lib/component/music/wavlake/wavlake_track_music_info_builder.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import 'dart:convert'; | ||
import 'dart:developer'; | ||
|
||
import 'package:nostrmo/component/music/music_info_builder.dart'; | ||
import 'package:nostrmo/provider/music_provider.dart'; | ||
import 'package:nostrmo/util/dio_util.dart'; | ||
import 'package:nostrmo/util/spider_util.dart'; | ||
import 'package:nostrmo/util/string_util.dart'; | ||
|
||
WavlakeTrackMusicInfoBuilder wavlakeTrackMusicInfoBuilder = | ||
WavlakeTrackMusicInfoBuilder(); | ||
|
||
class WavlakeTrackMusicInfoBuilder extends MusicInfoBuilder { | ||
static const String prefix = "https://wavlake.com/track/"; | ||
|
||
@override | ||
Future<MusicInfo?> build(String content, String? eventId) async { | ||
// try to fetch info from api | ||
try { | ||
var id = content.replaceAll(prefix, ""); | ||
var jsonObj = await DioUtil.get( | ||
"https://catalog-prod-dot-wavlake-alpha.uc.r.appspot.com/v1/episodes/${id}"); | ||
if (jsonObj != null && jsonObj["success"] == true) { | ||
var name = jsonObj["data"]["podcast"]["name"]; | ||
var title = jsonObj["data"]["title"]; | ||
|
||
var imageUrl = jsonObj["data"]["podcast"]["artworkUrl"]; | ||
var audioUrl = jsonObj["data"]["liveUrl"]; | ||
|
||
return MusicInfo( | ||
"assets/imgs/music/wavlake.png", | ||
eventId, | ||
title, | ||
name, | ||
audioUrl, | ||
"https://wavlake.com/_next/image?url=${Uri.encodeQueryComponent(imageUrl)}&w=256&q=75", | ||
sourceUrl: content); | ||
} | ||
} catch (e) {} | ||
|
||
String? source = await DioUtil.getStr(content); | ||
if (StringUtil.isBlank(source)) { | ||
return null; | ||
} | ||
|
||
String nameAndTitleStr = | ||
SpiderUtil.subUntil(source!, "<title>", "</title>"); | ||
var strs = nameAndTitleStr.split("•"); | ||
if (strs.length < 2) { | ||
return null; | ||
} | ||
var name = strs[0].trim(); | ||
var title = strs[1].trim(); | ||
|
||
String imageUrl = | ||
SpiderUtil.subUntil(source, '<meta property="og:image" content="', '"'); | ||
String audioUrl = | ||
SpiderUtil.subUntil(source, '<meta property="og:audio" content="', '"'); | ||
|
||
if (StringUtil.isBlank(audioUrl) || audioUrl.indexOf("http") != 0) { | ||
return null; | ||
} | ||
|
||
return MusicInfo( | ||
"assets/imgs/music/wavlake.png", | ||
eventId, | ||
title, | ||
name, | ||
audioUrl, | ||
"https://wavlake.com/_next/image?url=${Uri.encodeQueryComponent(imageUrl)}&w=256&q=75", | ||
sourceUrl: content); | ||
} | ||
|
||
@override | ||
bool check(String content) { | ||
if (content.indexOf(prefix) == 0) { | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
} |
Oops, something went wrong.