#!/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