Skip to content

Commit

Permalink
chore(yt): better channel/playlists streams management
Browse files Browse the repository at this point in the history
  • Loading branch information
MSOB7YY committed Jul 4, 2024
1 parent 5e4c3e3 commit 554c40f
Show file tree
Hide file tree
Showing 8 changed files with 565 additions and 375 deletions.
40 changes: 13 additions & 27 deletions lib/base/youtube_channel_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ import 'package:youtipie/class/stream_info_item/stream_info_item.dart';
import 'package:youtipie/youtipie.dart';

import 'package:namida/base/youtube_streams_manager.dart';
import 'package:namida/controller/connectivity.dart';
import 'package:namida/controller/current_color.dart';
import 'package:namida/core/extensions.dart';
import 'package:namida/core/utils.dart';
import 'package:namida/youtube/class/youtube_subscription.dart';
import 'package:namida/youtube/controller/youtube_info_controller.dart';
import 'package:namida/youtube/controller/youtube_subscriptions_controller.dart';

abstract class YoutubeChannelController<T extends StatefulWidget> extends State<T> with YoutubeStreamsManager {
abstract class YoutubeChannelController<T extends StatefulWidget> extends State<T> with YoutubeStreamsManager<YoutiPieChannelTabVideosResult> {
@override
List<StreamInfoItem>? get streamsList => channelVideoTab?.items;

@override
YoutiPieChannelTabVideosResult? get listWrapper => channelVideoTab;

@override
ScrollController get scrollController => uploadsScrollController;

Expand All @@ -27,20 +28,23 @@ abstract class YoutubeChannelController<T extends StatefulWidget> extends State<
@override
void onSortChanged(void Function() fn) => refreshState(fn);

@override
void onListChange(void Function() fn) => refreshState(fn);

@override
bool canRefreshList(YoutiPieChannelTabVideosResult result) => result.channelId == channel?.channelID;

late final ScrollController uploadsScrollController = ScrollController();
YoutubeSubscription? channel;
YoutiPieChannelTabVideosResult? channelVideoTab;
({DateTime oldest, DateTime newest})? streamsPeakDates;

bool isLoadingInitialStreams = true;
final isLoadingMoreUploads = false.obs;
final lastLoadingMoreWasEmpty = false.obs;

@override
void dispose() {
uploadsScrollController.dispose();
isLoadingMoreUploads.close();
lastLoadingMoreWasEmpty.close();
disposeResources();
super.dispose();
}
Expand Down Expand Up @@ -71,6 +75,9 @@ abstract class YoutubeChannelController<T extends StatefulWidget> extends State<
final result = await YoutubeInfoController.channel.fetchChannelTab(channelId: channelID, tab: tab, details: details);
if (result == null) return;

// -- would have prevented re-assigning if first video was the same, it would help check any deleted videos too
// -- but data like viewsCount will not be updated sadly.

final st = result.items;
updatePeakDates(st);
YoutubeSubscriptionsController.inst.refreshLastFetchedTime(channelID);
Expand All @@ -82,25 +89,4 @@ abstract class YoutubeChannelController<T extends StatefulWidget> extends State<
}
});
}

Future<void> fetchStreamsNextPage() async {
if (isLoadingMoreUploads.value) return;
if (lastLoadingMoreWasEmpty.value) return;

final result = this.channelVideoTab;
if (result == null) return;

isLoadingMoreUploads.value = true;
final didFetch = await result.fetchNext();
isLoadingMoreUploads.value = false;

if (didFetch) {
if (result.channelId == channel?.channelID) {
refreshState(trySortStreams);
}
} else {
if (ConnectivityController.inst.hasConnection) lastLoadingMoreWasEmpty.value = true;
return;
}
}
}
53 changes: 51 additions & 2 deletions lib/base/youtube_streams_manager.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:youtipie/class/result_wrapper/list_wrapper_base.dart';
import 'package:youtipie/class/stream_info_item/stream_info_item.dart';

import 'package:namida/core/extensions.dart';
Expand All @@ -13,12 +14,17 @@ enum YTVideosSorting {
duration,
}

