Skip to content

Commit

Permalink
chore: fetch all channels videos since specific date
Browse files Browse the repository at this point in the history
  • Loading branch information
MSOB7YY committed Feb 16, 2024
1 parent 752c3b4 commit 613ff6e
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 34 deletions.
1 change: 0 additions & 1 deletion lib/core/translations/keys.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ abstract class LanguageKeys {
String get CLEAR_TRACK_ITEM_MULTIPLE => _getKey('CLEAR_TRACK_ITEM_MULTIPLE');
String get CLEAR_TRACK_ITEM => _getKey('CLEAR_TRACK_ITEM');
String get CLEAR_VIDEO_CACHE_NOTE => _getKey('CLEAR_VIDEO_CACHE_NOTE');
String get CLEAR_VIDEO_CACHE_SUBTITLE => _getKey('CLEAR_VIDEO_CACHE_SUBTITLE');
String get CLEAR_VIDEO_CACHE => _getKey('CLEAR_VIDEO_CACHE');
String get CLEAR => _getKey('CLEAR');
String get CLEAR_AUDIO_CACHE => _getKey('CLEAR_AUDIO_CACHE');
Expand Down
15 changes: 9 additions & 6 deletions lib/youtube/controller/youtube_import_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,15 @@ class YoutubeImportController {
isImportingSubscriptions.value = true;
final res = await _parseSubscriptions.thready(subscriptionsFilePath);
res.loop((e, index) {
final valInMap = YoutubeSubscriptionsController.inst.subscribedChannels[e.id];
YoutubeSubscriptionsController.inst.subscribedChannels[e.id] = YoutubeSubscription(
title: valInMap != null && valInMap.title == '' ? e.title : valInMap?.title ?? e.title,
channelID: e.id,
subscribed: true,
lastFetched: valInMap?.lastFetched,
final valInMap = YoutubeSubscriptionsController.inst.getChannel(e.id);
YoutubeSubscriptionsController.inst.setChannel(
e.id,
YoutubeSubscription(
title: valInMap != null && valInMap.title == '' ? e.title : valInMap?.title ?? e.title,
channelID: e.id,
subscribed: true,
lastFetched: valInMap?.lastFetched,
),
);
});
YoutubeSubscriptionsController.inst.sortByLastFetched();
Expand Down
21 changes: 12 additions & 9 deletions lib/youtube/controller/youtube_subscriptions_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@ class YoutubeSubscriptionsController {
static final YoutubeSubscriptionsController inst = YoutubeSubscriptionsController._internal();
YoutubeSubscriptionsController._internal();

final subscribedChannels = <String, YoutubeSubscription>{}.obs;
Iterable<String> get subscribedChannels => _availableChannels.keys.where((key) => _availableChannels[key]?.subscribed == true);
final _availableChannels = <String, YoutubeSubscription>{}.obs;

YoutubeSubscription? getChannel(String channelId) => _availableChannels[channelId];
void setChannel(String channelId, YoutubeSubscription channel) => _availableChannels[channelId] = channel;
String? idOrUrlToChannelID(String? idOrURL) => idOrURL?.split('/').last;

/// Updates a channel subscription status, use null to toggle.
Future<void> changeChannelStatus(String channelIDOrURL, bool? subscribed) async {
Future<void> changeChannelStatus(String channelIDOrURL, {bool? subscribe}) async {
final channelID = channelIDOrURL.split('/').last;
final valInMap = subscribedChannels[channelID];
final newSubscribed = subscribed ?? !(valInMap?.subscribed ?? false);
subscribedChannels[channelID] = YoutubeSubscription(
final valInMap = _availableChannels[channelID];
final newSubscribed = subscribe ?? !(valInMap?.subscribed ?? false);
_availableChannels[channelID] = YoutubeSubscription(
title: valInMap?.title,
channelID: channelID,
subscribed: newSubscribed,
Expand All @@ -29,12 +32,12 @@ class YoutubeSubscriptionsController {
}

Future<void> sortByLastFetched() async {
subscribedChannels.sortBy((e) => e.value.lastFetched ?? DateTime(0));
_availableChannels.sortBy((e) => e.value.lastFetched ?? DateTime(0));
await saveFile();
}

Future<void> refreshLastFetchedTime(String channelID, {bool saveToStorage = true}) async {
subscribedChannels[channelID]?.lastFetched = DateTime.now();
_availableChannels[channelID]?.lastFetched = DateTime.now();
if (saveToStorage) await saveFile();
}

Expand All @@ -44,7 +47,7 @@ class YoutubeSubscriptionsController {

final res = await file.readAsJson() as Map?;

subscribedChannels.value = (res?.cast<String, Map>())?.map(
_availableChannels.value = (res?.cast<String, Map>())?.map(
(key, value) => MapEntry(
key,
YoutubeSubscription.fromJson(
Expand All @@ -57,6 +60,6 @@ class YoutubeSubscriptionsController {

Future<void> saveFile() async {
final file = File(AppPaths.YT_SUBSCRIPTIONS);
await file.writeAsJson(subscribedChannels.map((key, value) => MapEntry(key, value.toJson())));
await file.writeAsJson(_availableChannels.map((key, value) => MapEntry(key, value.toJson())));
}
}
2 changes: 1 addition & 1 deletion lib/youtube/pages/yt_channel_subpage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class YTChannelSubpage extends StatefulWidget {
}

class _YTChannelSubpageState extends YoutubeChannelController<YTChannelSubpage> {
late final YoutubeSubscription ch = YoutubeSubscriptionsController.inst.subscribedChannels[widget.channelID] ??
late final YoutubeSubscription ch = YoutubeSubscriptionsController.inst.getChannel(widget.channelID) ??
YoutubeSubscription(
channelID: widget.channelID.split('/').last,
subscribed: false,
Expand Down
144 changes: 129 additions & 15 deletions lib/youtube/pages/yt_channels_page.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import 'package:calendar_date_picker2/calendar_date_picker2.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:jiffy/jiffy.dart';
import 'package:newpipeextractor_dart/newpipeextractor_dart.dart';

import 'package:namida/base/youtube_channel_controller.dart';
import 'package:namida/controller/navigator_controller.dart';
import 'package:namida/core/dimensions.dart';
import 'package:namida/core/extensions.dart';
import 'package:namida/core/functions.dart';
import 'package:namida/core/icon_fonts/broken_icons.dart';
import 'package:namida/core/translations/language.dart';
import 'package:namida/ui/widgets/animated_widgets.dart';
import 'package:namida/ui/widgets/custom_widgets.dart';
import 'package:namida/ui/widgets/settings/extra_settings.dart';
import 'package:namida/youtube/class/youtube_subscription.dart';
import 'package:namida/base/youtube_channel_controller.dart';
import 'package:namida/youtube/controller/youtube_controller.dart';
import 'package:namida/youtube/controller/youtube_import_controller.dart';
import 'package:namida/youtube/controller/youtube_subscriptions_controller.dart';
Expand All @@ -31,20 +34,30 @@ class _YoutubeChannelsPageState extends YoutubeChannelController<YoutubeChannels
late final ScrollController _horizontalListController;

final _allChannelsStreamsProgress = 0.0.obs;
final _allChannelsStreamsLoading = false.obs;

late final Rx<DateTime> allChannelFetchOldestDate;

@override
void initState() {
_horizontalListController = ScrollController();
YoutubeSubscriptionsController.inst.sortByLastFetched();
final sub = YoutubeSubscriptionsController.inst.subscribedChannels.values.lastOrNull;
_updateChannel(sub);
final subCh = YoutubeSubscriptionsController.inst.subscribedChannels.lastOrNull;
if (subCh != null) {
final sub = YoutubeSubscriptionsController.inst.getChannel(subCh);
_updateChannel(sub);
}

final now = DateTime.now();
allChannelFetchOldestDate = DateTime(now.year, now.month, now.day - 32).obs;
super.initState();
}

@override
void dispose() {
_horizontalListController.dispose();
_allChannelsStreamsProgress.close();
_allChannelsStreamsLoading.close();
super.dispose();
}

Expand All @@ -61,28 +74,56 @@ class _YoutubeChannelsPageState extends YoutubeChannelController<YoutubeChannels
if (sub != null) {
fetchChannelStreams(sub);
} else {
_fetchAllChannelsStreams(null);
_fetchAllChannelsStreams();
}
}

Future<void> _fetchAllChannelsStreams(DateTime? since) async {
Future<void> _fetchAllChannelsStreams() async {
setState(() {
isLoadingInitialStreams = true;
streamsList.clear();
});
_allChannelsStreamsLoading.value = true;

final streams = <StreamInfoItem>[];
final ids = YoutubeSubscriptionsController.inst.subscribedChannels.keys.toList();
final ids = YoutubeSubscriptionsController.inst.subscribedChannels.toList();
final idsLength = ids.length;

final maxDateBeforeMS = allChannelFetchOldestDate.value.millisecondsSinceEpoch;

bool enoughStreams(List<StreamInfoItem> streams) {
final lastDate = streams.lastOrNull?.date;
if (lastDate == null || lastDate.millisecondsSinceEpoch < maxDateBeforeMS) {
streams.removeWhere((element) {
final date = element.date;
return date != null && date.millisecondsSinceEpoch < maxDateBeforeMS;
});
return true;
}
return false;
}

for (int i = 0; i < idsLength; i++) {
final channelID = ids[i];
_allChannelsStreamsProgress.value = i / idsLength;
final st = await YoutubeController.inst.getChannelStreams(channelID);
printy('p: $i / $idsLength = ${_allChannelsStreamsProgress.value} =>> ${st.length} videos');
final chStreams = await YoutubeController.inst.getChannelStreams(channelID);
while (!enoughStreams(chStreams)) {
final nextPage = await YoutubeController.inst.getChannelStreamsNextPage();
if (nextPage.isEmpty) break;
chStreams.addAll(nextPage);
}
printy('p: $i / $idsLength = ${_allChannelsStreamsProgress.value} =>> ${chStreams.length} videos');
if (channel != null) {
_allChannelsStreamsProgress.value = 0.0;
_allChannelsStreamsLoading.value = false;
return;
}
YoutubeSubscriptionsController.inst.refreshLastFetchedTime(channelID, saveToStorage: false);
streams.addAll(st);
streams.addAll(chStreams);
}
YoutubeSubscriptionsController.inst.sortByLastFetched();
_allChannelsStreamsProgress.value = 0.0;
_allChannelsStreamsLoading.value = false;

sortStreams(streams: streams);

Expand Down Expand Up @@ -126,7 +167,80 @@ class _YoutubeChannelsPageState extends YoutubeChannelController<YoutubeChannels
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
child: ch == null
? sortWidget
? Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Expanded(child: sortWidget),
NamidaIconButton(
icon: Broken.calendar,
onPressed: () {
showCalendarDialog(
title: lang.DATE,
buttonText: lang.CONFIRM,
useHistoryDates: false,
calendarType: CalendarDatePicker2Type.single,
lastDate: DateTime.now(),
onGenerate: (dates) {
if (dates.isNotEmpty) {
allChannelFetchOldestDate.value = dates.first;
_fetchAllChannelsStreams();
}
NamidaNavigator.inst.closeDialog();
},
);
},
),
],
),
const SizedBox(height: 12.0),
Row(
children: [
DecoratedBox(
decoration: BoxDecoration(
color: context.theme.cardColor,
borderRadius: BorderRadius.circular(6.0.multipliedRadius),
border: Border.all(
width: 1.2,
color: context.theme.colorScheme.secondary.withOpacity(0.6),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 3.0),
child: Text(
streamsList.length.displayVideoKeyword,
style: context.textTheme.displayMedium,
),
),
),
const SizedBox(width: 4.0),
DecoratedBox(
decoration: BoxDecoration(
color: context.theme.cardColor,
borderRadius: BorderRadius.circular(6.0.multipliedRadius),
border: Border.all(
width: 1.2,
color: context.theme.colorScheme.secondary.withOpacity(0.6),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 3.0),
child: Obx(
() {
final oldestDate = allChannelFetchOldestDate.value;
return Text(
"${oldestDate.millisecondsSinceEpoch.dateFormattedOriginal} - ${Jiffy.parseFromDateTime(oldestDate).fromNow()}",
style: context.textTheme.displayMedium,
);
},
),
),
),
],
)
],
)
: Row(
children: [
Expanded(
Expand Down Expand Up @@ -156,7 +270,7 @@ class _YoutubeChannelsPageState extends YoutubeChannelController<YoutubeChannels
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
ch.title,
ch.title != '' ? ch.title : YoutubeController.inst.fetchChannelDetailsFromCacheSync(ch.channelID)?.name ?? '',
style: context.textTheme.displayMedium,
overflow: TextOverflow.ellipsis,
),
Expand Down Expand Up @@ -227,7 +341,7 @@ class _YoutubeChannelsPageState extends YoutubeChannelController<YoutubeChannels
: LazyLoadListView(
scrollController: uploadsScrollController,
onReachingEnd: () async {
await fetchStreamsNextPage(channel);
if (channel != null) await fetchStreamsNextPage(channel);
},
listview: (controller) {
return ListView.builder(
Expand Down Expand Up @@ -282,7 +396,7 @@ class _YoutubeChannelsPageState extends YoutubeChannelController<YoutubeChannels
),
child: Obx(
() {
final channelIDS = YoutubeSubscriptionsController.inst.subscribedChannels.keys.toList();
final channelIDS = YoutubeSubscriptionsController.inst.subscribedChannels.toList();
final totalIDsLength = channelIDS.length;
return Row(
children: [
Expand Down Expand Up @@ -311,7 +425,7 @@ class _YoutubeChannelsPageState extends YoutubeChannelController<YoutubeChannels
child: FittedBox(
child: Obx(
() => CircularProgressIndicator(
value: _allChannelsStreamsProgress.value,
value: _allChannelsStreamsLoading.value && _allChannelsStreamsProgress.value <= 0 ? null : _allChannelsStreamsProgress.value,
strokeWidth: 2.0,
),
),
Expand All @@ -338,7 +452,7 @@ class _YoutubeChannelsPageState extends YoutubeChannelController<YoutubeChannels
itemBuilder: (context, indexPre) {
final index = totalIDsLength - indexPre - 1;
final key = channelIDS[index];
final ch = YoutubeSubscriptionsController.inst.subscribedChannels[key]!;
final ch = YoutubeSubscriptionsController.inst.getChannel(key)!;
final info = YoutubeController.inst.fetchChannelDetailsFromCacheSync(ch.channelID);
final channelName = info?.name == null || info?.name == '' ? ch.title : info?.name;
return NamidaInkWell(
Expand Down
4 changes: 2 additions & 2 deletions lib/youtube/widgets/yt_subscribe_buttons.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class YTSubscribeButton extends StatelessWidget {
() {
final channelID = YoutubeSubscriptionsController.inst.idOrUrlToChannelID(channelIDOrURL);
final disabled = channelID == null;
final subscribed = YoutubeSubscriptionsController.inst.subscribedChannels[channelID ?? '']?.subscribed ?? false;
final subscribed = YoutubeSubscriptionsController.inst.getChannel(channelID ?? '')?.subscribed ?? false;
return AnimatedOpacity(
opacity: disabled ? 0.5 : 1.0,
duration: const Duration(milliseconds: 300),
Expand All @@ -34,7 +34,7 @@ class YTSubscribeButton extends StatelessWidget {
),
onPressed: () async {
if (channelIDOrURL != null) {
await YoutubeSubscriptionsController.inst.changeChannelStatus(channelIDOrURL!, null);
await YoutubeSubscriptionsController.inst.changeChannelStatus(channelIDOrURL!);
}
},
),
Expand Down

0 comments on commit 613ff6e

Please sign in to comment.