0.6.2 - Spotify albums/tracks, album art gradient, languages, minor fixes
This commit is contained in:
parent
f877aa9d7b
commit
e9d97986b5
17 changed files with 497 additions and 221 deletions
|
@ -1,4 +1,5 @@
|
|||
import 'package:freezer/api/definitions.dart';
|
||||
import 'package:freezer/api/spotify.dart';
|
||||
import 'package:freezer/settings.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
|
@ -114,6 +115,23 @@ class DeezerAPI {
|
|||
String newUrl = response.headers['location'];
|
||||
return parseLink(newUrl);
|
||||
}
|
||||
//Spotify
|
||||
if (uri.host == 'open.spotify.com') {
|
||||
if (uri.pathSegments.length < 2) return null;
|
||||
String spotifyUri = 'spotify:' + uri.pathSegments.sublist(0, 2).join(':');
|
||||
try {
|
||||
//Tracks
|
||||
if (uri.pathSegments[0] == 'track') {
|
||||
String id = await spotify.convertTrack(spotifyUri);
|
||||
return DeezerLinkResponse(type: DeezerLinkType.TRACK, id: id);
|
||||
}
|
||||
//Albums
|
||||
if (uri.pathSegments[0] == 'album') {
|
||||
String id = await spotify.convertAlbum(spotifyUri);
|
||||
return DeezerLinkResponse(type: DeezerLinkType.ALBUM, id: id);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
//Search
|
||||
|
|
|
@ -49,8 +49,8 @@ Map<String, dynamic> _$TrackToJson(Track instance) => <String, dynamic>{
|
|||
'favorite': instance.favorite,
|
||||
'diskNumber': instance.diskNumber,
|
||||
'explicit': instance.explicit,
|
||||
'playbackDetails': instance.playbackDetails,
|
||||
'favoriteDate': instance.favoriteDate,
|
||||
'playbackDetails': instance.playbackDetails,
|
||||
};
|
||||
|
||||
Album _$AlbumFromJson(Map<String, dynamic> json) {
|
||||
|
@ -73,7 +73,7 @@ Album _$AlbumFromJson(Map<String, dynamic> json) {
|
|||
library: json['library'] as bool,
|
||||
type: _$enumDecodeNullable(_$AlbumTypeEnumMap, json['type']),
|
||||
releaseDate: json['releaseDate'] as String,
|
||||
favoriteDate: json['favoriteDate'] as String
|
||||
favoriteDate: json['favoriteDate'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ Map<String, dynamic> _$AlbumToJson(Album instance) => <String, dynamic>{
|
|||
'library': instance.library,
|
||||
'type': _$AlbumTypeEnumMap[instance.type],
|
||||
'releaseDate': instance.releaseDate,
|
||||
'favoriteDate': instance.favoriteDate
|
||||
'favoriteDate': instance.favoriteDate,
|
||||
};
|
||||
|
||||
T _$enumDecode<T>(
|
||||
|
@ -149,6 +149,7 @@ Artist _$ArtistFromJson(Map<String, dynamic> json) {
|
|||
offline: json['offline'] as bool,
|
||||
library: json['library'] as bool,
|
||||
radio: json['radio'] as bool,
|
||||
favoriteDate: json['favoriteDate'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -163,6 +164,7 @@ Map<String, dynamic> _$ArtistToJson(Artist instance) => <String, dynamic>{
|
|||
'offline': instance.offline,
|
||||
'library': instance.library,
|
||||
'radio': instance.radio,
|
||||
'favoriteDate': instance.favoriteDate,
|
||||
};
|
||||
|
||||
Playlist _$PlaylistFromJson(Map<String, dynamic> json) {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:audio_session/audio_session.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
@ -11,6 +9,7 @@ import 'package:connectivity/connectivity.dart';
|
|||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
import 'package:scrobblenaut/scrobblenaut.dart';
|
||||
|
||||
import 'definitions.dart';
|
||||
import '../settings.dart';
|
||||
|
@ -28,6 +27,8 @@ class PlayerHelper {
|
|||
StreamSubscription _playbackStateStreamSubscription;
|
||||
QueueSource queueSource;
|
||||
LoopMode repeatType = LoopMode.off;
|
||||
Timer _timer;
|
||||
Scrobblenaut scrobblenaut;
|
||||
//Find queue index by id
|
||||
int get queueIndex => AudioService.queue == null ? 0 : AudioService.queue.indexWhere((mi) => mi.id == AudioService.currentMediaItem?.id??'Random string so it returns -1');
|
||||
|
||||
|
@ -63,18 +64,6 @@ class PlayerHelper {
|
|||
await androidAuto.playItem(event['id']);
|
||||
}
|
||||
});
|
||||
_playbackStateStreamSubscription = AudioService.playbackStateStream.listen((event) {
|
||||
//Log song (if allowed)
|
||||
if (event == null) return;
|
||||
if (event.processingState == AudioProcessingState.ready && event.playing) {
|
||||
if (settings.logListen) {
|
||||
//Check if duplicate
|
||||
if (cache.loggedTrackId == AudioService.currentMediaItem.id) return;
|
||||
cache.loggedTrackId = AudioService.currentMediaItem.id;
|
||||
deezerAPI.logListen(AudioService.currentMediaItem.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
_mediaItemSubscription = AudioService.currentMediaItemStream.listen((event) {
|
||||
if (event == null) return;
|
||||
//Save queue
|
||||
|
@ -86,6 +75,31 @@ class PlayerHelper {
|
|||
cache.save();
|
||||
});
|
||||
|
||||
//Logging listen timer
|
||||
_timer = Timer.periodic(Duration(seconds: 2), (timer) async {
|
||||
if (AudioService.currentMediaItem == null || !AudioService.playbackState.playing) return;
|
||||
if (AudioService.playbackState.currentPosition.inSeconds > (AudioService.currentMediaItem.duration.inSeconds * 0.75)) {
|
||||
if (cache.loggedTrackId == AudioService.currentMediaItem.id) return;
|
||||
cache.loggedTrackId = AudioService.currentMediaItem.id;
|
||||
await cache.save();
|
||||
|
||||
//Log to Deezer
|
||||
if (settings.logListen) {
|
||||
deezerAPI.logListen(AudioService.currentMediaItem.id);
|
||||
}
|
||||
|
||||
//LastFM
|
||||
if (scrobblenaut != null) {
|
||||
await scrobblenaut.track.scrobble(
|
||||
track: AudioService.currentMediaItem.title,
|
||||
artist: AudioService.currentMediaItem.artist,
|
||||
album: AudioService.currentMediaItem.album,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
//Start audio_service
|
||||
await startService();
|
||||
}
|
||||
|
@ -108,6 +122,19 @@ class PlayerHelper {
|
|||
);
|
||||
}
|
||||
|
||||
Future authorizeLastFM() async {
|
||||
if (settings.lastFMUsername == null || settings.lastFMPassword == null) return;
|
||||
try {
|
||||
LastFM lastFM = await LastFM.authenticateWithPasswordHash(
|
||||
apiKey: 'b6ab5ae967bcd8b10b23f68f42493829',
|
||||
apiSecret: '861b0dff9a8a574bec747f9dab8b82bf',
|
||||
username: settings.lastFMUsername,
|
||||
passwordHash: settings.lastFMPassword
|
||||
);
|
||||
scrobblenaut = Scrobblenaut(lastFM: lastFM);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
Future toggleShuffle() async {
|
||||
await AudioService.customAction('shuffle');
|
||||
}
|
||||
|
|
|
@ -38,7 +38,13 @@ class SpotifyAPI {
|
|||
//Parse
|
||||
dom.Document document = parse(response.body);
|
||||
dom.Element element = document.getElementById('resource');
|
||||
return jsonDecode(element.innerHtml);
|
||||
|
||||
//Some are URL encoded
|
||||
try {
|
||||
return jsonDecode(element.innerHtml);
|
||||
} catch (e) {
|
||||
return jsonDecode(Uri.decodeComponent(element.innerHtml));
|
||||
}
|
||||
}
|
||||
|
||||
Future<SpotifyPlaylist> playlist(String uri) async {
|
||||
|
@ -50,6 +56,21 @@ class SpotifyAPI {
|
|||
return playlist;
|
||||
}
|
||||
|
||||
//Get Deezer track ID from Spotify URI
|
||||
Future<String> convertTrack(String uri) async {
|
||||
Map data = await getEmbedData(getEmbedUrl(uri));
|
||||
SpotifyTrack track = SpotifyTrack.fromJson(data);
|
||||
Map deezer = await deezerAPI.callPublicApi('track/isrc:' + track.isrc);
|
||||
return deezer['id'].toString();
|
||||
}
|
||||
|
||||
//Get Deezer album ID by UPC
|
||||
Future<String> convertAlbum(String uri) async {
|
||||
Map data = await getEmbedData(getEmbedUrl(uri));
|
||||
SpotifyAlbum album = SpotifyAlbum.fromJson(data);
|
||||
Map deezer = await deezerAPI.callPublicApi('album/upc:' + album.upc);
|
||||
return deezer['id'].toString();
|
||||
}
|
||||
|
||||
Future convertPlaylist(SpotifyPlaylist playlist, {bool downloadOnly = false, BuildContext context, AudioQuality quality}) async {
|
||||
doneImporting = false;
|
||||
|
@ -132,6 +153,17 @@ class SpotifyPlaylist {
|
|||
);
|
||||
}
|
||||
|
||||
class SpotifyAlbum {
|
||||
String upc;
|
||||
|
||||
SpotifyAlbum({this.upc});
|
||||
|
||||
//JSON
|
||||
factory SpotifyAlbum.fromJson(Map json) => SpotifyAlbum(
|
||||
upc: json['external_ids']['upc']
|
||||
);
|
||||
}
|
||||
|
||||
enum TrackImportState {
|
||||
NONE,
|
||||
ERROR,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue