Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import 'dart:async';
- import 'package:BlackHole/playlist.dart';
- import 'package:http/http.dart';
- import 'dart:math';
- import 'dart:convert';
- import 'package:des_plugin/des_plugin.dart';
- import 'package:audio_service/audio_service.dart';
- import 'package:audio_session/audio_session.dart';
- import 'package:flutter/foundation.dart';
- import 'package:flutter/material.dart';
- import 'package:just_audio/just_audio.dart';
- import 'package:rxdart/rxdart.dart';
- class TempScreen extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return Container(
- decoration: BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- // stops: [0, 0.2, 0.8, 1],
- colors: Theme.of(context).brightness == Brightness.dark
- ? [
- Colors.grey[850],
- Colors.grey[900],
- Colors.black,
- ]
- : [
- Colors.white,
- Theme.of(context).canvasColor,
- ],
- ),
- ),
- child: Scaffold(
- backgroundColor: Colors.transparent,
- // appBar: AppBar(
- // title: Text('Now Playing'),
- // centerTitle: true,
- // ),
- body: SafeArea(
- child: SingleChildScrollView(
- child: StreamBuilder<bool>(
- stream: AudioService.runningStream,
- builder: (context, snapshot) {
- if (snapshot.connectionState != ConnectionState.active) {
- return SizedBox();
- }
- final running = snapshot.data ?? false;
- return Column(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- if (!running) ...[
- audioPlayerButton(),
- ] else ...[
- Container(
- height: MediaQuery.of(context).size.height * 0.0725,
- child: Row(
- children: [
- IconButton(
- icon: Icon(Icons.keyboard_arrow_down_rounded),
- onPressed: () {
- Navigator.pop(context);
- }),
- ],
- ),
- ),
- Container(
- height: MediaQuery.of(context).size.width * 0.925,
- child: Hero(
- tag: 'image',
- child: Card(
- elevation: 10,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(15)),
- clipBehavior: Clip.antiAlias,
- child: Stack(
- children: [
- Image(
- image: AssetImage('assets/cover.jpg'),
- height:
- MediaQuery.of(context).size.width * 0.925,
- ),
- StreamBuilder<QueueState>(
- stream: _queueStateStream,
- builder: (context, snapshot) {
- final queueState = snapshot.data;
- // final queue = queueState?.queue ?? [];
- final mediaItem = queueState?.mediaItem;
- return mediaItem == null
- ? Image(
- image: AssetImage(
- 'assets/cover.jpg'),
- height: MediaQuery.of(context)
- .size
- .width *
- 0.925,
- )
- : Image(
- image: NetworkImage(
- mediaItem.artUri),
- height: MediaQuery.of(context)
- .size
- .width *
- 0.925,
- );
- }),
- ],
- ),
- ),
- ),
- ),
- //Title and subtitle
- Container(
- height: (MediaQuery.of(context).size.height * 0.9 -
- MediaQuery.of(context).size.width * 0.925) /
- 3,
- child: Padding(
- padding: const EdgeInsets.fromLTRB(15, 25, 15, 0),
- child: StreamBuilder<QueueState>(
- stream: _queueStateStream,
- builder: (context, snapshot) {
- final queueState = snapshot.data;
- final mediaItem = queueState?.mediaItem;
- return Column(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [
- // Title container
- Container(
- height:
- (MediaQuery.of(context).size.height *
- 0.9 -
- MediaQuery.of(context)
- .size
- .width *
- 0.925) *
- 2 /
- 14.0,
- child: FittedBox(
- child: Text(
- (mediaItem?.title != null)
- ? (mediaItem.title)
- : 'Unknown',
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 50,
- fontWeight: FontWeight.bold,
- color:
- Theme.of(context).accentColor),
- )),
- ),
- //Subtitle container
- Container(
- height:
- (MediaQuery.of(context).size.height *
- 0.9 -
- MediaQuery.of(context)
- .size
- .width *
- 0.925) *
- 1 /
- 16.0,
- child: Text(
- (mediaItem?.artist != null)
- ? (mediaItem.artist)
- : 'Unknown',
- textAlign: TextAlign.center,
- style: TextStyle(
- fontWeight: FontWeight.w500),
- overflow: TextOverflow.ellipsis,
- ),
- ),
- ],
- );
- }),
- ),
- ),
- //Seekbar starts from here
- Container(
- height: (MediaQuery.of(context).size.height * 0.9 -
- MediaQuery.of(context).size.width * 0.925) /
- 3.5,
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- StreamBuilder<MediaState>(
- stream: _mediaStateStream,
- builder: (context, snapshot) {
- final mediaState = snapshot.data;
- return SeekBar(
- duration: mediaState?.mediaItem?.duration ??
- Duration.zero,
- position:
- mediaState?.position ?? Duration.zero,
- onChangeEnd: (newPosition) {
- AudioService.seekTo(newPosition);
- },
- );
- },
- ),
- ],
- ),
- ),
- Container(
- height: (MediaQuery.of(context).size.height * 0.9 -
- MediaQuery.of(context).size.width * 0.925) /
- 3,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- StreamBuilder<QueueState>(
- stream: _queueStateStream,
- builder: (context, snapshot) {
- final queueState = snapshot.data;
- final queue = queueState?.queue ?? [];
- final mediaItem = queueState?.mediaItem;
- return (queue != null && queue.isNotEmpty)
- ? IconButton(
- icon: Icon(Icons.skip_previous_rounded),
- iconSize: 45.0,
- onPressed: mediaItem == queue.first
- ? null
- : AudioService.skipToPrevious,
- )
- : IconButton(
- icon: Icon(Icons.skip_previous_rounded),
- iconSize: 45.0,
- onPressed: null);
- },
- ),
- Stack(
- children: [
- Center(
- child: StreamBuilder<AudioProcessingState>(
- stream: AudioService.playbackStateStream
- .map((state) => state.processingState)
- .distinct(),
- builder: (context, snapshot) {
- final processingState = snapshot.data ??
- AudioProcessingState.none;
- return describeEnum(processingState) !=
- 'ready'
- ? SizedBox(
- height: 65,
- width: 65,
- child:
- CircularProgressIndicator(),
- )
- : SizedBox();
- },
- ),
- ),
- Center(
- child: StreamBuilder<bool>(
- stream: AudioService.playbackStateStream
- .map((state) => state.playing)
- .distinct(),
- builder: (context, snapshot) {
- final playing = snapshot.data ?? false;
- return Container(
- height: 65,
- width: 65,
- child: Center(
- child: SizedBox(
- height: 59,
- width: 59,
- child: playing
- ? pauseButton()
- : playButton(),
- ),
- ),
- );
- },
- ),
- ),
- ],
- ),
- // Queue display/controls.
- StreamBuilder<QueueState>(
- stream: _queueStateStream,
- builder: (context, snapshot) {
- final queueState = snapshot.data;
- final queue = queueState?.queue ?? [];
- final mediaItem = queueState?.mediaItem;
- return (queue != null && queue.isNotEmpty)
- ? IconButton(
- icon: Icon(Icons.skip_next_rounded),
- iconSize: 45.0,
- onPressed: mediaItem == queue.last
- ? null
- : AudioService.skipToNext,
- )
- : IconButton(
- icon: Icon(Icons.skip_next_rounded),
- iconSize: 45.0,
- onPressed: null);
- },
- ),
- ],
- ),
- ),
- ],
- ],
- );
- },
- ),
- ),
- ),
- ),
- );
- }
- /// A stream reporting the combined state of the current media item and its
- /// current position.
- Stream<MediaState> get _mediaStateStream =>
- Rx.combineLatest2<MediaItem, Duration, MediaState>(
- AudioService.currentMediaItemStream,
- AudioService.positionStream,
- (mediaItem, position) => MediaState(mediaItem, position));
- /// A stream reporting the combined state of the current queue and the current
- /// media item within that queue.
- Stream<QueueState> get _queueStateStream =>
- Rx.combineLatest2<List<MediaItem>, MediaItem, QueueState>(
- AudioService.queueStream,
- AudioService.currentMediaItemStream,
- (queue, mediaItem) => QueueState(queue, mediaItem));
- audioPlayerButton() {
- AudioService.start(
- backgroundTaskEntrypoint: _audioPlayerTaskEntrypoint,
- androidNotificationChannelName: 'Audio Service Demo',
- androidNotificationColor: 0xFF2196f3,
- androidNotificationIcon: 'mipmap/ic_launcher',
- androidEnableQueue: true,
- );
- List<MediaItem> queue = MediaLibrary().items;
- print('queue is $queue');
- AudioService.updateQueue(queue);
- AudioService.setRepeatMode(AudioServiceRepeatMode.none);
- AudioService.setShuffleMode(AudioServiceShuffleMode.none);
- AudioService.play();
- }
- FloatingActionButton playButton() => FloatingActionButton(
- elevation: 10,
- child: Icon(
- Icons.play_arrow_rounded,
- size: 40.0,
- color: Colors.white,
- ),
- onPressed: AudioService.play,
- );
- FloatingActionButton pauseButton() => FloatingActionButton(
- elevation: 10,
- child: Icon(
- Icons.pause_rounded,
- color: Colors.white,
- size: 40.0,
- ),
- onPressed: AudioService.pause,
- );
- // IconButton stopButton() => IconButton(
- // icon: Icon(Icons.stop),
- // iconSize: 64.0,
- // onPressed: AudioService.stop,
- // );
- }
- class QueueState {
- final List<MediaItem> queue;
- final MediaItem mediaItem;
- QueueState(this.queue, this.mediaItem);
- }
- class MediaState {
- final MediaItem mediaItem;
- final Duration position;
- MediaState(this.mediaItem, this.position);
- }
- class SeekBar extends StatefulWidget {
- final Duration duration;
- final Duration position;
- final ValueChanged<Duration> onChanged;
- final ValueChanged<Duration> onChangeEnd;
- SeekBar({
- @required this.duration,
- @required this.position,
- this.onChanged,
- this.onChangeEnd,
- });
- @override
- _SeekBarState createState() => _SeekBarState();
- }
- class _SeekBarState extends State<SeekBar> {
- double _dragValue;
- bool _dragging = false;
- @override
- Widget build(BuildContext context) {
- final value = min(_dragValue ?? widget.position?.inMilliseconds?.toDouble(),
- widget.duration.inMilliseconds.toDouble());
- if (_dragValue != null && !_dragging) {
- _dragValue = null;
- }
- return Column(
- children: [
- Slider(
- min: 0.0,
- max: widget.duration.inMilliseconds.toDouble(),
- value: value,
- activeColor: Theme.of(context).accentColor,
- inactiveColor: Theme.of(context).backgroundColor,
- onChanged: (value) {
- if (!_dragging) {
- _dragging = true;
- }
- setState(() {
- _dragValue = value;
- });
- if (widget.onChanged != null) {
- widget.onChanged(Duration(milliseconds: value.round()));
- }
- },
- onChangeEnd: (value) {
- if (widget.onChangeEnd != null) {
- widget.onChangeEnd(Duration(milliseconds: value.round()));
- }
- _dragging = false;
- },
- ),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Padding(
- padding: const EdgeInsets.only(left: 20.0),
- child: Text(
- RegExp(r'((^0*[1-9]\d*:)?\d{2}:\d{2})\.\d+$')
- .firstMatch("$_position")
- ?.group(1) ??
- '$_position',
- // style: Theme.of(context).textTheme.caption,
- ),
- ),
- Padding(
- padding: const EdgeInsets.only(right: 20.0),
- child: Text(
- RegExp(r'((^0*[1-9]\d*:)?\d{2}:\d{2})\.\d+$')
- .firstMatch("$_duration")
- ?.group(1) ??
- '$_duration',
- // style: Theme.of(context).textTheme.caption,
- ),
- ),
- ],
- ),
- ],
- );
- }
- // Duration get _remaining => widget.duration - widget.position;
- Duration get _position => widget.position;
- Duration get _duration => widget.duration;
- }
- void _audioPlayerTaskEntrypoint() async {
- AudioServiceBackground.run(() => AudioPlayerTask());
- }
- class AudioPlayerTask extends BackgroundAudioTask {
- final _mediaLibrary = MediaLibrary();
- AudioPlayer _player = new AudioPlayer();
- // AudioProcessingState _skipState;
- Seeker _seeker;
- StreamSubscription<PlaybackEvent> _eventSubscription;
- String kUrl = '';
- String key = "38346591";
- String decrypt = "";
- String preferredQuality = '320';
- List<MediaItem> get queue => _mediaLibrary.items;
- int get index => _player.currentIndex == null ? 0 : _player.currentIndex;
- MediaItem get mediaItem => index == null ? null : queue[index];
- fetchSongUrl(songId) async {
- print('starting fetching url');
- String songUrl =
- "https://www.jiosaavn.com/api.php?app_version=5.18.3&api_version=4&readable_version=5.18.3&v=79&_format=json&__call=song.getDetails&pids=" +
- songId;
- var res = await get(songUrl, headers: {"Accept": "application/json"});
- var resEdited = (res.body).split("-->");
- var getMain = jsonDecode(resEdited[1]);
- kUrl = await DesPlugin.decrypt(
- key, getMain[songId]["more_info"]["encrypted_media_url"]);
- kUrl = kUrl.replaceAll('96', '$preferredQuality');
- print('fetched url');
- return kUrl;
- }
- @override
- Future<void> onStart(Map<String, dynamic> params) async {
- final session = await AudioSession.instance;
- await session.configure(AudioSessionConfiguration.speech());
- // Broadcast media item changes.
- _player.currentIndexStream.listen((index) {
- if (index != null) AudioServiceBackground.setMediaItem(queue[index]);
- });
- // Propagate all events from the audio player to AudioService clients.
- _eventSubscription = _player.playbackEventStream.listen((event) {
- _broadcastState();
- });
- // Special processing for state transitions.
- _player.processingStateStream.listen((state) {
- switch (state) {
- case ProcessingState.completed:
- // In this example, the service stops when reaching the end.
- // onStop();
- onSkipToQueueItem(queue[index + 1].id);
- break;
- case ProcessingState.ready:
- // If we just came from skipping between tracks, clear the skip
- // state now that we're ready to play.
- // _skipState = null;
- break;
- default:
- break;
- }
- });
- // Load and broadcast the queue
- // AudioServiceBackground.setQueue(queue);
- print('queue is');
- print(queue);
- print('Index is $index');
- print('MediaItem is');
- print(queue[index]);
- // print(queue[index + 1]);
- try {
- if (queue[index].extras == null) {
- queue[index] = queue[index].copyWith(extras: {
- 'URL': await fetchSongUrl(queue[index].id),
- });
- }
- await AudioServiceBackground.setQueue(queue);
- await _player.setUrl(queue[index].extras['URL']);
- onPlay();
- // await _player.setAudioSource(ConcatenatingAudioSource(
- // children:
- // queue.map((item) => AudioSource.uri(Uri.parse(item.id))).toList(),
- // ));
- // onPlay();
- } catch (e) {
- print("Error: $e");
- onStop();
- }
- }
- @override
- Future<void> onSkipToQueueItem(String mediaId) async {
- // Then default implementations of onSkipToNext and onSkipToPrevious will
- // delegate to this method.
- final newIndex = queue.indexWhere((item) => item.id == mediaId);
- if (newIndex == -1) return;
- _player.pause();
- if (queue[newIndex].extras == null) {
- queue[newIndex] = queue[newIndex].copyWith(extras: {
- 'URL': await fetchSongUrl(queue[newIndex].id),
- });
- await AudioServiceBackground.setQueue(queue);
- // AudioService.updateQueue(queue);
- }
- await _player.setUrl(queue[newIndex].extras['URL']);
- _player.play();
- await AudioServiceBackground.setMediaItem(queue[newIndex]);
- // _skipState = newIndex > index
- // ? AudioProcessingState.skippingToNext
- // : AudioProcessingState.skippingToPrevious;
- // This jumps to the beginning of the queue item at newIndex.
- // _player.seek(Duration.zero, index: newIndex);
- // Demonstrate custom events.
- // AudioServiceBackground.sendCustomEvent('skip to $newIndex');
- }
- @override
- Future<void> onUpdateQueue(List<MediaItem> queue) {
- AudioServiceBackground.setQueue(queue = queue);
- return super.onUpdateQueue(queue);
- }
- @override
- Future<void> onPlay() => _player.play();
- @override
- Future<void> onPause() => _player.pause();
- @override
- Future<void> onSeekTo(Duration position) => _player.seek(position);
- @override
- Future<void> onFastForward() => _seekRelative(fastForwardInterval);
- @override
- Future<void> onRewind() => _seekRelative(-rewindInterval);
- @override
- Future<void> onSeekForward(bool begin) async => _seekContinuously(begin, 1);
- @override
- Future<void> onSeekBackward(bool begin) async => _seekContinuously(begin, -1);
- @override
- Future<void> onStop() async {
- await _player.dispose();
- _eventSubscription.cancel();
- // It is important to wait for this state to be broadcast before we shut
- // down the task. If we don't, the background task will be destroyed before
- // the message gets sent to the UI.
- await _broadcastState();
- // Shut down this task
- await super.onStop();
- }
- /// Jumps away from the current position by [offset].
- Future<void> _seekRelative(Duration offset) async {
- var newPosition = _player.position + offset;
- // Make sure we don't jump out of bounds.
- if (newPosition < Duration.zero) newPosition = Duration.zero;
- if (newPosition > mediaItem.duration) newPosition = mediaItem.duration;
- // Perform the jump via a seek.
- await _player.seek(newPosition);
- }
- /// Begins or stops a continuous seek in [direction]. After it begins it will
- /// continue seeking forward or backward by 10 seconds within the audio, at
- /// intervals of 1 second in app time.
- void _seekContinuously(bool begin, int direction) {
- _seeker?.stop();
- if (begin) {
- _seeker = Seeker(_player, Duration(seconds: 10 * direction),
- Duration(seconds: 1), mediaItem)
- ..start();
- }
- }
- /// Broadcasts the current state to all clients.
- Future<void> _broadcastState() async {
- await AudioServiceBackground.setState(
- controls: [
- MediaControl.skipToPrevious,
- if (_player.playing) MediaControl.pause else MediaControl.play,
- MediaControl.stop,
- MediaControl.skipToNext,
- ],
- systemActions: [
- MediaAction.seekTo,
- MediaAction.seekForward,
- MediaAction.seekBackward,
- ],
- androidCompactActions: [0, 1, 3],
- processingState: _getProcessingState(),
- playing: _player.playing,
- position: _player.position,
- bufferedPosition: _player.bufferedPosition,
- speed: _player.speed,
- );
- }
- /// Maps just_audio's processing state into into audio_service's playing
- /// state. If we are in the middle of a skip, we use [_skipState] instead.
- AudioProcessingState _getProcessingState() {
- // if (_skipState != null) return _skipState;
- switch (_player.processingState) {
- case ProcessingState.idle:
- return AudioProcessingState.stopped;
- case ProcessingState.loading:
- return AudioProcessingState.connecting;
- case ProcessingState.buffering:
- return AudioProcessingState.buffering;
- case ProcessingState.ready:
- return AudioProcessingState.ready;
- case ProcessingState.completed:
- return AudioProcessingState.completed;
- default:
- throw Exception("Invalid state: ${_player.processingState}");
- }
- }
- }
- /// Provides access to a library of media items. In your app, this could come
- /// from a database or web service.
- class MediaLibrary {
- // final _items = nowPlaying
- // .map((e) => MediaItem(
- // id: e['id'],
- // album: e['more_info']['album'],
- // title: e['title'],
- // artist: e['subtitle'],
- // artUri: e['image'],
- // duration: Duration(seconds: int.parse(e['more_info']['duration'])),
- // ))
- // .toList();
- // [
- // MediaItem(
- // id: "S3dGvXSb",
- // title: "Señorita",
- // artist: "Shawn Mendes, Camila Cabello",
- // album: "Señorita",
- // artUri:
- // "https://c.saavncdn.com/624/Se-orita-English-2019-20190822022326-500x500.jpg",
- // duration: Duration(seconds: 242),
- // ),
- // MediaItem(
- // id: "SEX82677",
- // title: "Past Life",
- // artist: "Trevor Daniel, Selena Gomez",
- // album: "Past Life",
- // artUri:
- // "https://c.saavncdn.com/022/Past-Life-English-2020-20200626000840-500x500.jpg",
- // duration: Duration(seconds: 242),
- // ),
- // MediaItem(
- // id: "v6NrwMud",
- // album: "Faraar",
- // title: "Faraar",
- // artist: "Akull",
- // duration: Duration(minutes: 3),
- // artUri:
- // "https://c.saavncdn.com/660/Faraar-Hindi-2021-20210113053337-500x500.jpg",
- // ),
- // ];
- MediaItem temp(e) {
- return MediaItem(
- id: e['id'],
- album: e['more_info']['album'],
- title: e['title'],
- artist: e['subtitle'],
- artUri: e['image'],
- duration: Duration(seconds: int.parse(e['more_info']['duration'])),
- );
- }
- List<MediaItem> get items {
- // nowPlaying is the json response conveted into list of dictionaries which contain all details.
- final _items = nowPlaying.map((e) => temp(e)).toList();
- print('The items setted are $_items');
- return _items;
- }
- }
- /// An object that performs interruptable sleep.
- class Sleeper {
- Completer _blockingCompleter;
- /// Sleep for a duration. If sleep is interrupted, a
- /// [SleeperInterruptedException] will be thrown.
- Future<void> sleep([Duration duration]) async {
- _blockingCompleter = Completer();
- if (duration != null) {
- await Future.any([Future.delayed(duration), _blockingCompleter.future]);
- } else {
- await _blockingCompleter.future;
- }
- final interrupted = _blockingCompleter.isCompleted;
- _blockingCompleter = null;
- if (interrupted) {
- throw SleeperInterruptedException();
- }
- }
- /// Interrupt any sleep that's underway.
- void interrupt() {
- if (_blockingCompleter?.isCompleted == false) {
- _blockingCompleter.complete();
- }
- }
- }
- class SleeperInterruptedException {}
- class Seeker {
- final AudioPlayer player;
- final Duration positionInterval;
- final Duration stepInterval;
- final MediaItem mediaItem;
- bool _running = false;
- Seeker(
- this.player,
- this.positionInterval,
- this.stepInterval,
- this.mediaItem,
- );
- start() async {
- _running = true;
- while (_running) {
- Duration newPosition = player.position + positionInterval;
- if (newPosition < Duration.zero) newPosition = Duration.zero;
- if (newPosition > mediaItem.duration) newPosition = mediaItem.duration;
- player.seek(newPosition);
- await Future.delayed(stepInterval);
- }
- }
- stop() {
- _running = false;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement