Initial commit

This commit is contained in:
2026-01-11 19:49:43 +09:00
commit 9cf16ce279
140 changed files with 7562 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/stream_entry.dart';
import '../services/m3u_service.dart';
final m3uServiceProvider = Provider<M3UService>((ref) => M3UService());
final channelListProvider =
StateNotifierProvider<ChannelListNotifier, AsyncValue<List<StreamEntry>>>(
(ref) {
final svc = ref.watch(m3uServiceProvider);
return ChannelListNotifier(svc);
});
final selectedChannelProvider = StateProvider<StreamEntry?>((ref) => null);
class ChannelListNotifier extends StateNotifier<AsyncValue<List<StreamEntry>>> {
final M3UService _svc;
ChannelListNotifier(this._svc) : super(const AsyncValue.data([]));
Future<void> fetch(String url, {bool force = false}) async {
state = const AsyncValue.loading();
try {
final entries = await _svc.fetch(url, forceRefresh: force);
state = AsyncValue.data(entries);
} catch (e, st) {
state = AsyncValue.error(e, st);
}
}
Future<void> refresh(String url) async => fetch(url, force: true);
}

View File

@@ -0,0 +1,66 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
class M3uSourcesState {
final List<String> sources;
final String? defaultUrl;
M3uSourcesState({required this.sources, this.defaultUrl});
M3uSourcesState copyWith({List<String>? sources, String? defaultUrl}) =>
M3uSourcesState(
sources: sources ?? this.sources,
defaultUrl: defaultUrl ?? this.defaultUrl,
);
}
class M3uSourcesNotifier extends StateNotifier<M3uSourcesState> {
static const _kSourcesKey = 'm3u_sources';
static const _kDefaultKey = 'm3u_default';
M3uSourcesNotifier() : super(M3uSourcesState(sources: [], defaultUrl: null));
Future<void> load() async {
final sp = await SharedPreferences.getInstance();
final list = sp.getStringList(_kSourcesKey) ?? <String>[];
final def = sp.getString(_kDefaultKey);
state = state.copyWith(sources: list, defaultUrl: def);
}
Future<void> add(String url) async {
final u = url.trim();
if (u.isEmpty) return;
if (state.sources.contains(u)) return;
final updated = [...state.sources, u];
state = state.copyWith(sources: updated);
final sp = await SharedPreferences.getInstance();
await sp.setStringList(_kSourcesKey, updated);
}
Future<void> remove(String url) async {
final updated = state.sources.where((s) => s != url).toList();
String? def = state.defaultUrl;
if (def == url) def = null;
state = state.copyWith(sources: updated, defaultUrl: def);
final sp = await SharedPreferences.getInstance();
await sp.setStringList(_kSourcesKey, updated);
if (def == null)
await sp.remove(_kDefaultKey);
else
await sp.setString(_kDefaultKey, def);
}
Future<void> setDefault(String? url) async {
state = state.copyWith(defaultUrl: url);
final sp = await SharedPreferences.getInstance();
if (url == null)
await sp.remove(_kDefaultKey);
else
await sp.setString(_kDefaultKey, url);
}
}
final m3uSourcesProvider =
StateNotifierProvider<M3uSourcesNotifier, M3uSourcesState>((ref) {
return M3uSourcesNotifier();
});