-
-
Save aahmd/921ff61249e1377bb6617cadc88e8f21 to your computer and use it in GitHub Desktop.
#! /usr/bin/python | |
# -*- coding: utf-8 -*- | |
"""vlc media player; based off example in vlc repo: | |
`http://git.videolan.org/?p=vlc/bindings/python.git;a=commit;h=HEAD` | |
See also: | |
`http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/menu.html` | |
`http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/menu-coptions.html` | |
""" | |
import os | |
import sys | |
from functools import partial | |
import time | |
import pathlib | |
import vlc | |
import tkinter as tk | |
from tkinter import ttk | |
from tkinter.font import Font | |
from tkinter.filedialog import askopenfilename | |
class PyPlayer(tk.Frame): | |
def __init__(self, container, container_instance, title=None): | |
tk.Frame.__init__(self, container_instance) | |
self.container = container | |
self.container_instance = container_instance | |
self.initial_directory = pathlib.Path(os.path.expanduser("~")) | |
self.menu_font = Font(family="Verdana", size=20) | |
self.default_font = Font(family="Times New Roman", size=16) | |
# create vlc instance | |
self.vlc_instance, self.vlc_media_player_instance = self.create_vlc_instance() | |
# main menubar | |
self.menubar = tk.Menu(self.container_instance) | |
self.menubar.config(font=self.menu_font) | |
# cascading file menu | |
self.file_menu = tk.Menu(self.menubar, tearoff=0) | |
self.create_file_menu() | |
# cascading list menu | |
self.list_menu = tk.Menu(self.menubar, tearoff=0) | |
self.create_list_menu() | |
# other menus | |
self.menubar.add_command(label="More", command=None) | |
self.menubar.add_command(label="Debug", command=self._debug) | |
self.menubar.add_command(label="Quit", command=self.close) | |
self.container_instance.config(menu=self.menubar) | |
# vlc video frame | |
self.video_panel = ttk.Frame(self.container_instance) | |
self.canvas = tk.Canvas(self.video_panel, background='black') | |
self.canvas.pack(fill=tk.BOTH, expand=1) | |
self.video_panel.pack(fill=tk.BOTH, expand=1) | |
# controls | |
self.create_control_panel() | |
def _debug(self): | |
"""Debugging.""" | |
import pdb; pdb.set_trace() | |
pass | |
def create_control_panel(self): | |
"""Add control panel.""" | |
control_panel = ttk.Frame(self.container_instance) | |
pause = ttk.Button(control_panel, text="Pause", command=self.pause) | |
play = ttk.Button(control_panel, text="Play", command=self.play) | |
stop = ttk.Button(control_panel, text="Stop", command=self.stop) | |
volume = ttk.Button(control_panel, text="Volume", command=None) | |
pause.pack(side=tk.LEFT) | |
play.pack(side=tk.LEFT) | |
stop.pack(side=tk.LEFT) | |
volume.pack(side=tk.LEFT) | |
control_panel.pack(side=tk.BOTTOM) | |
def create_vlc_instance(self): | |
"""Create a vlc instance; `https://www.olivieraubert.net/vlc/python-ctypes/doc/vlc.MediaPlayer-class.html`""" | |
vlc_instance = vlc.Instance() | |
vlc_media_player_instance = vlc_instance.media_player_new() | |
self.container_instance.update() | |
return vlc_instance, vlc_media_player_instance | |
def get_handle(self): | |
return self.video_panel.winfo_id() | |
def play(self): | |
"""Play a file.""" | |
if not self.vlc_media_player_instance.get_media(): | |
self.open() | |
else: | |
if self.vlc_media_player_instance.play() == -1: | |
pass | |
def close(self): | |
"""Close the window.""" | |
self.container.delete_window() | |
def pause(self): | |
"""Pause the player.""" | |
self.vlc_media_player_instance.pause() | |
def stop(self): | |
"""Stop the player.""" | |
self.vlc_media_player_instance.stop() | |
def open(self): | |
"""New window allowing user to select a file and play.""" | |
file = askopenfilename( | |
initialdir=self.initial_directory, | |
filetypes=( | |
("Audio Video Interleave", "*.avi"), | |
("Matroska", "*.mkv"), | |
) | |
) | |
if isinstance(file, tuple): | |
return | |
if os.path.isfile(file): | |
self.play_film(file) | |
def play_film(self, file): | |
"""Invokes the `play` method on the vlc instance for the current file.""" | |
directory_name = os.path.dirname(file) | |
file_name = os.path.basename(file) | |
self.Media = self.vlc_instance.media_new( | |
str(os.path.join(directory_name, file_name)) | |
) | |
self.Media.get_meta() | |
self.vlc_media_player_instance.set_media(self.Media) | |
self.vlc_media_player_instance.set_xwindow(self.get_handle()) | |
self.play() | |
@staticmethod | |
def get_film_name(film) -> str: | |
"""Removes directory from film name.""" | |
return film.split('/')[-1] | |
def create_file_menu(self): | |
"""Create file menu.""" | |
self.file_menu.add_command(label="Open", command=self.open, font=self.default_font, accelerator="ctrl + o") | |
self.file_menu.add_command(label="Search", command=None, font=self.default_font, accelerator="ctrl + s") | |
self.file_menu.add_separator() | |
self.file_menu.add_command(label="Quit", command=self.close, font=("Verdana", 14, "bold"), accelerator="ctrl + q") | |
self.menubar.add_cascade(label="File", menu=self.file_menu) | |
def create_film_entry(self, film): | |
"""Adds an entry to the `list_menu` for a given film.""" | |
self.list_menu.add_command( | |
label=self.get_film_name(film), | |
command=partial(self.play_film, film), | |
font=self.default_font | |
) | |
def create_list_menu(self): | |
"""List all films present on system.""" | |
films = [] | |
try: | |
for root, dirs, files in os.walk(str(self.initial_directory)): | |
for file in files: | |
if file.endswith(('.mkv', '.avi')) and not file.endswith(('.sample.mkv', '.sample.avi')): | |
films.append(os.path.join(root, file)) | |
except TypeError: | |
raise("Error") | |
films.sort(key=lambda s: s.lower().split('/')[-1]) | |
[self.create_film_entry(film) for film in films] | |
self.menubar.add_cascade(label="All Films", menu=self.list_menu) | |
class BaseTkContainer: | |
def __init__(self): | |
self.tk_instance = tk.Tk() | |
self.tk_instance.title("py player") | |
self.tk_instance.protocol("WM_DELETE_WINDOW", self.delete_window) | |
self.tk_instance.geometry("1920x1080") # default to 1080p | |
self.tk_instance.configure(background='black') | |
self.theme = ttk.Style() | |
self.theme.theme_use("alt") | |
def delete_window(self): | |
tk_instance = self.tk_instance | |
tk_instance.quit() | |
tk_instance.destroy() | |
os._exit(1) | |
def __repr__(self): | |
return "Base tk Container" | |
root = BaseTkContainer() | |
player = PyPlayer(root, root.tk_instance, title="pyplayer") | |
root.tk_instance.mainloop() |
@melodical-me I haven't ran this in a while, but I imagine you're right in that it's probably an issue while running it 'in a Jupyter' context. I should have a chance to play around with it this week, so i'll take a look.
I'm glad you like it! I too have long been seeking for some more features built-in to vlc or some oss alternative..
i got the vlc module to work in Anaconda under a different script, so at least an important part of the puzzle is solved. At least I know vlc will work in Anaconda.
The problem is, we have different systems and settings, and we dont know what is what right now.
Another problem exists in the second script. It starts the file, but provides no way to govern the playback. In other words, the second script opens the file, lets the video play, but you cant pause it, stop it, adjust the volume, etc...
What I can do is compare both scripts and try to identify the basic differences per opening the file. Also, your script is more of a "wrapper" (I think that is the right term), and it has more functions to control playback. I see this functionality in terms of code, as they do not show up at runtime, although maybe they will appear if I can get vlc to play the file inside your app.
What happens right now is, when I launch the script, it just idles, as if it is trying to load the video, so it may be getting hung up on the loading phase; or, in other words, since the program gets stuck at the load file phase, it hasn't reached the point where it loads the buttons, etc...
Am glad we met. Lets stay in touch about it. I hope it works out well for us. Will let you know how it goes. :)
a tutorial on how to use this would be cool
@not-nef I'll try to make an update soon that includes some documentation
Thanks :D
@melodical-me are you still using this at all? It's a shame the buttons don't work and one can't govern playback, I agree. I"m going to make some updates to address that. @not-nef documentation on the way!
This appears like it should play right out of the box, but, when I try to run it out of a Jupyter ipynb, it opens the screen but idles and spins with a "Not Responding" error message. Since the tkinter window does open, no error message appears in Jupyter, so I cannot tell whats going wrong.
I think this might be a Jupyter issue, and I see you use Jupyter in some of your other projects, so am wondering if you had to do any special configuring of Jupyter/Anaconda to get it to work right?
BTW, this app is very much like something I have been seeking for a long time, so I thank you for sharing. I hope we can get it to work online, but I'm also just outside of DC, if you do any consulting.