new SongListTile; discnumber; bug fixes

This commit is contained in:
Moritz Weber 2020-09-28 20:04:03 +02:00
parent ce4e029c2e
commit 3acfd0e978
8 changed files with 170 additions and 45 deletions

View file

@ -9,6 +9,7 @@ class Song extends Equatable {
@required this.path,
@required this.duration,
@required this.blocked,
this.discNumber,
this.trackNumber,
this.albumArtPath,
});
@ -19,6 +20,7 @@ class Song extends Equatable {
final String path;
/// Duration in milliseconds.
final int duration;
final int discNumber;
final int trackNumber;
final String albumArtPath;
/// Is this song blocked in shuffle mode?

View file

@ -7,7 +7,7 @@ import '../../domain/entities/song.dart';
import '../state/audio_store.dart';
import '../state/music_data_store.dart';
import '../utils.dart' as utils;
import '../widgets/album_art_list_tile.dart';
import '../widgets/song_list_tile.dart';
class AlbumDetailsPage extends StatelessWidget {
const AlbumDetailsPage({Key key, @required this.album}) : super(key: key);
@ -69,10 +69,9 @@ class AlbumDetailsPage extends StatelessWidget {
final songIndex = (index / 2).round();
final Song song = musicDataStore.albumSongs[songIndex];
return AlbumArtListTile(
title: song.title,
subtitle: '${song.artist}',
albumArtPath: song.albumArtPath,
return SongListTile(
song: song,
inAlbum: true,
onTap: () => audioStore.playSong(songIndex, musicDataStore.albumSongs),
);
}

View file

@ -5,7 +5,7 @@ import 'package:provider/provider.dart';
import '../../domain/entities/song.dart';
import '../state/audio_store.dart';
import '../state/music_data_store.dart';
import '../widgets/album_art_list_tile.dart';
import '../widgets/song_list_tile.dart';
class SongsPage extends StatefulWidget {
const SongsPage({Key key}) : super(key: key);
@ -42,11 +42,11 @@ class _SongsPageState extends State<SongsPage>
itemCount: songs.length,
itemBuilder: (_, int index) {
final Song song = songs[index];
return AlbumArtListTile(
title: song.title,
subtitle: '${song.artist}${song.album}',
albumArtPath: song.albumArtPath,
return SongListTile(
song: song,
inAlbum: false,
onTap: () => audioStore.playSong(index, songs),
onTapMore: () => print('Hello World'),
);
},
separatorBuilder: (BuildContext context, int index) => const Divider(

View file

@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import '../../domain/entities/song.dart';
import '../theming.dart';
import '../utils.dart' as utils;
class SongListTile extends StatelessWidget {
const SongListTile(
{Key key, this.song, this.onTap, this.inAlbum, this.onTapMore})
: super(key: key);
final Song song;
final bool inAlbum;
final Function onTap;
final Function onTapMore;
@override
Widget build(BuildContext context) {
final Widget leading = inAlbum
? Center(child: Text('${song.discNumber} - ${song.trackNumber}'))
: Image(
image: utils.getAlbumImage(null), // FIXME
fit: BoxFit.cover,
);
final Widget subtitle = inAlbum
? Text('${song.artist}')
: Text('${song.artist}${song.album}');
final EdgeInsets padding = (onTapMore != null)
? const EdgeInsets.only(left: 8.0)
: const EdgeInsets.only(left: 8.0, right: 16.0);
return ListTile(
contentPadding: padding,
leading: SizedBox(
height: 56,
width: 56,
child: leading,
),
title: Text(
song.title,
),
subtitle: subtitle,
onTap: () => onTap(),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (song.blocked)
Icon(
Icons.remove_circle_outline,
size: 14.0,
color: RASPBERRY.withOpacity(0.4),
),
if (onTapMore != null)
IconButton(
icon: const Icon(Icons.more_vert),
iconSize: 20.0,
onPressed: () => print('Hello'),
),
],
),
);
}
}

View file

@ -51,18 +51,15 @@ class AudioPlayerTask extends BackgroundAudioTask {
MediaControl.pause,
MediaControl.skipToNext
],
playing: true, // FIXME: not necessarily true
playing: audioPlayer.playing,
processingState: AudioProcessingState.ready,
updateTime:
Duration(milliseconds: DateTime.now().millisecondsSinceEpoch),
position: const Duration(
milliseconds: 0), // FIXME: not true for shuffle mode changes
position: audioPlayer.position,
);
}
}
Duration position;
@override
Future<void> onStop() async {
await audioPlayer.stop();
@ -70,13 +67,6 @@ class AudioPlayerTask extends BackgroundAudioTask {
await super.onStop();
}
// @override
// Future<void> onClose() async {
// audioPlayer.stop();
// AudioServiceBackground.setState(controls: null, processingState: null, playing: false);
// AudioServiceBackground.setMediaItem(null);
// }
@override
Future<void> onPlay() async {
audioPlayer.play();
@ -120,7 +110,6 @@ class AudioPlayerTask extends BackgroundAudioTask {
Future<void> init() async {
print('AudioPlayerTask.init');
audioPlayer.positionStream.listen((position) => handlePosition(position));
audioPlayer.playerStateStream.listen((event) => handlePlayerState(event));
audioPlayer.sequenceStateStream
.listen((event) => playbackIndex = event?.currentIndex);
@ -144,7 +133,6 @@ class AudioPlayerTask extends BackgroundAudioTask {
{SET_SHUFFLE_MODE: shuffleMode.toString()});
}
// FIXME: position (duration) reset when changing mode
Future<void> setShuffleMode(ShuffleMode mode) async {
shuffleMode = mode;
@ -197,10 +185,6 @@ class AudioPlayerTask extends BackgroundAudioTask {
audioPlayer.play();
}
void handlePosition(Duration position) {
this.position = position;
}
void handlePlayerState(PlayerState ps) {
if (ps.processingState == ProcessingState.ready && ps.playing) {
AudioServiceBackground.setState(
@ -213,7 +197,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
processingState: AudioProcessingState.ready,
updateTime:
Duration(milliseconds: DateTime.now().millisecondsSinceEpoch),
position: position,
position: audioPlayer.position,
);
} else if (ps.processingState == ProcessingState.ready && !ps.playing) {
AudioServiceBackground.setState(
@ -225,7 +209,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
processingState: AudioProcessingState.ready,
updateTime:
Duration(milliseconds: DateTime.now().millisecondsSinceEpoch),
position: position,
position: audioPlayer.position,
playing: false,
);
}

View file

@ -42,6 +42,7 @@ class Songs extends Table {
TextColumn get path => text()();
IntColumn get duration => integer().nullable()();
TextColumn get albumArtPath => text().nullable()();
IntColumn get discNumber => integer().nullable()();
IntColumn get trackNumber => integer().nullable()();
BoolColumn get blocked => boolean().withDefault(const Constant(false))();
BoolColumn get present => boolean().withDefault(const Constant(true))();
@ -90,7 +91,10 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
Future<List<SongModel>> getSongsFromAlbum(AlbumModel album) {
return (select(songs)
..where((tbl) => tbl.albumTitle.equals(album.title))
..orderBy([(t) => OrderingTerm(expression: t.trackNumber)]))
..orderBy([
(t) => OrderingTerm(expression: t.discNumber),
(t) => OrderingTerm(expression: t.trackNumber)
]))
.get()
.then((moorSongList) => moorSongList
.map((moorSong) => SongModel.fromMoorSong(moorSong))
@ -143,15 +147,12 @@ class MoorMusicDataSource extends _$MoorMusicDataSource
@override
Future<void> insertSongs(List<SongModel> songModels) async {
await update(songs).write(const SongsCompanion(present: Value(false)));
await batch((batch) {
batch.insertAllOnConflictUpdate(
songs,
songModels
.map((e) => e.toMoorInsert())
.toList(),
songModels.map((e) => e.toMoorInsert()).toList(),
);
});

View file

@ -496,6 +496,7 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
final String path;
final int duration;
final String albumArtPath;
final int discNumber;
final int trackNumber;
final bool blocked;
final bool present;
@ -507,6 +508,7 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
@required this.path,
this.duration,
this.albumArtPath,
this.discNumber,
this.trackNumber,
@required this.blocked,
@required this.present});
@ -530,6 +532,8 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
intType.mapFromDatabaseResponse(data['${effectivePrefix}duration']),
albumArtPath: stringType
.mapFromDatabaseResponse(data['${effectivePrefix}album_art_path']),
discNumber: intType
.mapFromDatabaseResponse(data['${effectivePrefix}disc_number']),
trackNumber: intType
.mapFromDatabaseResponse(data['${effectivePrefix}track_number']),
blocked:
@ -562,6 +566,9 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
if (!nullToAbsent || albumArtPath != null) {
map['album_art_path'] = Variable<String>(albumArtPath);
}
if (!nullToAbsent || discNumber != null) {
map['disc_number'] = Variable<int>(discNumber);
}
if (!nullToAbsent || trackNumber != null) {
map['track_number'] = Variable<int>(trackNumber);
}
@ -593,6 +600,9 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
albumArtPath: albumArtPath == null && nullToAbsent
? const Value.absent()
: Value(albumArtPath),
discNumber: discNumber == null && nullToAbsent
? const Value.absent()
: Value(discNumber),
trackNumber: trackNumber == null && nullToAbsent
? const Value.absent()
: Value(trackNumber),
@ -616,6 +626,7 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
path: serializer.fromJson<String>(json['path']),
duration: serializer.fromJson<int>(json['duration']),
albumArtPath: serializer.fromJson<String>(json['albumArtPath']),
discNumber: serializer.fromJson<int>(json['discNumber']),
trackNumber: serializer.fromJson<int>(json['trackNumber']),
blocked: serializer.fromJson<bool>(json['blocked']),
present: serializer.fromJson<bool>(json['present']),
@ -632,6 +643,7 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
'path': serializer.toJson<String>(path),
'duration': serializer.toJson<int>(duration),
'albumArtPath': serializer.toJson<String>(albumArtPath),
'discNumber': serializer.toJson<int>(discNumber),
'trackNumber': serializer.toJson<int>(trackNumber),
'blocked': serializer.toJson<bool>(blocked),
'present': serializer.toJson<bool>(present),
@ -646,6 +658,7 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
String path,
int duration,
String albumArtPath,
int discNumber,
int trackNumber,
bool blocked,
bool present}) =>
@ -657,6 +670,7 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
path: path ?? this.path,
duration: duration ?? this.duration,
albumArtPath: albumArtPath ?? this.albumArtPath,
discNumber: discNumber ?? this.discNumber,
trackNumber: trackNumber ?? this.trackNumber,
blocked: blocked ?? this.blocked,
present: present ?? this.present,
@ -671,6 +685,7 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
..write('path: $path, ')
..write('duration: $duration, ')
..write('albumArtPath: $albumArtPath, ')
..write('discNumber: $discNumber, ')
..write('trackNumber: $trackNumber, ')
..write('blocked: $blocked, ')
..write('present: $present')
@ -694,9 +709,11 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
$mrjc(
albumArtPath.hashCode,
$mrjc(
trackNumber.hashCode,
$mrjc(blocked.hashCode,
present.hashCode))))))))));
discNumber.hashCode,
$mrjc(
trackNumber.hashCode,
$mrjc(blocked.hashCode,
present.hashCode)))))))))));
@override
bool operator ==(dynamic other) =>
identical(this, other) ||
@ -708,6 +725,7 @@ class MoorSong extends DataClass implements Insertable<MoorSong> {
other.path == this.path &&
other.duration == this.duration &&
other.albumArtPath == this.albumArtPath &&
other.discNumber == this.discNumber &&
other.trackNumber == this.trackNumber &&
other.blocked == this.blocked &&
other.present == this.present);
@ -721,6 +739,7 @@ class SongsCompanion extends UpdateCompanion<MoorSong> {
final Value<String> path;
final Value<int> duration;
final Value<String> albumArtPath;
final Value<int> discNumber;
final Value<int> trackNumber;
final Value<bool> blocked;
final Value<bool> present;
@ -732,6 +751,7 @@ class SongsCompanion extends UpdateCompanion<MoorSong> {
this.path = const Value.absent(),
this.duration = const Value.absent(),
this.albumArtPath = const Value.absent(),
this.discNumber = const Value.absent(),
this.trackNumber = const Value.absent(),
this.blocked = const Value.absent(),
this.present = const Value.absent(),
@ -744,6 +764,7 @@ class SongsCompanion extends UpdateCompanion<MoorSong> {
@required String path,
this.duration = const Value.absent(),
this.albumArtPath = const Value.absent(),
this.discNumber = const Value.absent(),
this.trackNumber = const Value.absent(),
this.blocked = const Value.absent(),
this.present = const Value.absent(),
@ -760,6 +781,7 @@ class SongsCompanion extends UpdateCompanion<MoorSong> {
Expression<String> path,
Expression<int> duration,
Expression<String> albumArtPath,
Expression<int> discNumber,
Expression<int> trackNumber,
Expression<bool> blocked,
Expression<bool> present,
@ -772,6 +794,7 @@ class SongsCompanion extends UpdateCompanion<MoorSong> {
if (path != null) 'path': path,
if (duration != null) 'duration': duration,
if (albumArtPath != null) 'album_art_path': albumArtPath,
if (discNumber != null) 'disc_number': discNumber,
if (trackNumber != null) 'track_number': trackNumber,
if (blocked != null) 'blocked': blocked,
if (present != null) 'present': present,
@ -786,6 +809,7 @@ class SongsCompanion extends UpdateCompanion<MoorSong> {
Value<String> path,
Value<int> duration,
Value<String> albumArtPath,
Value<int> discNumber,
Value<int> trackNumber,
Value<bool> blocked,
Value<bool> present}) {
@ -797,6 +821,7 @@ class SongsCompanion extends UpdateCompanion<MoorSong> {
path: path ?? this.path,
duration: duration ?? this.duration,
albumArtPath: albumArtPath ?? this.albumArtPath,
discNumber: discNumber ?? this.discNumber,
trackNumber: trackNumber ?? this.trackNumber,
blocked: blocked ?? this.blocked,
present: present ?? this.present,
@ -827,6 +852,9 @@ class SongsCompanion extends UpdateCompanion<MoorSong> {
if (albumArtPath.present) {
map['album_art_path'] = Variable<String>(albumArtPath.value);
}
if (discNumber.present) {
map['disc_number'] = Variable<int>(discNumber.value);
}
if (trackNumber.present) {
map['track_number'] = Variable<int>(trackNumber.value);
}
@ -849,6 +877,7 @@ class SongsCompanion extends UpdateCompanion<MoorSong> {
..write('path: $path, ')
..write('duration: $duration, ')
..write('albumArtPath: $albumArtPath, ')
..write('discNumber: $discNumber, ')
..write('trackNumber: $trackNumber, ')
..write('blocked: $blocked, ')
..write('present: $present')
@ -947,6 +976,18 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, MoorSong> {
);
}
final VerificationMeta _discNumberMeta = const VerificationMeta('discNumber');
GeneratedIntColumn _discNumber;
@override
GeneratedIntColumn get discNumber => _discNumber ??= _constructDiscNumber();
GeneratedIntColumn _constructDiscNumber() {
return GeneratedIntColumn(
'disc_number',
$tableName,
true,
);
}
final VerificationMeta _trackNumberMeta =
const VerificationMeta('trackNumber');
GeneratedIntColumn _trackNumber;
@ -988,6 +1029,7 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, MoorSong> {
path,
duration,
albumArtPath,
discNumber,
trackNumber,
blocked,
present
@ -1045,6 +1087,12 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, MoorSong> {
albumArtPath.isAcceptableOrUnknown(
data['album_art_path'], _albumArtPathMeta));
}
if (data.containsKey('disc_number')) {
context.handle(
_discNumberMeta,
discNumber.isAcceptableOrUnknown(
data['disc_number'], _discNumberMeta));
}
if (data.containsKey('track_number')) {
context.handle(
_trackNumberMeta,

View file

@ -15,6 +15,7 @@ class SongModel extends Song {
@required String path,
@required int duration,
@required bool blocked,
int discNumber,
int trackNumber,
String albumArtPath})
: super(
@ -24,6 +25,7 @@ class SongModel extends Song {
path: path,
duration: duration,
blocked: blocked,
discNumber: discNumber,
trackNumber: trackNumber,
albumArtPath: albumArtPath,
);
@ -36,12 +38,14 @@ class SongModel extends Song {
path: moorSong.path,
duration: moorSong.duration,
blocked: moorSong.blocked,
discNumber: moorSong.discNumber,
trackNumber: moorSong.trackNumber,
albumArtPath: moorSong.albumArtPath,
);
factory SongModel.fromSongInfo(SongInfo songInfo) {
final String duration = songInfo.duration;
final List<int> numbers = _parseTrackNumber(songInfo.track);
return SongModel(
title: songInfo.title,
@ -51,8 +55,9 @@ class SongModel extends Song {
path: songInfo.filePath,
duration: duration == null ? null : int.parse(duration),
blocked: false,
discNumber: numbers[0],
trackNumber: numbers[1],
albumArtPath: songInfo.albumArtwork,
trackNumber: _parseTrackNumber(songInfo.track),
);
}
@ -62,6 +67,9 @@ class SongModel extends Song {
}
final String artUri = mediaItem.artUri?.replaceFirst('file://', '');
final dn = mediaItem.extras['discNumber'];
int discNumber;
final tn = mediaItem.extras['trackNumber'];
int trackNumber;
@ -71,6 +79,12 @@ class SongModel extends Song {
trackNumber = tn as int;
}
if (dn == null) {
discNumber = null;
} else {
discNumber = dn as int;
}
return SongModel(
title: mediaItem.title,
album: mediaItem.album,
@ -79,8 +93,9 @@ class SongModel extends Song {
path: mediaItem.id,
duration: mediaItem.duration.inMilliseconds,
blocked: mediaItem.extras['blocked'] == 'true',
albumArtPath: artUri,
discNumber: discNumber,
trackNumber: trackNumber,
albumArtPath: artUri,
);
}
@ -98,6 +113,7 @@ class SongModel extends Song {
String path,
int duration,
bool blocked,
int discNumber,
int trackNumber,
String albumArtPath,
int albumId,
@ -109,6 +125,7 @@ class SongModel extends Song {
path: path ?? this.path,
title: title ?? this.title,
blocked: blocked ?? this.blocked,
discNumber: discNumber ?? this.discNumber,
trackNumber: trackNumber ?? this.trackNumber,
albumArtPath: albumArtPath ?? this.albumArtPath,
albumId: albumId ?? this.albumId,
@ -122,8 +139,9 @@ class SongModel extends Song {
path: Value(path),
duration: Value(duration),
blocked: Value(blocked),
albumArtPath: Value(albumArtPath),
discNumber: Value(discNumber),
trackNumber: Value(trackNumber),
albumArtPath: Value(albumArtPath),
);
SongsCompanion toMoorInsert() => SongsCompanion(
@ -134,6 +152,7 @@ class SongModel extends Song {
path: Value(path),
duration: Value(duration),
albumArtPath: Value(albumArtPath),
discNumber: Value(discNumber),
trackNumber: Value(trackNumber),
// blocked: Value(blocked),
present: const Value(true),
@ -149,21 +168,28 @@ class SongModel extends Song {
extras: {
'albumId': albumId,
'blocked': blocked.toString(),
'discNumber': discNumber,
'trackNumber': trackNumber,
});
static int _parseTrackNumber(String trackNumberString) {
static List<int> _parseTrackNumber(String trackNumberString) {
int discNumber = 1;
int trackNumber;
if (trackNumberString == null) {
return null;
return [null, null];
}
trackNumber = int.tryParse(trackNumberString);
if (trackNumber == null) {
if (trackNumberString.contains('/')) {
trackNumber = int.tryParse(trackNumberString.split('/')[0]);
discNumber = int.tryParse(trackNumberString.split('/')[0]);
trackNumber = int.tryParse(trackNumberString.split('/')[1]);
}
} else if (trackNumber > 1000) {
discNumber = int.tryParse(trackNumberString.substring(0, 1));
trackNumber = int.tryParse(trackNumberString.substring(1));
}
return trackNumber;
return [discNumber, trackNumber];
}
}