Skip to content

Commit

Permalink
attempt to fix bug notification frequency
Browse files Browse the repository at this point in the history
fix issue where tapping an offline video in the player queue would not switch the video
add some texts to show if some settings are enabled / used without having to go inside the settings sub screen
reduce download listener frequency to avoid unnecessary redraws, will update at most every 500ms
  • Loading branch information
lamarios committed Dec 11, 2023
1 parent 297ea6f commit 827deb8
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 83 deletions.
68 changes: 45 additions & 23 deletions lib/downloads/states/download_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:io';
import 'package:bloc/bloc.dart';
import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:dio/dio.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:invidious/downloads/models/downloaded_video.dart';
import 'package:invidious/extensions.dart';
Expand Down Expand Up @@ -64,28 +65,35 @@ class DownloadManagerCubit extends Cubit<DownloadManagerState> {

void playAll() {
setVideos();
player.playOfflineVideos(state.videos.where((element) => element.downloadComplete && !element.downloadFailed).toList());
player.playOfflineVideos(
state.videos.where((element) => element.downloadComplete && !element.downloadFailed).toList());
}

onProgress(int count, int total, DownloadedVideo video) {
// log.fine('Download of video $videoId}, $count / $total = ${count / total}');
var progresses = Map<String, DownloadProgress>.from(state.downloadProgresses);
var downloadProgress = progresses[video.videoId];
downloadProgress?.count = count;
downloadProgress?.total = total;
emit(state.copyWith(downloadProgresses: progresses));

if (count == total) {
var progresses = Map<String, DownloadProgress>.from(state.downloadProgresses);
var downloadProgress = progresses[video.videoId];
progresses.remove(video.videoId);
video.downloadComplete = true;
db.upsertDownload(video);
emit(state.copyWith(downloadProgresses: progresses));
setVideos();
}

for (var f in downloadProgress?.listeners ?? []) {
f(count / total);
for (var f in downloadProgress?.listeners ?? []) {
f(count / total);
}
} else {
EasyThrottle.throttle('download-${video.id}', const Duration(milliseconds: 500), () {
log.fine('Download of video ${video.id}, $count / $total = ${count / total}, Total: ${state.totalProgress}');
var progresses = Map<String, DownloadProgress>.from(state.downloadProgresses);
var downloadProgress = progresses[video.videoId];
downloadProgress?.count = count;
downloadProgress?.total = total;
emit(state.copyWith(downloadProgresses: progresses));
setVideos();
for (var f in downloadProgress?.listeners ?? []) {
f(count / total);
}
});
}
}

Expand All @@ -94,8 +102,14 @@ class DownloadManagerCubit extends Cubit<DownloadManagerState> {
return false;
} else {
Video vid = await service.getVideo(videoId);
var downloadedVideo =
DownloadedVideo(videoId: vid.videoId, title: vid.title, author: vid.author, authorUrl: vid.authorUrl, audioOnly: audioOnly, lengthSeconds: vid.lengthSeconds, quality: quality);
var downloadedVideo = DownloadedVideo(
videoId: vid.videoId,
title: vid.title,
author: vid.author,
authorUrl: vid.authorUrl,
audioOnly: audioOnly,
lengthSeconds: vid.lengthSeconds,
quality: quality);
db.upsertDownload(downloadedVideo);

String contentUrl;
Expand All @@ -104,7 +118,9 @@ class DownloadManagerCubit extends Cubit<DownloadManagerState> {
FormatStream stream = vid.formatStreams.firstWhere((element) => element.resolution == quality);
contentUrl = stream.url;
} else {
AdaptiveFormat audio = vid.adaptiveFormats.sortByReversed((e) => int.parse(e.bitrate ?? "0")).firstWhere((element) => element.type.contains("audio"));
AdaptiveFormat audio = vid.adaptiveFormats
.sortByReversed((e) => int.parse(e.bitrate ?? "0"))
.firstWhere((element) => element.type.contains("audio"));
contentUrl = audio.url;
}

Expand All @@ -129,9 +145,11 @@ class DownloadManagerCubit extends Cubit<DownloadManagerState> {
// download video
var videoPath = await downloadedVideo.mediaPath;

log.info("Downloading video ${vid.title}, audioOnly ? $audioOnly, quality: $quality (if not only audio) to path: $videoPath");
log.info(
"Downloading video ${vid.title}, audioOnly ? $audioOnly, quality: $quality (if not only audio) to path: $videoPath");
dio
.download(contentUrl, videoPath, onReceiveProgress: (count, total) => onProgress(count, total, downloadedVideo), cancelToken: cancelToken)
.download(contentUrl, videoPath,
onReceiveProgress: (count, total) => onProgress(count, total, downloadedVideo), cancelToken: cancelToken)
.catchError((err) => onDownloadError(err, downloadedVideo));

return true;
Expand Down Expand Up @@ -202,21 +220,25 @@ class DownloadManagerCubit extends Cubit<DownloadManagerState> {
}

bool canPlayAll() => state.videos.where((element) => element.downloadComplete).isNotEmpty;
}

@freezed
class DownloadManagerState with _$DownloadManagerState {
const factory DownloadManagerState(
{@Default([]) List<DownloadedVideo> videos,
@Default({}) Map<String, DownloadProgress> downloadProgresses}) = _DownloadManagerState;

const DownloadManagerState._();

double get totalProgress {
int downloaded = 0;
int total = 0;

for (var element in state.downloadProgresses.values) {
for (var element in downloadProgresses.values) {
downloaded += element.count;
total += element.total;
}

return downloaded / total;
}
}

@freezed
class DownloadManagerState with _$DownloadManagerState {
const factory DownloadManagerState({@Default([]) List<DownloadedVideo> videos, @Default({}) Map<String, DownloadProgress> downloadProgresses}) = _DownloadManagerState;
}
8 changes: 5 additions & 3 deletions lib/downloads/states/download_manager.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,13 @@ class __$$DownloadManagerStateImplCopyWithImpl<$Res>

/// @nodoc
class _$DownloadManagerStateImpl implements _DownloadManagerState {
class _$DownloadManagerStateImpl extends _DownloadManagerState {
const _$DownloadManagerStateImpl(
{final List<DownloadedVideo> videos = const [],
final Map<String, DownloadProgress> downloadProgresses = const {}})
: _videos = videos,
_downloadProgresses = downloadProgresses;
_downloadProgresses = downloadProgresses,
super._();

final List<DownloadedVideo> _videos;
@override
Expand Down Expand Up @@ -164,11 +165,12 @@ class _$DownloadManagerStateImpl implements _DownloadManagerState {
this, _$identity);
}

abstract class _DownloadManagerState implements DownloadManagerState {
abstract class _DownloadManagerState extends DownloadManagerState {
const factory _DownloadManagerState(
{final List<DownloadedVideo> videos,
final Map<String, DownloadProgress> downloadProgresses}) =
_$DownloadManagerStateImpl;
const _DownloadManagerState._() : super._();

@override
List<DownloadedVideo> get videos;
Expand Down
82 changes: 44 additions & 38 deletions lib/downloads/views/components/download_app_bar_button.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:invidious/globals.dart';
import 'package:invidious/router.dart';

import '../../states/download_manager.dart';
Expand All @@ -15,46 +16,51 @@ class AppBarDownloadButton extends StatelessWidget {
return BlocBuilder<DownloadManagerCubit, DownloadManagerState>(
// buildWhen: (previous, current) => previous.videos.length != current.videos.length || previous.totalProgress != current.totalProgress,
builder: (context, _) {
var cubit = context.read<DownloadManagerCubit>();
return Stack(
alignment: Alignment.center,
children: [
IconButton(
onPressed: () => openDownloadManager(context),
icon: Icon(
Icons.download,
color: _.downloadProgresses.isNotEmpty ? colors.background : null,
alignment: Alignment.center,
children: [
IconButton(
onPressed: () => openDownloadManager(context),
icon: Icon(
Icons.download,
color: _.downloadProgresses.isNotEmpty ? colors.background : null,
),
),
),
_.downloadProgresses.isNotEmpty
? InkWell(
onTap: () => openDownloadManager(context),
child: SizedBox(
width: 15,
height: 15,
child: CircularProgressIndicator(
value: cubit.totalProgress == 0 ? null : cubit.totalProgress,
strokeWidth: 2,
)),
)
: const SizedBox.shrink(),
Positioned(
top: 1,
right: 1,
child: _.videos.isNotEmpty
? GestureDetector(
onTap: () => openDownloadManager(context),
child: Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(color: colors.secondaryContainer, shape: BoxShape.circle),
child: Text(
_.videos.length.toString(),
style: textTheme.labelSmall,
)),
)
: const SizedBox.shrink())
],
);
_.downloadProgresses.isNotEmpty
? InkWell(
onTap: () => openDownloadManager(context),
child: SizedBox(
width: 15,
height: 15,
child: TweenAnimationBuilder(
builder: (context, value, child) {
return CircularProgressIndicator(
value: _.totalProgress == 0 ? null: value,
strokeWidth: 2,
);
},
duration: animationDuration,
tween: Tween<double>(begin: 0, end: _.totalProgress),
)),
)
: const SizedBox.shrink(),
Positioned(
top: 1,
right: 1,
child: _.videos.isNotEmpty
? GestureDetector(
onTap: () => openDownloadManager(context),
child: Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(color: colors.secondaryContainer, shape: BoxShape.circle),
child: Text(
_.videos.length.toString(),
style: textTheme.labelSmall,
)),
)
: const SizedBox.shrink())
],
);
},
);
}
Expand Down
24 changes: 17 additions & 7 deletions lib/downloads/views/components/downloaded_video.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ class DownloadedVideoView extends StatelessWidget {
var downloadManager = context.read<DownloadManagerCubit>();
var player = context.read<PlayerCubit>();
return BlocProvider(
create: (BuildContext context) => DownloadedVideoCubit(downloadManager, DownloadedVideoState.init(video.id), player),
create: (BuildContext context) =>
DownloadedVideoCubit(downloadManager, DownloadedVideoState.init(video.id), player),
child: BlocBuilder<DownloadedVideoCubit, DownloadedVideoState>(builder: (context, _) {
bool downloadFailed = _.video?.downloadFailed ?? false;
var cubit = context.read<DownloadedVideoCubit>();
Expand All @@ -46,12 +47,21 @@ class DownloadedVideoView extends StatelessWidget {
: const SizedBox.shrink(),
(_.video?.downloadComplete ?? false) || downloadFailed
? const SizedBox.shrink()
: SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
value: _.progress == 0 ? null : _.progress,
: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: SizedBox(
height: 20,
width: 20,
child: TweenAnimationBuilder(
duration: animationDuration,
tween: Tween<double>(begin: 0, end: _.progress),
builder: (context, value, child) {
return CircularProgressIndicator(
strokeWidth: 2,
value: _.progress == 0 ? null : value,
);
}
),
),
)
],
Expand Down
10 changes: 10 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,16 @@
"@videoFilters": {
"description": "Title for video filter settings"
},
"nFilters": "{count, plural, =0{No videos} =1{1 filter} other{{count} filters}}",
"@nFilters": {
"description": "One or more video filters",
"placeholders": {
"count": {
"type": "num",
"format": "compact"
}
}
},
"videoFiltersExplanation": "Hide or Obfuscate videos from all the video feeds in the application based on the filters defined below. This allow you for example to hide sports spoilers or hide shorts from a certain channel.",
"@videoFiltersExplanation": {
"description": "Description on how filter work"
Expand Down
9 changes: 5 additions & 4 deletions lib/player/states/player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -418,16 +418,17 @@ class PlayerCubit extends Cubit<PlayerState> with WidgetsBindingObserver {
/// skip to queue video of index
/// if we're not shuffling, we also rebuild the playnext and played previously queue
skipToVideo(int index) {
if (index < 0 || index >= state.videos.length) {
var listToCheckAgainst = state.videos.isNotEmpty ? state.videos : state.offlineVideos;
if (index < 0 || index >= listToCheckAgainst.length) {
return;
}

if (settings.state.playerShuffleMode) {
} else {
List<String> played = [];
List<String> playNext = [];
for (int i = 0; i < state.videos.length; i++) {
var v = state.videos[i];
for (int i = 0; i < listToCheckAgainst.length; i++) {
var v = listToCheckAgainst[i];
if (i < index) {
played.add(v.videoId);
} else if (i > index) {
Expand All @@ -436,7 +437,7 @@ class PlayerCubit extends Cubit<PlayerState> with WidgetsBindingObserver {
}
emit(state.copyWith(playedVideos: played, playQueue: ListQueue.from(playNext)));
}
switchToVideo(state.videos[index]);
_switchToVideo(listToCheckAgainst[index]);
}

/// Switches to a video without changing the queue
Expand Down
5 changes: 3 additions & 2 deletions lib/settings/views/screens/browsing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class BrowsingSettingsScreen extends StatelessWidget {
),
body: SafeArea(child: BlocBuilder<SettingsCubit, SettingsState>(builder: (context, _) {
var cubit = context.read<SettingsCubit>();
var filterCount = db.getAllFilters().length;
return DefaultTabController(
length: 2,
child: SettingsList(
Expand Down Expand Up @@ -139,13 +140,13 @@ class BrowsingSettingsScreen extends StatelessWidget {
SettingsTile.navigation(
leading: const Icon(Icons.manage_search),
title: Text(locals.searchHistory),
description: Text(locals.searchHistoryDescription),
description: Text(_.useSearchHistory ? locals.enabled : locals.searchHistoryDescription),
onPressed: (context) => openSearchHistorySettings(context),
),
SettingsTile.navigation(
leading: const Icon(Icons.rule_sharp),
title: Text(locals.videoFilters),
description: Text(locals.videoFiltersSettingTileDescriptions),
description: Text('${locals.videoFiltersSettingTileDescriptions}${filterCount > 0 ? '\n${locals.nFilters(filterCount)}':''}'),
onPressed: openVideoFilterSettings,
),
SettingsTile.navigation(
Expand Down
17 changes: 11 additions & 6 deletions lib/videos/views/screens/video.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ class VideoScreen extends StatelessWidget {
child: BlocConsumer<VideoCubit, VideoState>(
listenWhen: (previous, current) => settings.state.autoplayVideoOnLoad && previous.video != current.video,
listener: (context, state) {
AutoRouter.of(context).pop();
context.read<VideoCubit>().playVideo(false);
AutoRouter.of(context).pop();
context.read<VideoCubit>().playVideo(false);
},
builder: (context, _) {
var cubit = context.read<VideoCubit>();
Expand Down Expand Up @@ -117,10 +117,15 @@ class VideoScreen extends StatelessWidget {
SizedBox(
height: 15,
width: 15,
child: CircularProgressIndicator(
value: _.downloadProgress == 0 ? null : _.downloadProgress,
strokeWidth: 2,
))
child: TweenAnimationBuilder(
duration: animationDuration,
tween: Tween<double>(begin: 0, end: _.downloadProgress),
builder: (context, value, child) {
return CircularProgressIndicator(
value: _.downloadProgress == 0 ? null : value,
strokeWidth: 2,
);
}))
],
),
)
Expand Down
Loading

0 comments on commit 827deb8

Please sign in to comment.