#!/usr/bin/env python3
# file: radio.py 
# (C) 2019 by marnout à free pt fr
# Released under the GPL
"""
Radio internet avec un raspberry pi
lit les flux radios en direct et des podcasts

"""
from os import system, getenv
from time import strftime 
import xml.etree.ElementTree as et
from urllib.request import urlopen
import tkinter as tk
# listbox
listboxH, listboxW = 5, 32 # hauteur et largeur de la listbox
with open('streaming.xml') as f: 
	listboxSize = f.read().count('<item>')
# utilisteur
login = getenv('LOGNAME') # getlogin n'est pas reconnue par raspbian
scale, duration = (4, 30) if login == "pi" else (1, 1)
# channels
channels = []
with open('channels') as fp:
	for r in fp: channels.append(r[:-1])
# Playlist ------------------------------------------------------------
class Playlist(tk.Listbox):
	"""
	Listbox de tkinter augmentée de l'url et de la duréee 
	Propriétés: active, duration
	Méthodes : next, next_channel, play, prev, set, stop, update
	"""
	def __init__(self, parent, **kw):
		self.parent = parent
		tk.Listbox.__init__(self, parent,
			font = ("FreeSans", 11*scale),
			bg = "black", fg = "darkgrey",
			highlightcolor='darkgrey', highlightbackground='darkgrey',
			height=listboxH, width=listboxW, 
			**kw
		)
		self.focus_set()
		self.channel_id = 0
		self.update(channels[self.channel_id])
		# bidings
		self.bind("<Tab>", self.next_channel)
		self.bind('<KP_Up>', self.prev)
		self.bind('<KP_Down>', self.next)

	@property
	def active(self):
		" active item in the listbox "
		return self.index(tk.ACTIVE)

	@property
	def duration(self):
		" podcast's duration of the active item "
		return self.durations[self.active]

	def update(self, channel):
		" update listbox and add urls to mpd "
		system("mpc clear") # clear mpd
		self.delete(0, tk.END) # clear listbox
		self.durations = []
		with urlopen(channel) as fp:
			rss = et.parse(fp).getroot()
		ns = {'itunes': ' '}
		self.title = next(rss.iter('title')).text
		i = 0
		items = rss.iter('item')
		while i < listboxSize:
			item = next(items)
			self.insert(tk.END, item.find('title').text)
			system(f"mpc add {item.find('enclosure').attrib['url']}")
			try:
				d = item.find('itunes:duration', ns).text
				d = d.split(':')
				self.durations.append(int(d[0])*60 + int(d[1]))
			except:
				self.durations.append(duration)

			i += 1
		self.selection_set(0)
		self.after_id = None
		self.play()

	def set(self, i):
		" activate selection item i (0 ... size - 1) "
		i %= self.size()
		self.select_clear(self.active)
		self.see(i)
		self.selection_set(i)
		self.activate(i)

	def next_channel(self, evt):
		" cross through channels "
		self.channel_id = (self.channel_id + 1) % len(channels)
		self.update(channels[self.channel_id])
		self.parent.playlist_title.update(self.title)
		self.play()

	def play(self, evt=None):
		" play selected item and add a timer "
		# mpc starts at 1 not 0
		system(f"mpc play {self.active + 1}")	
		d = self.durations[self.active]
		self.after_id = self.after(d*60000, self.stop)

	def stop(self, evt=None):
		" stop playing "
		system("mpc stop")

	def next(self, evt):
		" go to the next item  and starts playing "
		self.set(self.active + 1)
		self.play()

	def prev(self, evt):
		" go up to the previous item and play it "
		self.set(self.active - 1)
		self.play()

# Clock ---------------------------------------------------------------
class Clock(tk.Label):
	" simple clock "
	def __init__(self, **kw):
		tk.Label.__init__(self,
			font = ("FreeSans", 22*scale, "bold"), 
			bg = "black", fg = "darkgrey", **kw)
		self.update()
	
	def update(self):
		" update the clock and display time "
		s = strftime('%H:%M') 
		self.config(text = f" {s} ") 
		self.clock_id = self.after(1000, self.update) 

class Title(tk.Label):
	" simple Label to display the title of the channel "
	def __init__(self, **kw):
		tk.Label.__init__(self,
			font = ("FreeSans", 11*scale, "bold"), 
			bg = "black", fg = "darkgrey", **kw)

	def update(self, title):
		self.configure(text = title)

# Gui -----------------------------------------------------------------		
class Gui(tk.Tk):
	"""
	Interface graphique utilisant un pavé numérique
	Méthodes: halt, mute, off, seek, vol

	"""
	def __init__(self):
		tk.Tk.__init__(self)
		self.configure(bg='black')
		# clock()
		self.clock = Clock()
		self.clock.pack()
		# playlist title
		self.playlist_title = Title()
		self.playlist_title.pack()
		# listbox
		self.playlist = Playlist(self)
		self.playlist.pack()
		self.playlist_title.update(self.playlist.title)
		# bindings
		self.bind("<BackSpace>", self.off) 
		self.bind('<KP_Delete>', self.off)
		self.bind('<KP_0>', self.halt)
		self.bind("<KP_Begin>", self.mute)
		self.bind('<KP_Left>', self.vol)
		self.bind('<KP_Right>', self.vol)
		self.bind('<KP_Add>', self.seek)
		self.bind('<KP_Subtract>', self.seek)
	

	def off(self, evt=None):
		" stop playing, clear and quit this script "
		system("mpc stop")
		system("mpc clear")
		#if self.after_id != None:
		#	self.after_cancel(self.after_id)
		self.clock.after_cancel(self.clock.clock_id)
		self.quit()

	def halt(self, evt=None):
		" close all and shutdown the rpi "
		self.off()
		if login == 'pi':
			system("sudo halt")

	def mute(self, evt=None):
		" toggle between sound and mute "
		system("mpc toggle")

	def vol(self, evt=None):
		" adjust volume "
		if evt.keysym == "KP_Left":
			system("mpc volume -10")
		else:
			system("mpc volume +10")

	def seek(self, evt):
		" seek in the current playing stream "
		if evt.keysym == "KP_Subtract":
			system("mpc seek -01:00")
		else:
			system("mpc seek +01:00")


gui = Gui()
gui.title('Radio internet')
if login == "pi":
	gui.wm_attributes("-fullscreen", "1")
gui.mainloop()
