Skip to content

Commit

Permalink
core: migrate to youtipie
Browse files Browse the repository at this point in the history
ref: #227
  • Loading branch information
MSOB7YY committed Jun 24, 2024
1 parent d3d32e0 commit cc66282
Show file tree
Hide file tree
Showing 68 changed files with 4,219 additions and 3,490 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ Animating Thumbnail | Recommends & Listens
### Special Thanks:

> - [@Artx-II](https://github.com/Artx-II) for their initial dart port of Newpipe Extractor, which powers youtube section.
> - [@MSOB7YY](https://github.com/MSOB7YY) for their youtube client, which powers youtube section.
> - [@cameralis](https://github.com/cameralis) for their awesome miniplayer physics.
> - [@alexmercerind](https://github.com/alexmercerind) for helping me out a lot.
> - [@lusaxweb](https://github.com/lusaxweb) for their awesome Iconsax icon pack.
Expand Down
8 changes: 7 additions & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ if (flutterVersionName == null) {
android {
namespace 'com.msob7y.namida'
compileSdkVersion 34
ndkVersion flutter.ndkVersion
ndkVersion "26.3.11579264"
splits {

abi {
Expand All @@ -55,6 +55,12 @@ android {
main.java.srcDirs += 'src/main/kotlin'
}

packaging {
jniLibs {
exclude("lib/x86/*.so")
}
}

applicationVariants.all { variant ->
variant.outputs.all { output ->
def abi = output.getFilter(com.android.build.OutputFile.ABI)
Expand Down
466 changes: 269 additions & 197 deletions lib/base/audio_handler.dart

Large diffs are not rendered by default.

52 changes: 31 additions & 21 deletions lib/base/youtube_channel_controller.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import 'package:flutter/material.dart';
import 'package:newpipeextractor_dart/newpipeextractor_dart.dart';
import 'package:youtipie/class/channels/channel_page_result.dart';
import 'package:youtipie/class/channels/tabs/channel_tab_videos_result.dart';
import 'package:youtipie/class/stream_info_item/stream_info_item.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_controller.dart';
import 'package:namida/youtube/controller/youtube_info_controller.dart';
import 'package:namida/youtube/controller/youtube_subscriptions_controller.dart';
import 'package:youtipie/youtipie.dart';

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

@override
ScrollController get scrollController => uploadsScrollController;
Expand All @@ -25,7 +27,7 @@ abstract class YoutubeChannelController<T extends StatefulWidget> extends State<

late final ScrollController uploadsScrollController = ScrollController();
YoutubeSubscription? channel;
late final _streamsList = <StreamInfoItem>[];
YoutiPieChannelTabVideosResult? channelVideoTab;
({DateTime oldest, DateTime newest})? streamsPeakDates;

bool isLoadingInitialStreams = true;
Expand All @@ -41,11 +43,12 @@ abstract class YoutubeChannelController<T extends StatefulWidget> extends State<
super.dispose();
}

/// TODO(youtipie): this is not really accurate
void updatePeakDates(List<StreamInfoItem> streams) {
int oldest = (streamsPeakDates?.oldest ?? DateTime.now()).millisecondsSinceEpoch;
int newest = (streamsPeakDates?.newest ?? DateTime(0)).millisecondsSinceEpoch;
streams.loop((e) {
final d = e.date;
final d = e.publishedAt.date;
if (d != null) {
final ms = d.millisecondsSinceEpoch;
if (ms < oldest) {
Expand All @@ -58,36 +61,43 @@ abstract class YoutubeChannelController<T extends StatefulWidget> extends State<
streamsPeakDates = (oldest: DateTime.fromMillisecondsSinceEpoch(oldest), newest: DateTime.fromMillisecondsSinceEpoch(newest));
}

Future<void> fetchChannelStreams(YoutubeSubscription sub) async {
final st = await YoutubeController.inst.getChannelStreams(sub.channelID);
Future<void> fetchChannelStreams(YoutiPieChannelPageResult channelPage) async {
final tab = channelPage.tabs.getVideosTab();
if (tab == null) return;
final channelID = channelPage.id;
final result = await YoutubeInfoController.channel.fetchChannelTab(channelId: channelID, tab: tab);
if (result == null) return;
this.channelVideoTab = result;

final st = result.items;
updatePeakDates(st);
YoutubeSubscriptionsController.inst.refreshLastFetchedTime(sub.channelID);
YoutubeSubscriptionsController.inst.refreshLastFetchedTime(channelID);
setState(() {
isLoadingInitialStreams = false;
if (sub.channelID == channel?.channelID) {
streamsList.addAll(st);
if (channelID == channel?.channelID) {
trySortStreams();
}
});
}

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

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

isLoadingMoreUploads.value = true;
final st = await YoutubeController.inst.getChannelStreamsNextPage();
updatePeakDates(st);
final didFetch = await result.fetchNext();
isLoadingMoreUploads.value = false;
if (st.isEmpty) {

if (didFetch) {
if (result.channelId == channel?.channelID) {
setState(trySortStreams);
}
} else {
if (ConnectivityController.inst.hasConnection) lastLoadingMoreWasEmpty.value = true;
return;
}
if (sub?.channelID == channel?.channelID) {
setState(() {
streamsList.addAll(st);
trySortStreams();
});
}
}
}
127 changes: 70 additions & 57 deletions lib/base/youtube_streams_manager.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:newpipeextractor_dart/newpipeextractor_dart.dart';
import 'package:youtipie/class/stream_info_item/stream_info_item.dart';

import 'package:namida/core/extensions.dart';
import 'package:namida/core/icon_fonts/broken_icons.dart';
Expand All @@ -14,7 +14,7 @@ enum YTVideosSorting {
}

mixin YoutubeStreamsManager {
List<StreamInfoItem> get streamsList;
List<StreamInfoItem>? get streamsList;
ScrollController get scrollController;
BuildContext get context;
Color? get sortChipBGColor;
Expand All @@ -32,57 +32,60 @@ mixin YoutubeStreamsManager {

Widget get sortWidget => SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Obx(
() => Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
...YTVideosSorting.values.map(
(e) {
final details = sortToTextAndIcon(e);
final enabled = sorting.value == e;
final itemsColor = enabled ? Colors.white.withOpacity(0.8) : null;
return NamidaInkWell(
animationDurationMS: 200,
borderRadius: 6.0,
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
margin: const EdgeInsets.symmetric(horizontal: 3.0),
bgColor: enabled ? sortChipBGColor : context.theme.cardColor,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
enabled
? ObxO(
rx: sortingByTop,
builder: (sortingByTop) => StackedIcon(
baseIcon: details.$2,
secondaryIcon: sortingByTop ? Broken.arrow_down_2 : Broken.arrow_up_3,
iconSize: 20.0,
secondaryIconSize: 10.0,
blurRadius: 4.0,
baseIconColor: itemsColor,
// secondaryIconColor: enabled ? context.theme.colorScheme.surface : null,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
...YTVideosSorting.values.map(
(e) {
final details = sortToTextAndIcon(e);
return ObxO(
rx: sorting,
builder: (s) {
final enabled = s == e;
final itemsColor = enabled ? Colors.white.withOpacity(0.8) : null;
return NamidaInkWell(
animationDurationMS: 200,
borderRadius: 6.0,
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
margin: const EdgeInsets.symmetric(horizontal: 3.0),
bgColor: enabled ? sortChipBGColor : context.theme.cardColor,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
enabled
? ObxO(
rx: sortingByTop,
builder: (sortingByTop) => StackedIcon(
baseIcon: details.$2,
secondaryIcon: sortingByTop ? Broken.arrow_down_2 : Broken.arrow_up_3,
iconSize: 20.0,
secondaryIconSize: 10.0,
blurRadius: 4.0,
baseIconColor: itemsColor,
// secondaryIconColor: enabled ? context.theme.colorScheme.surface : null,
),
)
: Icon(
details.$2,
size: 20.0,
color: null,
),
)
: Icon(
details.$2,
size: 20.0,
color: null,
),
const SizedBox(width: 4.0),
Text(
details.$1,
style: context.textTheme.displayMedium?.copyWith(color: itemsColor),
),
],
),
onTap: () => onSortChanged(
() => sortStreams(sort: e, sortingByTop: enabled ? !sortingByTop.value : null),
),
);
},
),
],
),
const SizedBox(width: 4.0),
Text(
details.$1,
style: context.textTheme.displayMedium?.copyWith(color: itemsColor),
),
],
),
onTap: () => onSortChanged(
() => sortStreams(sort: e, sortingByTop: enabled ? !sortingByTop.value : null),
),
);
},
);
},
),
],
),
);
void trySortStreams() {
Expand All @@ -92,20 +95,21 @@ mixin YoutubeStreamsManager {
}

void sortStreams({List<StreamInfoItem>? streams, YTVideosSorting? sort, bool? sortingByTop, bool jumpToZero = true}) {
sort ??= sorting.value;
streams ??= streamsList;
if (streams == null) return;
sort ??= sorting.value;
sortingByTop ??= this.sortingByTop.value;
switch (sort) {
case YTVideosSorting.date:
sortingByTop ? streams.sortByReverse((e) => e.date ?? DateTime(0)) : streams.sortBy((e) => e.date ?? DateTime(0));
sortingByTop ? streams.sortByReverse((e) => e.publishedAt.date ?? DateTime(0)) : streams.sortBy((e) => e.publishedAt.date ?? DateTime(0));
break;

case YTVideosSorting.views:
sortingByTop ? streams.sortByReverse((e) => e.viewCount ?? 0) : streams.sortBy((e) => e.viewCount ?? 0);
sortingByTop ? streams.sortByReverse((e) => e.viewsCount ?? 0) : streams.sortBy((e) => e.viewsCount ?? 0);
break;

case YTVideosSorting.duration:
sortingByTop ? streams.sortByReverse((e) => e.duration ?? Duration.zero) : streams.sortBy((e) => e.duration ?? Duration.zero);
sortingByTop ? streams.sortByReverse((e) => e.durSeconds ?? 0) : streams.sortBy((e) => e.durSeconds ?? 0);
break;

default:
Expand All @@ -114,7 +118,16 @@ mixin YoutubeStreamsManager {
sorting.value = sort;
this.sortingByTop.value = sortingByTop;

if (jumpToZero && scrollController.hasClients) scrollController.jumpTo(0);
if (jumpToZero && scrollController.hasClients) {
final scrolledFar = scrollController.offset > context.height * 0.7;
if (scrolledFar) {
scrollController.animateToEff(
0,
duration: const Duration(milliseconds: 300),
curve: Curves.fastEaseInToSlowEaseOut,
);
}
}
}

(String, IconData) sortToTextAndIcon(YTVideosSorting sort) {
Expand Down
4 changes: 2 additions & 2 deletions lib/controller/backup_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import 'package:namida/core/constants.dart';
import 'package:namida/core/extensions.dart';
import 'package:namida/core/translations/language.dart';
import 'package:namida/main.dart';
import 'package:namida/youtube/controller/youtube_controller.dart';
import 'package:namida/youtube/controller/youtube_history_controller.dart';
import 'package:namida/youtube/controller/youtube_info_controller.dart';
import 'package:namida/youtube/controller/youtube_playlist_controller.dart';

class BackupController {
Expand Down Expand Up @@ -292,6 +292,6 @@ class BackupController {
YoutubePlaylistController.inst.prepareAllPlaylists();
YoutubeHistoryController.inst.prepareHistoryFile();
await YoutubePlaylistController.inst.prepareDefaultPlaylistsFile();
YoutubeController.inst.fillBackupInfoMap(); // for history videos info.
YoutubeInfoController.utils.fillBackupInfoMap(); // for history videos info.
}
}
4 changes: 2 additions & 2 deletions lib/controller/json_to_history_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import 'package:namida/core/utils.dart';
import 'package:namida/ui/dialogs/track_advanced_dialog.dart';
import 'package:namida/ui/widgets/custom_widgets.dart';
import 'package:namida/youtube/class/youtube_id.dart';
import 'package:namida/youtube/controller/youtube_controller.dart';
import 'package:namida/youtube/controller/youtube_history_controller.dart';
import 'package:namida/youtube/controller/youtube_info_controller.dart';

class JsonToHistoryParser {
static JsonToHistoryParser get inst => _instance;
Expand Down Expand Up @@ -529,7 +529,7 @@ class JsonToHistoryParser {
printy('updatedIds: ${updatedIds.length}');
},
);
YoutubeController.inst.fillBackupInfoMap();
YoutubeInfoController.utils.fillBackupInfoMap();
}

HistoryController.inst.historyMap.value = res.localHistory;
Expand Down
6 changes: 3 additions & 3 deletions lib/controller/logs_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import 'package:namida/core/extensions.dart';
final logger = _Log();

class _Log {
late Logger _logger = updateLogger(Level.all);
late Logger logger = updateLogger(Level.all);

void updateLoggerPath() => updateLogger(Level.all);

Logger updateLogger(Level? level) {
final filter = kDebugMode ? DevelopmentFilter() : ProductionFilter();
return _logger = Logger(
return logger = Logger(
level: level,
filter: filter,
printer: PrettyPrinter(
Expand All @@ -35,7 +35,7 @@ class _Log {
StackTrace? st,
}) {
printo('$e => $message\n=> $st', isError: true);
_logger.e(message, error: e, stackTrace: st);
logger.e(message, error: e, stackTrace: st);
}
}

Expand Down
6 changes: 3 additions & 3 deletions lib/controller/lyrics_search_utils/lrc_search_utils_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import 'package:namida/controller/lyrics_search_utils/lrc_search_details.dart';
import 'package:namida/controller/lyrics_search_utils/lrc_search_utils_selectable.dart';
import 'package:namida/controller/lyrics_search_utils/lrc_search_utils_youtubeid.dart';
import 'package:namida/youtube/class/youtube_id.dart';
import 'package:namida/youtube/controller/youtube_controller.dart';
import 'package:namida/youtube/controller/youtube_info_controller.dart';

abstract class LrcSearchUtils {
const LrcSearchUtils();
Expand All @@ -17,8 +17,8 @@ abstract class LrcSearchUtils {
final tr = item.track;
return LrcSearchUtilsSelectable(tr.toTrackExt(), tr);
} else if (item is YoutubeID) {
final videoInfo = YoutubeController.inst.getVideoInfo(item.id, checkFromStorage: true);
return LrcSearchUtilsYoutubeID(item, videoInfo?.name);
final title = YoutubeInfoController.utils.getVideoName(item.id);
return LrcSearchUtilsYoutubeID(item, title);
}
return null;
}
Expand Down
Loading

0 comments on commit cc66282

Please sign in to comment.