Skip to content

Commit

Permalink
feat: lyrics source
Browse files Browse the repository at this point in the history
  • Loading branch information
MSOB7YY committed Mar 26, 2024
1 parent 267e1a3 commit 9964839
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 36 deletions.
66 changes: 38 additions & 28 deletions lib/controller/lyrics_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'package:namida/class/track.dart';
import 'package:namida/controller/settings_controller.dart';
import 'package:namida/controller/wakelock_controller.dart';
import 'package:namida/core/constants.dart';
import 'package:namida/core/enums.dart';
import 'package:namida/core/extensions.dart';
import 'package:namida/packages/lyrics_lrc_parsed_view.dart';

Expand All @@ -33,25 +34,29 @@ class Lyrics {

Track? _currentTrack;

bool get _lyricsEnabled => settings.enableLyrics.value;
bool get _lyricsPrioritizeEmbedded => settings.prioritizeEmbeddedLyrics.value;
LyricsSource get _lyricsSource => settings.lyricsSource.value;

void _updateWidgets(Lrc? lrc) {
WakelockController.inst.updateLRCStatus(lrc != null);
lrcViewKey?.currentState?.fillLists(lrc);
lrcViewKeyFullscreen.currentState?.fillLists(lrc);
}

Future<void> updateLyrics(Track track, {bool preferEmbedded = false}) async {
Future<void> updateLyrics(Track track) async {
_currentTrack = track;
currentLyricsText.value = '';
currentLyricsLRC.value = null;
_updateWidgets(null);
lrcViewKey = GlobalKey<LyricsLRCParsedViewState>();

lyricsCanBeAvailable.value = true;
if (!settings.enableLyrics.value) return;
if (!_lyricsEnabled) return;

final embedded = track.lyrics;

if (settings.prioritizeEmbeddedLyrics.value && embedded != '') {
if (_lyricsPrioritizeEmbedded && embedded != '') {
final lrc = embedded.parseLRC();
if (lrc != null) {
currentLyricsLRC.value = lrc;
Expand All @@ -66,7 +71,7 @@ class Lyrics {
/// 2. cached lrc
/// 3. track embedded lrc
/// 4. database.
final lrcLyrics = await _fetchLRCBasedLyrics(track, embedded);
final lrcLyrics = await _fetchLRCBasedLyrics(track, embedded, _lyricsSource);

if (lrcLyrics != null) {
if (_currentTrack == track) {
Expand All @@ -77,7 +82,7 @@ class Lyrics {
/// 1. cached txt lyrics
/// 2. track embedded txt
/// 3. google search
final textLyrics = await _fetchTextBasedLyrics(track, embedded);
final textLyrics = await _fetchTextBasedLyrics(track, embedded, _lyricsSource);
if (textLyrics != '') {
if (_currentTrack == track) {
currentLyricsText.value = textLyrics;
Expand All @@ -94,22 +99,24 @@ class Lyrics {

File lyricsFileText(Track tr) => File(p.join(AppDirs.LYRICS, "${tr.filename}.txt"));
File lyricsFileCache(Track tr) => File(p.join(AppDirs.LYRICS, "${tr.filename}.lrc"));
List<File> lyricsFilesDevice(Track tr) => [
File(p.join(tr.path.getDirectoryPath, "${tr.filename}.lrc")),
File(p.join(tr.path.getDirectoryPath, "${tr.filenameWOExt}.lrc")),
File(p.join(tr.path.getDirectoryPath, "${tr.filename}.LRC")),
File(p.join(tr.path.getDirectoryPath, "${tr.filenameWOExt}.LRC")),
];
List<File> lyricsFilesDevice(Track tr) {
final dirPath = tr.path.getDirectoryPath;
return [
File(p.join(dirPath, "${tr.filename}.lrc")),
File(p.join(dirPath, "${tr.filenameWOExt}.lrc")),
File(p.join(dirPath, "${tr.filename}.LRC")),
File(p.join(dirPath, "${tr.filenameWOExt}.LRC")),
];
}

Future<void> saveLyricsToCache(Track track, String lyricsText, bool isSynced) async {
final fc = isSynced ? lyricsFileCache(track) : lyricsFileText(track);
await fc.create();
await fc.writeAsString(lyricsText);
}

Future<Lrc?> _fetchLRCBasedLyrics(Track track, String trackLyrics) async {
Future<Lrc?> _fetchLRCBasedLyrics(Track track, String trackLyrics, LyricsSource source) async {
final fc = lyricsFileCache(track);
final lyricsFilesLocal = lyricsFilesDevice(track);

Future<Lrc?> parseLRCFile(File file) async {
final content = await file.readAsString();
Expand All @@ -121,22 +128,25 @@ class Lyrics {
/// 1. device lrc
/// 2. cached lrc
/// 3. track embedded
for (final lf in lyricsFilesLocal) {
if (await lf.existsAndValid()) {
lrc = await parseLRCFile(lf);
break;
if (source != LyricsSource.internet) {
final lyricsFilesLocal = lyricsFilesDevice(track);
for (final lf in lyricsFilesLocal) {
if (await lf.existsAndValid()) {
lrc = await parseLRCFile(lf);
break;
}
}
}
if (lrc == null) {
if (await fc.existsAndValid()) {
lrc = await parseLRCFile(fc);
} else if (trackLyrics != '') {
lrc = trackLyrics.parseLRC();
if (lrc == null) {
if (await fc.existsAndValid()) {
lrc = await parseLRCFile(fc);
} else if (trackLyrics != '') {
lrc = trackLyrics.parseLRC();
}
}
}

/// 4. if still null, fetch from database.
if (lrc == null) {
if (source != LyricsSource.local && lrc == null) {
final tries = <(String, String, String)>[]; // title, artist, album
tries.addAll([
(track.title, track.originalArtist, ''),
Expand Down Expand Up @@ -239,18 +249,18 @@ class Lyrics {
return [];
}

Future<String> _fetchTextBasedLyrics(Track track, String trackLyrics) async {
Future<String> _fetchTextBasedLyrics(Track track, String trackLyrics, LyricsSource source) async {
final lyricsFile = lyricsFileText(track);

/// get from storage
if (await lyricsFile.existsAndValid()) {
if (source != LyricsSource.internet && await lyricsFile.existsAndValid()) {
return await lyricsFile.readAsString();
} else if (trackLyrics != '') {
} else if (source != LyricsSource.internet && trackLyrics != '') {
return trackLyrics;
}

/// download lyrics
else {
else if (source != LyricsSource.local) {
final lyrics = await _fetchLyricsGoogle(artist: track.artistsList.first, title: track.title);
final regex = RegExp(r'<[^>]*>');
if (lyrics != '') {
Expand Down
5 changes: 5 additions & 0 deletions lib/controller/settings_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class SettingsController with SettingsFileWriter {
].obs;
final RxBool enableVideoPlayback = true.obs;
final RxBool enableLyrics = false.obs;
final Rx<LyricsSource> lyricsSource = LyricsSource.auto.obs;
final Rx<VideoPlaybackSource> videoPlaybackSource = VideoPlaybackSource.auto.obs;
final RxList<String> youtubeVideoQualities = ['480p', '360p', '240p', '144p'].obs;
final RxDouble animatingThumbnailScaleMultiplier = 1.0.obs;
Expand Down Expand Up @@ -379,6 +380,7 @@ class SettingsController with SettingsFileWriter {
backupItemslist.value = List<String>.from(json['backupItemslist'] ?? backupItemslist);
enableVideoPlayback.value = json['enableVideoPlayback'] ?? enableVideoPlayback.value;
enableLyrics.value = json['enableLyrics'] ?? enableLyrics.value;
lyricsSource.value = LyricsSource.values.getEnum(json['lyricsSource']) ?? lyricsSource.value;
videoPlaybackSource.value = VideoPlaybackSource.values.getEnum(json['videoPlaybackSource']) ?? videoPlaybackSource.value;
youtubeVideoQualities.value = List<String>.from(json['youtubeVideoQualities'] ?? youtubeVideoQualities);

Expand Down Expand Up @@ -564,6 +566,7 @@ class SettingsController with SettingsFileWriter {
'backupItemslist': backupItemslist.toList(),
'enableVideoPlayback': enableVideoPlayback.value,
'enableLyrics': enableLyrics.value,
'lyricsSource': lyricsSource.value.convertToString,
'videoPlaybackSource': videoPlaybackSource.value.convertToString,
'youtubeVideoQualities': youtubeVideoQualities.toList(),
'animatingThumbnailScaleMultiplier': animatingThumbnailScaleMultiplier.value,
Expand Down Expand Up @@ -726,6 +729,7 @@ class SettingsController with SettingsFileWriter {
List<String>? backupItemslist,
bool? enableVideoPlayback,
bool? enableLyrics,
LyricsSource? lyricsSource,
VideoPlaybackSource? videoPlaybackSource,
List<String>? youtubeVideoQualities,
double? animatingThumbnailScaleMultiplier,
Expand Down Expand Up @@ -942,6 +946,7 @@ class SettingsController with SettingsFileWriter {
}
if (enableVideoPlayback != null) this.enableVideoPlayback.value = enableVideoPlayback;
if (enableLyrics != null) this.enableLyrics.value = enableLyrics;
if (lyricsSource != null) this.lyricsSource.value = lyricsSource;
if (videoPlaybackSource != null) this.videoPlaybackSource.value = videoPlaybackSource;
if (animatingThumbnailScaleMultiplier != null) this.animatingThumbnailScaleMultiplier.value = animatingThumbnailScaleMultiplier;
if (animatingThumbnailIntensity != null) this.animatingThumbnailIntensity.value = animatingThumbnailIntensity;
Expand Down
6 changes: 6 additions & 0 deletions lib/core/enums.dart
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ enum VideoPlaybackSource {
youtube,
}

enum LyricsSource {
auto,
local,
internet,
}

enum QueueInsertionType {
moreAlbum,
moreArtist,
Expand Down
9 changes: 9 additions & 0 deletions lib/core/namida_converter_ext.dart
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ extension VideoSource on VideoPlaybackSource {
String? toSubtitle() => _NamidaConverters.inst.getSubtitle(this);
}

extension LyricsSourceUtils on LyricsSource {
String toText() => _NamidaConverters.inst.getTitle(this);
}

extension TrackItemSubstring on TrackTileItem {
String toText() => _NamidaConverters.inst.getTitle(this);
}
Expand Down Expand Up @@ -1351,6 +1355,11 @@ class _NamidaConverters {
VideoPlaybackSource.youtube: lang.VIDEO_PLAYBACK_SOURCE_YOUTUBE,
VideoPlaybackSource.local: lang.VIDEO_PLAYBACK_SOURCE_LOCAL,
},
LyricsSource: {
LyricsSource.auto: lang.AUTO,
LyricsSource.local: lang.LOCAL,
LyricsSource.internet: lang.DATABASE,
},
WakelockMode: {
WakelockMode.none: lang.KEEP_SCREEN_AWAKE_NONE,
WakelockMode.expanded: lang.KEEP_SCREEN_AWAKE_MINIPLAYER_EXPANDED,
Expand Down
1 change: 1 addition & 0 deletions lib/core/translations/keys.dart
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ abstract class LanguageKeys {
String get LOUDNESS_ENHANCER => _getKey('LOUDNESS_ENHANCER');
String get LYRICIST => _getKey('LYRICIST');
String get LYRICS => _getKey('LYRICS');
String get LYRICS_SOURCE => _getKey('LYRICS_SOURCE');
String get M3U_PLAYLIST => _getKey('M3U_PLAYLIST');
String get MAKE_YOUR_FIRST_LISTEN => _getKey('MAKE_YOUR_FIRST_LISTEN');
String get MANUAL_BACKUP_SUBTITLE => _getKey('MANUAL_BACKUP_SUBTITLE');
Expand Down
54 changes: 54 additions & 0 deletions lib/ui/widgets/settings/extra_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ enum _ExtraSettingsKeys {
filterTracksBy,
searchCleanup,
prioritizeEmbeddedLyrics,
lyricsSource,
immersiveMode,
swipeToOpenDrawer,
enableClipboardMonitoring,
Expand All @@ -54,6 +55,7 @@ class ExtrasSettings extends SettingSubpageProvider {
_ExtraSettingsKeys.filterTracksBy: [lang.FILTER_TRACKS_BY],
_ExtraSettingsKeys.searchCleanup: [lang.ENABLE_SEARCH_CLEANUP, lang.ENABLE_SEARCH_CLEANUP_SUBTITLE],
_ExtraSettingsKeys.prioritizeEmbeddedLyrics: [lang.PRIORITIZE_EMBEDDED_LYRICS],
_ExtraSettingsKeys.lyricsSource: [lang.LYRICS_SOURCE],
_ExtraSettingsKeys.immersiveMode: [lang.IMMERSIVE_MODE, lang.IMMERSIVE_MODE_SUBTITLE],
_ExtraSettingsKeys.swipeToOpenDrawer: [lang.SWIPE_TO_OPEN_DRAWER],
_ExtraSettingsKeys.enableClipboardMonitoring: [lang.ENABLE_CLIPBOARD_MONITORING, lang.ENABLE_CLIPBOARD_MONITORING_SUBTITLE],
Expand Down Expand Up @@ -306,6 +308,58 @@ class ExtrasSettings extends SettingSubpageProvider {
),
),
),
getItemWrapper(
key: _ExtraSettingsKeys.lyricsSource,
child: Obx(
() => CustomListTile(
bgColor: getBgColor(_ExtraSettingsKeys.lyricsSource),
enabled: settings.enableLyrics.value,
title: lang.LYRICS_SOURCE,
leading: const StackedIcon(
baseIcon: Broken.mobile_programming,
secondaryIcon: Broken.cpu_setting,
),
trailingText: settings.lyricsSource.value.toText(),
onTap: () {
bool isEnabled(LyricsSource val) => settings.lyricsSource.value == val;
void tileOnTap(LyricsSource val) => settings.save(lyricsSource: val);
NamidaNavigator.inst.navigateDialog(
dialog: CustomBlurryDialog(
title: lang.LYRICS_SOURCE,
actions: [
IconButton(
onPressed: () => tileOnTap(LyricsSource.auto),
icon: const Icon(Broken.refresh),
),
NamidaButton(
text: lang.DONE,
onPressed: NamidaNavigator.inst.closeDialog,
),
],
child: Obx(
() => ListView(
padding: EdgeInsets.zero,
shrinkWrap: true,
children: [
...LyricsSource.values.map(
(e) => Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child: ListTileWithCheckMark(
active: isEnabled(e),
title: e.toText(),
onTap: () => tileOnTap(e),
),
),
),
],
),
),
),
);
},
),
),
),
getItemWrapper(
key: _ExtraSettingsKeys.immersiveMode,
child: Obx(
Expand Down
10 changes: 2 additions & 8 deletions lib/ui/widgets/settings/playback_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,8 @@ class PlaybackSettings extends SettingSubpageProvider {
icon: Broken.scroll,
trailingText: settings.videoPlaybackSource.value.toText(),
onTap: () {
bool isEnabled(VideoPlaybackSource val) {
return settings.videoPlaybackSource.value == val;
}

void tileOnTap(VideoPlaybackSource val) {
settings.save(videoPlaybackSource: val);
}

bool isEnabled(VideoPlaybackSource val) => settings.videoPlaybackSource.value == val;
void tileOnTap(VideoPlaybackSource val) => settings.save(videoPlaybackSource: val);
NamidaNavigator.inst.navigateDialog(
dialog: CustomBlurryDialog(
title: lang.VIDEO_PLAYBACK_SOURCE,
Expand Down

0 comments on commit 9964839

Please sign in to comment.