#!/usr/bin/python3 #recorder import pyaudio, wave, sched, sys, time, os from playsound import playsound from pynput import keyboard 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 from pydub import AudioSegment 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 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() # list available audio devices 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')) if args.sound_card == False: audio_device_number = input(''' ------------------------------------------------------------- what audio device do you choose: ------------------------------------------------------------- ''') else: audio_device_number = args.sound_card 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 fill = args.fill; 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? #TODO GET VARIATION PROMPT def get_playlist_for_fill_recording(conn, episode_number, episode_duration): 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] # os.system("clear") 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(''' [l]isten to track [r]ecord voice fill >>>>>>>>>>>>>>>>>>>>>>>>>>> OR PRESS ENTER TO PROCEED................... : ''') if user_input == 'l': playlist_preview_track(conn, episode_number, episode_duration) elif user_input == "r": 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") print(the_file) r = recorder(the_file) p = player(the_file) l = listener(r, p) #os.system("clear") 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 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()), input_device_index=4, output_device_index=4, 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 self.p = pyaudio.PyAudio() 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) self.wf.setsampwidth(self.p.get_sample_size(self.dataformat)) 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) 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) self.stream.start_stream() self.recording = True print('recording started',end='') def stop(self): if self.recording: self.stream.stop_stream() self.stream.close() self.wf.close() self.recording = False print('recording finished',end='') 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)