Added media downloader project

This commit is contained in:
itdominator 2024-01-22 22:14:22 -06:00
parent 93cde0000d
commit 2309170ee9
7 changed files with 453 additions and 0 deletions

View File

@ -0,0 +1,49 @@
#!/usr/bin/python3
# Gtk imports
import gi, faulthandler, signal
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib
# Python imports
import inspect
from setproctitle import setproctitle
# Application imports
from utils.settings import Settings
from signal_classes.controller import Controller
class Main:
def __init__(self):
setproctitle('Media Downloader')
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, Gtk.main_quit)
faulthandler.enable() # For better debug info
settings = Settings()
builder = settings.returnBuilder()
# Gets the methods from the classes and sets to handler.
# Then, builder connects to any signals it needs.
classes = [Controller(settings)]
handlers = {}
for c in classes:
methods = inspect.getmembers(c, predicate=inspect.ismethod)
handlers.update(methods)
builder.connect_signals(handlers)
window = settings.createWindow()
window.show()
if __name__ == "__main__":
try:
main = Main()
Gtk.main()
except Exception as e:
print(e)

View File

@ -0,0 +1,221 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkImage" id="downloadImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-goto-bottom</property>
<property name="icon_size">5</property>
</object>
<object class="GtkListStore" id="downloadTypes">
<columns>
<!-- column-name Format -->
<column type="gchararray"/>
<!-- column-name Video -->
<column type="gchararray"/>
<!-- column-name Resolution -->
<column type="gchararray"/>
<!-- column-name Size -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkImage" id="updateImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-connect</property>
<property name="icon_size">5</property>
</object>
<object class="GtkWindow" id="Main_Window">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Youtube-DL Gui</property>
<property name="window_position">center</property>
<property name="default_width">650</property>
<property name="default_height">420</property>
<property name="gravity">center</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Update</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Update...</property>
<property name="image">updateImage</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="updateYoutubeDl" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="urlEntry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="has_default">True</property>
<property name="placeholder_text" translatable="yes">http://...</property>
<signal name="changed" handler="urlChanged" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label">gtk-paste</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Paste...</property>
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="paste" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<property name="overlay_scrolling">False</property>
<child>
<object class="GtkTreeView" id="tree">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="model">downloadTypes</property>
<property name="headers_clickable">False</property>
<property name="enable_grid_lines">horizontal</property>
<property name="activate_on_single_click">True</property>
<signal name="row-activated" handler="setSelected" swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Format Code</property>
<property name="sort_order">descending</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Video Type</property>
<property name="sort_order">descending</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Resolution</property>
<property name="sort_order">descending</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">2</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Size</property>
<property name="sort_order">descending</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">3</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="downloadButton">
<property name="label" translatable="yes">Download</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="focus_on_click">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Download...</property>
<property name="image">downloadImage</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="download" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkImage" id="thumbnailImg">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-missing-image</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@ -0,0 +1,128 @@
# Gtk imports
import gi, faulthandler, signal
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository.GdkPixbuf import Pixbuf
# Python imports
import threading, subprocess, os, requests
from setproctitle import setproctitle
# Application imports
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
return wrapper
class Controller:
def __init__(self, settings):
setproctitle('Media Downloader')
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, Gtk.main_quit)
faulthandler.enable() # For better debug info
self.settings = settings
self.builder = self.settings.returnBuilder()
self.downloadTypes = self.builder.get_object("downloadTypes")
self.urlEntry = self.builder.get_object("urlEntry")
self.downloadButton = self.builder.get_object("downloadButton")
self.thumbnail = self.builder.get_object("thumbnailImg")
self.selected = None
def updateYoutubeDl(self, widget):
pass
def paste(self, widget):
entry = self.getClipboardData()
self.urlEntry.set_text(entry)
@threaded
def urlChanged(self, widget): # This also gets called by paste
entry = widget.get_text().strip()
size = len(entry)
thumbUrl = "https://img.youtube.com/vi/" + entry[(size - 11): size] + "/hqdefault.jpg"
self.setThumbnail(thumbUrl)
self.downloadButton.set_sensitive(False)
self.downloadTypes.clear()
if entry:
if len(entry) in [28, 43]:
if "youtu.be" in entry or "watch?v=" in entry:
self.showDownloadTypes()
def showDownloadTypes(self):
lines = self.getFormatData().split("\n")
# Remove pre format strings
i = 0
while i <= 3:
del lines[0]
i += 1
for line in lines:
parts = line.split()
while len(parts) != 4:
parts.append("NO DATA")
if parts[3][len(parts[3]) - 1] == ",":
parts[3] = parts[3][0:len(parts[3]) - 1]
self.downloadTypes.append(parts)
@threaded
def download(self, widget):
url = self.urlEntry.get_text()
dlComm = ["youtube-dl", "--no-playlist", "-o", "~/Downloads/%(title)s.%(ext)s",
"-f", self.selected, url]
proc = subprocess.Popen(dlComm, stdout=subprocess.PIPE) # Gets formats
while True:
line = proc.stdout.readline()
if not line: break
print(line.decode("utf-8").rstrip())
def setSelected(self, widget, row, col):
model = widget.get_model()
self.selected = model[row][0]
self.downloadButton.set_sensitive(True)
@threaded
def setThumbnail(self, url):
fname = "/tmp/thumbnail-hqdefault.jpg"
r = requests.get(url, allow_redirects=True)
open(fname, 'wb').write(r.content)
self.thumbnail.set_from_pixbuf(Pixbuf.new_from_file(fname))
def getFormatData(self):
url = self.urlEntry.get_text()
dlComm = ["youtube-dl", "-F", url]
awkComm = ["awk", "{ print $1, $2, $3, $11 }"]
proc = subprocess.Popen(dlComm, stdout=subprocess.PIPE) # Gets formats
proc.wait()
data = subprocess.check_output(awkComm, stdin=proc.stdout) # "Piping" to awk to get columns
return data.decode("utf-8").strip()
def getClipboardData(self):
proc = subprocess.Popen(['xclip','-selection', 'clipboard', '-o'], stdout=subprocess.PIPE)
retcode = proc.wait()
data = proc.stdout.read()
return data.decode("utf-8").strip()
def setClipboardData(self, data):
proc = subprocess.Popen(['xclip','-selection','clipboard'], stdin=subprocess.PIPE)
proc.stdin.write(data)
proc.stdin.close()
retcode = proc.wait()

View File

@ -0,0 +1,55 @@
# Gtk imports
import gi, cairo
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk as gtk
from gi.repository import Gdk as gdk
# Python imports
import os
# Application imports
class Settings:
def __init__(self):
self.scrptPth = os.path.dirname(os.path.realpath(__file__)) + "/"
self.builder = gtk.Builder()
self.builder.add_from_file(self.scrptPth + "../resources/Main_Window.glade")
def createWindow(self):
# Get window and connect signals
window = self.builder.get_object("Main_Window")
window.connect("delete-event", gtk.main_quit)
self.setWindowData(window)
return window
def setWindowData(self, window):
screen = window.get_screen()
visual = screen.get_rgba_visual()
if visual != None and screen.is_composited():
window.set_visual(visual)
# bind css file
cssProvider = gtk.CssProvider()
cssProvider.load_from_path(self.scrptPth + '../resources/stylesheet.css')
screen = gdk.Screen.get_default()
styleContext = gtk.StyleContext()
styleContext.add_provider_for_screen(screen, cssProvider, gtk.STYLE_PROVIDER_PRIORITY_USER)
# window.set_app_paintable(True)
def getMonitorData(self, screen):
monitors = []
for m in range(screen.get_n_monitors()):
monitors.append(screen.get_monitor_geometry(m))
for monitor in monitors:
print(str(monitor.width) + "x" + str(monitor.height) + "+" + str(monitor.x) + "+" + str(monitor.y))
return monitors
def returnBuilder(self): return self.builder