agropunx 11 months ago
commit 5b9232a937

5
.gitignore vendored

@ -0,0 +1,5 @@
*.tmp
*.zip
*.tar
__pycache__/
music/

@ -0,0 +1,12 @@
Welcome to celaigia
an app to gently query youtube music, and possibly download only if you don't have already locally
- gitignore music
- standardize postprocess
- parametrize and make config
- fix ui and reduce number of searches
- add direct download with url
- place query on ui
- document

@ -0,0 +1,12 @@
import importlib
import config
importlib.reload(config)
import utils
importlib.reload(utils)
import database
importlib.reload(database)
import ui
importlib.reload(ui)
import app
importlib.reload(app)

@ -0,0 +1,9 @@
from config import music_path
from ui import UIEngine
from database import Database
if __name__ == "__main__":
# Add your main code here
db = Database(base_path=music_path)
ui = UIEngine(db)
ui.run()

@ -0,0 +1,11 @@
music_path = "./music"
yt_dlp_opts = {
'format': 'bestaudio/best',
'default_search': 'ytsearch5',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192',
}],
}

@ -0,0 +1,26 @@
# Updated database.py
import json
import datetime
class Database:
def __init__(self, base_path):
self.base_path = base_path
self.file_path = f'{base_path}/index.json'
try:
with open(self.file_path, 'r') as file:
self.data = json.load(file)
except FileNotFoundError:
self.data = []
def add_entry(self, entry):
entry['timestamp'] = datetime.datetime.now().strftime('%d/%m/%Y')
self.data.append(entry)
with open(self.file_path, 'w') as file:
json.dump(self.data, file, indent=4)
def downloadable(self, url, title):
idx = url.split('https://www.youtube.com/watch?v=')[-1]
for entry in self.data:
if entry['id'] == idx or entry['title'] == title:
return False
return True

86
ui.py

@ -0,0 +1,86 @@
import tkinter as tk
from tkinter import ttk
import tkinter.simpledialog as sd
import time
import os
from utils import search_youtube, download_audio
class UIEngine:
def __init__(self, db):
self.db = db
self.root = tk.Tk()
self.root.title("celaigia")
self.root.geometry("400x300") # Set a fixed size
self.status_label = tk.Label(self.root, text="Ready")
self.status_label.pack()
self.search_frame = ttk.Frame(self.root)
self.search_frame.pack()
self.search_status_label = tk.Label(self.search_frame, text="Enter search query:")
self.search_status_label.grid(row=0, column=0)
self.search_entry = tk.Entry(self.search_frame)
self.search_entry.grid(row=0, column=1)
self.search_button = tk.Button(self.search_frame, text="Search", command=lambda: self.search_and_display())
self.search_button.grid(row=0, column=2)
self.results_frame = ttk.Frame(self.root)
self.results_frame.pack()
self.download_status_label = tk.Label(self.root, text="Enter URL to download:")
self.download_status_label.pack()
self.download_entry = tk.Entry(self.root)
self.download_entry.pack()
self.download_button = tk.Button(self.root, text="Download", command=lambda: self.handle_download(self.download_entry.get()))
self.download_button.pack()
def search_and_display(self):
self.set_status("searching")
query = self.search_entry.get()
# Clear the existing results
for widget in self.results_frame.winfo_children():
widget.destroy()
results = search_youtube(query)
self.display_results(results)
self.set_status("Enter search query or select item to download")
def set_status(self, status):
self.status_label.config(text=status)
self.root.update() # Force GUI update
def display_results(self, results):
for i, result in enumerate(results):
result_button = ttk.Button(self.results_frame, text=result['title'], command=lambda url=result['webpage_url'], filename=result['title']: self.handle_download(url, filename))
result_button.pack()
def handle_download(self, url, filename):
if self.db.downloadable(url, filename):
self.set_status("Downloading...")
success = download_audio(url, self.db.base_path)
if success:
self.set_status("Download completed, adding to db")
self.prompt_user_for_details(url, filename)
self.set_status("Enter search query or select item to download")
# Modify the prompt_user_for_details method
def prompt_user_for_details(self,url, filename):
title = sd.askstring("Enter Title", "Enter Title:", initialvalue=filename)
artist = sd.askstring("Enter Artist", "Enter Artist:")
tags = sd.askstring("Enter Tags", "Enter Tags (comma separated):")
entry = {
"id": url.split('https://www.youtube.com/watch?v=')[-1].strip(),
"title": title.strip(),
"artist": artist.strip(),
"filename": filename.strip(),
"tags": [tag.strip() for tag in tags.split(",")] if tags else [],
"timestamp": time.time()
}
self.add_to_db(entry)
def add_to_db(self, entry):
self.db.add_entry(entry)
def run(self):
self.root.mainloop()

@ -0,0 +1,28 @@
import yt_dlp
from config import yt_dlp_opts
# Function to search YouTube
def search_youtube(query, opts = yt_dlp_opts):
with yt_dlp.YoutubeDL(opts) as ydl:
info = ydl.extract_info(query, download=False)
return info['entries']
def download_audio(url, base_path, opts = yt_dlp_opts):
opts['outtmpl'] = f'{base_path}/audios/%(title)s.%(ext)s'
if not isinstance(url, list):
url = [url]
success = False
try:
with yt_dlp.YoutubeDL(opts) as ydl:
ydl.download(url)
success = True
except:
success = False
return success
Loading…
Cancel
Save