2024-01-29 23:22:53 +01:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
2024-01-30 14:30:11 +01:00
|
|
|
#recorder
|
|
|
|
import pyaudio, wave, sched, sys, time, os
|
2024-01-29 23:22:53 +01:00
|
|
|
from playsound import playsound
|
2024-01-30 14:30:11 +01:00
|
|
|
from pynput import keyboard
|
2024-01-29 23:22:53 +01:00
|
|
|
from threading import Thread, Lock
|
|
|
|
from pynput import keyboard
|
|
|
|
|
|
|
|
import sys, os, datetime, fnmatch, glob, random, time, pathlib, re,\
|
|
|
|
sqlite3, json, subprocess, uuid, argparse, contextlib
|
2024-01-30 14:30:11 +01:00
|
|
|
|
2024-01-29 23:22:53 +01:00
|
|
|
from pydub import AudioSegment
|
2024-01-30 14:30:11 +01:00
|
|
|
|
2024-01-29 23:22:53 +01:00
|
|
|
from datetime import timedelta
|
|
|
|
from os.path import join
|
|
|
|
from tinytag import TinyTag
|
|
|
|
from random import shuffle
|
|
|
|
from pypika import Query, Table, Field, Column
|
|
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
from jinja2 import Template, Environment, FileSystemLoader
|
|
|
|
from glob import glob
|
|
|
|
|
|
|
|
import tkinter as tk
|
|
|
|
from tkinter import filedialog as fd
|
|
|
|
|
2024-01-30 21:32:07 +01:00
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument("-d", "--date", help="Show Date")
|
|
|
|
parser.add_argument("-e", "--episode", help="Episode Number", type=int)
|
|
|
|
parser.add_argument("-p", "--playlist", help="new, keep or edit", default="keep")
|
|
|
|
parser.add_argument("-m", "--mp3", action="store_true")
|
|
|
|
parser.add_argument("-w", "--web", action="store_true")
|
|
|
|
parser.add_argument("-a", "--art", action="store_true")
|
|
|
|
parser.add_argument("-i", "--insert_fills", action="store_true")
|
|
|
|
parser.add_argument("-f", "--fill", help="fill type", type=str)
|
|
|
|
parser.add_argument("-s", "--sound_card", help="audio device", type=int)
|
|
|
|
args = parser.parse_args()
|
2024-01-30 14:30:11 +01:00
|
|
|
|
2024-01-30 21:32:07 +01:00
|
|
|
# list available audio devices
|
2024-01-30 14:30:11 +01:00
|
|
|
p = pyaudio.PyAudio()
|
|
|
|
info = p.get_host_api_info_by_index(0)
|
|
|
|
numdevices = info.get('deviceCount')
|
|
|
|
|
|
|
|
for i in range(0, numdevices):
|
|
|
|
if (p.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')) > 0:
|
|
|
|
print("Input Device id ", i, " - ", p.get_device_info_by_host_api_device_index(0, i).get('name'))
|
|
|
|
|
2024-01-30 21:32:07 +01:00
|
|
|
if args.sound_card == False:
|
|
|
|
audio_device_number = input('''
|
|
|
|
-------------------------------------------------------------
|
|
|
|
what audio device do you choose:
|
|
|
|
-------------------------------------------------------------
|
|
|
|
''')
|
|
|
|
else:
|
|
|
|
audio_device_number = args.sound_card
|
2024-01-30 14:30:11 +01:00
|
|
|
|
|
|
|
|
2024-01-29 23:22:53 +01:00
|
|
|
|
|
|
|
path = pathlib.Path.cwd()
|
|
|
|
|
|
|
|
show_name = "UhO"
|
|
|
|
show_url = "https://uho.rizom.si"
|
|
|
|
show_rss = "podcast_rss.xml"
|
|
|
|
show_email = "uho.podcast@gmail.com"
|
|
|
|
episode_author="Rob Canning"
|
|
|
|
|
|
|
|
episode_number = args.episode
|
2024-01-30 14:30:11 +01:00
|
|
|
fill = args.fill;
|
2024-01-29 23:22:53 +01:00
|
|
|
input_date = args.date
|
|
|
|
episode_date = datetime.datetime.now().strftime("%Y-%m-%d")
|
|
|
|
episode_duration = 10
|
|
|
|
archive = []
|
|
|
|
|
|
|
|
# sqlite database connection
|
|
|
|
conn = sqlite3.connect("database/show.db")
|
|
|
|
|
|
|
|
fill_path = "archive/e/{0}/audio_fills".format(episode_number)
|
|
|
|
if not os.path.exists(fill_path):
|
|
|
|
os.makedirs(fill_path)
|
|
|
|
|
|
|
|
#TODO lookup metadata from music_db not from episodoe = use uuids!
|
|
|
|
#TODO scrape artist bio from bandcamp?
|
2024-01-30 14:30:11 +01:00
|
|
|
#TODO GET VARIATION PROMPT
|
2024-01-29 23:22:53 +01:00
|
|
|
|
|
|
|
def get_playlist_for_fill_recording(conn, episode_number, episode_duration):
|
2024-01-30 21:32:07 +01:00
|
|
|
|
2024-01-29 23:22:53 +01:00
|
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute('SELECT * FROM EPISODES WHERE EPISODE=? ORDER BY track_number ASC', [episode_number])
|
|
|
|
preview = cursor.fetchall()
|
|
|
|
cursor.execute('SELECT SUM(trackdur) FROM EPISODES WHERE EPISODE=? ', [episode_number])
|
|
|
|
episode_duration = cursor.fetchone()[0]
|
|
|
|
|
2024-01-30 14:30:11 +01:00
|
|
|
# os.system("clear")
|
2024-01-29 23:22:53 +01:00
|
|
|
print("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
|
|
|
|
print(">> PROPOSED EPISODE #{0} PLAYLIST: ({1}) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"\
|
|
|
|
.format(episode_number, timedelta(seconds=round(episode_duration))))
|
|
|
|
print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n")
|
|
|
|
|
|
|
|
for i in preview:
|
|
|
|
print('| {0} | {3} ||| {2} ||| {1} ||| {5} ||| [{6}] ||| {4} |||\n'\
|
|
|
|
.format('{:0>2}'.format(i[2]), i[4], i[5], i[6],\
|
|
|
|
timedelta(seconds=round(i[7])), i[9], i[11] ) )
|
|
|
|
|
|
|
|
print(">> SELECT AN OPTION: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
|
|
|
|
|
|
|
|
user_input = input('''
|
2024-01-30 21:32:07 +01:00
|
|
|
[l]isten to track
|
|
|
|
[r]ecord voice fill
|
2024-01-29 23:22:53 +01:00
|
|
|
|
2024-01-30 21:32:07 +01:00
|
|
|
>>>>>>>>>>>>>>>>>>>>>>>>>>> OR PRESS ENTER TO PROCEED................... : ''')
|
2024-01-29 23:22:53 +01:00
|
|
|
|
|
|
|
if user_input == 'l':
|
|
|
|
playlist_preview_track(conn, episode_number, episode_duration)
|
|
|
|
|
|
|
|
elif user_input == "r":
|
2024-01-30 14:30:11 +01:00
|
|
|
|
|
|
|
if fill == "out":
|
|
|
|
num = input("Which TRACK # to record OUTRO fill for : ")
|
|
|
|
the_file = "archive/e/{0}/audio_fills/{1}_out.wav".format(episode_number, num)
|
|
|
|
elif fill == "in":
|
|
|
|
num = input("Which TRACK # to record INTRO fill for : ")
|
|
|
|
the_file = "archive/e/{0}/audio_fills/{1}_in.wav".format(episode_number, num)
|
|
|
|
else:
|
|
|
|
print("must speficy in or out")
|
|
|
|
|
|
|
|
|
2024-01-29 23:22:53 +01:00
|
|
|
print(the_file)
|
|
|
|
r = recorder(the_file)
|
|
|
|
p = player(the_file)
|
|
|
|
l = listener(r, p)
|
2024-01-30 14:30:11 +01:00
|
|
|
#os.system("clear")
|
2024-01-29 23:22:53 +01:00
|
|
|
|
|
|
|
for i in preview[int(num)-1:int(num)]:
|
|
|
|
print('''
|
|
|
|
|
|
|
|
POD TRACK#: {0}
|
|
|
|
TRACK: {2}
|
|
|
|
ARTIST: {3}
|
|
|
|
ALBUM: {1}
|
|
|
|
YEAR: {5}
|
|
|
|
LABEL: [{6}]
|
|
|
|
DURATION: {4}
|
|
|
|
GENRE: {8}
|
|
|
|
PATH:
|
|
|
|
{9}
|
|
|
|
COMMENT: {7}
|
|
|
|
'''\
|
|
|
|
.format('{:0>2}'.format(i[2]), i[4], i[5], i[6],\
|
|
|
|
timedelta(seconds=round(i[7])), i[9], i[11], i[12], i[8], i[10] ) )
|
|
|
|
|
|
|
|
|
|
|
|
print('............................Hold ctrl to record, press p to playback, press q to quit')
|
|
|
|
l.start() #keyboard listener is a thread so we start it here
|
|
|
|
l.join() #wait for the tread to terminate so the program doesn't instantly close
|
|
|
|
|
|
|
|
# todo implement a check box TUI showing track fills completed
|
|
|
|
|
|
|
|
get_playlist_for_fill_recording(conn, episode_number, 10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
get_playlist_for_fill_recording(conn, episode_number, 10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class player:
|
|
|
|
def __init__(self, wavfile):
|
|
|
|
self.wavfile = wavfile
|
|
|
|
self.playing = 0 #flag so we don't try to record while the wav file is in use
|
|
|
|
self.lock = Lock() #muutex so incrementing and decrementing self.playing is safe
|
|
|
|
|
|
|
|
#contents of the run function are processed in another thread so we use the blocking
|
|
|
|
# version of pyaudio play file example: http://people.csail.mit.edu/hubert/pyaudio/#play-wave-example
|
2024-01-30 14:30:11 +01:00
|
|
|
|
2024-01-29 23:22:53 +01:00
|
|
|
def run(self):
|
|
|
|
with self.lock:
|
|
|
|
self.playing += 1
|
|
|
|
with wave.open(self.wavfile, 'rb') as wf:
|
|
|
|
p = pyaudio.PyAudio()
|
|
|
|
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
|
2024-01-30 14:30:11 +01:00
|
|
|
input_device_index=4,
|
|
|
|
output_device_index=4,
|
2024-01-29 23:22:53 +01:00
|
|
|
channels=wf.getnchannels(),
|
|
|
|
rate=wf.getframerate(),
|
|
|
|
output=True)
|
|
|
|
data = wf.readframes(8192)
|
|
|
|
while data != b'':
|
|
|
|
stream.write(data)
|
|
|
|
data = wf.readframes(8192)
|
|
|
|
|
|
|
|
stream.stop_stream()
|
|
|
|
stream.close()
|
|
|
|
p.terminate()
|
|
|
|
wf.close()
|
|
|
|
with self.lock:
|
|
|
|
self.playing -= 1
|
|
|
|
|
|
|
|
def start(self):
|
|
|
|
Thread(target=self.run).start()
|
|
|
|
|
|
|
|
|
|
|
|
class recorder:
|
|
|
|
def __init__(self,
|
|
|
|
wavfile,
|
|
|
|
chunksize=8192,
|
|
|
|
dataformat=pyaudio.paInt16,
|
|
|
|
channels=1,
|
|
|
|
rate=44100):
|
|
|
|
self.filename = wavfile
|
|
|
|
self.chunksize = chunksize
|
|
|
|
self.dataformat = dataformat
|
|
|
|
self.channels = channels
|
|
|
|
self.rate = rate
|
|
|
|
self.recording = False
|
2024-01-30 14:30:11 +01:00
|
|
|
self.p = pyaudio.PyAudio()
|
2024-01-29 23:22:53 +01:00
|
|
|
|
|
|
|
def start(self):
|
|
|
|
#we call start and stop from the keyboard listener, so we use the asynchronous
|
|
|
|
# version of pyaudio streaming. The keyboard listener must regain control to
|
|
|
|
# begin listening again for the key release.
|
|
|
|
if not self.recording:
|
|
|
|
self.wf = wave.open(self.filename, 'wb')
|
|
|
|
self.wf.setnchannels(self.channels)
|
2024-01-30 14:30:11 +01:00
|
|
|
self.wf.setsampwidth(self.p.get_sample_size(self.dataformat))
|
2024-01-29 23:22:53 +01:00
|
|
|
self.wf.setframerate(self.rate)
|
|
|
|
|
|
|
|
def callback(in_data, frame_count, time_info, status):
|
|
|
|
#file write should be able to keep up with audio data stream (about 1378 Kbps)
|
|
|
|
self.wf.writeframes(in_data)
|
|
|
|
return (in_data, pyaudio.paContinue)
|
|
|
|
|
2024-01-30 14:30:11 +01:00
|
|
|
self.stream = self.p.open(format = self.dataformat,
|
|
|
|
input_device_index=4,
|
|
|
|
output_device_index=4,
|
|
|
|
channels = self.channels,
|
|
|
|
rate = self.rate,
|
|
|
|
input = True,
|
|
|
|
stream_callback = callback)
|
2024-01-29 23:22:53 +01:00
|
|
|
self.stream.start_stream()
|
|
|
|
self.recording = True
|
2024-01-30 14:30:11 +01:00
|
|
|
print('recording started',end='')
|
2024-01-29 23:22:53 +01:00
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
if self.recording:
|
|
|
|
self.stream.stop_stream()
|
|
|
|
self.stream.close()
|
|
|
|
self.wf.close()
|
|
|
|
|
|
|
|
self.recording = False
|
2024-01-30 14:30:11 +01:00
|
|
|
print('recording finished',end='')
|
2024-01-29 23:22:53 +01:00
|
|
|
|
|
|
|
class listener(keyboard.Listener):
|
|
|
|
|
|
|
|
def __init__(self, recorder, player):
|
|
|
|
super().__init__(on_press = self.on_press, on_release = self.on_release)
|
|
|
|
self.recorder = recorder
|
|
|
|
self.player = player
|
|
|
|
|
|
|
|
def on_press(self, key):
|
|
|
|
if key is None: #unknown event
|
|
|
|
pass
|
|
|
|
elif isinstance(key, keyboard.Key): #special key event
|
|
|
|
if key.ctrl and self.player.playing == 0:
|
|
|
|
self.recorder.start()
|
|
|
|
elif isinstance(key, keyboard.KeyCode): #alphanumeric key event
|
|
|
|
if key.char == 'q': #press q to quit
|
|
|
|
if self.recorder.recording:
|
|
|
|
self.recorder.stop()
|
|
|
|
return False #this is how you stop the listener thread
|
|
|
|
if key.char == 'p' and not self.recorder.recording:
|
|
|
|
self.player.start()
|
|
|
|
|
|
|
|
def on_release(self, key):
|
|
|
|
if key is None: #unknown event
|
|
|
|
pass
|
|
|
|
elif isinstance(key, keyboard.Key): #special key event
|
|
|
|
if key.ctrl:
|
|
|
|
self.recorder.stop()
|
|
|
|
elif isinstance(key, keyboard.KeyCode): #alphanumeric key event
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
get_playlist_for_fill_recording(conn, episode_number, 10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|