Skip to content

Instantly share code, notes, and snippets.

@aahmd
Created February 24, 2019 06:00
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save aahmd/921ff61249e1377bb6617cadc88e8f21 to your computer and use it in GitHub Desktop.
Save aahmd/921ff61249e1377bb6617cadc88e8f21 to your computer and use it in GitHub Desktop.
video player using tkinter + vlc python bindings
#! /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
Copy link

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.

@aahmd
Copy link
Author

aahmd commented Jul 20, 2021

@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..

@melodical-me
Copy link

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. :)

@not-nef
Copy link

not-nef commented Oct 24, 2021

a tutorial on how to use this would be cool

@aahmd
Copy link
Author

aahmd commented Nov 4, 2021

@not-nef I'll try to make an update soon that includes some documentation

@not-nef
Copy link

not-nef commented Nov 5, 2021

Thanks :D

@aahmd
Copy link
Author

aahmd commented Dec 10, 2021

@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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment