Created
October 23, 2012 13:48
-
-
Save faleev/3938842 to your computer and use it in GitHub Desktop.
Interactive wrapper for watching stream TV from http://tv.jampo.com.ua using cvlc player.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
# Bassed on https://gist.github.com/2494781 | |
# Topmenu and the submenus are based of the example found at this location http://blog.skeltonnetworks.com/2010/03/python-curses-custom-menu/ | |
# The rest of the work was done by Matthew Bennett and he requests you keep these two mentions when you reuse the code :-) | |
# Basic code refactoring by Andrew Scheller | |
import os | |
import sys | |
import re | |
import time | |
import requests | |
import curses # curses is the interface for capturing key presses on the menu, os launches the files | |
screen = curses.initscr() # initializes a new window for capturing key presses | |
curses.noecho() # Disables automatic echoing of key presses (prevents program from input each key twice) | |
curses.cbreak() # Disables line buffering (runs each key as it is pressed rather than waiting for the return key to pressed) | |
curses.start_color() # Lets you use colors when highlighting selected menu option | |
screen.keypad(1) # Capture input from keypad | |
curses.curs_set(0) # Hide cursor | |
# Change this to use different colors when highlighting | |
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE) # Sets up color pair #1, it does black text with white background | |
h = curses.color_pair(1) # h is the coloring for a highlighted menu option | |
n = curses.A_NORMAL # n is the coloring for a non highlighted menu option | |
MENU = "menu" | |
COMMAND = "command" | |
menu_data = { | |
'title': "Jampo TV Guide", 'type': MENU, 'subtitle': "Please selection a section...", | |
'options': [ | |
{'title': "Ukrainian channels", 'type': MENU, 'subtitle': "Please selection a channel...", | |
'options': [ | |
{'title': "1+1", 'type': COMMAND, 'command': '1plus1'}, | |
{'title': "2+2", 'type': COMMAND, 'command': '22'}, | |
{'title': "ICTV", 'type': COMMAND, 'command': 'ictv'}, | |
{'title': "K1", 'type': COMMAND, 'command': 'k1'}, | |
{'title': "QTV", 'type': COMMAND, 'command': 'qtv'}, | |
{'title': "Inter", 'type': COMMAND, 'command': 'inter'}, | |
{'title': "Mega", 'type': COMMAND, 'command': 'mega'}, | |
{'title': "More ...", 'type': MENU, 'subtitle': 'More Ukrainian channels...', | |
'options': [ | |
{'title': "Novyi Kanal", 'type': COMMAND, 'command': 'novyi'}, | |
{'title': "HTH", 'type': COMMAND, 'command': 'ntn'}, | |
{'title': "Pershiy Avtomobilniy", 'type': COMMAND, 'command': '1auto'}, | |
{'title': "UT-1", 'type': COMMAND, 'command': 'ut1'}, | |
{'title': "STB", 'type': COMMAND, 'command': 'stb'}, | |
{'title': "TET", 'type': COMMAND, 'command': 'tet'}, | |
{'title': "STB", 'type': COMMAND, 'command': 'stb'}, | |
{'title': "TRC Ukraine", 'type': COMMAND, 'command': 'ukraine'}, | |
] | |
}, | |
] | |
}, | |
{'title': "Russian channels", 'type': MENU, 'subtitle': "Please selection a channel...", | |
'options': [ | |
{'title': "HTB", 'type': COMMAND, 'command': 'ntv'}, | |
{'title': "Perets TV", 'type': COMMAND, 'command': 'perec'}, | |
{'title': "Perviy kanal ORT", 'type': COMMAND, 'command': 'ort'}, | |
{'title': "Ren-TV", 'type': COMMAND, 'command': 'ren-tv'}, | |
{'title': "TV-3", 'type': COMMAND, 'command': 'tv3'}, | |
] | |
}, | |
{'title': "Sport", 'type': MENU, 'subtitle': "Please selection a channel...", | |
'options': [ | |
{'title': "Eurosport", 'type': COMMAND, 'command': 'eurosport'}, | |
{'title': "Russia 2", 'type': COMMAND, 'command': 'russia2'}, | |
{'title': "Football", 'type': COMMAND, 'command': 'football'}, | |
] | |
}, | |
{'title': "Entertainment", 'type': MENU, 'subtitle': "Please selection a channel...", | |
'options': [ | |
{'title': "2x2", 'type': COMMAND, 'command': '2x2'}, | |
{'title': "CTC", 'type': COMMAND, 'command': 'sts'}, | |
{'title': "THT", 'type': COMMAND, 'command': 'tnt'}, | |
] | |
}, | |
{'title': "Educational", 'type': MENU, 'subtitle': "Please selection a channel...", | |
'options': [ | |
{'title': "Animal Planet", 'type': COMMAND, 'command': 'animalplanet'}, | |
{'title': "Discovery", 'type': COMMAND, 'command': 'discovery'}, | |
{'title': "National Geographic Russia", 'type': COMMAND, 'command': 'ngrus'}, | |
] | |
}, | |
{'title': "News", 'type': MENU, 'subtitle': "Please selection a channel...", | |
'options': [ | |
{'title': "5 kanal", 'type': COMMAND, 'command': '5tv'}, | |
{'title': "Euronews Ukraine", 'type': COMMAND, 'command': 'euronews-ukr'}, | |
{'title': "Russia 1", 'type': COMMAND, 'command': 'russia1'}, | |
] | |
}, | |
{'title': "Music", 'type': MENU, 'subtitle': "Please selection a channel...", | |
'options': [ | |
{'title': "A-ONE", 'type': COMMAND, 'command': 'a-one'}, | |
{'title': "M1", 'type': COMMAND, 'command': 'm1'}, | |
{'title': "M2", 'type': COMMAND, 'command': 'm2'}, | |
{'title': "MTV", 'type': COMMAND, 'command': 'mtv'}, | |
{'title': "MTV Live HD", 'type': COMMAND, 'command': 'mtvlivehd'}, | |
{'title': "MTV Ukraine", 'type': COMMAND, 'command': 'mtvua'}, | |
{'title': "MusicBox Russia", 'type': COMMAND, 'command': 'musicboxru'}, | |
{'title': "More ...", 'type': MENU, 'subtitle': 'More Music channels...', | |
'options': [ | |
{'title': "O-TV", 'type': COMMAND, 'command': 'otv'}, | |
{'title': "RU TV", 'type': COMMAND, 'command': 'rutv'}, | |
{'title': "MuzTV", 'type': COMMAND, 'command': 'muztv'}, | |
] | |
}, | |
] | |
}, | |
{'title': "More ...", 'type': MENU, 'subtitle': "Please selection a channel...", | |
'options': [ | |
{'title': "Fashion", 'type': MENU, 'subtitle': "Please selection a channel...", | |
'options': [ | |
{'title': "Fashion One", 'type': COMMAND, 'command': 'fasion-one'}, | |
{'title': "Fashion TV", 'type': COMMAND, 'command': 'ftv'}, | |
{'title': "Style TV", 'type': COMMAND, 'command': 'styletv'}, | |
{'title': "World Fasion", 'type': COMMAND, 'command': 'wf'}, | |
] | |
} | |
] | |
}, | |
] | |
} | |
# This function displays the appropriate menu and returns the option selected | |
def runmenu(menu, parent): | |
# work out what text to display as the last menu option | |
if parent is None: | |
lastoption = "Exit" | |
else: | |
lastoption = "Return to %s menu" % parent['title'] | |
optioncount = len(menu['options']) # how many options in this menu | |
pos = 0 # pos is the zero-based index of the hightlighted menu option. Every time runmenu is called, position returns to 0, when runmenu ends the position is returned and tells the program what option has been selected | |
oldpos = None # used to prevent the screen being redrawn every time | |
x = None # control for while loop, let's you scroll through options until return key is pressed then returns pos to program | |
# Loop until return key is pressed | |
while x != ord('\n'): | |
if pos != oldpos: | |
oldpos = pos | |
screen.clear() # clears previous screen on key press and updates display based on pos | |
#screen.border(0) | |
screen.addstr(2, 2, menu['title'], curses.A_STANDOUT) # Title for this menu | |
screen.addstr(4, 2, menu['subtitle'], curses.A_BOLD) # Subtitle for this menu | |
# Display all the menu items, showing the 'pos' item highlighted | |
for index in range(optioncount): | |
textstyle = n | |
if pos == index: | |
textstyle = h | |
screen.addstr(5 + index, 4, "%d - %s" % (index + 1, menu['options'][index]['title']), textstyle) | |
# Now display Exit/Return at bottom of menu | |
textstyle = n | |
if pos == optioncount: | |
textstyle = h | |
screen.addstr(5 + optioncount, 4, "%d - %s" % (optioncount + 1, lastoption), textstyle) | |
screen.addstr(7 + optioncount, 1, '') | |
screen.refresh() | |
# finished updating screen | |
x = screen.getch() # Gets user input | |
# What is user input? | |
if x >= ord('1') and x <= ord(str(optioncount + 1)): | |
pos = x - ord('0') - 1 # convert keypress back to a number, then subtract 1 to get index | |
elif x == 258: # down arrow | |
if pos < optioncount: | |
pos += 1 | |
else: | |
pos = 0 | |
elif x == 259: # up arrow | |
if pos > 0: | |
pos += -1 | |
else: | |
pos = optioncount | |
elif x != ord('\n'): | |
curses.flash() | |
# return index of the selected item | |
return pos | |
def get_site_page(target_url): | |
for i in range(10): | |
try: | |
site = requests.get(target_url) | |
except requests.ConnectionError as e: | |
time.sleep(2) | |
else: | |
break | |
else: | |
curses.endwin() | |
print 'ERROR: ', e | |
sys.exit(1) | |
if site.status_code != 200: | |
curses.endwin() | |
print 'ERROR: Site "{0}" returns status code: {1}'.format(target_url, site.status_code) | |
sys.exit(1) | |
if site.url != target_url: | |
curses.endwin() | |
print 'ERROR: Site URL does not match URL for the requested channel.' | |
sys.exit(1) | |
return site.text | |
def get_stream(channel_name, player='cvlc'): | |
if channel_name == 'a-one': | |
cmd_line = '{player} http://89.187.1.165/a1rock'.format(player=player) | |
os.system(cmd_line) | |
return 2 | |
base_url = 'http://tv.jampo.com.ua' | |
channel_url = base_url + '/play/channel/{0}/'.format(channel_name) | |
page = get_site_page(channel_url) | |
session_key = re.search('\'rtmp\.session\'\:\s\'(.*?)\'', page).group(1) | |
flash_player = re.search('flashplayer\:\s\"(.*?)\"', page).group(1) | |
name = re.search('file\:\s\'(.*?)\'', page).group(1) | |
streamer = re.search('streamer\:\s\'(.*?)\'', page).group(1) | |
flash_player_url = base_url + flash_player | |
cmd_line = 'rtmpdump -r {streamer} -W {flashplayer} -a stream/ -p {channel_url} -C S:{rtmp_session} -y {channel} | {player} -'\ | |
.format(streamer=streamer, flashplayer=flash_player_url, channel_url=channel_url, | |
rtmp_session=session_key, channel=name, player=player) | |
os.system(cmd_line) | |
# This function calls showmenu and then acts on the selected item | |
def processmenu(menu, parent=None): | |
optioncount = len(menu['options']) | |
exitmenu = False | |
while not exitmenu: # Loop until the user exits the menu | |
getin = runmenu(menu, parent) | |
if getin == optioncount: | |
exitmenu = True | |
elif menu['options'][getin]['type'] == COMMAND: | |
curses.reset_shell_mode() | |
get_stream(menu['options'][getin]['command']) | |
curses.reset_prog_mode() | |
elif menu['options'][getin]['type'] == MENU: | |
processmenu(menu['options'][getin], menu) # display the submenu | |
# Main program | |
try: | |
processmenu(menu_data) | |
except KeyboardInterrupt: | |
curses.endwin() | |
curses.endwin() # VITAL! This closes out the menu system and returns you to the bash prompt. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment