Skip to content

Commit

Permalink
Merge pull request #2 from lilmistake/lilmistake-dev [skip CI]
Browse files Browse the repository at this point in the history
Big Changes + Refactoring
  • Loading branch information
lilmistake authored Dec 28, 2023
2 parents cf6a2c7 + 26419f9 commit 1198eef
Show file tree
Hide file tree
Showing 31 changed files with 993 additions and 129 deletions.
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.
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

0 comments on commit 1198eef

Please sign in to comment.