496 lines
20 KiB
Python
Executable File
496 lines
20 KiB
Python
Executable File
#!/usr/bin/python3
|
|
|
|
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 uuid
|
|
|
|
#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
|
|
complete_playlist = []
|
|
episode_playlist = []
|
|
show_cover = ""
|
|
archive = []
|
|
artists_played = []
|
|
artist_abreviated = []
|
|
|
|
web_path = "/home/rob/antena/html/episode/{0}/img".format(episode_number)
|
|
|
|
if os.path.exists(web_path):
|
|
print("path_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)
|
|
|
|
conn = ''
|
|
def database_create():
|
|
# the show database
|
|
global conn
|
|
conn = sqlite3.connect("database/show.db")
|
|
|
|
#cursor = conn.cursor()
|
|
#cursor.execute("DROP TABLE SHOW")
|
|
#conn.commit()
|
|
|
|
conn.execute('''CREATE TABLE IF NOT EXISTS SHOW(
|
|
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
EPISODE INT NOT NULL,
|
|
DATE TEXT NOT NULL,
|
|
ALBUM TEXT ,
|
|
TRACK TEXT NOT NULL,
|
|
ARTIST TEXT NOT NULL,
|
|
TRACKDUR INT NOT NULL,
|
|
YEAR TEXT NOT NULL,
|
|
PATH TEXT NOT NULL
|
|
);''')
|
|
|
|
print("Table created successfully");
|
|
|
|
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))
|
|
|
|
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?
|
|
|
|
def check_archive(track):
|
|
global archive
|
|
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 load_all_music():
|
|
with open('playlists/complete_music_archive.pls') as playlist_file:
|
|
for line in playlist_file:
|
|
complete_playlist.append(line)
|
|
print('''
|
|
-------------------------------
|
|
loaded {0} tracks from playlist
|
|
-------------------------------
|
|
'''.format(str(len(complete_playlist))))
|
|
return complete_playlist
|
|
|
|
def create_episode_playlist(episode_playlist: list, complete_playlist:list):
|
|
load_all_music()
|
|
|
|
global episode_duration
|
|
global archive
|
|
|
|
track_count = 0
|
|
global artists_played
|
|
max_track_dur = 9
|
|
min_track_dur = 2
|
|
|
|
# TODO what is most important 12 tracks or 60 minutes
|
|
while episode_duration < 60 * 60 and track_count < 12 :
|
|
|
|
song = random.choice(random.sample(\
|
|
complete_playlist, len(complete_playlist) )).rstrip() # pick a song
|
|
track = TinyTag.get(song) # get its metadata
|
|
# check if the artist not in the played list and the song is less than 8mins
|
|
track_path = '/'.join(song.split('/')[0:-1])
|
|
if track.artist not in artists_played:
|
|
if check_archive(track.title) is True:
|
|
if int(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.title) # and write entry to archive file
|
|
|
|
if not track.year: # where missing metadata give a dummy value
|
|
track.year = 0000
|
|
|
|
cursor = conn.cursor()
|
|
#long_string = json.dumps(["' SomeWord"])
|
|
|
|
print([episode_number, episode_date, \
|
|
track.album, track.title, track.artist, \
|
|
track.duration, track.year, track_path])
|
|
|
|
cursor.execute("INSERT INTO SHOW (\
|
|
ID, EPISODE, DATE, ALBUM, TRACK, ARTIST, TRACKDUR, YEAR, PATH) \
|
|
VALUES (NULL, ?, ?, ?, ?, ?,?,?,? )", [episode_number, episode_date, \
|
|
track.album, track.title, track.artist, \
|
|
track.duration, track.year, track_path]);
|
|
conn.commit()
|
|
print("DB Record created successfully");
|
|
|
|
track_count += 1; print(track_count)
|
|
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")
|
|
|
|
conn.close()
|
|
|
|
episode_duration = timedelta(seconds=round(episode_duration))
|
|
|
|
print("total tracks = {0} \n total duration = {1} ".format(track_count, episode_duration))
|
|
|
|
return episode_playlist, 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''')
|
|
|
|
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.png'.format(episode_number,episode_date, variants)
|
|
background.save("html/" + "episode/{0}/{1}".format(episode_number, show_cover))
|
|
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_pls_file():
|
|
# write the selection as a playlist file
|
|
with open("shows/antena_playlist_" + episode_date + ".pls","w") as file:
|
|
file.writelines("\n".join(episode_playlist))
|
|
|
|
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
|
|
|
|
# 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...")
|
|
|
|
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")
|
|
cur = conn.cursor()
|
|
|
|
show_info = []
|
|
|
|
episode_artists = []
|
|
for i in range(10):
|
|
artists = []
|
|
cur.execute("SELECT ARTIST FROM SHOW WHERE EPISODE=?", [i])
|
|
rows = cur.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(10): # get this from new table EPISODE_STATS number of tracks in DB
|
|
print(episode_artists[i])
|
|
an_episode = dict(date="2012-02-", \
|
|
episode_artists=episode_artists[i], episode_number=i, \
|
|
episode_date=episode_date, \
|
|
episode_duration=episode_duration)
|
|
episodes.append(an_episode)
|
|
episodes = reversed(episodes) # reversed order to most recent episode appears first in list
|
|
|
|
#TODO get database sorted - music table with metadata, bandcamp, label info
|
|
cur.execute('SELECT * FROM SHOW WHERE EPISODE=?', [episode_number])
|
|
# database query instead of the below
|
|
# maybe a jinja2 template loop here instead
|
|
for i in episode_playlist: # liar od files
|
|
#for i in cur: # liar od files
|
|
|
|
track = TinyTag.get(i)
|
|
detail = str(track.artist) + " | " + str(track.album) + \
|
|
" | " + str(track.title) + " | " + str(track.year) + \
|
|
" | " + str(timedelta(seconds=round(track.duration)))
|
|
show_info.append("" + detail)
|
|
|
|
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=episode_artists[episode_number], \
|
|
about_show=show_short_description, episode_playlist=show_info, \
|
|
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):
|
|
|
|
from jinja2 import Template, Environment, FileSystemLoader
|
|
|
|
env = Environment(loader=FileSystemLoader('html/templates'))
|
|
episode_template = env.get_template('episode.jinja')
|
|
|
|
show_info = []
|
|
|
|
# maybe a jinja2 template loop here instead
|
|
for i in episode_playlist:
|
|
track = TinyTag.get(i)
|
|
detail = str(track.artist) + " | " + str(track.album) + \
|
|
" | " + str(track.title) + " | " + str(track.year) + \
|
|
" | " + str(timedelta(seconds=round(track.duration)))
|
|
show_info.append("" + detail)
|
|
|
|
output_from_parsed_template = episode_template.render(\
|
|
show_name=show_name, episode_author="Rob Canning",\
|
|
episode_duration=episode_duration, \
|
|
episode_date=episode_date, about_show=show_short_description, \
|
|
episode_playlist=show_info, \
|
|
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)
|
|
|
|
|
|
|
|
|
|
database_create()
|
|
|
|
set_episode_date(input_date)
|
|
create_episode_playlist(episode_playlist, complete_playlist)
|
|
create_show_coverart(episode_playlist, 1) #episode_duration = 100
|
|
#create_animated_gif()
|
|
|
|
|
|
create_intro(episode_playlist)
|
|
create_pls_file()
|
|
|
|
create_html_episode_from_template(episode_playlist)
|
|
create_html_homepage_from_template(episode_playlist)
|
|
create_RSS_XML_from_template()
|
|
|
|
#create_podcast(episode_playlist)
|
|
|
|
|
|
#convert -delay 100 -loop 0 html/episode/2/img/show_cover_2024-01-12* animatedGIF.gif
|