diff --git a/meson.build b/meson.build
index 1b9bca2..6cb6713 100644
--- a/meson.build
+++ b/meson.build
@@ -18,7 +18,8 @@ asresources = gnome.compile_resources(
# Create a new executable, list the files we want to compile, list the dependencies we need, and install
executable(
meson.project_name(),
- 'src/vido.vala',
+ 'src/Application.vala',
+ 'src/MainWindow.vala',
asresources,
vala_args: [
'--pkg=posix'
diff --git a/src/Application.vala b/src/Application.vala
new file mode 100644
index 0000000..d953a2f
--- /dev/null
+++ b/src/Application.vala
@@ -0,0 +1,42 @@
+/*
+* Copyright 2017-2019 Bernardo Anderson
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*/
+
+public class Application : Gtk.Application {
+ private MainWindow window;
+
+ public Application () {
+ Object (
+ application_id: "com.github.bernardodsanderson.vido",
+ flags: ApplicationFlags.FLAGS_NONE
+ );
+ }
+
+ protected override void activate () {
+ if (window != null) {
+ window.present ();
+ return;
+ }
+
+ window = new MainWindow (this);
+ window.show_all ();
+ }
+
+ public static int main (string[] args) {
+ var app = new Application ();
+ return app.run (args);
+ }
+}
diff --git a/src/MainWindow.vala b/src/MainWindow.vala
new file mode 100644
index 0000000..a69ed29
--- /dev/null
+++ b/src/MainWindow.vala
@@ -0,0 +1,286 @@
+/*
+* Copyright 2017-2019 Bernardo Anderson
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*/
+
+public class MainWindow : Gtk.ApplicationWindow {
+ private string folder_location;
+ private string video_info;
+
+ public MainWindow (Gtk.Application app) {
+ Object (
+ application: app,
+ border_width: 15,
+ resizable: false
+ );
+ }
+
+ construct {
+ // Add CSS file
+ var css_provider = new Gtk.CssProvider ();
+ css_provider.load_from_resource ("/com/github/bernardodsanderson/vido/style.css");
+ Gtk.StyleContext.add_provider_for_screen (Gdk.Screen.get_default (),
+ css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+ // Header
+ var header = new Gtk.HeaderBar ();
+ header.show_close_button = true;
+ header.title = _("VIDO - Video Downloader");
+ set_titlebar (header);
+
+ // URL input
+ var url_input = new Gtk.Entry ();
+ url_input.get_style_context ().add_class ("inputurl");
+ url_input.placeholder_text = _("Enter URL…");
+
+ // Add a clear icon
+ url_input.secondary_icon_name = "edit-clear";
+ url_input.input_purpose = Gtk.InputPurpose.URL;
+
+ // Save location button
+ var location_button = new Gtk.Button.with_label (_("Select Folder to Save"));
+
+ // Audio Only
+ var audio_only = new Gtk.CheckButton.with_label (_("Audio Only"));
+
+ // With Subtitles
+ var with_subtitles = new Gtk.CheckButton.with_label (_("Add Subtitles"));
+
+ // Video Label
+ var video_label = new Gtk.Label ("");
+ video_label.get_style_context ().add_class ("videolabel");
+ video_label.margin_top = 10;
+
+ // Get info button
+ var info_button = new Gtk.Button.with_label (_("Get Video Info"));
+ info_button.sensitive = false;
+
+ // download_button button
+ var download_button = new Gtk.Button.with_label (_("Download"));
+ download_button.margin_top = 10;
+ download_button.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION);
+ download_button.get_style_context ().add_class ("downloadbutton");
+ download_button.sensitive = false;
+
+ location_button.grab_focus ();
+
+ var grid = new Gtk.Grid ();
+ grid.row_spacing = 6;
+ grid.column_spacing = 6;
+ grid.attach (url_input, 0, 0, 75, 1);
+ grid.attach (location_button, 0, 1, 75, 1);
+ grid.attach (audio_only, 0, 7, 20, 1);
+ grid.attach_next_to (with_subtitles, audio_only, Gtk.PositionType.RIGHT, 2, 1);
+ grid.attach (video_label, 0, 3, 75, 1);
+ grid.attach (info_button, 0, 2, 75, 1);
+ grid.attach (download_button, 0, 8, 75, 12);
+ add (grid);
+
+ url_input.changed.connect (() => {
+ if (url_input.text != "") {
+ info_button.sensitive = true;
+
+ if (folder_location != "") {
+ download_button.sensitive = true;
+ }
+ } else {
+ info_button.sensitive = false;
+ download_button.sensitive = false;
+ }
+ });
+
+ url_input.icon_press.connect ((pos, event) => {
+ if (pos == Gtk.EntryIconPosition.SECONDARY) {
+ info_button.label = _("Get Video Info");
+ video_label.label = "";
+ url_input.text = "";
+ download_button.label = _("Download");
+ with_subtitles.active = false;
+ download_button.sensitive = false;
+ audio_only.active = false;
+ }
+ });
+
+ location_button.clicked.connect (() => {
+ on_open_clicked ();
+ location_button.label = folder_location;
+
+ if (folder_location != "") {
+ if (url_input.text != "") {
+ download_button.sensitive = true;
+ }
+ }
+ });
+
+ audio_only.toggled.connect (() => {
+ // Emitted when the audio_only has been clicked:
+ if (audio_only.active) {
+ with_subtitles.active = false;
+ }
+ });
+
+ with_subtitles.toggled.connect (() => {
+ // Emitted when the with_subtitles has been clicked:
+ if (with_subtitles.active) {
+ audio_only.active = false;
+ }
+ });
+
+ info_button.clicked.connect (() => {
+ string str = _("Loading info…");
+ info_button.label = str;
+ MainLoop loop = new MainLoop ();
+ try {
+ string[] spawn_args = { "youtube-dl", "-e", "--get-duration", "--get-format", url_input.text };
+ string[] spawn_env = Environ.get ();
+ Pid child_pid;
+
+ int standard_input;
+ int standard_output;
+ int standard_error;
+
+ Process.spawn_async_with_pipes ("/",
+ spawn_args,
+ spawn_env,
+ SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
+ null,
+ out child_pid,
+ out standard_input,
+ out standard_output,
+ out standard_error);
+
+ // stdout:
+ IOChannel output = new IOChannel.unix_new (standard_output);
+ output.add_watch (IOCondition.IN | IOCondition.HUP, (channel, condition) => {
+ return process_line (channel, condition, "stdout");
+ });
+
+ // stderr:
+ IOChannel error = new IOChannel.unix_new (standard_error);
+ error.add_watch (IOCondition.IN | IOCondition.HUP, (channel, condition) => {
+ return process_line (channel, condition, "stderr");
+ });
+
+ ChildWatch.add (child_pid, (pid, status) => {
+ // Triggered when the child indicated by child_pid exits
+ if (status == 0) {
+ video_label.label = video_info;
+ } else {
+ video_label.label = "";
+ var error_dialog = new Granite.MessageDialog.with_image_from_icon_name (
+ _("Unable to fetch the video info"),
+ _("The following error message may be helpful:"),
+ "dialog-error");
+ error_dialog.transient_for = this;
+ error_dialog.show_error_details (video_info);
+ error_dialog.run ();
+ error_dialog.destroy ();
+ }
+
+ video_info = ""; // Clear the video info (or the error message)
+ info_button.label = _("Get Video Info");
+ Process.close_pid (pid);
+ loop.quit ();
+ });
+
+ loop.run ();
+ } catch (SpawnError e) {
+ stdout.printf ("Error: %s\n", e.message);
+ }
+ });
+
+ download_button.clicked.connect (() => {
+ download_button.label = _("Downloading…");
+ download_button.sensitive = false;
+ // var notification = new Notification (_("Hello World"));
+ // notification.set_body (_("This is my first notification!"));
+ // this.send_notification ("notify.app", notification);
+ // var image = new Gtk.Image.from_icon_name ("dialog-warning", Gtk.IconSize.DIALOG);
+ // notification.set_icon (image.gicon);
+ string[] spawn_args;
+ if (audio_only.active) { // --extract-audio
+ spawn_args = { "youtube-dl", "--no-warnings", "--extract-audio", url_input.text };
+ } else if (with_subtitles.active) {
+ spawn_args = { "youtube-dl", "--no-warnings", "--all-subs", url_input.text };
+ } else {
+ spawn_args = { "youtube-dl", "--no-warnings", url_input.text };
+ }
+
+ MainLoop loop = new MainLoop ();
+ try {
+ string[] spawn_env = Environ.get ();
+ Pid child_pid;
+
+ Process.spawn_async (folder_location,
+ spawn_args,
+ spawn_env,
+ SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
+ null,
+ out child_pid);
+
+ ChildWatch.add (child_pid, (pid, status) => {
+ // Triggered when the child indicated by child_pid exits
+ download_button.label = _("Finished!");
+ download_button.sensitive = true;
+ Process.close_pid (pid);
+ loop.quit ();
+ });
+
+ loop.run ();
+ } catch (SpawnError e) {
+ stdout.printf ("Error: %s\n", e.message);
+ }
+ });
+ }
+
+ private void on_open_clicked () {
+ var file_chooser = new Gtk.FileChooserDialog (
+ _("Open Folder"),
+ this,
+ Gtk.FileChooserAction.SELECT_FOLDER,
+ _("_Cancel"), Gtk.ResponseType.CANCEL,
+ _("_Open"), Gtk.ResponseType.ACCEPT
+ );
+
+ if (file_chooser.run () == Gtk.ResponseType.ACCEPT) {
+ folder_location = file_chooser.get_filename ();
+ stderr.printf ("Folder Selected: %s\n", folder_location);
+ }
+
+ file_chooser.destroy ();
+ }
+
+ private bool process_line (IOChannel channel, IOCondition condition, string stream_name) {
+ if (condition == IOCondition.HUP) {
+ stdout.printf ("%s: The fd has been closed.\n", stream_name);
+ return false;
+ }
+
+ try {
+ string line;
+ channel.read_line (out line, null, null);
+ video_info = line + video_info;
+ stdout.printf ("%s: %s", stream_name, line);
+ } catch (IOChannelError e) {
+ stdout.printf ("%s: IOChannelError: %s\n", stream_name, e.message);
+ return false;
+ } catch (ConvertError e) {
+ stdout.printf ("%s: ConvertError: %s\n", stream_name, e.message);
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/src/vido.vala b/src/vido.vala
deleted file mode 100644
index d6ee2ce..0000000
--- a/src/vido.vala
+++ /dev/null
@@ -1,279 +0,0 @@
-using Gtk;
-Window window;
-string folder_location;
-string video_info;
-
-public static int main(string[] args) {
- init(ref args);
-
- // Global Vars
- var css_provider = new Gtk.CssProvider();
- var download_button = new Button.with_label (_("Download"));
- var header = new HeaderBar();
- var window = new Window();
- var url_input = new Entry();
- bool has_input = false;
- bool has_location = false;
- var location_button = new Button.with_label (_("Select Folder to Save"));
- var video_label = new Label("");
- var info_button = new Button.with_label (_("Get Video Info"));
- var audio_only = new CheckButton.with_label (_("Audio Only"));
- var with_subtitles = new CheckButton.with_label (_("Add Subtitles"));
- bool audio = false;
- bool subtitles = false;
- // Grid
- var grid = new Grid();
-
- // Add CSS file
- try {
- css_provider.load_from_resource("/com/github/bernardodsanderson/vido/style.css");
- } catch (GLib.Error e) {
- stderr.printf("%s", e.message);
- }
- Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
-
- // Header
- header.set_show_close_button(true);
- header.set_title (_("VIDO - Video Downloader"));
-
- // Window
- window.set_border_width(15);
- // window.set_default_size(600, 800);
- window.resizable = false;
- window.set_titlebar(header);
- window.destroy.connect(Gtk.main_quit);
-
- // grid.orientation = Gtk.Orientation.VERTICAL;
- grid.row_spacing = 6;
- grid.column_spacing = 6;
-
- // URL input
- url_input.get_style_context().add_class("inputurl");
- url_input.set_placeholder_text (_("Enter URL…"));
- url_input.changed.connect (() => {
- string url_input_text = url_input.text;
- if (url_input_text != "") {
- info_button.sensitive = true;
- if (has_location) {
- download_button.set_sensitive (true);
- }
- has_input = true;
- } else {
- info_button.sensitive = false;
- download_button.sensitive = false;
- }
- });
- // Add a delete-button:
- url_input.set_icon_from_icon_name (Gtk.EntryIconPosition.SECONDARY, "edit-clear");
- url_input.set_input_purpose(Gtk.InputPurpose.URL);
- url_input.icon_press.connect ((pos, event) => {
- if (pos == Gtk.EntryIconPosition.SECONDARY) {
- info_button.label = _("Get Video Info");
- video_label.label = "";
- url_input.set_text ("");
- download_button.label = _("Download");
- with_subtitles.active = false;
- download_button.set_sensitive (false);
- audio_only.active = false;
- }
- });
- // url_input.set_text("");
- grid.attach (url_input, 0, 0, 75, 1);
-
- // Save location button
- location_button.clicked.connect (() => {
- on_open_clicked();
- location_button.label = folder_location;
- if (folder_location.length > 0) { //&& url_input.get_text() != ""
- has_location = true;
- if (has_input) {
- download_button.set_sensitive (true);
- }
- }
- });
- grid.attach (location_button, 0, 1, 75, 1);
-
- // Audio Only
- audio_only.toggled.connect (() => {
- // Emitted when the audio_only has been clicked:
- if (audio_only.active) {
- with_subtitles.active = false;
- audio = true;
- } else {
- audio = false;
- }
- });
- grid.attach (audio_only, 0, 7, 20, 1);
-
- // With Subtitles
- with_subtitles.toggled.connect (() => {
- // Emitted when the with_subtitles has been clicked:
- if (with_subtitles.active) {
- audio_only.active = false;
- subtitles = true;
- } else {
- subtitles = false;
- }
- });
- grid.attach_next_to (with_subtitles, audio_only, Gtk.PositionType.RIGHT, 2, 1);
-
- // Video Label
- video_label.get_style_context().add_class("videolabel");
- video_label.margin_top = 10;
- grid.attach (video_label, 0, 3, 75, 1);
-
- // Get info button
- info_button.sensitive = false;
- info_button.clicked.connect (() => {
- string str = _("Loading info…");
- info_button.label = str;
- MainLoop loop = new MainLoop ();
- try {
- string[] spawn_args = {"youtube-dl", "-e", "--get-duration", "--get-format", url_input.get_text()};
- string[] spawn_env = Environ.get ();
- Pid child_pid;
-
- int standard_input;
- int standard_output;
- int standard_error;
-
- Process.spawn_async_with_pipes ("/",
- spawn_args,
- spawn_env,
- SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
- null,
- out child_pid,
- out standard_input,
- out standard_output,
- out standard_error);
-
- // stdout:
- IOChannel output = new IOChannel.unix_new (standard_output);
- output.add_watch (IOCondition.IN | IOCondition.HUP, (channel, condition) => {
- return process_line (channel, condition, "stdout");
- });
-
- // stderr:
- IOChannel error = new IOChannel.unix_new (standard_error);
- error.add_watch (IOCondition.IN | IOCondition.HUP, (channel, condition) => {
- return process_line (channel, condition, "stderr");
- });
-
- ChildWatch.add (child_pid, (pid, status) => {
- // Triggered when the child indicated by child_pid exits
- if (status == 0) {
- video_label.label = video_info;
- } else {
- video_label.label = "";
- var error_dialog = new Granite.MessageDialog.with_image_from_icon_name (_("Unable to fetch the video info"), _("The following error message may be helpful:"), "dialog-error");
- error_dialog.transient_for = window;
- error_dialog.show_error_details (video_info);
- error_dialog.run ();
- error_dialog.destroy ();
- }
- video_info = ""; // Clear the video info (or the error message)
- info_button.label = _("Get Video Info");
- Process.close_pid (pid);
- loop.quit ();
- });
-
- loop.run ();
- } catch (SpawnError e) {
- stdout.printf ("Error: %s\n", e.message);
- }
- // info_button.set_sensitive (false);
- });
- grid.attach (info_button, 0, 2, 75, 1);
-
- // download_button button
- download_button.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION);
- download_button.get_style_context().add_class("downloadbutton");
- download_button.set_sensitive (false);
- download_button.clicked.connect (() => {
- download_button.label = _("Downloading…");
- download_button.set_sensitive (false);
- // var notification = new Notification (_("Hello World"));
- // notification.set_body (_("This is my first notification!"));
- // this.send_notification ("notify.app", notification);
- // var image = new Gtk.Image.from_icon_name ("dialog-warning", Gtk.IconSize.DIALOG);
- // notification.set_icon (image.gicon);
- string[] spawn_args;
- if (audio) { // --extract-audio
- spawn_args = {"youtube-dl", "--no-warnings", "--extract-audio", url_input.get_text()};
- } else if (subtitles) {
- spawn_args = {"youtube-dl", "--no-warnings", "--all-subs", url_input.get_text()};
- } else {
- spawn_args = {"youtube-dl", "--no-warnings", url_input.get_text()};
- }
- MainLoop loop = new MainLoop ();
- try {
- string[] spawn_env = Environ.get ();
- Pid child_pid;
-
- Process.spawn_async (folder_location,
- spawn_args,
- spawn_env,
- SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
- null,
- out child_pid);
-
- ChildWatch.add (child_pid, (pid, status) => {
- // Triggered when the child indicated by child_pid exits
- download_button.label = _("Finished!");
- download_button.set_sensitive (true);
- Process.close_pid (pid);
- loop.quit ();
- });
-
- loop.run ();
- } catch (SpawnError e) {
- stdout.printf ("Error: %s\n", e.message);
- }
- });
- download_button.margin_top = 10;
- grid.attach (download_button, 0, 8, 75, 12);
-
- // Add to window
- location_button.grab_focus();
- window.add(grid);
- window.show_all();
-
- Gtk.main();
- return 0;
-}
-
-void on_open_clicked () {
- var file_chooser = new FileChooserDialog (
- _("Open Folder"),
- window,
- FileChooserAction.SELECT_FOLDER,
- _("_Cancel"), ResponseType.CANCEL,
- _("_Open"), ResponseType.ACCEPT);
- if (file_chooser.run () == ResponseType.ACCEPT) {
- folder_location = file_chooser.get_filename ();
- stderr.printf ("Folder Selected: %s\n", file_chooser.get_filename ());
- }
- file_chooser.destroy ();
-}
-
-private static bool process_line (IOChannel channel, IOCondition condition, string stream_name) {
- if (condition == IOCondition.HUP) {
- stdout.printf ("%s: The fd has been closed.\n", stream_name);
- return false;
- }
-
- try {
- string line;
- channel.read_line (out line, null, null);
- video_info = line + video_info;
- stdout.printf ("%s: %s", stream_name, line);
- } catch (IOChannelError e) {
- stdout.printf ("%s: IOChannelError: %s\n", stream_name, e.message);
- return false;
- } catch (ConvertError e) {
- stdout.printf ("%s: ConvertError: %s\n", stream_name, e.message);
- return false;
- }
-
- return true;
-}