#!/usr/bin/python3 #import cProfile #import re import requests from datetime import datetime #import calendar import os from html import unescape import urllib.parse #import time import curses import sqlite3 as lite import youtube #import os.path #import math from curses.textpad import Textbox, rectangle #527 biale z podkreśleniem 520 szare z podkresleniem #https://www.googleapis.com/youtube/v3/search?key=AIzaSyBwecAq2aCjRxStpOclQZNSRpJEI2HtvX0&part=snippet&type=channel&q=etho&maxResults=10 def get_video_id(url): url_data = urllib.parse.urlparse(url) query = urllib.parse.parse_qs(url_data.query) return query['v'][0] def get_valid_filename(string): return "".join([c for c in string if c.isalpha() or c.isdigit() or c == ' ']).rstrip().replace(' ', '_') def file_exists(video_title): return os.path.isfile("/public/youtube/" + get_valid_filename(video_title)+".mp4") class Mc(object): youtube_info_url = 'http://youtube.com/get_video_info?video_id=' youtube_channels = [] youtube_api_key = "AIzaSyBwecAq2aCjRxStpOclQZNSRpJEI2HtvX0" videos = {} timestamp = 0 longest_nick = 0 selected_video = 0 connection = lite.connect('/public/python/test.db') offset = 0 channel_id_filter = 0 total_videos = 0 search = '' def __init__(self): self.read_data() self.total_videos = self.get_total_videos() def read_data(self): with self.connection: cur = self.connection.cursor() cur.execute('SELECT id, nick, uploadsid FROM channel') self.youtube_channels = cur.fetchall() if self.channel_id_filter > 0: cur.execute("SELECT nick, title, videoid, channel_id, seen FROM video JOIN channel ON video.channel_id = channel.id WHERE deleted IS NULL AND channel.id = ? AND title LIKE ? ORDER BY publishtime DESC LIMIT ?, 30", (self.channel_id_filter, '%'+self.search+'%', self.offset)) else: cur.execute("SELECT nick, title, videoid, channel_id, seen FROM video JOIN channel ON video.channel_id = channel.id WHERE deleted IS NULL AND title LIKE ? ORDER BY publishtime DESC LIMIT ?, 30", ('%'+self.search+'%', self.offset)) self.videos = cur.fetchall() def lite(self, channels): with self.connection: cur = self.connection.cursor() for channel in channels: cur.execute("SELECT * FROM channel WHERE nick = :nick", {"nick": channel['nick']}) self.connection.commit() row = cur.fetchone() if row == None: cur.execute( "INSERT INTO channel('playlist', 'nick', 'channel', 'userid', 'uploadsid') VALUES (?, ?, ?, ?, ?)", ('1', channel['nick'], channel['channel'], channel['userid'], channel['uploadsid'])) self.connection.commit() def get_video_info_url(self, video_id): return self.youtube_info_url + video_id def print_videos(self, stdscr): iterator = 0 for video in self.videos: is_file = "X" if file_exists(video[1]) else " " if iterator == self.selected_video: stdscr.addstr(iterator, 0, "[%s] %s %-60s" % (video[0].ljust(14, ' '), is_file, video[1]), curses.A_STANDOUT) elif video[4] == 1: stdscr.addstr(iterator, 0, "[%s] %s %-60s" % (video[0].ljust(14, ' '), is_file, video[1]), curses.color_pair(236)) #stdscr.addstr(iterator, 0, "[%s] %s %s %-60s" % (video[0].ljust(14, ' '), is_file, video[1], iterator+self.offset), curses.color_pair(iterator+self.offset)) else: #stdscr.addstr(iterator, 0, "[%s] %s %s %-60s" % (video[0].ljust(14, ' '), is_file, video[1], iterator+self.offset), curses.color_pair(iterator+self.offset)) stdscr.addstr(iterator, 0, "[%s] %s %-60s" % (video[0].ljust(14, ' '), is_file, video[1]), curses.color_pair(255)) iterator += 1 def print_botom_info(self, stdscr): pages = int(self.total_videos) // 30 stdscr.addstr(len(self.videos), 0, "Play [Enter] | Download [D] | Refresh [R] | Exit [Q] |", curses.A_BOLD ) if_search = curses.color_pair(90) if len(self.search) > 0 else curses.A_BOLD if_filter = curses.color_pair(90) if self.channel_id_filter > 0 else curses.A_BOLD stdscr.addstr(len(self.videos), 55, "Search [S]", if_search) stdscr.addstr(len(self.videos), 65, " | ", curses.A_BOLD) stdscr.addstr(len(self.videos), 68, "Filter [F]" , if_filter) stdscr.addstr(len(self.videos), 78, " | " , curses.A_BOLD) stdscr.addstr(len(self.videos), 81, "[<-/->] | [%d / %d] " % (self.offset /30+1, pages), curses.A_BOLD) def get_total_videos(self): with self.connection: cur = self.connection.cursor() if self.channel_id_filter > 0: cur.execute("SELECT count(video.id) FROM video JOIN channel ON video.channel_id = channel.id AND channel.id = ? WHERE title like ?", (self.channel_id_filter, '%'+self.search+'%')) else: cur.execute("SELECT count(video.id) FROM video JOIN channel ON video.channel_id = channel.id WHERE title LIKE ?", ('%'+self.search+'%',)) return int(cur.fetchone()[0]) def get_video_direct_urls(self, video_id): streams = [] data = requests.get(self.get_video_info_url(video_id)) unescaped = unescape(data.text) decoded = urllib.parse.parse_qs(data.text) for stream in decoded['url_encoded_fmt_stream_map'][0].split(','): streams.append(urllib.parse.parse_qs(stream)) return streams def get_all_videos(self): for id, channelName, uploadListId in self.youtube_channels: videos = youtube.playlistItems(uploadListId, self.youtube_api_key, True) for row in videos: self.save_video(id, row) def get_recent_videos_silent(self, allVideos=False): for id, channelName, uploadListId in self.youtube_channels: videos = youtube.playlistItems(uploadListId, self.youtube_api_key, allVideos) for row in videos: self.save_video(id, row) def get_recent_videos(self, stdscr): channel_iterator = 1 for id, channelName, uploadListId in self.youtube_channels: stdscr.addstr(channel_iterator, 0, '%s %d/%d' % (channelName,0,50)) channel_iterator+=1 channel_iterator = 1 for id, channelName, uploadListId in self.youtube_channels: videos = youtube.playlistItems(uploadListId, self.youtube_api_key) videos_iterator = 1 for row in videos: self.save_video(id, row) stdscr.addstr(channel_iterator, 0, '%s %d/%d' % (channelName,videos_iterator, len(videos))) stdscr.refresh() videos_iterator+=1 channel_iterator+=1 def save_video(self, channel_id, video_row): with self.connection: cur = self.connection.cursor() cur.execute( "INSERT OR IGNORE INTO video('channel_id', 'title', 'views', 'duration', 'videoid', 'publishtime') VALUES (?, ?, ?, ?, ?, ?)", (channel_id, video_row['title'], 0, 0, video_row['videoid'], video_row['timestamp'])) self.connection.commit() def main_loop(self, stdscr): stdscr.keypad(True) stdscr.clear() curses.use_default_colors() for i in range(0, curses.COLORS): curses.init_pair(i, i, -1); self.print_videos(stdscr) mc.print_botom_info(stdscr) curses.curs_set(0) while 1: stdscr.refresh() key = stdscr.getch() if key == curses.KEY_UP: stdscr.refresh() mc.selected_video -= 1 if mc.selected_video < 0: mc.selected_video = len(mc.videos) - 1 elif key == curses.KEY_DOWN: stdscr.refresh() mc.selected_video += 1 if mc.selected_video >= len(mc.videos): mc.selected_video = 0 elif key == curses.KEY_RIGHT: stdscr.clear() self.offset += 30; self.read_data() elif key == curses.KEY_LEFT: stdscr.clear() if(self.offset > 30): self.offset -= 30 else: self.offset = 0 self.read_data() elif key in [ord('F'), ord('f')]: stdscr.clear() if self.channel_id_filter > 0: self.channel_id_filter = 0; else: self.channel_id_filter = mc.videos[mc.selected_video][3] self.offset = 0 self.read_data() self.total_videos = self.get_total_videos() elif key in [ord('A'), ord('a')]: stdscr.clear() editwin = curses.newwin(1, 49, 2, 1) stdscr.addstr(0,0,'Search channel:') stdscr.addstr(1, 0, '+-------------------------------------------------+') stdscr.addstr(2, 0, '| |') stdscr.addstr(3, 0, '+-------------------------------------------------+') stdscr.refresh() box = Textbox(editwin) box.edit() self.searchChannel = box.gather() elif key in [ord('S'), ord('s')]: stdscr.clear() editwin = curses.newwin(1,49, 2,1) stdscr.addstr(0, 0, 'SEARCH BY TITLE:') stdscr.addstr(1, 0, '+-------------------------------------------------+') stdscr.addstr(2, 0, '| |') stdscr.addstr(3, 0, '+-------------------------------------------------+') stdscr.refresh() box = Textbox(editwin) box.edit() self.search = box.gather() self.read_data() self.total_videos = self.get_total_videos() stdscr.clear() elif key in [ord('R'), ord('r')]: stdscr.clear() stdscr.addstr(0, 0, 'REFRESHING LIST') stdscr.refresh() self.read_data() self.total_videos = self.get_total_videos() elif key in [ord('D'), ord('d')]: video_id = mc.videos[mc.selected_video][2] streams = mc.get_video_direct_urls(video_id) for stream in streams: if stream['itag'][0] == "22": os.system('wget -nv --show-progress -O "/public/youtube/%s.mp4" "%s" >> /dev/null &' % ( get_valid_filename(mc.videos[mc.selected_video][1]), stream['url'][0])) elif key in [ord('Q'), ord('q')]: # esc curses.endwin() os._exit(0) elif key == 10 or key == curses.KEY_ENTER: video_id = mc.videos[mc.selected_video][2] #stdscr.clear() #stdscr.addstr(0, 0, '/public/youtube/' + get_valid_filename(mc.videos[mc.selected_video][1])+".mp4") # stdscr.refresh() # time.sleep(4) with self.connection: cur = self.connection.cursor() cur.execute('UPDATE video SET seen = 1 WHERE videoid = ?', (video_id, )) self.youtube_channels = cur.fetchall() self.connection.commit() if file_exists(mc.videos[mc.selected_video][1]): # stdscr.addstr(1, 1, '/public/youtube/' + get_valid_filename(mc.videos[mc.selected_video][1])+".mp4") # stdscr.refresh() os.system('omxplayer -b "%s"' % ("/public/youtube/" + get_valid_filename(mc.videos[mc.selected_video][1])+".mp4" )) else: streams = mc.get_video_direct_urls(video_id) for stream in streams: # print (stream['itag']) if stream['itag'][0] == "22": os.system('omxplayer -b "%s"' % stream['url'][0]) stdscr.clear() mc.print_videos(stdscr) mc.print_botom_info(stdscr) if __name__ == "__main__": mc = Mc() #test('test') #print (youtube.search('generikb', 'channel', "AIzaSyBwecAq2aCjRxStpOclQZNSRpJEI2HtvX0")) #videos = youtube.playlistItems("UU-_VTaWqRsZ1nzZLHQIGwQA", "AIzaSyBwecAq2aCjRxStpOclQZNSRpJEI2HtvX0") #print(videos) #mc.get_all_videos() #mc.lite(channels) # print (mc.get_video_info_url('https://www.youtube.com/watch?v=vw61gCe2oqI')) # mc.get_videos('UUFKDEp9si4RmHFWJW1vYsMA') # mc.get_videos('UUJTWU5K7kl9EE109HBeoldA') # os.system('clear') # try: curses.wrapper(mc.main_loop) #cProfile.run('re.compile("foo|bar")') except KeyboardInterrupt: curses.endwin() os._exit(0)