many fix
commit
5b9232a937
@ -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
|
@ -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…
Reference in New Issue