mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45:18 +00:00
optimized search for both youtube track & synched subtitle
flutter_distributor .deb build support
This commit is contained in:
parent
ce6e4dc677
commit
cea6fd9797
@ -25,3 +25,17 @@ releases:
|
|||||||
build_args:
|
build_args:
|
||||||
dart-define:
|
dart-define:
|
||||||
APP_ENV: dev
|
APP_ENV: dev
|
||||||
|
- name: release-dev-windows-exe
|
||||||
|
package:
|
||||||
|
platform: windows
|
||||||
|
target: exe
|
||||||
|
build_args:
|
||||||
|
dart-define:
|
||||||
|
APP_ENV: dev
|
||||||
|
- name: release-dev-macos-dmg
|
||||||
|
package:
|
||||||
|
platform: macos
|
||||||
|
target: dmg
|
||||||
|
build_args:
|
||||||
|
dart-define:
|
||||||
|
APP_ENV: dev
|
||||||
|
@ -12,7 +12,6 @@ import 'package:spotube/components/Category/CategoryCard.dart';
|
|||||||
import 'package:spotube/components/Home/Sidebar.dart';
|
import 'package:spotube/components/Home/Sidebar.dart';
|
||||||
import 'package:spotube/components/Home/SpotubeNavigationBar.dart';
|
import 'package:spotube/components/Home/SpotubeNavigationBar.dart';
|
||||||
import 'package:spotube/components/Lyrics/SyncedLyrics.dart';
|
import 'package:spotube/components/Lyrics/SyncedLyrics.dart';
|
||||||
import 'package:spotube/components/Lyrics.dart';
|
|
||||||
import 'package:spotube/components/Search/Search.dart';
|
import 'package:spotube/components/Search/Search.dart';
|
||||||
import 'package:spotube/components/Shared/PageWindowTitleBar.dart';
|
import 'package:spotube/components/Shared/PageWindowTitleBar.dart';
|
||||||
import 'package:spotube/components/Player/Player.dart';
|
import 'package:spotube/components/Player/Player.dart';
|
||||||
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/components/Lyrics.dart';
|
import 'package:spotube/components/Lyrics/Lyrics.dart';
|
||||||
import 'package:spotube/components/Shared/SpotubeMarqueeText.dart';
|
import 'package:spotube/components/Shared/SpotubeMarqueeText.dart';
|
||||||
import 'package:spotube/helpers/artist-to-string.dart';
|
import 'package:spotube/helpers/artist-to-string.dart';
|
||||||
import 'package:spotube/helpers/timed-lyrics.dart';
|
import 'package:spotube/helpers/timed-lyrics.dart';
|
||||||
@ -26,9 +26,10 @@ class SyncedLyrics extends HookConsumerWidget {
|
|||||||
if (playback.currentTrack == null ||
|
if (playback.currentTrack == null ||
|
||||||
playback.currentTrack is! SpotubeTrack) return null;
|
playback.currentTrack is! SpotubeTrack) return null;
|
||||||
try {
|
try {
|
||||||
|
if (failed.value) failed.value = false;
|
||||||
final lyrics =
|
final lyrics =
|
||||||
await getTimedLyrics(playback.currentTrack as SpotubeTrack);
|
await getTimedLyrics(playback.currentTrack as SpotubeTrack);
|
||||||
if (failed.value) failed.value = false;
|
if (lyrics == null) failed.value = true;
|
||||||
return lyrics;
|
return lyrics;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e == "Subtitle lookup failed") {
|
if (e == "Subtitle lookup failed") {
|
||||||
@ -52,13 +53,42 @@ class SyncedLyrics extends HookConsumerWidget {
|
|||||||
final textTheme = Theme.of(context).textTheme;
|
final textTheme = Theme.of(context).textTheme;
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
controller.scrollToIndex(
|
controller.scrollToIndex(0);
|
||||||
0,
|
|
||||||
preferPosition: AutoScrollPosition.middle,
|
|
||||||
);
|
|
||||||
return null;
|
return null;
|
||||||
}, [playback.currentTrack]);
|
}, [playback.currentTrack]);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
if (lyricsSnapshot.data != null && lyricsSnapshot.data!.rating <= 2) {
|
||||||
|
Future.delayed(const Duration(seconds: 5), () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return AlertDialog(
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: const Text("No"),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: const Text("Yes"),
|
||||||
|
onPressed: () {
|
||||||
|
failed.value = true;
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
content: const Text(
|
||||||
|
"The found lyrics might not be properly synced. Do you want to default to static (genius.com) lyrics?"),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, [lyricsSnapshot.data]);
|
||||||
|
|
||||||
// when synced lyrics not found, fallback to GeniusLyrics
|
// when synced lyrics not found, fallback to GeniusLyrics
|
||||||
if (failed.value) return const Lyrics();
|
if (failed.value) return const Lyrics();
|
||||||
|
|
||||||
|
6
lib/helpers/contains-text-in-bracket.dart
Normal file
6
lib/helpers/contains-text-in-bracket.dart
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
bool containsTextInBracket(String matcher, String text) {
|
||||||
|
return RegExp(r"(?<=\().+?(?=\))")
|
||||||
|
.allMatches(matcher)
|
||||||
|
.map((e) => e.group(0))
|
||||||
|
.every((match) => match?.contains(text) ?? false);
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
|
import 'package:spotube/helpers/contains-text-in-bracket.dart';
|
||||||
import 'package:spotube/helpers/getLyrics.dart';
|
import 'package:spotube/helpers/getLyrics.dart';
|
||||||
import 'package:spotube/models/Logger.dart';
|
import 'package:spotube/models/Logger.dart';
|
||||||
import 'package:spotube/models/SpotubeTrack.dart';
|
import 'package:spotube/models/SpotubeTrack.dart';
|
||||||
@ -36,15 +37,40 @@ Future<SpotubeTrack> toSpotubeTrack(
|
|||||||
// the find should be lazy thus everything case insensitive
|
// the find should be lazy thus everything case insensitive
|
||||||
final ytTitle = video.title.toLowerCase();
|
final ytTitle = video.title.toLowerCase();
|
||||||
final bool hasTitle = ytTitle.contains(title);
|
final bool hasTitle = ytTitle.contains(title);
|
||||||
|
final bool hasYtTitle = title.contains(ytTitle);
|
||||||
final bool hasAllArtists = track.artists?.every(
|
final bool hasAllArtists = track.artists?.every(
|
||||||
(artist) => ytTitle.contains(artist.name!.toLowerCase()),
|
(artist) => ytTitle.contains(artist.name!.toLowerCase()),
|
||||||
) ??
|
) ??
|
||||||
false;
|
false;
|
||||||
final bool authorIsArtist = track.artists
|
final bool authorIsArtist = track.artists?.any((artist) {
|
||||||
?.any((artist) => artist.name?.toLowerCase() == video.author) ??
|
return artist.name?.toLowerCase() == video.author.toLowerCase();
|
||||||
|
}) ??
|
||||||
false;
|
false;
|
||||||
|
|
||||||
|
final bool hasNoLive = !containsTextInBracket(ytTitle, "live");
|
||||||
|
final bool hasOfficialVideo = [
|
||||||
|
"(official video)",
|
||||||
|
"[official video]",
|
||||||
|
"(official music video)",
|
||||||
|
"[official music video]"
|
||||||
|
].any((v) => ytTitle.contains(v));
|
||||||
|
|
||||||
|
final bool hasOfficialAudio = [
|
||||||
|
"[official audio]",
|
||||||
|
"(official audio)",
|
||||||
|
].any((v) => ytTitle.contains(v));
|
||||||
|
|
||||||
int rate = 0;
|
int rate = 0;
|
||||||
for (final el in [hasTitle, hasAllArtists, authorIsArtist]) {
|
for (final el in [
|
||||||
|
hasTitle,
|
||||||
|
hasYtTitle,
|
||||||
|
hasAllArtists,
|
||||||
|
authorIsArtist,
|
||||||
|
hasNoLive,
|
||||||
|
!video.isLive,
|
||||||
|
hasOfficialAudio,
|
||||||
|
hasOfficialVideo,
|
||||||
|
]) {
|
||||||
if (el) rate++;
|
if (el) rate++;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
@ -2,6 +2,7 @@ import 'package:html/dom.dart';
|
|||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:spotube/helpers/contains-text-in-bracket.dart';
|
||||||
import 'package:spotube/helpers/getLyrics.dart';
|
import 'package:spotube/helpers/getLyrics.dart';
|
||||||
import 'package:spotube/models/Logger.dart';
|
import 'package:spotube/models/Logger.dart';
|
||||||
import 'package:spotube/models/SpotubeTrack.dart';
|
import 'package:spotube/models/SpotubeTrack.dart';
|
||||||
@ -12,10 +13,12 @@ class SubtitleSimple {
|
|||||||
Uri uri;
|
Uri uri;
|
||||||
String name;
|
String name;
|
||||||
List<LyricSlice> lyrics;
|
List<LyricSlice> lyrics;
|
||||||
|
int rating;
|
||||||
SubtitleSimple({
|
SubtitleSimple({
|
||||||
required this.uri,
|
required this.uri,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.lyrics,
|
required this.lyrics,
|
||||||
|
required this.rating,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,9 +63,10 @@ Future<SubtitleSimple?> getTimedLyrics(SpotubeTrack track) async {
|
|||||||
.every((artist) => title.contains(artist.toLowerCase())) ??
|
.every((artist) => title.contains(artist.toLowerCase())) ??
|
||||||
false;
|
false;
|
||||||
final hasTrackName = title.contains(track.name!.toLowerCase());
|
final hasTrackName = title.contains(track.name!.toLowerCase());
|
||||||
|
final isNotLive = !containsTextInBracket(title, "live");
|
||||||
final exactYtMatch = title == track.ytTrack.title.toLowerCase();
|
final exactYtMatch = title == track.ytTrack.title.toLowerCase();
|
||||||
if (exactYtMatch) points = 8;
|
if (exactYtMatch) points = 7;
|
||||||
for (final criteria in [hasTrackName, hasAllArtists]) {
|
for (final criteria in [hasTrackName, hasAllArtists, isNotLive]) {
|
||||||
if (criteria) points++;
|
if (criteria) points++;
|
||||||
}
|
}
|
||||||
return {"result": result, "points": points};
|
return {"result": result, "points": points};
|
||||||
@ -111,6 +115,7 @@ Future<SubtitleSimple?> getTimedLyrics(SpotubeTrack track) async {
|
|||||||
name: topResult.text.trim(),
|
name: topResult.text.trim(),
|
||||||
uri: subtitleUri,
|
uri: subtitleUri,
|
||||||
lyrics: lrcList,
|
lyrics: lrcList,
|
||||||
|
rating: rateSortedResults.first["points"] as int,
|
||||||
);
|
);
|
||||||
|
|
||||||
return subtitle;
|
return subtitle;
|
||||||
|
27
linux/packaging/deb/make_config.yaml
Normal file
27
linux/packaging/deb/make_config.yaml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
display_name: Spotube
|
||||||
|
package_name: spotube
|
||||||
|
|
||||||
|
maintainer:
|
||||||
|
name: Kingkor Roy Tirtho
|
||||||
|
email: krtirtho@gmail.com
|
||||||
|
|
||||||
|
priority: optional
|
||||||
|
section: x11
|
||||||
|
installed_size: 24400
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
- libkeybinder-3.0-0 (>= 0.3.2)
|
||||||
|
|
||||||
|
essential: false
|
||||||
|
icon: assets/spotube-logo.png
|
||||||
|
|
||||||
|
keywords:
|
||||||
|
- Music
|
||||||
|
- Spotify
|
||||||
|
- Media
|
||||||
|
- Streaming
|
||||||
|
- YouTube
|
||||||
|
|
||||||
|
generic_name: Music Streaming Application
|
||||||
|
categories:
|
||||||
|
- Music
|
Loading…
Reference in New Issue
Block a user