uho/mk_show.py

629 lines
25 KiB
Python
Executable File

#!/usr/bin/python3
# CREATE PODCAST -----------------------------------------
import sys, os, datetime, fnmatch, glob, random, time, pathlib, re
from datetime import timedelta
from os.path import join
from tinytag import TinyTag
from random import shuffle
import sqlite3, json
import subprocess
import uuid
from pypika import Query, Table, Field, Column
#from mk_web import *
path = "/home/rob/antena/"
show_name = "The_SLO_Indie"
show_short_description = "An eclectic selection of independent and experimental music from Slovenia. \
Published weekly by The Rizoma Institute. Hosted and compiled by Rob Canning. \
Broadcast in Slovenia on FM by Mariborski radio student - MARŠ"
episode_author="Rob Canning"
episode_number = int(sys.argv[1])
input_date = sys.argv[2]
episode_date = datetime.datetime.now().strftime("%Y-%m-%d")
episode_duration = 0
episode_playlist = []
show_cover = ""
archive = []
artists_played = []
artist_abreviated = []
web_path = "/home/rob/antena/html/episode/{0}/img".format(episode_number)
conn = sqlite3.connect("database/show.db")
if os.path.exists(web_path):
print("web path already exists .... doing_nothing")
else: os.makedirs(web_path)
# /////////////////////////////////////////////////
def set_episode_date(input_date):
global episode_date
date_str = input_date
date_format = '%Y-%m-%d'
episode_date = str(datetime.datetime.strptime(date_str, date_format))
#return episode_date
print(episode_date)
def create_intro(episode_playlist):
intropath = path + "audio/texts/clips/this_is"
intro = random.choice(os.listdir(intropath))
episode_playlist.insert(0, str(os.path.abspath(intropath)) + "/" + str(intro))
# TODO make this go away....
#def add_to_tracks_played(add_to_played: str):
# with open('playlists/track_playout_history.txt', "a") as tracks_played_file:
# tracks_played_file.write(str(add_to_played) + "\n") # newline \n needed here?
#TODO replace the below with database stuff
def check_archive(track):
global archive
cursor = conn.cursor()
cursor.execute("SELECT * FROM EPISODES WHERE path = ?", (track,))
data=cursor.fetchone()
if data is None:
print('')
#print('There is no component named ?', [track])
else:
print('Component ? found with rowid ? /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////', [track, data[0]])
with open('playlists/track_playout_history.txt') as archive_file:
for line in archive_file:
archive.append(line)
if track not in archive:
#print("____ TRACK NOT YET PLAYED ... ADDING _____")
return True
else:
#print("____ TRACK ALREADY PLAYED _____")
return False
def playlist_replace_track(conn, episode_playlist, episode_number, track_to_replace):
cursor = conn.cursor()
cursor.execute("SELECT * FROM MUSIC_LIBRARY ORDER BY RANDOM() LIMIT 1 ;")
r = cursor.fetchone() # FETCH ONE RANDOM TRACK FROM THE DATABASE
print('new suggestion================================' + str(r))
# for t in r:
song = str(r[9])
track_label = str(r[1])
track_album = str(r[2])
track_title = str(r[3])
track_artist = str(r[4])
track_duration = float(r[6])
track_genre = str(r[5])
track_year = str(r[7])
track_path = song #'/'.join(song.split('/')[0:-1])
track_comment = str(r[11])
e = Table('EPISODES')
# TODO This is not replace at the desired index / row but appending to the end of the table
q = e.insert(1, episode_number, track_to_replace, episode_date,\
track_album, track_title, track_artist, \
track_duration,track_genre, track_year, \
track_path, track_label, track_comment).where('e.track_number'==1)('e.episode'==1)
cursor.execute(str(q))
conn.commit()
print("sqlite: Episode track successfully updated into SHOW table");
#cursor = conn.cursor()
#cursor.execute('INSERT OR REPLACE FROM EPISODES WHERE EPISODE=? and track_number=?', [episode_number, track_to_replace])
#preview = cursor.fetchall()
#for i in preview:
# print('{0} : {1} {2} {3} {4} \n'.format(i[2], i[4], i[5], i[6], i[7],))
def create_episode_playlist(conn, episode_playlist: list, episode_number):
episode_duration = 0
global archive
track_number = 0
track_count = 0
global artists_played
max_track_dur = 9
min_track_dur = 2
# first clear the sqlite table rows for the episode
cursor = conn.cursor()
cursor.execute('DELETE FROM EPISODES WHERE episode = {0}'.format(episode_number))
# TODO what is most important 12 tracks or 60 minutes
while episode_duration < 60 * 60 and track_count < 12 :
cursor.execute("SELECT * FROM MUSIC_LIBRARY ORDER BY RANDOM() LIMIT 1 ;")
r = cursor.fetchone() # FETCH ONE RANDOM TRACK FROM THE DATABASE
# for t in r:
song = str(r[9])
track_label = str(r[1])
track_album = str(r[2])
track_title = str(r[3])
track_artist = str(r[4])
track_duration = float(r[6])
track_genre = str(r[5])
track_year = str(r[7])
track_path = song #'/'.join(song.split('/')[0:-1])
track_comment = str(r[11])
# SOME LOGIC TO SEE IF WE ALLOW THAT TRACK OR NOT
# TODO here we need to append to DB not the static file
if track_artist not in artists_played:
if check_archive(track_path) is True:
if track_duration > min_track_dur * 60:
if int(track_duration) < max_track_dur * 60:
episode_playlist.append(song.rstrip()) # if 'not in' is true then add the song
art = string=re.sub("\(.*?\)","",track_artist)
# shorten verbose artist names such as trojnik Trojnik (Cene Resnik, Tomaž Grom, Vid Drašler)
art = string=re.sub("and","&",art)
artist_abreviated.append(art)
artists_played.append(track_artist) # and add the artist to the played list
#add_to_tracks_played(track_path) # and write entry to archive file
if not track_year: # where missing metadata give a dummy value
track_year = "0000"
e = Table('EPISODES')
track_number = track_count
id = str(uuid.uuid4())
q = e.insert(id, episode_number, track_number, episode_date,\
track_album, track_title, track_artist, \
track_duration,track_genre, track_year, \
track_path, track_label, track_comment)
cursor.execute(str(q))
conn.commit()
# print("sqlite: Episode track successfully inserted into SHOW table");
track_count += 1;
episode_duration = episode_duration + track_duration
# else: print("TRACK TOO SHORT..........." )
# else: print("TRACK TOO LONG..........." )
# else: print("SONG PLAYED IN PREVIOUS EPISODE" )
#else: print("ARTIST ALREADY IN PODCAST")
#TODO am i happy with track selection? change an entry ? add a specific entry? to replace
# preview and modify option -TODO move to its own function
cursor = conn.cursor()
cursor.execute('SELECT * FROM EPISODES WHERE EPISODE=?', [episode_number])
preview = cursor.fetchall()
print("/////////////////////////////////////////////////")
for i in preview:
print('{0} : {1} {2} {3} {4}'.format(i[2], i[4], i[5], i[6], i[7],))
dj_is_unsure=True
cursor = conn.cursor()
if dj_is_unsure:
user_input = input("nice playlist? [y to proceed, anything else to suggest new] :")
if user_input == 'y':
print("action")
create_episode_playlist(conn, episode_playlist, episode_number)
elif user_input == 'p':
# preview a track --------------------------------------
preview_track = input("which track would you like to preview [0-12]:")
cursor.execute('SELECT path FROM EPISODES WHERE EPISODE=? AND track_number=?', [episode_number, preview_track ])
preview_track_path = cursor.fetchone()[0]
print(preview_track_path)
#os.system("mplayer '{0}' &".format(preview_track_path))
subprocess.Popen(["nohup mplayer '{0}' &".format(preview_track_path)], shell=True)
#Popen.run("mplayer '{0}' &".format(preview_track_path))
track_previewed = input("track OK? :")
if track_previewed == 'y':
print("ok groovy choice then...")
else:
input("do the db replace logic")
playlist_replace_track(conn, episode_playlist, episode_number, preview_track)
else:
print("Goodbye happy dj")
return user_input
def modify_playlist(conn, episode_playlist, episode_number):
cursor = conn.cursor()
cursor.execute('SELECT * FROM EPISODES WHERE EPISODE=?', [episode_number])
preview = cursor.fetchall()
for i in preview:
print('{0} : {1} {2} {3} {4} \n'.format(i[2], i[4], i[5], i[6], i[7],))
# episode_duration = timedelta(seconds=round(episode_duration))
# print("Total Tracks = {0} \nTotal Duration = {1}".format(track_count, episode_duration))
def combine_images(columns, space, images, variants:int):
global show_cover
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
rows = len(images) // columns
if len(images) % columns:
rows += 1
width_max = 500 #max([Image.open(image).width for image in images])
height_max = 500# max([Image.open(image).height for image in images])
background_width = width_max*columns + (space*columns)-space
background_height = height_max*rows + (space*rows)-space
#background = Image.new('RGBA', (background_width, background_height), (0, 0, 0, 255))
background = Image.new('RGBA', (width_max*columns , height_max*columns), (0, 0, 0, 255))
x = 0
y = 0
for i, image in enumerate(images):
imga = Image.open(image)
size = (500,500)
img = imga.resize(size)
#img = img.rotate(random.randrange(360))
x_offset = int((width_max-img.width)/2)
y_offset = int((height_max-img.height)/2)
background.paste(img, (x+x_offset, y+y_offset+100))
x += width_max + space
if (i+1) % columns == 0:
y += height_max + space
x = 0
im = ImageDraw.Draw(background)
mf_h1 = ImageFont.truetype('fonts/Antonio-Light.ttf', 280)
mf_h2 = ImageFont.truetype('fonts/Antonio-Light.ttf', 65)
mf_h3 = ImageFont.truetype('fonts/Antonio-Regular.ttf', 50)
mf_h4 = ImageFont.truetype('fonts/Antonio-Light.ttf', 50)
h2_spc = 85
h2_baseline = 1530
# Add Text to the image ----------------------------------------
# some logic to shuffle the list if sub sections of list are too long for layout
str_length_thresh = 50
#TODO if an artist is listed as Various Arist then remove it from cover display and think of logic
#TODO exit while loop if it cant find a solution and restart script or shrink font and adjust rules
while \
[len(s) for s in [''.join(artist_abreviated[0:3])]][0] > str_length_thresh or\
[len(s) for s in [''.join(artist_abreviated[3:6])]][0] > str_length_thresh or\
[len(s) for s in [''.join(artist_abreviated[6:9])]][0] > str_length_thresh or \
[len(s) for s in [''.join(artist_abreviated[9:12])]][0] > str_length_thresh:
print('''one of the lines is longer than fits the page...
...................shuffling the list for a better look: {0}'''.format(artist_abreviated))
random.shuffle(artist_abreviated)
im.text((30,10), '''an eclectic selection of contemporary independent music from slovenia: {0} - E P I S O D E #{1}
'''\
.format(episode_date,episode_number), fill="white", font=mf_h3)
im.text((30,280), ''' THIS WEEK ON \n EPISODE #{0} of \n {1}!'''.upper()\
.format(episode_number, show_name), fill="white", font=mf_h1, stroke_width=2, stroke_fill='black')
im.text((30, h2_baseline + (h2_spc*1) ), '''m u s i c _ f r o m _ : {0}'''\
.format(' | '.join(artist_abreviated[0:3])), (255,255,255), font=mf_h2)
im.text((30, h2_baseline + (h2_spc*2) ), "{0}"\
.format(' | '.join(artist_abreviated[3:6])), (255,255,255), font=mf_h2)
im.text((30, h2_baseline + (h2_spc*3)), "{0}"\
.format(' | '.join(artist_abreviated[6:9])), (255,255,255), font=mf_h2)
im.text((30, h2_baseline + (h2_spc*4)), "{0}"\
.format(' | '.join(artist_abreviated[9:12])), (255,255,255), font=mf_h2)
im.text((1540,1888), ''' http://{0}.rizom.si '''\
.format(show_name, episode_date,episode_number), fill="white", font=mf_h4)
show_cover = 'img/cover{2}.png'.format(episode_number,episode_date, variants)
background.save("html/" + "episode/{0}/{1}".format(episode_number, show_cover))
#convert -delay 100 -loop 0 html/episode/2/img/show_cover_2024-01-12* animatedGIF.gif
return show_cover
def create_show_coverart(episode_playlist, variants):
show_cover_jpgs = []
# in the directory containing songs find jpg and pngs containing string "cover"
for dir in episode_playlist:
path = pathlib.Path(dir).parent
for file in os.listdir(path):
for p in [".png", ".jpg", ".jpeg"]:
if p in file:
for i in ["cover", "COVER"]:
if i in file:
show_cover_jpgs.append(str(path) + "/" + file)
print(''''\n ------------------------
creating show cover art
------------------------ ''')
# if len(show_cover_jpgs) > 0: # duplicate this for variations of geometry
for i in range(variants):
x = show_cover_jpgs[:12]
combine_images(columns=4, space=10, images=random.sample(x,len(x)),variants=i)
return show_cover
def create_animated_gif():
import contextlib
from PIL import Image
# filepaths
fp_in = "/home/rob/antena/html/episode/2/img/*.png".format(episode_number)
fp_out = "/home/rob/antena/html/episode/2/img/show_cover.gif"
# use exit stack to automatically close opened images
with contextlib.ExitStack() as stack:
# lazily load images
imgs = (stack.enter_context(Image.open(f))
for f in sorted(glob.glob(fp_in)))
# extract first image from iterator
img = next(imgs)
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#gif
img.save(fp=fp_out, format='GIF', append_images=imgs,
save_all=True, duration=200, loop=0)
def create_podcast(episode_playlist: list):
print('''------------------------
creating show audio
------------------------''')
from glob import glob
from pydub import AudioSegment
playlist_songs = [AudioSegment.from_file(flac_file) for flac_file in episode_playlist]
print(playlist_songs)
show_intro = playlist_songs[0]
# first_song = playlist_songs[0].fade_in(0) # only fadein if used over show intro - currently not used
# intro_and_first = first_song.overlay(show_intro)
first_three_blurb = playlist_songs[0]
second_three_blurb = playlist_songs[0]
final_songs_blurb = playlist_songs[0]
final_show_outro = playlist_songs[0]
# next two commented lines are for if intro goes over first track or not
#playlist = intro_and_first
playlist = show_intro
# playlist = []
#for song in playlist_songs[2:3]:
for song in playlist_songs[1:4]: # first three songs (first added with intro)
# We don't want an abrupt stop at the end, so let's do a 1 second crossfades
playlist = playlist.append(song)
print(song)
print("first songs added")
# blurb about first three tracks
# playlist = playlist.append(first_three_blurb) # <--------------BLURB INSERT
for song in playlist_songs[4:7]: # second three songs
playlist = playlist.append(song)
print(song)
print("second songs added")
# playlist = playlist.append(second_three_blurb) # <--------------BLURB INSERT
for song in playlist_songs[7:10]: # second three song
#playlist = playlist.append(song, crossfade=(1 * 1000))
playlist = playlist.append(song)
print(song)
print("third songs added")
# playlist = playlist.append(final_songs_blurb) # <--------------BLURB INSERT
for song in playlist_songs[10:13]: # second three song
playlist = playlist.append(song)
print(song)
print("final songs added")
# playlist = playlist.append(final_show_outro) # <-------------- OUTRO SEQUENCE
# get length of final show / podcast
playlist_length = len(playlist) / (1000*60)
# save the entire poidcast as FLAC
# with open("html/episode/{0}/show.flac".format(episode_number), 'wb') as out_f:
# print("FLAC output file opened...writing to file...")
# playlist.export(out_f, format='flac')
# print("FLAC audio file exported...")
# save the entire poidcast as MP3
with open("html/episode/{0}/show.mp3".format(episode_number), 'wb') as out_f:
print("MP3 output file opened...writing to file...")
playlist.export(out_f, format='mp3')
print("MP3 audio file exported...")
### ------------------------------------------------------------
def create_html_homepage_from_template(episode_playlist):
# TODO "on this weeks show" varients fed to html from list here
from jinja2 import Template, Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader('html/templates'))
homepage_template = env.get_template('homepage.jinja')
conn = sqlite3.connect("database/show.db")
cursor = conn.cursor()
show_info = []
episode_artists = []
#TODO fix this - now it reports zero
# get number of episodes from DB
cursor.execute('SELECT MAX(episode) FROM EPISODES')
num_eps = cursor.fetchone()[0]
num_eps = num_eps+1
for i in range(num_eps):
artists = []
cursor.execute("SELECT artist FROM EPISODES WHERE episode=?", [i])
rows = cursor.fetchall()
for artist in rows:
art = string=re.sub("\(.*?\)","", artist[0])
# shorten verbose artist names such as trojnik Trojnik (Cene Resnik, Tomaž Grom, Vid Drašler)
artist = string=re.sub("and","&",art)
artists.append(artist)
episode_artists.append(artists)
episodes = []
for i in range(num_eps): # get this from new table EPISODE_STATS number of tracks in DB
print(episode_artists[i])
an_episode = dict(date="2012-02-", \
episode_artists=str(episode_artists[i]).strip("[").strip("]").strip("\'"), episode_number=i, \
episode_date=episode_date, \
episode_duration=episode_duration)
episodes.append(an_episode)
episodes = reversed(episodes[1:episode_number])
# reversed order to most recent episode appears first in list
cursor = conn.cursor()
cursor.execute('SELECT * FROM EPISODES WHERE episode=?', [episode_number])
r = cursor.fetchall()
for t in r:
song = str(t[0])
#track_label = str(t[1])
track_number = str(t[2])
track_album = str(t[3])
track_title = str(t[4])
track_artist = str(t[5])
track_duration = float(t[7])
track_genre = str(t[8])
track_year = str(t[9])
detail = str(track_artist) + " | " + str(track_album) + \
" | " + str(track_title) + " | " + str(track_year) + \
" | " + str(timedelta(seconds=round(track_duration)))
show_info.append("" + detail)
#TODO remove single quptes not working in episode_artists
output_from_parsed_template = homepage_template.render(\
show_name=''.join(random.choice((str.upper,str.lower))(x) for x in show_name), \
episodes=episodes, episode_author="Rob Canning",\
episode_duration=episode_duration, episode_number=episode_number, \
episode_artists=str(episode_artists[episode_number]).strip("[").strip("]").strip("\'").strip("'"), \
about_show=show_short_description, episode_playlist=episode_playlist, \
episode_image="episode/{0}/img/cover.png".format(episode_number))
with open("html/index.html".format(episode_number), "w") as episode_page:
episode_page.write(output_from_parsed_template)
def create_html_episode_from_template(episode_playlist, episode_number):
from jinja2 import Template, Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader('html/templates'))
episode_template = env.get_template('episode.jinja')
show_info = []
playlist_table = []
# maybe a jinja2 template loop here instead
cursor = conn.cursor()
e = Table('episodes')
q = Query.from_(e).select(
e.episode
).where(
e.episode == episode_number)
# raw 'SELECT * FROM EPISODES WHERE EPISODE=?', [episode_number]
cursor.execute('SELECT * FROM EPISODES WHERE EPISODE=?', [episode_number])
#cursor.execute(str(q))
r = cursor.fetchall()
for t in r:
song = str(t[0])
track_number = str(t[2])
track_album = str(t[4])
track_title = str(t[5])
track_artist = str(t[6]).upper()
track_duration = float(t[7])
track_genre = str(t[8])
track_year = str(t[9])
track_label = str(t[11])
track_comment = str(t[12])
detail = str(track_artist) + " | " + str(track_album) + \
" | " + str(track_title) + " | " + str(track_year) + \
" | " + str(timedelta(seconds=round(track_duration)))
playlist_entry = [track_number, track_title, track_artist, track_album, track_year, timedelta(seconds=round(track_duration)), track_label.upper() ]
playlist_table.append(playlist_entry)
show_info.append("" + detail)
output_from_parsed_template = episode_template.render(\
show_name=show_name, episode_author="Rob Canning",\
episode_number=episode_number, episode_duration=episode_duration, \
episode_date=episode_date, about_show=show_short_description, \
episode_playlist=playlist_table, \
episode_image="img/cover.png".format(episode_number))
with open("html/episode/{0}/index.html".format(episode_number), "w") as episode_page:
episode_page.write(output_from_parsed_template)
def create_RSS_XML_from_template():
from jinja2 import Template, Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader('html/templates'))
rss_template = env.get_template('show_RSS.jinja.xml')
output_from_parsed_template = \
rss_template.render(\
show_name=show_name, \
episode_number=int(episode_number), episode_author="Rob Canning",\
episode_duration=episode_duration, about_show=show_short_description, \
episode_image="img/cover.png".format(episode_number))
with open("html/show_rss.xml".format(episode_number), "w") as rss_page:
rss_page.write(output_from_parsed_template)
def main():
set_episode_date(input_date)
# while create_episode_playlist(conn, episode_playlist, episode_number) != 'y':
# create_episode_playlist(conn, episode_playlist, episode_number)
create_episode_playlist(conn, episode_playlist, episode_number)
# modify_playlist(conn, episode_playlist, episode_number)
create_show_coverart(episode_playlist, 4) #episode_duration = 100
#create_animated_gif()
create_intro(episode_playlist)
create_html_episode_from_template(episode_playlist, episode_number)
create_html_homepage_from_template(episode_playlist)
create_RSS_XML_from_template()
conn.close()
create_podcast(episode_playlist)
main()
#