From 16fa4304d0336c087e607564103e18daa1af8ae1 Mon Sep 17 00:00:00 2001 From: Ashley //// Date: Sat, 2 Mar 2024 19:27:19 +0000 Subject: [PATCH] add poke desktop --- poke-gtk-app.py | 201 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 poke-gtk-app.py diff --git a/poke-gtk-app.py b/poke-gtk-app.py new file mode 100644 index 00000000..cc7a65e4 --- /dev/null +++ b/poke-gtk-app.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024-20xx Poke! (https://codeberg.org/ashley/poke) +# +# 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 . +# + +import gi +import json +import vlc +import subprocess +import urllib.request + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, GdkPixbuf, GObject +from gi.repository import Gio + +class PokeGtkApp(Gtk.Window): + def __init__(self): + Gtk.Window.__init__(self, title="Poke Desktop") + self.set_default_size(800, 600) + + self.vlc_instance = vlc.Instance('--no-xlib') + self.player = self.vlc_instance.media_player_new() + self.is_playing = False + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + self.add(vbox) + + self.search_entry = Gtk.Entry() + self.search_entry.set_placeholder_text("Enter search query") + vbox.pack_start(self.search_entry, False, False, 0) + + hbox_buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) + vbox.pack_start(hbox_buttons, False, False, 0) + + self.search_button = Gtk.Button(label="Search") + self.search_button.connect("clicked", self.on_search_clicked) + hbox_buttons.pack_start(self.search_button, True, True, 0) + + self.play_button = Gtk.Button(label="Play") + self.play_button.connect("clicked", self.on_play_clicked) + hbox_buttons.pack_start(self.play_button, False, False, 0) + self.play_button.set_sensitive(False) # Initially disabled + + self.pause_button = Gtk.Button(label="Pause") + self.pause_button.connect("clicked", self.on_pause_clicked) + hbox_buttons.pack_start(self.pause_button, False, False, 0) + self.pause_button.set_sensitive(False) # Initially disabled + + self.bottom_bar_label = Gtk.Label() + vbox.pack_end(self.bottom_bar_label, False, False, 0) + + scrolled_window = Gtk.ScrolledWindow() + scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + vbox.pack_start(scrolled_window, True, True, 0) + + self.video_liststore = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str, str) # Changed the order of columns + self.treeview = Gtk.TreeView(model=self.video_liststore) + + thumbnail_column = Gtk.TreeViewColumn("Thumbnail") # Thumbnail column at the first position + title_column = Gtk.TreeViewColumn("Title") # Title column at the second position + author_column = Gtk.TreeViewColumn("Author") + + self.treeview.append_column(thumbnail_column) + self.treeview.append_column(title_column) + self.treeview.append_column(author_column) + + thumbnail_cell = Gtk.CellRendererPixbuf() + title_cell = Gtk.CellRendererText() + author_cell = Gtk.CellRendererText() + + thumbnail_column.pack_start(thumbnail_cell, True) + title_column.pack_start(title_cell, True) + author_column.pack_start(author_cell, True) + + thumbnail_column.add_attribute(thumbnail_cell, "pixbuf", 0) + title_column.add_attribute(title_cell, "text", 1) + author_column.add_attribute(author_cell, "text", 2) + + self.treeview.connect("row-activated", self.on_video_selected) + + scrolled_window.add(self.treeview) + + # Tray icon setup + self.tray_icon = Gtk.StatusIcon.new() + self.tray_icon.set_from_gicon(Gio.ThemedIcon.new("media-playback-start-symbolic")) # Set the icon + self.tray_icon.connect("activate", self.on_tray_icon_clicked) + self.tray_icon.connect("popup-menu", self.on_tray_icon_popup_menu) + + def on_tray_icon_clicked(self, icon, button): + if button == Gdk.BUTTON_PRIMARY: + if self.is_playing: + self.player.pause() + self.tray_icon.set_from_icon_name("media-playback-start") + else: + self.player.play() + self.tray_icon.set_from_icon_name("media-playback-pause") + self.is_playing = not self.is_playing + + def on_tray_icon_popup_menu(self, icon, button, time): + menu = Gtk.Menu() + + play_pause_item = Gtk.MenuItem("Play/Pause") + play_pause_item.connect("activate", self.on_tray_icon_clicked, Gdk.BUTTON_PRIMARY) + menu.append(play_pause_item) + + quit_item = Gtk.MenuItem("Quit") + quit_item.connect("activate", Gtk.main_quit) + menu.append(quit_item) + + menu.show_all() + menu.popup(None, None, None, None, button, time) + + def on_search_clicked(self, button): + search_query = self.search_entry.get_text() + json_data = self.fetch_data(search_query) + self.populate_video_list(json_data) + + def on_video_selected(self, treeview, path, column): + selection = self.video_liststore[path] + video_title, video_url, author_name = selection[1], selection[3], selection[2] # Adjusted index due to column order change + + # Play the selected video + self.play_video(video_url) + self.update_bottom_bar(video_title) + + def fetch_data(self, search_query): + invid_api_url = "https://invid-api.poketube.fun" + search_query_encoded = search_query.replace(" ", "+") + curl_command = f'curl -s "{invid_api_url}/api/v1/search?q={search_query_encoded}&type=video"' + try: + output = subprocess.check_output(curl_command, shell=True) + data = output.decode("utf-8") + return json.loads(data) + except subprocess.CalledProcessError as e: + print(f"Error fetching data: {e}") + return [] + + def populate_video_list(self, json_data): + self.video_liststore.clear() + for video in json_data: + title = video["title"] + video_id = video["videoId"] + author = video["author"] # Added to retrieve author's name + video_url = f"https://poketube.fun/watch?v={video_id}" + thumbnail_url = f"https://i.ytimg.com/vi/{video_id}/default.jpg" + thumbnail = self.load_thumbnail(thumbnail_url) # Load thumbnail from URL + self.video_liststore.append([thumbnail, title, author, video_url]) + + def load_thumbnail(self, url): + response = urllib.request.urlopen(url) + data = response.read() + loader = GdkPixbuf.PixbufLoader() + loader.write(data) + loader.close() + return loader.get_pixbuf() + + def play_video(self, video_url): + try: + command = ['yt-dlp', '-g', video_url] + video_direct_url = subprocess.check_output(command).decode('utf-8').strip() + media = self.vlc_instance.media_new(video_direct_url) + self.player.set_media(media) + self.player.play() + self.is_playing = True + self.play_button.set_sensitive(True) # Enable play button + self.pause_button.set_sensitive(True) # Enable pause button + except Exception as e: + print(f"Error playing video: {e}") + + def on_play_clicked(self, button): + if self.is_playing: + self.player.play() + + def on_pause_clicked(self, button): + if self.is_playing: + self.player.pause() + + def update_bottom_bar(self, text): + self.bottom_bar_label.set_text(text) + +def main(): + app = PokeGtkApp() + app.connect("destroy", Gtk.main_quit) + app.show_all() + Gtk.main() + +if __name__ == "__main__": + main()