From fb780da327a213d7a82cbc3b567ece858dc2f0e8 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Tue, 16 May 2023 10:50:17 +0600 Subject: [PATCH] feat: remove SponsorBlock in favor of YT Music and remove pocketbase backend track support --- .env.example | 4 - CONTRIBUTION.md | 4 +- lib/collections/env.dart | 9 - lib/extensions/piped.dart | 10 - lib/main.dart | 11 +- lib/models/matched_track.dart | 17 ++ lib/models/matched_track.g.dart | 44 +++ lib/models/spotube_track.dart | 250 ++++-------------- lib/models/track.dart | 29 -- lib/models/track.g.dart | 30 --- lib/pages/settings/settings.dart | 8 - .../proxy_playlist_provider.dart | 21 -- lib/provider/user_preferences_provider.dart | 10 - lib/services/pocketbase.dart | 14 - lib/utils/type_conversion_utils.dart | 1 - pubspec.lock | 10 +- pubspec.yaml | 3 +- 17 files changed, 132 insertions(+), 343 deletions(-) delete mode 100644 lib/extensions/piped.dart create mode 100644 lib/models/matched_track.dart create mode 100644 lib/models/matched_track.g.dart delete mode 100644 lib/models/track.dart delete mode 100644 lib/models/track.g.dart delete mode 100644 lib/services/pocketbase.dart diff --git a/.env.example b/.env.example index 17a748775..0ea04df78 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,3 @@ -POCKETBASE_URL= -USERNAME= -PASSWORD= - # The format: # SPOTIFY_SECRETS=clintId1:clientSecret1,clientId2:clientSecret2 SPOTIFY_SECRETS= diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md index 36748101d..da630db0e 100644 --- a/CONTRIBUTION.md +++ b/CONTRIBUTION.md @@ -118,7 +118,7 @@ Enhancement suggestions are tracked as [GitHub issues](https://github.com/KRTirt Do the following: -- Download the latest Flutter SDK (>=2.15.1) & enable desktop support +- Download the latest Flutter SDK (>=3.10.0) & enable desktop support - Install Development dependencies in linux - Debian (>=12/Bookworm)/Ubuntu ```bash @@ -137,7 +137,7 @@ Do the following: - Create a `.env` in root of the project following the `.env.example` template - Now run the following to bootstrap the project ```bash - flutter pub get && flutter pub run build_runner build --delete-conflicting-outputs + flutter pub get && dart run build_runner build --delete-conflicting-outputs --enable-experiment=records,patterns ``` - Finally run these following commands in the root of the project to start the Spotube Locally ```bash diff --git a/lib/collections/env.dart b/lib/collections/env.dart index 9a06dbda6..e03104e7f 100644 --- a/lib/collections/env.dart +++ b/lib/collections/env.dart @@ -4,15 +4,6 @@ part 'env.g.dart'; @Envied(obfuscate: true, requireEnvFile: true, path: ".env") abstract class Env { - @EnviedField(varName: 'POCKETBASE_URL', defaultValue: 'http://127.0.0.1:8090') - static final pocketbaseUrl = _Env.pocketbaseUrl; - - @EnviedField(varName: 'USERNAME', defaultValue: 'root') - static final username = _Env.username; - - @EnviedField(varName: 'PASSWORD', defaultValue: '12345678') - static final password = _Env.password; - @EnviedField(varName: 'SPOTIFY_SECRETS') static final spotifySecrets = _Env.spotifySecrets.split(',').map((e) { final secrets = e.trim().split(":").map((e) => e.trim()); diff --git a/lib/extensions/piped.dart b/lib/extensions/piped.dart deleted file mode 100644 index 1d890f863..000000000 --- a/lib/extensions/piped.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:piped_client/piped_client.dart'; -import 'package:spotube/models/track.dart'; -import 'package:spotube/services/youtube.dart'; - -extension PipedStreamResponseExtension on PipedStreamResponse { - static Future fromBackendTrack( - BackendTrack track) async { - return await PipedSpotube.client.streams(track.youtubeId); - } -} diff --git a/lib/main.dart b/lib/main.dart index 02347d0df..4e0150817 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,6 +9,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_desktop_tools/flutter_desktop_tools.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:hive/hive.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:media_kit/media_kit.dart'; import 'package:metadata_god/metadata_god.dart'; @@ -19,11 +20,11 @@ import 'package:spotube/collections/routes.dart'; import 'package:spotube/collections/intents.dart'; import 'package:spotube/l10n/l10n.dart'; import 'package:spotube/models/logger.dart'; +import 'package:spotube/models/matched_track.dart'; import 'package:spotube/provider/downloader_provider.dart'; import 'package:spotube/provider/palette_provider.dart'; import 'package:spotube/provider/user_preferences_provider.dart'; import 'package:spotube/services/audio_player/audio_player.dart'; -import 'package:spotube/services/pocketbase.dart'; import 'package:spotube/services/youtube.dart'; import 'package:spotube/themes/theme.dart'; import 'package:spotube/utils/persisted_state_notifier.dart'; @@ -93,6 +94,13 @@ Future main(List rawArgs) async { cachePrefix: "oss.krtirtho.spotube", cacheDir: (await getApplicationSupportDirectory()).path, ); + Hive.registerAdapter(MatchedTrackAdapter()); + + await Hive.openLazyBox( + MatchedTrack.boxName, + path: (await getApplicationSupportDirectory()).path, + ); + await PersistedStateNotifier.initializeBoxes(); Catcher( @@ -164,7 +172,6 @@ Future main(List rawArgs) async { ); }, ); - await initializePocketBase(); } class Spotube extends StatefulHookConsumerWidget { diff --git a/lib/models/matched_track.dart b/lib/models/matched_track.dart new file mode 100644 index 000000000..c1146dc40 --- /dev/null +++ b/lib/models/matched_track.dart @@ -0,0 +1,17 @@ +import "package:hive/hive.dart"; + +part "matched_track.g.dart"; + +@HiveType(typeId: 1) +class MatchedTrack { + @HiveField(0) + String youtubeId; + @HiveField(1) + String spotifyId; + + static const boxName = "oss.krtirtho.spotube.matched_tracks"; + + static LazyBox get box => Hive.lazyBox(boxName); + + MatchedTrack({required this.youtubeId, required this.spotifyId}); +} diff --git a/lib/models/matched_track.g.dart b/lib/models/matched_track.g.dart new file mode 100644 index 000000000..b89f26934 --- /dev/null +++ b/lib/models/matched_track.g.dart @@ -0,0 +1,44 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'matched_track.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class MatchedTrackAdapter extends TypeAdapter { + @override + final int typeId = 1; + + @override + MatchedTrack read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return MatchedTrack( + youtubeId: fields[0] as String, + spotifyId: fields[1] as String, + ); + } + + @override + void write(BinaryWriter writer, MatchedTrack obj) { + writer + ..writeByte(2) + ..writeByte(0) + ..write(obj.youtubeId) + ..writeByte(1) + ..write(obj.spotifyId); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is MatchedTrackAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/lib/models/spotube_track.dart b/lib/models/spotube_track.dart index a6fe070e0..25418a191 100644 --- a/lib/models/spotube_track.dart +++ b/lib/models/spotube_track.dart @@ -1,45 +1,29 @@ import 'dart:async'; -import 'dart:convert'; -import 'package:catcher/catcher.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:http/http.dart'; import 'package:piped_client/piped_client.dart'; -import 'package:pocketbase/pocketbase.dart'; import 'package:spotify/spotify.dart'; -import 'package:spotube/extensions/piped.dart'; import 'package:spotube/extensions/album_simple.dart'; import 'package:spotube/extensions/artist_simple.dart'; -import 'package:spotube/models/logger.dart'; -import 'package:spotube/models/track.dart'; +import 'package:spotube/models/matched_track.dart'; import 'package:spotube/provider/user_preferences_provider.dart'; -import 'package:spotube/services/pocketbase.dart'; import 'package:spotube/services/youtube.dart'; import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/service_utils.dart'; import 'package:collection/collection.dart'; -enum SpotubeTrackMatchAlgorithm { - // selects the first result returned from YouTube - youtube, - // selects the most popular one - popular, - // selects the most popular one from the author of the track - authenticPopular, -} - typedef SkipSegment = ({int start, int end}); class SpotubeTrack extends Track { final PipedStreamResponse ytTrack; final String ytUri; - final List skipSegments; + final List siblings; SpotubeTrack( this.ytTrack, this.ytUri, - this.skipSegments, this.siblings, ) : super(); @@ -47,7 +31,6 @@ class SpotubeTrack extends Track { required Track track, required this.ytTrack, required this.ytUri, - required this.skipSegments, required this.siblings, }) : super() { album = track.album; @@ -84,53 +67,7 @@ class SpotubeTrack extends Track { } } - static Future> getSkipSegments( - String id, - UserPreferences preferences, - ) async { - if (!preferences.skipSponsorSegments) return []; - try { - final res = await get(Uri( - scheme: "https", - host: "sponsor.ajay.app", - path: "/api/skipSegments", - queryParameters: { - "videoID": id, - "category": [ - 'sponsor', - 'selfpromo', - 'interaction', - 'intro', - 'outro', - 'music_offtopic' - ], - "actionType": 'skip' - }, - )); - - if (res.body == "Not Found") { - return List.castFrom([]); - } - - final data = jsonDecode(res.body) as List; - final segments = data.map((obj) { - return ( - start: obj["segment"].first.toInt(), - end: obj["segment"].last.toInt(), - ); - }).toList(); - getLogger(SpotubeTrack).v( - "[SponsorBlock] successfully fetched skip segments for $id", - ); - return List.castFrom(segments); - } catch (e, stack) { - Catcher.reportCheckedError(e, stack); - return List.castFrom([]); - } - } - - static Future fetchFromTrack( - Track track, UserPreferences preferences) async { + static Future> fetchSiblings(Track track) async { final artists = (track.artists ?? []) .map((ar) => ar.name) .toList() @@ -143,69 +80,59 @@ class SpotubeTrack extends Track { onlyCleanArtist: true, ).trim(); - final cachedTracks = await Future.value( - pb - .collection(BackendTrack.collection) - .getFirstListItem("spotify_id = '${track.id}'"), - ).catchError((e, stack) { - return null; - }); + final List siblings = await PipedSpotube.client + .search( + "$title - ${artists.join(", ")}", + PipedFilter.musicSongs, + ) + .then( + (res) { + final siblings = res.items + .whereType() + .where((item) { + return artists.any( + (artist) => + artist.toLowerCase() == item.uploaderName.toLowerCase(), + ); + }) + .take(10) + .toList(); + + if (siblings.isEmpty) { + return res.items.whereType().take(10).toList(); + } + + return siblings; + }, + ); - final cachedTrack = - cachedTracks != null ? BackendTrack.fromRecord(cachedTracks) : null; + return siblings; + } + static Future fetchFromTrack( + Track track, + UserPreferences preferences, + ) async { + final matchedCachedTrack = await MatchedTrack.box.get(track.id!); + var siblings = []; PipedStreamResponse ytVideo; - PipedAudioStream ytStream; - List siblings = []; - List skipSegments = []; - if (cachedTrack != null) { - final responses = await Future.wait( - [ - PipedStreamResponseExtension.fromBackendTrack(cachedTrack), - if (preferences.skipSponsorSegments) - getSkipSegments(cachedTrack.youtubeId, preferences) - else - Future.value([]) - ], - ); - ytVideo = responses.first as PipedStreamResponse; - skipSegments = responses.last as List; - ytStream = getStreamInfo(ytVideo, preferences.audioQuality); + if (matchedCachedTrack != null) { + ytVideo = await PipedSpotube.client.streams(matchedCachedTrack.youtubeId); } else { - final videos = await PipedSpotube.client - .search("${artists.join(", ")} - $title", PipedFilter.musicSongs); - // await PrimitiveUtils.raceMultiple( - // () => youtube.search.search("${artists.join(", ")} - $title"), - // ); - siblings = - videos.items.whereType().take(10).toList(); - final responses = await Future.wait( - [ - PipedSpotube.client.streams(siblings.first.id), - if (preferences.skipSponsorSegments) - getSkipSegments(siblings.first.id, preferences) - else - Future.value([]) - ], + siblings = await fetchSiblings(track); + ytVideo = await PipedSpotube.client.streams(siblings.first.id); + + await MatchedTrack.box.put( + track.id!, + MatchedTrack( + youtubeId: ytVideo.id, + spotifyId: track.id!, + ), ); - ytVideo = responses.first as PipedStreamResponse; - skipSegments = responses.last as List; - ytStream = getStreamInfo(ytVideo, preferences.audioQuality); } - if (cachedTrack == null) { - await Future.value( - pb.collection(BackendTrack.collection).create( - body: BackendTrack( - spotifyId: track.id!, - youtubeId: ytVideo.id, - votes: 0, - ).toJson(), - )).catchError((e, stack) { - Catcher.reportCheckedError(e, stack); - return null; - }); - } + final PipedAudioStream ytStream = + getStreamInfo(ytVideo, preferences.audioQuality); if (preferences.predownload && ytVideo.duration < const Duration(minutes: 15)) { @@ -230,7 +157,6 @@ class SpotubeTrack extends Track { track: track, ytTrack: ytVideo, ytUri: ytStream.url, - skipSegments: skipSegments, siblings: siblings, ); } @@ -241,59 +167,19 @@ class SpotubeTrack extends Track { ) async { if (siblings.none((element) => element.id == video.id)) return null; - final [PipedStreamResponse ytVideo, List skipSegments] = - await Future.wait( - [ - PipedSpotube.client.streams(video.id), - if (preferences.skipSponsorSegments) - getSkipSegments(video.id, preferences) - else - Future.value(>[]) - ], - ); - - // await PrimitiveUtils.raceMultiple( - // () => youtube.videos.streams.getManifest(video.id), - // ); + final ytVideo = await PipedSpotube.client.streams(video.id); final ytStream = getStreamInfo(ytVideo, preferences.audioQuality); final ytUri = ytStream.url; - final cachedTracks = await Future.value( - pb.collection(BackendTrack.collection).getFirstListItem( - "spotify_id = '$id' && youtube_id = '${video.id}'", - ), - ).catchError((e, stack) { - Catcher.reportCheckedError(e, stack); - return null; - }); - - final cachedTrack = - cachedTracks != null ? BackendTrack.fromRecord(cachedTracks) : null; - - if (cachedTrack == null) { - await Future.value( - pb.collection(BackendTrack.collection).create( - body: BackendTrack( - spotifyId: id!, - youtubeId: video.id, - votes: 1, - ).toJson(), - )).catchError((e, stack) { - Catcher.reportCheckedError(e, stack); - return null; - }); - } else { - await Future.value( - pb.collection(BackendTrack.collection).update( - cachedTrack.id, - body: {"votes": cachedTrack.votes + 1}, - )).catchError((e, stack) { - Catcher.reportCheckedError(e, stack); - return null; - }); - } + await MatchedTrack.box.put( + id!, + MatchedTrack( + youtubeId: video.id, + spotifyId: id!, + ), + ); if (preferences.predownload && video.duration < const Duration(minutes: 15)) { @@ -318,7 +204,6 @@ class SpotubeTrack extends Track { track: this, ytTrack: ytVideo, ytUri: ytUri, - skipSegments: skipSegments, siblings: [ video, ...siblings.where((element) => element.id != video.id), @@ -331,7 +216,6 @@ class SpotubeTrack extends Track { track: Track.fromJson(map), ytTrack: PipedStreamResponse.fromJson(map["ytTrack"]), ytUri: map["ytUri"], - skipSegments: List.castFrom(map["skipSegments"]), siblings: List.castFrom>(map["siblings"]) .map((sibling) => PipedSearchItemStream.fromJson(sibling)) .toList(), @@ -340,30 +224,13 @@ class SpotubeTrack extends Track { Future populatedCopy() async { if (this.siblings.isNotEmpty) return this; - final artists = (this.artists ?? []) - .map((ar) => ar.name) - .toList() - .whereNotNull() - .toList(); - - final title = ServiceUtils.getTitle( - name!, - artists: artists, - onlyCleanArtist: true, - ).trim(); - final videos = await PipedSpotube.client.search( - "${artists.join(", ")} - $title", - PipedFilter.musicSongs, - ); - final siblings = - videos.items.whereType().take(10).toList(); + final siblings = await fetchSiblings(this); return SpotubeTrack.fromTrack( track: this, ytTrack: ytTrack, ytUri: ytUri, - skipSegments: skipSegments, siblings: siblings, ); } @@ -388,7 +255,6 @@ class SpotubeTrack extends Track { "uri": uri, "ytTrack": ytTrack.toJson(), "ytUri": ytUri, - "skipSegments": skipSegments, "siblings": siblings.map((sibling) => sibling.toJson()).toList(), }; } diff --git a/lib/models/track.dart b/lib/models/track.dart deleted file mode 100644 index 57f9da2d9..000000000 --- a/lib/models/track.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; -import 'package:pocketbase/pocketbase.dart'; -part 'track.g.dart'; - -@JsonSerializable() -class BackendTrack extends RecordModel { - @JsonKey(name: "spotify_id") - final String spotifyId; - @JsonKey(name: "youtube_id") - final String youtubeId; - final int votes; - - BackendTrack({ - required this.spotifyId, - required this.youtubeId, - required this.votes, - }); - - factory BackendTrack.fromRecord(RecordModel record) => - BackendTrack.fromJson(record.toJson()); - - factory BackendTrack.fromJson(Map json) => - _$BackendTrackFromJson(json); - - @override - Map toJson() => _$BackendTrackToJson(this); - - static String collection = "tracks"; -} diff --git a/lib/models/track.g.dart b/lib/models/track.g.dart deleted file mode 100644 index 9dc4c23dc..000000000 --- a/lib/models/track.g.dart +++ /dev/null @@ -1,30 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'track.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -BackendTrack _$BackendTrackFromJson(Map json) => BackendTrack( - spotifyId: json['spotify_id'] as String, - youtubeId: json['youtube_id'] as String, - votes: json['votes'] as int, - ) - ..id = json['id'] as String - ..created = json['created'] as String - ..updated = json['updated'] as String - ..collectionId = json['collectionId'] as String - ..collectionName = json['collectionName'] as String; - -Map _$BackendTrackToJson(BackendTrack instance) => - { - 'id': instance.id, - 'created': instance.created, - 'updated': instance.updated, - 'collectionId': instance.collectionId, - 'collectionName': instance.collectionName, - 'spotify_id': instance.spotifyId, - 'youtube_id': instance.youtubeId, - 'votes': instance.votes, - }; diff --git a/lib/pages/settings/settings.dart b/lib/pages/settings/settings.dart index 9a1c604db..b53aabeaa 100644 --- a/lib/pages/settings/settings.dart +++ b/lib/pages/settings/settings.dart @@ -296,14 +296,6 @@ class SettingsPage extends HookConsumerWidget { preferences.setPredownload(state); }, ), - SwitchListTile( - secondary: const Icon(SpotubeIcons.fastForward), - title: Text(context.l10n.skip_non_music), - value: preferences.skipSponsorSegments, - onChanged: (state) { - preferences.setSkipSponsorSegments(state); - }, - ), ListTile( leading: const Icon(SpotubeIcons.playlistRemove), title: Text(context.l10n.blacklist), diff --git a/lib/provider/proxy_playlist/proxy_playlist_provider.dart b/lib/provider/proxy_playlist/proxy_playlist_provider.dart index 12cbbfbc3..4b499ee30 100644 --- a/lib/provider/proxy_playlist/proxy_playlist_provider.dart +++ b/lib/provider/proxy_playlist/proxy_playlist_provider.dart @@ -104,27 +104,6 @@ class ProxyPlaylistNotifier extends StateNotifier if (nextSource == null || isPlayable(nextSource)) return; await audioPlayer.pause(); }); - - audioPlayer.positionStream.listen((pos) async { - if (audioPlayer.currentIndex == -1) return; - final activeSource = - audioPlayer.sources.elementAtOrNull(audioPlayer.currentIndex); - if (activeSource == null) return; - final activeTrack = state.tracks.firstWhereOrNull( - (element) => element is SpotubeTrack && element.ytUri == activeSource, - ) as SpotubeTrack?; - if (activeTrack == null) return; - // skip all the activeTrack.skipSegments - if (activeTrack.skipSegments.isNotEmpty == true && - preferences.skipSponsorSegments) { - for (final segment in activeTrack.skipSegments) { - if (pos.inSeconds < segment.start || pos.inSeconds >= segment.end) { - continue; - } - await audioPlayer.seek(Duration(seconds: segment.end)); - } - } - }); }(); } diff --git a/lib/provider/user_preferences_provider.dart b/lib/provider/user_preferences_provider.dart index 14e2eed01..7eed354e8 100644 --- a/lib/provider/user_preferences_provider.dart +++ b/lib/provider/user_preferences_provider.dart @@ -37,7 +37,6 @@ class UserPreferences extends PersistedChangeNotifier { SpotubeColor accentColorScheme; bool albumColorSync; - bool skipSponsorSegments; String downloadLocation; @@ -64,7 +63,6 @@ class UserPreferences extends PersistedChangeNotifier { this.saveTrackLyrics = false, this.checkUpdate = true, this.audioQuality = AudioQuality.high, - this.skipSponsorSegments = true, this.downloadLocation = "", this.closeBehavior = CloseBehavior.minimizeToTray, this.showSystemTrayIcon = true, @@ -132,12 +130,6 @@ class UserPreferences extends PersistedChangeNotifier { updatePersistence(); } - void setSkipSponsorSegments(bool should) { - skipSponsorSegments = should; - notifyListeners(); - updatePersistence(); - } - void setDownloadLocation(String downloadDir) { if (downloadDir.isEmpty) return; downloadLocation = downloadDir; @@ -195,7 +187,6 @@ class UserPreferences extends PersistedChangeNotifier { audioQuality = map["audioQuality"] != null ? AudioQuality.values[map["audioQuality"]] : audioQuality; - skipSponsorSegments = map["skipSponsorSegments"] ?? skipSponsorSegments; downloadLocation = map["downloadLocation"] ?? await _getDefaultDownloadDirectory(); @@ -227,7 +218,6 @@ class UserPreferences extends PersistedChangeNotifier { "albumColorSync": albumColorSync, "checkUpdate": checkUpdate, "audioQuality": audioQuality.index, - "skipSponsorSegments": skipSponsorSegments, "downloadLocation": downloadLocation, "layoutMode": layoutMode.name, "predownload": predownload, diff --git a/lib/services/pocketbase.dart b/lib/services/pocketbase.dart deleted file mode 100644 index f590c3eed..000000000 --- a/lib/services/pocketbase.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:catcher/catcher.dart'; -import 'package:pocketbase/pocketbase.dart'; -import 'package:spotube/collections/env.dart'; - -final pb = PocketBase(Env.pocketbaseUrl); -bool isLoggedIn = false; -Future initializePocketBase() async { - try { - await pb.collection("users").authWithPassword(Env.username, Env.password); - isLoggedIn = true; - } catch (e, stack) { - Catcher.reportCheckedError(e, stack); - } -} diff --git a/lib/utils/type_conversion_utils.dart b/lib/utils/type_conversion_utils.dart index 45cb7a4b0..a528ff443 100644 --- a/lib/utils/type_conversion_utils.dart +++ b/lib/utils/type_conversion_utils.dart @@ -152,7 +152,6 @@ abstract class TypeConversionUtils { ), file.path, [], - [], ); track.album = Album() ..name = metadata?.album ?? "Spotube" diff --git a/pubspec.lock b/pubspec.lock index 96ee45281..9550de622 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1296,14 +1296,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - pocketbase: - dependency: "direct main" - description: - name: pocketbase - sha256: "125e32fe39393cc54436ce518ccfd5649419f15879a1278599f2f58564e5231c" - url: "https://pub.dev" - source: hosted - version: "0.7.1+1" pool: dependency: transitive description: @@ -1945,4 +1937,4 @@ packages: version: "3.1.1" sdks: dart: ">=3.0.0 <4.0.0" - flutter: ">=3.3.0" + flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index 8b9469bfa..ddcd6f379 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -71,7 +71,6 @@ dependencies: path: ^1.8.0 path_provider: ^2.0.8 permission_handler: ^10.2.0 - pocketbase: ^0.7.1+1 popover: ^0.2.6+3 queue: ^3.1.0+1 scroll_to_index: ^3.0.1 @@ -155,4 +154,4 @@ flutter_native_splash: branding: assets/branding.png color: "#000000" icon_background_color: "#000000" - web: false \ No newline at end of file + web: false