Skip to content

Commit

Permalink
feat: add option to use URL directly to play
Browse files Browse the repository at this point in the history
This commit also solves brainwo#3
  • Loading branch information
brainwo committed Oct 26, 2022
1 parent 2fb672a commit 34c2cd1
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 50 deletions.
26 changes: 15 additions & 11 deletions lib/helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'dart:io';
import 'package:flutter/services.dart';
import 'package:youtube_api/youtube_api.dart';

void playVideo(YouTubeVideo item) async {
Future<void> playVideo(String url) async {
// ignore: unused_local_variable
Process process = await Process.start('xwinwrap', [
'-ov',
Expand All @@ -15,8 +15,12 @@ void playVideo(YouTubeVideo item) async {
'-wid',
'WID',
'--profile=wallpaper',
item.url
url
]);
}

void processVideo(YouTubeVideo item) async {
await playVideo(item.url);

List<Map<String, dynamic>> entry = [
{
Expand All @@ -37,16 +41,16 @@ void playVideo(YouTubeVideo item) async {
// TODO: load data.json
// var file = File('data.json');
// if (await file.exists()) {
// List<Map<String, dynamic>> previousData =
// jsonDecode(await file.readAsString());
// previousData.addAll(entry);
// await file
// .writeAsString(jsonEncode(previousData))
// .whenComplete(() => SystemNavigator.pop());
// List<Map<String, dynamic>> previousData =
// jsonDecode(await file.readAsString());
// previousData.addAll(entry);
// await file
// .writeAsString(jsonEncode(previousData))
// .whenComplete(() => SystemNavigator.pop());
// } else {
// await file
// .writeAsString(jsonEncode(entry))
// .whenComplete(() => SystemNavigator.pop());
// await file
// .writeAsString(jsonEncode(entry))
// .whenComplete(() => SystemNavigator.pop());
// }

SystemNavigator.pop();
Expand Down
85 changes: 61 additions & 24 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,17 @@ void main() async {
// File file = File('data.json');
// if (await file.exists()) {
// String fileAsString = await file.readAsString()
runApp(AnimatedBuilder(animation: notifier,
builder: (context,widget) {
return const App();
}));
runApp(AnimatedBuilder(
animation: notifier,
builder: (context, widget) {
return const App();
}));
}

class App extends StatelessWidget {
const App({super.key});
class App extends StatelessWidget {
const App({super.key});

@override
@override
Widget build(BuildContext context) {
FocusNode searchBoxFocus = FocusNode();
FocusNode scrollItemFocus = FocusNode();
Expand Down Expand Up @@ -95,7 +96,7 @@ void main() async {
break;
}
if (event.isKeyPressed(LogicalKeyboardKey.enter) && result.isNotEmpty) {
playVideo(result[selectedVideo.value!]);
processVideo(result[selectedVideo.value!]);
}
return KeyEventResult.ignored;
}),
Expand Down Expand Up @@ -134,44 +135,55 @@ class HomePage extends StatefulWidget {
State<HomePage> createState() => _HomePageState();
}

enum SearchBoxMode { video, playlist, channel, all }
enum SearchBoxMode { video, playlist, channel, all, play }

class _HomePageState extends State<HomePage> {
YoutubeAPI ytApi = YoutubeAPI(innerKey);
String noResultString = AppString.noSearchQuery;
bool loading = false;
SearchBoxMode searchBoxMode = SearchBoxMode.all;
TextEditingController textBoxController = TextEditingController();

@override
Widget build(BuildContext context) {
TextEditingController textBoxController = TextEditingController();

textBoxController.addListener(() {
if (searchBoxMode == SearchBoxMode.all) {
switch (textBoxController.text) {
case 'v ':
setState(() {
searchBoxMode = SearchBoxMode.video;
textBoxController.text = '';
});
break;
case 'c ':
setState(() {
searchBoxMode = SearchBoxMode.channel;
textBoxController.text = '';
});
break;
case 'p ':
setState(() {
searchBoxMode = SearchBoxMode.playlist;
textBoxController.text = '';
});
break;
}
}
if (textBoxController.text.startsWith('https://')) {
setState(() {
searchBoxMode = SearchBoxMode.play;
});
} else if (searchBoxMode == SearchBoxMode.play) {
setState(() {
searchBoxMode = SearchBoxMode.all;
});
}
});

Widget searchResult = Container(
color: appTheme.background,
child: (result.isEmpty)
? Center(child: Text(noResultString))
? Container() // TODO: a nice elementaryOS-like welcoming message
: ListItem(
homepage: widget,
result: result,
Expand All @@ -185,18 +197,31 @@ class _HomePageState extends State<HomePage> {
automaticallyImplyLeading: false,
title: Row(
children: [
if (searchBoxMode != SearchBoxMode.all)
if (searchBoxMode != SearchBoxMode.all &&
searchBoxMode != SearchBoxMode.play)
Row(
children: [
Text(
'${searchBoxMode.name[0].toUpperCase()}${searchBoxMode.name.substring(1)}'),
Container(
decoration: BoxDecoration(
color: appTheme.primary,
borderRadius: const BorderRadius.all(
Radius.circular(6.0),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'${searchBoxMode.name[0].toUpperCase()}${searchBoxMode.name.substring(1)}'),
),
),
const SizedBox(width: 8.0),
],
),
Expanded(
child: KeyboardListener(
focusNode: FocusNode(onKey: ((node, event) {
if (searchBoxMode != SearchBoxMode.all &&
searchBoxMode != SearchBoxMode.play &&
event.isKeyPressed(LogicalKeyboardKey.backspace) &&
textBoxController.text == '') {
setState(() {
Expand All @@ -207,26 +232,28 @@ class _HomePageState extends State<HomePage> {
return KeyEventResult.ignored;
})),
child: TextBox(
// foregroundDecoration: BoxDecoration(
// border: Border.all(width: 1.0),
// ),
autocorrect: false,
placeholder: searchBoxMode == SearchBoxMode.all
autofocus: true,
placeholder: (searchBoxMode == SearchBoxMode.all ||
searchBoxMode == SearchBoxMode.play)
? AppString.searchPlaceholderAll
: AppString.searchPlaceholder,
focusNode: widget.searchBoxFocus,
controller: textBoxController,
onSubmitted: searchHandler,
onSubmitted: topbarHandler,
),
),
),
const SizedBox(width: 16.0),
const SizedBox(width: 8.0),
Padding(
padding: const EdgeInsets.all(8.0),
child: Button(
child: const Center(child: Icon(FluentIcons.search)),
child: Center(
child: Icon(searchBoxMode == SearchBoxMode.play
? FluentIcons.play
: FluentIcons.search)),
onPressed: () {
searchHandler(textBoxController.text);
topbarHandler(textBoxController.text);
}),
)
],
Expand All @@ -247,10 +274,20 @@ class _HomePageState extends State<HomePage> {
);
}

void searchHandler(String query) {
void topbarHandler(String query) {
if (searchBoxMode == SearchBoxMode.play) {
playVideo(query).then(
(_) {
SystemNavigator.pop();
},
);
return;
}

setState(() {
loading = true;
});

ytApi
.search(query,
type: searchBoxMode == SearchBoxMode.all
Expand Down
2 changes: 1 addition & 1 deletion lib/string/en_us.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class AppString {
AppString._();

static const String noSearchQuery = 'No search query';
static const String searchPlaceholderAll = 'Search... (video, channel, playlist)';
static const String searchPlaceholderAll = 'Search or paste your link here';
static const String searchPlaceholder = 'Search...';
static const String errorInformation = 'Error: ';

Expand Down
4 changes: 2 additions & 2 deletions lib/widget/list_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class _ListItemState extends State<ListItem> {
return ListItemVideo(
onClick: () async {
if (widget.selected.value == index) {
playVideo(item);
processVideo(item);
}
widget.selected.value = index;
},
Expand All @@ -81,7 +81,7 @@ class _ListItemState extends State<ListItem> {
return ListItemPlaylist(
onClick: () async {
if (widget.selected.value == index) {
playVideo(item);
processVideo(item);
}
widget.selected.value = index;
},
Expand Down
36 changes: 24 additions & 12 deletions linux/my_application.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

#include "my_application.h"

#include <flutter_linux/flutter_linux.h>
Expand All @@ -17,6 +18,13 @@ G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
MyApplication* self = MY_APPLICATION(application);

GList* windows = gtk_application_get_windows(GTK_APPLICATION(application));
if (windows) {
gtk_window_present(GTK_WINDOW(windows->data));
return;
}

GtkWindow* window =
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));

Expand Down Expand Up @@ -45,15 +53,16 @@ static void my_application_activate(GApplication* application) {
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_header_bar_set_title(header_bar, "ythacker");
gtk_window_set_title(window, "ythacker");
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
}

gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window));

g_autoptr(FlDartProject) project = fl_dart_project_new();
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
fl_dart_project_set_dart_entrypoint_arguments(
project, self->dart_entrypoint_arguments);

FlView* view = fl_view_new(project);
gtk_widget_show(GTK_WIDGET(view));
Expand All @@ -65,22 +74,24 @@ static void my_application_activate(GApplication* application) {
}

// Implements GApplication::local_command_line.
static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
static gboolean my_application_local_command_line(GApplication* application,
gchar*** arguments,
int* exit_status) {
MyApplication* self = MY_APPLICATION(application);
// Strip out the first argument as it is the binary name.
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);

g_autoptr(GError) error = nullptr;
if (!g_application_register(application, nullptr, &error)) {
g_warning("Failed to register: %s", error->message);
*exit_status = 1;
return TRUE;
g_warning("Failed to register: %s", error->message);
*exit_status = 1;
return TRUE;
}

g_application_activate(application);
*exit_status = 0;

return TRUE;
return FALSE;
}

// Implements GObject::dispose.
Expand All @@ -92,15 +103,16 @@ static void my_application_dispose(GObject* object) {

static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
G_APPLICATION_CLASS(klass)->local_command_line =
my_application_local_command_line;
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
}

static void my_application_init(MyApplication* self) {}

MyApplication* my_application_new() {
return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID,
"flags", G_APPLICATION_NON_UNIQUE,
nullptr));
return MY_APPLICATION(g_object_new(
my_application_get_type(), "application-id", APPLICATION_ID, "flags",
G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_HANDLES_OPEN,
nullptr));
}

0 comments on commit 34c2cd1

Please sign in to comment.