added cargo files

This commit is contained in:
2026-03-03 10:57:43 -05:00
parent 478a90e01b
commit 169df46bc2
813 changed files with 227273 additions and 9 deletions

View File

@@ -0,0 +1,125 @@
// Copyright 2020 Ben Hills and the project contributors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:pinepods_mobile/bloc/bloc.dart';
import 'package:pinepods_mobile/entities/episode.dart';
import 'package:pinepods_mobile/services/audio/audio_player_service.dart';
import 'package:pinepods_mobile/services/podcast/podcast_service.dart';
import 'package:pinepods_mobile/state/bloc_state.dart';
import 'package:logging/logging.dart';
import 'package:rxdart/rxdart.dart';
/// The BLoC provides access to [Episode] details outside the direct scope
/// of a [Podcast].
class EpisodeBloc extends Bloc {
final log = Logger('EpisodeBloc');
final PodcastService podcastService;
final AudioPlayerService audioPlayerService;
/// Add to sink to fetch list of current downloaded episodes.
final BehaviorSubject<bool> _downloadsInput = BehaviorSubject<bool>();
/// Add to sink to fetch list of current episodes.
final BehaviorSubject<bool> _episodesInput = BehaviorSubject<bool>();
/// Add to sink to delete the passed [Episode] from storage.
final PublishSubject<Episode?> _deleteDownload = PublishSubject<Episode>();
/// Add to sink to toggle played status of the [Episode].
final PublishSubject<Episode?> _togglePlayed = PublishSubject<Episode>();
/// Stream of currently downloaded episodes
Stream<BlocState<List<Episode>>>? _downloadsOutput;
/// Stream of current episodes
Stream<BlocState<List<Episode>>>? _episodesOutput;
/// Cache of our currently downloaded episodes.
List<Episode>? _episodes;
EpisodeBloc({
required this.podcastService,
required this.audioPlayerService,
}) {
_init();
}
void _init() {
_downloadsOutput = _downloadsInput.switchMap<BlocState<List<Episode>>>((bool silent) => _loadDownloads(silent));
_episodesOutput = _episodesInput.switchMap<BlocState<List<Episode>>>((bool silent) => _loadEpisodes(silent));
_handleDeleteDownloads();
_handleMarkAsPlayed();
_listenEpisodeEvents();
}
void _handleDeleteDownloads() async {
_deleteDownload.stream.listen((episode) async {
var nowPlaying = audioPlayerService.nowPlaying?.guid == episode?.guid;
/// If we are attempting to delete the episode we are currently playing, we need to stop the audio.
if (nowPlaying) {
await audioPlayerService.stop();
}
await podcastService.deleteDownload(episode!);
fetchDownloads(true);
});
}
void _handleMarkAsPlayed() async {
_togglePlayed.stream.listen((episode) async {
await podcastService.toggleEpisodePlayed(episode!);
fetchDownloads(true);
});
}
void _listenEpisodeEvents() {
// Listen for episode updates. If the episode is downloaded, we need to update.
podcastService.episodeListener!.where((event) => event.episode.downloaded || event.episode.played).listen((event) => fetchDownloads(true));
}
Stream<BlocState<List<Episode>>> _loadDownloads(bool silent) async* {
if (!silent) {
yield BlocLoadingState();
}
_episodes = await podcastService.loadDownloads();
yield BlocPopulatedState<List<Episode>>(results: _episodes);
}
Stream<BlocState<List<Episode>>> _loadEpisodes(bool silent) async* {
if (!silent) {
yield BlocLoadingState();
}
_episodes = await podcastService.loadEpisodes();
yield BlocPopulatedState<List<Episode>>(results: _episodes);
}
@override
void dispose() {
_downloadsInput.close();
_deleteDownload.close();
_togglePlayed.close();
}
void Function(bool) get fetchDownloads => _downloadsInput.add;
void Function(bool) get fetchEpisodes => _episodesInput.add;
Stream<BlocState<List<Episode>>>? get downloads => _downloadsOutput;
Stream<BlocState<List<Episode>>>? get episodes => _episodesOutput;
void Function(Episode?) get deleteDownload => _deleteDownload.add;
void Function(Episode?) get togglePlayed => _togglePlayed.add;
}