mixin YoutubeStreamsManager {
mixin YoutubeStreamsManager<W extends YoutiPieListWrapper<StreamInfoItem>> {
List<StreamInfoItem>? get streamsList;
W? get listWrapper;
ScrollController get scrollController;
BuildContext get context;
Color? get sortChipBGColor;
void onSortChanged(void Function() fn);
void onListChange(void Function() fn);
bool canRefreshList(W result);

final isLoadingMoreUploads = false.obs;

void disposeResources() {
sorting.close();
Expand Down Expand Up @@ -88,10 +94,14 @@ mixin YoutubeStreamsManager {
],
),
);
void trySortStreams() {

/// return if sorting was done.
bool trySortStreams() {
if (sorting.value != _defaultSorting || sortingByTop.value != _defaultSortingByTop) {
sortStreams(jumpToZero: false);
return true;
}
return false;
}

void sortStreams({List<StreamInfoItem>? streams, YTVideosSorting? sort, bool? sortingByTop, bool jumpToZero = true}) {
Expand Down Expand Up @@ -140,4 +150,43 @@ mixin YoutubeStreamsManager {
return (lang.DURATION, Broken.timer_1);
}
}

Future<void> fetchStreamsNextPage() async {
if (isLoadingMoreUploads.value) return;

final result = this.listWrapper;
if (result == null) return;
if (!result.canFetchNext) return;

isLoadingMoreUploads.value = true;
final didFetch = await result.fetchNext();
isLoadingMoreUploads.value = false;

if (didFetch) {
if (canRefreshList(result)) {
onListChange(trySortStreams); // refresh state even if will not sort
}
}
}

Future<YoutiPieFetchAllResType?> fetchAllStreams(void Function(YoutiPieFetchAllRes fetchAllRes) controller) async {
if (isLoadingMoreUploads.value) return null;

final videosTab = this.listWrapper;
if (videosTab == null) return null;

final res = videosTab.fetchAll(
onProgress: () {
onListChange(trySortStreams); // refresh state even if will not sort
},
);

if (res == null) return null;
controller(res);

isLoadingMoreUploads.value = true;
final didFetch = await res.result;
isLoadingMoreUploads.value = false;
return didFetch;
}
}
109 changes: 66 additions & 43 deletions lib/youtube/functions/yt_playlist_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,49 +117,52 @@ extension PlaylistBasicInfoExt on PlaylistBasicInfo {
builder: (context) {
final iconSize = context.width * 0.5;
final iconColor = context.theme.colorScheme.onSurface.withOpacity(0.6);
return SizedBox(
width: context.width,
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Obx(
() {
final totalC = totalCount.valueR;
return AnimatedSwitcher(
key: const Key('circle_switch'),
duration: switchAnimationDurHalf,
child: totalC == null || currentCount.valueR < totalC
? ThreeArchedCircle(
size: iconSize,
color: iconColor,
)
: Icon(
key: const Key('tick_switch'),
Broken.tick_circle,
size: iconSize,
color: iconColor,
),
);
},
),
const SizedBox(height: 12.0),
Text(
'${lang.FETCHING}...',
style: context.textTheme.displayLarge,
),
const SizedBox(height: 8.0),
Obx(
() {
final totalC = totalCount.valueR;
return Text(
'${currentCount.valueR.formatDecimal()}/${totalC == null ? '?' : totalC.formatDecimal()}',
style: context.textTheme.displayLarge,
);
},
),
],
return _DisposableWidget(
onDispose: fetchAllRes.cancel,
child: SizedBox(
width: context.width,
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Obx(
() {
final totalC = totalCount.valueR;
return AnimatedSwitcher(
key: const Key('circle_switch'),
duration: switchAnimationDurHalf,
child: totalC == null || currentCount.valueR < totalC
? ThreeArchedCircle(
size: iconSize,
color: iconColor,
)
: Icon(
key: const Key('tick_switch'),
Broken.tick_circle,
size: iconSize,
color: iconColor,
),
);
},
),
const SizedBox(height: 12.0),
Text(
'${lang.FETCHING}...',
style: context.textTheme.displayLarge,
),
const SizedBox(height: 8.0),
Obx(
() {
final totalC = totalCount.valueR;
return Text(
'${currentCount.valueR.formatDecimal()}/${totalC == null ? '?' : totalC.formatDecimal()}',
style: context.textTheme.displayLarge,
);
},
),
],
),
),
),
);
Expand Down Expand Up @@ -346,3 +349,23 @@ extension PlaylistBasicInfoExt on PlaylistBasicInfo {
];
}
}

class _DisposableWidget extends StatefulWidget {
final Widget child;
final void Function() onDispose;
const _DisposableWidget({super.key, required this.child, required this.onDispose});

@override
State<_DisposableWidget> createState() => __DisposableWidgetState();
}

class __DisposableWidgetState extends State<_DisposableWidget> {
@override
void dispose() {
widget.onDispose();
super.dispose();
}

@override
Widget build(BuildContext context) => widget.child;
}
Loading

0 comments on commit 554c40f

Please sign in to comment.