Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add user customerization #4

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 185 additions & 15 deletions example/piped_client_example.dart
Original file line number Diff line number Diff line change
@@ -1,24 +1,194 @@
import 'package:piped_client/piped_client.dart';

void main() async {
final client = PipedClient();
import 'package:flutter/material.dart';

final instances = await client.instanceList();

for (final instance in instances) {
print(
'Instance: ${instance.name}${instance.locations} => ${instance.apiUrl}');

void main() {
runApp(MyApp());
}

class MyApp extends StatefulWidget {
@override
MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
// 使用 PipedClient 的默认构造函数,初始时使用默认值
final _pipedClient = PipedClient();
final _instanceUrlController = TextEditingController();
final _instanceListUrlController = TextEditingController();
List<PipedInstance> _instances = [];
String _searchQuery = "";
List<dynamic> _searchResults = [];

@override
void initState() {
super.initState();
_fetchInstances(); // 初始获取实例列表
}

final result = await client.search('piped');
Future<void> _fetchInstances() async {
try {
final instances = await _pipedClient.instanceList();
setState(() {
_instances = instances;
});
_printInstances();
} catch (e) {
print("Error fetching instances: $e");
_showError("Failed to fetch instances: $e");
}
}

for (final item in result.items) {
if (item is PipedSearchItemStream) {
print('Stream: ${item.title} => ${item.url}');
} else if (item is PipedSearchItemChannel) {
print('Channel: ${item.name} => ${item.url}');
} else if (item is PipedSearchItemPlaylist) {
print('Playlist: ${item.name} => ${item.url}');
Future<void> _search() async {
if (_searchQuery.isEmpty) return;
try {
final result = await _pipedClient.search(_searchQuery);
setState(() {
_searchResults = result.items;
});
_printSearchResults();
} catch (e) {
print("Error searching: $e");
_showError("Failed to search: $e");
}
}
}

void _showError(String message) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
}

void _printInstances() {
print("Available Instances:");
for (final instance in _instances) {
print('Instance: ${instance.name} ${instance.locations.join(", ")} => ${instance.apiUrl}');
}
}

void _printSearchResults() {
print("\nSearch results for '$_searchQuery':");
for (final item in _searchResults) {
if (item is PipedSearchItemStream) {
print('Stream: ${item.title} (${item.uploaderName}) - ${item.url}');
_fetchStreamDetails(item.url);
} else if (item is PipedSearchItemChannel) {
print('Channel: ${item.name} - ${item.url}');
} else if (item is PipedSearchItemPlaylist) {
print('Playlist: ${item.name} - ${item.url}');
}
}
}

Future<void> _fetchStreamDetails(String url) async {
try {
final videoId = Uri.parse(url).pathSegments.last;
final streamResponse = await _pipedClient.streams(videoId);
print(' └─ Audio Streams: ${streamResponse.audioStreams.length}');
print(' └─ Video Streams: ${streamResponse.videoStreams.length}');
} catch (e) {
print(' └─ Error fetching stream details: $e');
}
}

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Piped Client UI Example'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: _instanceUrlController,
decoration: const InputDecoration(
labelText: 'Instance URL',
hintText: 'Enter custom instance URL (optional)',
),
),
TextField(
controller: _instanceListUrlController,
decoration: const InputDecoration(
labelText: 'Instance List URL',
hintText: 'Enter custom instance list URL (optional)',
),
),
ElevatedButton(
onPressed: () {
// 更新 PipedClient 的设置
_pipedClient.setInstanceUrl(_instanceUrlController.text);
_pipedClient.setCustomInstanceListUrl(_instanceListUrlController.text);
_fetchInstances(); // 重新获取 Instance 列表
},
child: const Text('Update Settings and Fetch Instances'),
),
const SizedBox(height: 20),
const Text("Available Instances:", style: TextStyle(fontWeight: FontWeight.bold)),
Expanded(
child: ListView.builder(
itemCount: _instances.length,
itemBuilder: (context, index) {
final instance = _instances[index];
return ListTile(
title: Text(instance.name),
subtitle: Text("${instance.apiUrl} - ${instance.locations.join(', ')}"),
);
},
),
),
TextField(
onChanged: (value) {
_searchQuery = value;
},
decoration: const InputDecoration(
labelText: 'Search',
hintText: 'Enter search query',
),
),
ElevatedButton(
onPressed: _search,
child: const Text('Search'),
),
Expanded(
child: ListView.builder(
itemCount: _searchResults.length,
itemBuilder: (context, index) {
final item = _searchResults[index];
if (item is PipedSearchItemStream) {
return ListTile(
title: Text(item.title),
subtitle: Text('Stream - ${item.uploaderName}'),
);
} else if (item is PipedSearchItemChannel) {
return ListTile(
title: Text(item.name),
subtitle: Text('Channel'),
);
} else if (item is PipedSearchItemPlaylist) {
return ListTile(
title: Text(item.name),
subtitle: Text('Playlist'),
);
}
return const SizedBox.shrink();
},
),
),
],
),
),
),
);
}

@override
void dispose() {
_instanceUrlController.dispose();
_instanceListUrlController.dispose();
super.dispose();
}
}
46 changes: 41 additions & 5 deletions lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,25 @@ enum PipedFilter {
class PipedClient {
final Dio client;
final bool debug;
String? _customInstanceListUrl; // Used to store the user-defined Instance list URL
String _instance; // Used to store the user-defined Piped instance URL

/// Default Instance list URL
static const String defaultInstanceListUrl =
"https://raw.githubusercontent.com/wiki/TeamPiped/Piped-Frontend/Instances.md";

/// Default Piped instance URL
static const String defaultInstance = "https://pipedapi.kavin.rocks";

PipedClient({
String instance = "https://pipedapi.kavin.rocks",
String? instance, // Optional: User-defined Piped instance URL
this.debug = false,
}) : client = Dio(
String? customInstanceListUrl, // Optional: User-defined Instance list URL
}) : _instance = instance ?? defaultInstance,
_customInstanceListUrl = customInstanceListUrl,
client = Dio(
BaseOptions(
baseUrl: instance,
baseUrl: instance ?? defaultInstance, // Use custom instance or default instance
responseType: ResponseType.json,
),
);
Expand All @@ -51,8 +63,11 @@ class PipedClient {
}

Future<List<PipedInstance>> instanceList() async {
// Use user-defined link, if not then use the default link
final String url = _customInstanceListUrl ?? defaultInstanceListUrl;

final res = await client.get(
"https://raw.githubusercontent.com/wiki/TeamPiped/Piped-Frontend/Instances.md",
url,
options: Options(
responseType: ResponseType.plain,
),
Expand Down Expand Up @@ -86,4 +101,25 @@ class PipedClient {
.where((instance) => instance.apiUrl.startsWith("http"))
.toList();
}
}

/// Set a custom Instance list URL
void setCustomInstanceListUrl(String url) {
_customInstanceListUrl = url;
}

/// Set a custom Piped instance URL
void setInstanceUrl(String url) {
_instance = url;
client.options.baseUrl = url;
}

/// Get the current Piped instance URL
String getInstanceUrl() {
return _instance;
}

/// Get the current Instance list URL
String getInstanceListUrl() {
return _customInstanceListUrl ?? defaultInstanceListUrl;
}
}
2 changes: 2 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ environment:

dependencies:
dio: ^5.1.2
flutter:
sdk: flutter
json_annotation: ^4.8.1

dev_dependencies:
Expand Down