0.6.10 - Spotify is ass
This commit is contained in:
parent
3105ed6c1d
commit
676d5d45cc
23 changed files with 1000 additions and 339 deletions
171
lib/api/importer.dart
Normal file
171
lib/api/importer.dart
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezer/api/deezer.dart';
|
||||
import 'package:freezer/api/definitions.dart';
|
||||
import 'package:freezer/api/download.dart';
|
||||
|
||||
Importer importer = Importer();
|
||||
|
||||
class Importer {
|
||||
//Options
|
||||
bool download = false;
|
||||
|
||||
//Preserve context
|
||||
BuildContext context;
|
||||
String title;
|
||||
String description;
|
||||
List<ImporterTrack> tracks;
|
||||
String playlistId;
|
||||
Playlist playlist;
|
||||
|
||||
bool done = false;
|
||||
bool busy = false;
|
||||
Future _future;
|
||||
StreamController _streamController;
|
||||
|
||||
Stream get updateStream => _streamController.stream;
|
||||
int get ok => tracks.fold(0, (v, t) => (t.state == TrackImportState.OK) ? v+1 : v);
|
||||
int get error => tracks.fold(0, (v, t) => (t.state == TrackImportState.ERROR) ? v+1 : v);
|
||||
|
||||
Importer();
|
||||
|
||||
//Start importing wrapper
|
||||
Future<void> start(BuildContext context, String title, String description, List<ImporterTrack> tracks) async {
|
||||
//Save variables
|
||||
this.playlist = null;
|
||||
this.context = context;
|
||||
this.title = title;
|
||||
this.description = description??'';
|
||||
this.tracks = tracks.map((t) {t.state = TrackImportState.NONE; return t;}).toList();
|
||||
|
||||
//Create playlist
|
||||
playlistId = await deezerAPI.createPlaylist(title, description: description);
|
||||
|
||||
busy = true;
|
||||
done = false;
|
||||
_streamController = StreamController.broadcast();
|
||||
_future = _start();
|
||||
}
|
||||
|
||||
//Start importer
|
||||
Future _start() async {
|
||||
for (int i=0; i<tracks.length; i++) {
|
||||
try {
|
||||
String id = await _searchTrack(tracks[i]);
|
||||
//Not found
|
||||
if (id == null) {
|
||||
tracks[i].state = TrackImportState.ERROR;
|
||||
_streamController.add(tracks[i]);
|
||||
continue;
|
||||
}
|
||||
//Add to playlist
|
||||
await deezerAPI.addToPlaylist(id, playlistId);
|
||||
tracks[i].state = TrackImportState.OK;
|
||||
} catch (_) {
|
||||
//Error occurred, mark as error
|
||||
tracks[i].state = TrackImportState.ERROR;
|
||||
}
|
||||
_streamController.add(tracks[i]);
|
||||
}
|
||||
//Get full playlist
|
||||
playlist = await deezerAPI.playlist(playlistId, nb: 10000);
|
||||
playlist.library = true;
|
||||
|
||||
//Download
|
||||
if (download) {
|
||||
await downloadManager.addOfflinePlaylist(playlist, private: false, context: context);
|
||||
}
|
||||
|
||||
//Mark as done
|
||||
done = true;
|
||||
busy = false;
|
||||
//To update UI
|
||||
_streamController.add(null);
|
||||
_streamController.close();
|
||||
}
|
||||
|
||||
//Find track on Deezer servers
|
||||
Future<String> _searchTrack(ImporterTrack track) async {
|
||||
//Try by ISRC
|
||||
if (track.isrc != null && track.isrc.length == 12) {
|
||||
Map deezer = await deezerAPI.callPublicApi('track/isrc:' + track.isrc);
|
||||
if (deezer["id"] != null) {
|
||||
return deezer["id"].toString();
|
||||
}
|
||||
}
|
||||
|
||||
//Search
|
||||
String cleanedTitle = track.title.trim().toLowerCase().replaceAll("-", "").replaceAll("&", "").replaceAll("+", "");
|
||||
SearchResults results = await deezerAPI.search("${track.artists[0]} $cleanedTitle");
|
||||
for (Track t in results.tracks) {
|
||||
//Match title
|
||||
if (_cleanMatching(t.title) == _cleanMatching(track.title)) {
|
||||
//Match artist
|
||||
if (_matchArtists(track.artists, t.artists.map((a) => a.name))) {
|
||||
return t.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Clean title for matching
|
||||
String _cleanMatching(String t) {
|
||||
return t.toLowerCase()
|
||||
.replaceAll(",", "")
|
||||
.replaceAll("-", "")
|
||||
.replaceAll(" ", "")
|
||||
.replaceAll("&", "")
|
||||
.replaceAll("+", "")
|
||||
.replaceAll("/", "");
|
||||
}
|
||||
|
||||
String _cleanArtist(String a) {
|
||||
return a.toLowerCase()
|
||||
.replaceAll(" ", "")
|
||||
.replaceAll(",", "");
|
||||
}
|
||||
|
||||
//Match at least 1 artist
|
||||
bool _matchArtists(List<String> a, List<String> b) {
|
||||
//Clean
|
||||
List<String> _a = a.map(_cleanArtist).toList();
|
||||
List<String> _b = b.map(_cleanArtist).toList();
|
||||
|
||||
for (String artist in _a) {
|
||||
if (_b.contains(artist)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ImporterTrack {
|
||||
String title;
|
||||
List<String> artists;
|
||||
String isrc;
|
||||
TrackImportState state;
|
||||
|
||||
ImporterTrack(this.title, this.artists, {this.isrc, this.state = TrackImportState.NONE});
|
||||
}
|
||||
|
||||
enum TrackImportState {
|
||||
NONE,
|
||||
ERROR,
|
||||
OK
|
||||
}
|
||||
|
||||
extension TrackImportStateExtension on TrackImportState {
|
||||
Widget get icon {
|
||||
switch (this) {
|
||||
case TrackImportState.ERROR:
|
||||
return Icon(Icons.error, color: Colors.red,);
|
||||
case TrackImportState.OK:
|
||||
return Icon(Icons.done, color: Colors.green);
|
||||
default:
|
||||
return Container(width: 0, height: 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue