Skip to content

Commit

Permalink
feat(lyrics): use official spotify API for fetching lyrics and add zo…
Browse files Browse the repository at this point in the history
…om controls
  • Loading branch information
KRTirtho committed Mar 4, 2023
1 parent bd48ca4 commit 10d0660
Show file tree
Hide file tree
Showing 9 changed files with 306 additions and 113 deletions.
2 changes: 2 additions & 0 deletions lib/collections/spotube_icons.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,6 @@ abstract class SpotubeIcons {
static const minimize = FeatherIcons.chevronDown;
static const personalized = FeatherIcons.star;
static const genres = FeatherIcons.music;
static const zoomIn = FeatherIcons.zoomIn;
static const zoomOut = FeatherIcons.zoomOut;
}
54 changes: 54 additions & 0 deletions lib/components/lyrics/zoom_controls.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/spotube_icons.dart';

class ZoomControls extends HookWidget {
final int value;
final ValueChanged<int> onChanged;
final int min;
final int max;

const ZoomControls({
Key? key,
required this.value,
required this.onChanged,
this.min = 50,
this.max = 200,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: PlatformTheme.of(context)
.secondaryBackgroundColor
?.withOpacity(0.7),
borderRadius: BorderRadius.circular(10),
),
constraints: const BoxConstraints(maxHeight: 50),
margin: const EdgeInsets.all(8),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
PlatformIconButton(
icon: const Icon(SpotubeIcons.zoomOut),
onPressed: () {
if (value == min) return;
onChanged(value - 10);
},
),
PlatformText("$value%"),
PlatformIconButton(
icon: const Icon(SpotubeIcons.zoomIn),
onPressed: () {
if (value == max) return;
onChanged(value + 10);
},
),
],
),
);
}
}
34 changes: 34 additions & 0 deletions lib/models/lyrics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ class SubtitleSimple {
required this.lyrics,
required this.rating,
});

factory SubtitleSimple.fromJson(Map<String, dynamic> json) {
return SubtitleSimple(
uri: Uri.parse(json["uri"] as String),
name: json["name"] as String,
lyrics: (json["lyrics"] as List<dynamic>)
.map((e) => LyricSlice.fromJson(e as Map<String, dynamic>))
.toList(),
rating: json["rating"] as int,
);
}

Map<String, dynamic> toJson() {
return {
"uri": uri.toString(),
"name": name,
"lyrics": lyrics.map((e) => e.toJson()).toList(),
"rating": rating,
};
}
}

class LyricSlice {
Expand All @@ -17,6 +37,20 @@ class LyricSlice {

LyricSlice({required this.time, required this.text});

factory LyricSlice.fromJson(Map<String, dynamic> json) {
return LyricSlice(
time: Duration(milliseconds: json["time"]),
text: json["text"] as String,
);
}

Map<String, dynamic> toJson() {
return {
"time": time.inMilliseconds,
"text": text,
};
}

@override
String toString() {
return "LyricsSlice({time: $time, text: $text})";
Expand Down
102 changes: 0 additions & 102 deletions lib/pages/lyrics/genius_lyrics.dart

This file was deleted.

8 changes: 4 additions & 4 deletions lib/pages/lyrics/lyrics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'package:spotube/components/shared/page_window_title_bar.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/hooks/use_custom_status_bar_color.dart';
import 'package:spotube/hooks/use_palette_color.dart';
import 'package:spotube/pages/lyrics/genius_lyrics.dart';
import 'package:spotube/pages/lyrics/plain_lyrics.dart';
import 'package:spotube/pages/lyrics/synced_lyrics.dart';
import 'package:spotube/provider/playlist_queue_provider.dart';
import 'package:spotube/utils/platform.dart';
Expand Down Expand Up @@ -41,7 +41,7 @@ class LyricsPage extends HookConsumerWidget {

Widget body = [
SyncedLyrics(palette: palette, isModal: isModal),
GeniusLyrics(palette: palette, isModal: isModal),
PlainLyrics(palette: palette, isModal: isModal),
][index.value];

final tabbar = PreferredSize(
Expand All @@ -58,7 +58,7 @@ class LyricsPage extends HookConsumerWidget {
color: PlatformTextTheme.of(context).caption?.color,
),
PlatformTab(
label: "Genius",
label: "Plain",
icon: const SizedBox.shrink(),
color: PlatformTextTheme.of(context).caption?.color,
),
Expand All @@ -71,7 +71,7 @@ class LyricsPage extends HookConsumerWidget {
child: Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
color: Theme.of(context).backgroundColor.withOpacity(.4),
color: Theme.of(context).colorScheme.background.withOpacity(.4),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
Expand Down
132 changes: 132 additions & 0 deletions lib/pages/lyrics/plain_lyrics.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:palette_generator/palette_generator.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/components/lyrics/zoom_controls.dart';
import 'package:spotube/components/shared/shimmers/shimmer_lyrics.dart';
import 'package:spotube/hooks/use_breakpoints.dart';
import 'package:spotube/provider/playlist_queue_provider.dart';

import 'package:spotube/services/queries/queries.dart';
import 'package:spotube/utils/type_conversion_utils.dart';

class PlainLyrics extends HookConsumerWidget {
final PaletteColor palette;
final bool? isModal;
const PlainLyrics({
required this.palette,
this.isModal,
Key? key,
}) : super(key: key);

@override
Widget build(BuildContext context, ref) {
final playlist = ref.watch(PlaylistQueueNotifier.provider);
final lyricsQuery = useQueries.lyrics.spotifySynced(
ref,
playlist?.activeTrack,
);
final breakpoint = useBreakpoints();
final textTheme = Theme.of(context).textTheme;

final textZoomLevel = useState<int>(100);

return Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (isModal != true) ...[
Center(
child: Text(
playlist?.activeTrack.name ?? "",
style: breakpoint >= Breakpoints.md
? textTheme.displaySmall
: textTheme.headlineMedium?.copyWith(
fontSize: 25,
color: palette.titleTextColor,
),
),
),
Center(
child: Text(
TypeConversionUtils.artists_X_String<Artist>(
playlist?.activeTrack.artists ?? []),
style: (breakpoint >= Breakpoints.md
? textTheme.headlineSmall
: textTheme.titleLarge)
?.copyWith(color: palette.bodyTextColor),
),
)
],
Expanded(
child: SingleChildScrollView(
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Builder(
builder: (context) {
if (lyricsQuery.isLoading || lyricsQuery.isRefreshing) {
return const ShimmerLyrics();
} else if (lyricsQuery.hasError) {
return Text(
"Sorry, no Lyrics were found for `${playlist?.activeTrack.name}` :'(\n${lyricsQuery.error.toString()}",
style: textTheme.bodyLarge?.copyWith(
color: palette.bodyTextColor,
),
);
}

final lyrics =
lyricsQuery.data?.lyrics.mapIndexed((i, e) {
final next =
lyricsQuery.data?.lyrics.elementAtOrNull(i + 1);
if (next != null &&
e.time - next.time >
const Duration(milliseconds: 700)) {
return "${e.text}\n";
}

return e.text;
}).join("\n");

return AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 200),
style: TextStyle(
color: palette.bodyTextColor,
fontSize: 24 * textZoomLevel.value / 100,
height: textZoomLevel.value < 70
? 1.5
: textZoomLevel.value > 150
? 1.7
: 2,
),
child: Text(
lyrics == null && playlist?.activeTrack == null
? "No Track being played currently"
: lyrics ?? "",
),
);
},
),
),
),
),
),
],
),
Align(
alignment: Alignment.bottomRight,
child: ZoomControls(
value: textZoomLevel.value,
onChanged: (value) => textZoomLevel.value = value,
min: 50,
max: 200,
),
),
],
);
}
}
Loading

0 comments on commit 10d0660

Please sign in to comment.