Skip to content

Commit

Permalink
chore: many performance & ui tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
MSOB7YY committed Mar 9, 2024
1 parent 00bd918 commit 828ce5f
Show file tree
Hide file tree
Showing 26 changed files with 352 additions and 278 deletions.
121 changes: 76 additions & 45 deletions lib/base/audio_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ class NamidaAudioVideoHandler<Q extends Playable> extends BasicAudioHandler<Q> {
late final equalizer = AndroidEqualizer();
late final loudnessEnhancer = AndroidLoudnessEnhancer();

Duration? get currentItemDuration => _currentItemDuration.value;
final _currentItemDuration = Rxn<Duration>();

Timer? _resourcesDisposeTimer;

@override
Expand Down Expand Up @@ -441,6 +444,8 @@ class NamidaAudioVideoHandler<Q extends Playable> extends BasicAudioHandler<Q> {

@override
Future<void> onItemPlay(Q item, int index, bool startPlaying) async {
_currentItemDuration.value = null;

await item._execute(
selectable: (finalItem) async {
await onItemPlaySelectable(item, finalItem, index, startPlaying);
Expand Down Expand Up @@ -504,44 +509,55 @@ class NamidaAudioVideoHandler<Q extends Playable> extends BasicAudioHandler<Q> {

Duration? duration;

bool checkInterrupted() {
if (item.track != currentTrack.track) {
return true;
} else {
if (duration != null) _currentItemDuration.value = duration;
return false;
}
}

try {
duration = await setPls();
if (checkInterrupted()) return;
} on Exception catch (e) {
if (duration != null && currentPositionMS > 0) return;
if (item.track != currentTrack.track) return;
printy(e, isError: true);
// -- playing music from root folders still require `all_file_access`
// -- this is a fix for not playing some external files reported by some users.
final hadPermissionBefore = await Permission.manageExternalStorage.isGranted;
if (hadPermissionBefore) {
pause();
cancelPlayErrorSkipTimer();
_playErrorRemainingSecondsToSkip.value = 7;

_playErrorSkipTimer = Timer.periodic(
const Duration(seconds: 1),
(timer) {
_playErrorRemainingSecondsToSkip.value--;
if (_playErrorRemainingSecondsToSkip.value <= 0) {
NamidaNavigator.inst.closeDialog();
skipToNext();
timer.cancel();
}
},
);
NamidaDialogs.inst.showTrackDialog(
tr,
isFromPlayerQueue: true,
errorPlayingTrack: e,
source: QueueSource.playerQueue,
);
return;
} else {
final hasPermission = await requestManageStoragePermission();
if (hasPermission) {
duration = await setPls();
} else {
if (checkInterrupted()) return;
final reallyError = !(duration != null && currentPositionMS > 0);
if (reallyError) {
printy(e, isError: true);
// -- playing music from root folders still require `all_file_access`
// -- this is a fix for not playing some external files reported by some users.
final hadPermissionBefore = await Permission.manageExternalStorage.isGranted;
if (hadPermissionBefore) {
pause();
cancelPlayErrorSkipTimer();
_playErrorRemainingSecondsToSkip.value = 7;

_playErrorSkipTimer = Timer.periodic(
const Duration(seconds: 1),
(timer) {
_playErrorRemainingSecondsToSkip.value--;
if (_playErrorRemainingSecondsToSkip.value <= 0) {
NamidaNavigator.inst.closeDialog();
skipToNext();
timer.cancel();
}
},
);
NamidaDialogs.inst.showTrackDialog(
tr,
isFromPlayerQueue: true,
errorPlayingTrack: e,
source: QueueSource.playerQueue,
);
return;
} else {
final hasPermission = await requestManageStoragePermission();
if (!hasPermission) return;
try {
duration = await setPls();
} catch (_) {}
}
}
}
Expand Down Expand Up @@ -837,7 +853,20 @@ class NamidaAudioVideoHandler<Q extends Playable> extends BasicAudioHandler<Q> {
possibleAudioFiles: audioCacheMap[item.id] ?? [],
possibleLocalFiles: Indexer.inst.allTracksMappedByYTID[item.id] ?? [],
);
if (item != currentVideo) return;

Duration? duration = playedFromCacheDetails.duration;

// race avoidance when playing multiple videos
bool checkInterrupted() {
if (item != currentVideo) {
return true;
} else {
if (duration != null) _currentItemDuration.value = duration;
return false;
}
}

if (checkInterrupted()) return;

currentCachedAudio.value = playedFromCacheDetails.audio;
currentCachedVideo.value = playedFromCacheDetails.video;
Expand All @@ -852,7 +881,7 @@ class NamidaAudioVideoHandler<Q extends Playable> extends BasicAudioHandler<Q> {
heyIhandledAudioPlaying = false;
}

if (item != currentVideo) return;
if (checkInterrupted()) return;

if (ConnectivityController.inst.hasConnection) {
try {
Expand All @@ -865,7 +894,7 @@ class NamidaAudioVideoHandler<Q extends Playable> extends BasicAudioHandler<Q> {
}
_isFetchingInfo.value = false;
if (streams == null) return;
if (item != currentVideo) return; // race avoidance when playing multiple videos
if (checkInterrupted()) return;
YoutubeController.inst.currentYTQualities.value = streams.videoOnlyStreams ?? [];
YoutubeController.inst.currentYTAudioStreams.value = streams.audioOnlyStreams ?? [];
currentVideoInfo.value = streams.videoInfo;
Expand Down Expand Up @@ -895,7 +924,7 @@ class NamidaAudioVideoHandler<Q extends Playable> extends BasicAudioHandler<Q> {
final cachedAudio = prefferedAudioStream?.getCachedFile(item.id);
final mediaItem = item.toMediaItem(currentVideoInfo.value, currentVideoThumbnail.value, index, currentQueue.length);
_isCurrentAudioFromCache = cachedAudio != null;
if (item != currentVideo) return; // race avoidance when playing multiple videos
if (checkInterrupted()) return;
final isVideoCacheSameAsPrevSet = cachedVideo != null &&
playedFromCacheDetails.video != null &&
playedFromCacheDetails.video?.path == cachedVideo.path; // only if not the same cache path (i.e. diff resolution)
Expand All @@ -920,13 +949,13 @@ class NamidaAudioVideoHandler<Q extends Playable> extends BasicAudioHandler<Q> {
);
}
await playerStoppingSeikoo.future;
if (item != currentVideo) return;
if (checkInterrupted()) return;

if (cachedVideo?.path != null) {
File(cachedVideo!.path).setLastAccessedTry(DateTime.now());
}
if (shouldResetAudioSource) {
cachedAudio != null
duration = cachedAudio != null
? await setSource(
AudioSource.file(cachedAudio.path, tag: mediaItem),
startPlaying: startPlaying,
Expand Down Expand Up @@ -955,7 +984,7 @@ class NamidaAudioVideoHandler<Q extends Playable> extends BasicAudioHandler<Q> {
refreshNotification();
}
} catch (e) {
if (item != currentVideo) return; // race avoidance when playing multiple videos
if (checkInterrupted()) return;
void showSnackError(String nextAction) {
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
if (item == currentItem) {
Expand All @@ -977,10 +1006,12 @@ class NamidaAudioVideoHandler<Q extends Playable> extends BasicAudioHandler<Q> {
possibleAudioFiles: audioCacheMap[item.id] ?? [],
possibleLocalFiles: Indexer.inst.allTracksMappedByYTID[item.id] ?? [],
);
if (item == currentVideo) generateWaveform();
if (!okaySetFromCache()) {
showSnackError('skipping');
skipToNext();
if (!checkInterrupted()) {
generateWaveform();
if (!okaySetFromCache()) {
showSnackError('skipping');
skipToNext();
}
}
}
}
Expand Down
49 changes: 38 additions & 11 deletions lib/base/pull_to_refresh.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import 'package:namida/core/icon_fonts/broken_icons.dart';
mixin PullToRefreshMixin<T extends StatefulWidget> on State<T> implements TickerProvider {
final turnsTween = Tween<double>(begin: 0.0, end: 1.0);
late final animation = AnimationController(vsync: this, duration: Duration.zero);
AnimationController get animation2;

AnimationController? get refreshAnimation => null;
AnimationController? _animation2Backup;
AnimationController get _animation2 => refreshAnimation ?? _animation2Backup!;

final _minTrigger = 20;

num get pullNormalizer => 20;
num get pullNormalizer => 100;

double _distanceDragged = 0;
bool onVerticalDragUpdate(double dy) {
Expand All @@ -23,16 +26,28 @@ mixin PullToRefreshMixin<T extends StatefulWidget> on State<T> implements Ticker
return true;
}

void onPointerMove(ScrollController sc, PointerMoveEvent event) {
if (!sc.hasClients) return;
final p = sc.position.pixels;
if (p <= 0 && event.delta.dx < 0.1) onVerticalDragUpdate(event.delta.dy);
}

void onVerticalDragFinish() {
animation.animateTo(0, duration: const Duration(milliseconds: 100));
_distanceDragged = 0;
}

Future<void> showRefreshingAnimation(Future<void> Function() whileExecuting) async {
animation2.repeat();
await whileExecuting();
await animation2.fling();
animation2.stop();
bool _isRefreshing = false;
Future<void> onRefresh(Future<void> Function() execute) async {
onVerticalDragFinish();
if (animation.value != 1 || _isRefreshing) return;
_isRefreshing = true;
_animation2.repeat();
await execute();
await _animation2.fling();
_animation2.stop();
_isRefreshing = false;
onVerticalDragFinish();
}

Widget get pullToRefreshWidget {
Expand All @@ -48,22 +63,22 @@ mixin PullToRefreshMixin<T extends StatefulWidget> on State<T> implements Ticker
),
builder: (context, circleAvatar) {
final p = animation.value;
if (!animation2.isAnimating && p == 0) return const SizedBox();
if (!_animation2.isAnimating && p == 0) return const SizedBox();
const multiplier = 4.5;
const minus = multiplier / 3;
return Padding(
padding: EdgeInsets.only(top: 12.0 + p * 128.0),
child: Transform.rotate(
angle: (p * multiplier) - minus,
child: AnimatedBuilder(
animation: animation2,
animation: _animation2,
child: circleAvatar,
builder: (context, circleAvatar) {
return Opacity(
opacity: animation2.status == AnimationStatus.forward ? 1.0 : p,
opacity: _animation2.status == AnimationStatus.forward ? 1.0 : p,
child: RotationTransition(
key: const Key('rotatie'),
turns: turnsTween.animate(animation2),
turns: turnsTween.animate(_animation2),
child: circleAvatar,
),
);
Expand All @@ -76,9 +91,21 @@ mixin PullToRefreshMixin<T extends StatefulWidget> on State<T> implements Ticker
);
}

@override
void initState() {
super.initState();
if (refreshAnimation == null) {
_animation2Backup = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
}
}

@override
void dispose() {
animation.dispose();
_animation2Backup?.dispose();
super.dispose();
}
}
4 changes: 3 additions & 1 deletion lib/base/youtube_streams_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ mixin YoutubeStreamsManager {
case YTVideosSorting.date:
return (lang.DATE, Broken.calendar);
case YTVideosSorting.views:
return (lang.VIEWS, Broken.eye);
final word = lang.VIEWS;
final finalw = word.length > 1 ? "${word[0].toUpperCase()}${word.substring(1)}" : word;
return (finalw, Broken.eye);
case YTVideosSorting.duration:
return (lang.DURATION, Broken.timer_1);
}
Expand Down
9 changes: 3 additions & 6 deletions lib/controller/current_color.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class CurrentColor {

final _namidaColorMiniplayer = Rxn<Color>();

final Rx<NamidaColor> _namidaColor = NamidaColor(
late final Rx<NamidaColor> _namidaColor = NamidaColor(
used: playerStaticColor,
mix: playerStaticColor,
palette: [playerStaticColor],
Expand Down Expand Up @@ -352,8 +352,8 @@ class CurrentColor {
final nc = await extractPaletteFromImage(imagePath, track: track, forceReExtract: true, useIsolate: useIsolate);
_updateInColorMap(filename, nc);
}
if (Player.inst.nowPlayingTrack == track) {
updatePlayerColorFromTrack(track, null);
if (Player.inst.nowPlayingTWD == track) {
updatePlayerColorFromTrack(Player.inst.nowPlayingTWD, null);
}
}

Expand Down Expand Up @@ -424,9 +424,6 @@ class CurrentColor {

void _updateInColorMap(String filenameWoExt, NamidaColor? nc) {
if (nc != null) _colorsMap[filenameWoExt] = nc;
if (filenameWoExt == Player.inst.nowPlayingTrack.path.getFilename) {
updatePlayerColorFromTrack(Player.inst.nowPlayingTrack, null);
}
}

void _updateCurrentPaletteHalfs(NamidaColor nc) {
Expand Down
1 change: 1 addition & 0 deletions lib/controller/lyrics_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ class Lyrics {
if (lyricsRes.contains('<meta charset="UTF-8">')) return '';
if (lyricsRes.contains('please enable javascript on your web browser')) return '';
if (lyricsRes.contains('Error 500 (Server Error)')) return '';
if (lyricsRes.contains('systems have detected unusual traffic from your computer network')) return '';
return lyricsRes;
}

Expand Down
7 changes: 6 additions & 1 deletion lib/controller/player_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@ class Player {
bool get isBuffering => _audioHandler.currentState == ProcessingState.buffering;
bool get isLoading => _audioHandler.currentState == ProcessingState.loading;
bool get isFetchingInfo => _audioHandler.isFetchingInfo;
bool get shouldShowLoadingIndicator => (isFetchingInfo && _audioHandler.currentCachedVideo.value == null) || isBuffering || isLoading;
bool get shouldShowLoadingIndicator {
if (isBuffering || isLoading) return true;
if (isFetchingInfo && _audioHandler.currentState != ProcessingState.ready) return true;
return false;
}

Duration get buffered => _audioHandler.buffered;
int get numberOfRepeats => _audioHandler.numberOfRepeats;
int get latestInsertedIndex => _audioHandler.latestInsertedIndex;
Expand Down
2 changes: 0 additions & 2 deletions lib/controller/thumbnail_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ class ThumbnailManager {
String? id,
String? channelUrlOrID,
bool isImportantInCache = true,
FutureOr<void> Function()? beforeFetchingFromInternet,
void Function(Uint8List? bytes)? bytesIfWontWriteToFile,
bool hqChannelImage = false,
}) async {
Expand All @@ -128,7 +127,6 @@ class ThumbnailManager {
}

_printie('Downloading Thumbnail Started');
await beforeFetchingFromInternet?.call();

if (channelUrlOrID != null && hqChannelImage) {
final res = await _getChannelAvatarUrlIsolate.thready(channelUrlOrID);
Expand Down

0 comments on commit 828ce5f

Please sign in to comment.