# Streaming scheduled TV shows according to a CSV playlist

I'm very new to Python, and I've made a small program/script that will allow me to stream specific files at a given time.

My idea is that I have a .csv file for each day, with one row for each movie/tv show i want to watch and a cron job that checks every 30 minutes (at :00 and :30). If the job finds any row that matches the current time, then it will play the file given in the row.

As it is now, it has been configured to my installation of Mac OS High Sierra, using VLC as player. Since i've split the config out to a seperate file, I'm hoping it will be easy to setup on another environment (e.g. a Raspberry Pi)

I've split things into three seperate files (all located in the same folder), one of the files being a config file.

cron.py is the file, that is executed by the cron job.

import tvshow
import config

tvShowFile = tvshow.getTodaysFilename()
time = tvshow.getTime()
print ("Scanning file %s for %s" % (tvShowFile, time))
row = tvshow.scanFileForTime(tvShowFile, time)

if(row):
urlColIndex = config.csvIndexes['url']
streamUrl = row[urlColIndex]
print("Executing app:" + config.apppath)
print("URL:" + streamUrl)
try:
tvshow.show(streamUrl)
except Exception as e:
print ("Something went wrong:" + str(e))
else:
print("No show at time %s" % time)


tvshow.py is the file with all the actual stuff

from datetime import datetime
import config
import csv
import config
from subprocess import call

def getTodaysFilename():
filename = datetime.now().strftime(config.fileTimeFormat)
return config.filepath + "/" + filename + "." + config.extension

def getTime():
return datetime.now().strftime(config.hourMinuteFormat)

def getFileContent(filename):
lines = []
with open(filename) as csvfile:
csvContent = csv.reader(csvfile, delimiter = ',')
for row in csvContent:
lines.append(row)
return lines

def scanFileForTime(filename, time):
timeColIndex = config.csvIndexes['time']
content = getFileContent(filename)
for row in content:
if(row[timeColIndex] == time):
return row

def compileArguments(streamUrl):
arguments = []
arguments.append(config.apppath)
arguments.append(streamUrl)
for item in config.appArguments:
arguments.append(item)
return arguments

def show(streamUrl):
arguments = compileArguments(streamUrl)
call(arguments)


config.py is the configuration

apppath = '/Applications/VLC.app/Contents/MacOS/VLC'
filepath = '/Users/Shared/tvshow'
fileTimeFormat = "%Y_%m_%d"
hourMinuteFormat = "%H:%M"
extension = 'csv'
csvIndexes = dict(
time = 0,
title = 1,
url = 2
)
appArguments = [
'--fullscreen',
'--no-loop',
'--play-and-exit'
]


Tv show listings sample file - The "title" is not used for anything and could be left out.

"19:47","Big Buck Bunny","http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4"


Any feedback on the code would be appreciated.

• Is it python 2.7 or 3? Since you're using print() as a function, I'd suggest moving to 3.5+ if you're still in 2.7. – hjpotter92 Jul 22 '18 at 20:23

1. Consistent naming. You are mixing snake_case and camelCase. Check the section on naming conventions.
2. Whitespaces around keyword arguments is advised against.
3. Try to use str.format() or str % () modes to insert variables into strings, preferring the .format over % style. If you do decide to switch to python 3.6+ (as commented), you also have f"" (format-type strings).
4. Order your imports in the following suggested order:
• Standard library imports.
• Related third party imports.
• Local application/library specific imports.

Now, to some changes you can do for optimisations, maintainability and portability.

1. Your entire cron.py is redundant. Since everything it does depends only on the tvshow.py, switch to only using that file. Place the code currently in cron.py inside the if __name__ == "__main__" block.
2. You're trying to import config twice.

import config
import csv
import config

3. You can have a class TVSchedule(r), which reads the config file when initialising, and sets up the file_name, time, app_path beforehand.
4. The performance of scanFileForTime and getFileContent will get better if the latter yields the rows :)
5. Make use of .extend method; so that the following:

for item in config.appArguments:
arguments.append(item)


becomes

arguments.extend(config.appArguments)


It's bad practice to open a file and not call close on it. The with statement should stay

def getFileContent(filename):
...
with open(filename) as csvfile:
csvContent = csv.reader(csvfile, delimiter = ',')
for row in csvContent:
lines.append(row)
...


and agree to @hjpotter92 advice "4. The performance of scanFileForTime and getFileContent will get better if the latter yields the rows :)" so it can be replaced like

def getFileContent(filename):
csvfile = open(filename)
csvContent = csv.reader(csvfile, delimiter = ',')
csvfile.close()
for content in csvContent:
yield content