Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Big Changes + Refactoring #2

Merged
merged 11 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ jobs:
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get install -y libgtk-3-dev pkg-config

- name: Install Unwind (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get install libunwind-dev

- name: Install GStreamer (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev

- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
Expand Down
1 change: 1 addition & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="soundbox"
android:name="${applicationName}"
Expand Down
13 changes: 13 additions & 0 deletions lib/core/extensions/with_gap_y.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';

extension WithGapY on List<Widget> {
List<Widget> withGapY({double height = 15}) {
List<Widget> children = toList();
List<Widget> result = [];
for (var child in children) {
result.add(child);
result.add(SizedBox(height: height));
}
return result;
}
}
File renamed without changes.
12 changes: 12 additions & 0 deletions lib/core/models/lyrics_model.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class Lyrics {
final String lyrics;
final String copyright;

Lyrics({required this.copyright, required this.lyrics});

factory Lyrics.fromJson(Map<String, dynamic> json) {
return Lyrics(
copyright: json['data']['copyright'], lyrics: json['data']['lyrics']);
}

}
File renamed without changes.
14 changes: 8 additions & 6 deletions lib/models/song_model.dart → lib/core/models/song_model.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import 'package:soundbox/models/album_model.dart';
import 'package:soundbox/models/song_download_model.dart';
import 'package:soundbox/models/song_image_model.dart';
import 'package:soundbox/core/models/album_model.dart';
import 'package:soundbox/core/models/song_download_model.dart';
import 'package:soundbox/core/models/song_image_model.dart';

class Song {
String id;
String name;
String? type;
String? lyrics;
Album album;
String year;
String? releaseDate;
String duration;
int duration;
String label;
String primaryArtists;
String primaryArtistsId;
Expand Down Expand Up @@ -37,6 +38,7 @@ class Song {
required this.playCount,
required this.language,
required this.hasLyrics,
this.lyrics,
required this.url,
required this.images,
required this.downloadUrls,
Expand All @@ -50,10 +52,10 @@ class Song {
factory Song.fromJson(Map<String, dynamic> json) {
return Song(
id: json["id"],
name: json["name"],
name: json["name"].toString().replaceAll("&amp;", "&"),
album: Album.fromJson(json["album"]),
year: json["year"],
duration: json["duration"],
duration: int.parse(json["duration"]),
label: json["label"],
primaryArtists: json["primaryArtists"],
primaryArtistsId: json["primaryArtistsId"],
Expand Down
116 changes: 116 additions & 0 deletions lib/core/providers/song_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
import 'package:soundbox/core/models/lyrics_model.dart';
import 'package:soundbox/core/models/song_model.dart';
import 'package:soundbox/services/saavn_api_service.dart';

class SongProvider extends ChangeNotifier {
final _queue = ConcatenatingAudioSource(
useLazyPreparation: true,
children: [],
);
final List<Song> _queueMeta = [];
final AudioPlayer _player = AudioPlayer();
int _currentSongIndex = 0;
bool _isPlaying = false;

AudioPlayer get player => _player;
bool get isPlaying => _isPlaying;
Song? get currentSong =>
_queueMeta.isEmpty ? null : _queueMeta[_currentSongIndex];
List<Song>? get currentQueue => _queueMeta;
ConcatenatingAudioSource get queue => _queue;
int get currentSongIndex => _currentSongIndex;

SongProvider() {
_player.playingStream.listen((state) {
_isPlaying = state;
});
_player.currentIndexStream.listen((index) {
if (index != null) {
_currentSongIndex = index;
notifyListeners();
}
});
}

Future<void> handlePausePlay() async {
if (_player.playing) {
await _player.pause();
} else {
await _player.play();
}
notifyListeners();
}

Future<void> setAudioSource() async {
await _player.setAudioSource(_queue,
initialIndex: 0, initialPosition: Duration.zero);
}

Future<void> setCurrentSong(Song song) async {
int c = _currentSongIndex;
if (_queue.length == 0) c = -1;
await _queue.insert(
c + 1, AudioSource.uri(Uri.parse(song.downloadUrls.last.link)));

if (_queueMeta.isEmpty) await setAudioSource();
_queueMeta.insert(c + 1, song);
notifyListeners();
await Future.delayed(Durations.medium1);
playNext();
}

Future<void> addToQueue(Song song) async {
await _queue.add(AudioSource.uri(Uri.parse(song.downloadUrls.last.link)));
if (_queueMeta.isEmpty) await setAudioSource();
_queueMeta.add(song);
notifyListeners();
}

Future<void> seekToPoint(int seconds) async {
Duration currentDuration = _player.position;
await _player.seek(Duration(seconds: seconds + currentDuration.inSeconds));
notifyListeners();
}

Future<void> playPrevious() async {
Duration currentDuration = _player.position;
if (currentDuration.inSeconds < 2) {
await _player.seekToPrevious();
notifyListeners();
} else {
seekToPoint(-currentDuration.inSeconds);
}
}

Future<void> playNext() async {
await _player.seekToNext();
if (!_player.playing) await _player.play();
notifyListeners();
}

Stream<Duration> positionStream() {
return _player.createPositionStream(
maxPeriod: const Duration(milliseconds: 20),
minPeriod: const Duration(milliseconds: 20),
);
}

Future<void> getLyrics() async {
Song song = _queueMeta[_currentSongIndex];
if (song.lyrics == null) {
if (song.hasLyrics == false) {
_queueMeta[_currentSongIndex].lyrics = 'No Lyrics Found';
} else {
Lyrics? lyrics = await SaavnApiService().getLyrics(song.id);
if (lyrics == null) {
_queueMeta[_currentSongIndex].lyrics = 'No Lyrics Found';
} else {
_queueMeta[_currentSongIndex].lyrics = lyrics.lyrics;
}
}
notifyListeners();
}
}
}
14 changes: 12 additions & 2 deletions lib/theme_provider.dart → lib/core/providers/theme_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ class ThemeProvider extends ChangeNotifier {
}

ThemeData themeData(Brightness brightness) {
return ThemeData(
ThemeData theme = ThemeData(
sliderTheme: SliderThemeData(
overlayShape: SliderComponentShape.noOverlay,
trackHeight: 40,
inactiveTrackColor: Colors.black,
trackShape: const RectangularSliderTrackShape(),
thumbShape: SliderComponentShape.noThumb,
),
brightness: brightness,
fontFamily: GoogleFonts.poppins().fontFamily,
textTheme: TextTheme(
Expand All @@ -28,6 +35,9 @@ ThemeData themeData(Brightness brightness) {
fontWeight: FontWeight.w700,
),
),

);
theme = theme.copyWith(
sliderTheme: theme.sliderTheme
.copyWith(inactiveTrackColor: theme.colorScheme.background));
return theme;
}
44 changes: 0 additions & 44 deletions lib/home/home_page.dart

This file was deleted.

7 changes: 5 additions & 2 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:soundbox/home/home_page.dart';
import 'package:soundbox/theme_provider.dart';
import 'package:soundbox/pages/home/home_page.dart';
import 'package:soundbox/core/providers/song_provider.dart';
import 'package:soundbox/core/providers/theme_provider.dart';

void main(List<String> args) {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}

Expand All @@ -13,6 +15,7 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MultiProvider(providers: [
ChangeNotifierProvider(create: (_) => ThemeProvider()),
ChangeNotifierProvider(create: (_) => SongProvider()),
], child: const Root());
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:soundbox/theme_provider.dart';
import 'package:soundbox/core/providers/theme_provider.dart';

class HomeAppBar extends StatelessWidget {
const HomeAppBar({
Expand Down
64 changes: 64 additions & 0 deletions lib/pages/home/home_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import 'package:flutter/material.dart';
import 'package:soundbox/core/extensions/with_gap_y.dart';
import 'package:soundbox/pages/home/home_app_bar.dart';
import 'package:soundbox/pages/home/home_queue_list.dart';
import 'package:soundbox/pages/search/search_bar.dart';
import 'package:soundbox/widgets/song_control_bar.dart';

class HomePage extends StatefulWidget {
const HomePage({super.key});

@override
State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
Widget drawer = const Drawer();

void setDrawer(Widget newDrawer) {
setState(() {
drawer = newDrawer;
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
drawer: drawer,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(10),
child: Stack(
children: [
SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const HomeAppBar(),
SongSearchBar(setDrawer: setDrawer),
const HomeQueueList()
].withGapY(height: 10),
),
),
Align(
alignment: Alignment.bottomCenter,
child: SongControleBar(setDrawer: setDrawer))
],
),
),
),
);
}
}


/*
Self host the API
Cache recently searched songs' results
Create artist page - visible by clicking on their name under a song or maybe even by searching(?)
Add functionality to add to favourites
Add functionality to create playlist
Add functionality to play the song
Add functionality to show the lyrics
Add functionality to generate playlists based on interest using ML
*/
Loading
Loading