Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-python-vlc for openSUSE:Factory checked in at 2022-12-04 14:58:46 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-python-vlc (Old) and /work/SRC/openSUSE:Factory/.python-python-vlc.new.1835 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-python-vlc" Sun Dec 4 14:58:46 2022 rev:12 rq:1039902 version:3.0.18121 Changes: -------- --- /work/SRC/openSUSE:Factory/python-python-vlc/python-python-vlc.changes 2022-03-01 17:04:29.092348030 +0100 +++ /work/SRC/openSUSE:Factory/.python-python-vlc.new.1835/python-python-vlc.changes 2022-12-04 14:59:17.280593004 +0100 @@ -1,0 +2,6 @@ +Sat Dec 3 21:50:45 UTC 2022 - Yogalakshmi Arunachalam <yarunacha...@suse.com> + +- Update to version 3.0.18121 + No Changelog + +------------------------------------------------------------------- Old: ---- python-vlc-3.0.16120.tar.gz New: ---- python-vlc-3.0.18121.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-python-vlc.spec ++++++ --- /var/tmp/diff_new_pack.UlKiVw/_old 2022-12-04 14:59:17.748595700 +0100 +++ /var/tmp/diff_new_pack.UlKiVw/_new 2022-12-04 14:59:17.752595723 +0100 @@ -17,7 +17,7 @@ Name: python-python-vlc -Version: 3.0.16120 +Version: 3.0.18121 Release: 0 Summary: VLC bindings for python License: LGPL-2.0-or-later ++++++ python-vlc-3.0.16120.tar.gz -> python-vlc-3.0.18121.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-vlc-3.0.16120/PKG-INFO new/python-vlc-3.0.18121/PKG-INFO --- old/python-vlc-3.0.16120/PKG-INFO 2022-02-28 20:33:24.841742800 +0100 +++ new/python-vlc-3.0.18121/PKG-INFO 2022-11-16 12:06:24.821384000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: python-vlc -Version: 3.0.16120 +Version: 3.0.18121 Summary: VLC bindings for python. Home-page: http://wiki.videolan.org/PythonBinding Author: Olivier Aubert @@ -9,7 +9,6 @@ Maintainer-email: cont...@olivieraubert.net License: LGPL-2.1+ Keywords: vlc,video -Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+) @@ -31,6 +30,5 @@ player. Note that it relies on an already present install of VLC. It has been automatically generated from the include files of - vlc 3.0.16, using generator 1.20. + vlc 3.0.18, using generator 1.21. - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-vlc-3.0.16120/examples/cocoavlc.py new/python-vlc-3.0.18121/examples/cocoavlc.py --- old/python-vlc-3.0.16120/examples/cocoavlc.py 2021-10-08 11:14:06.000000000 +0200 +++ new/python-vlc-3.0.18121/examples/cocoavlc.py 2022-11-16 11:55:28.000000000 +0100 @@ -1,4 +1,4 @@ -#! /usr/bin/env python3 +#!/usr/bin/env python # -*- coding: utf-8 -*- # Example of using PyCocoa <https://PyPI.org/project/PyCocoa> to create a @@ -6,18 +6,19 @@ # The Python-VLC binding <https://PyPI.Python.org/pypi/python-vlc> and the # corresponding VLC App, see <https://www.VideoLan.org/index.html>. -# PyCocoa version 21.8.18 or later must be installed. +# PyCocoa version 21.11.02 or later must be installed (on macOS Monterey) -# This VLC player has been tested with VLC 3.0.10-12, 3.0.6-8, 3.0.4, +# This VLC player has been tested with VLC 3.0.10-16, 3.0.6-8, 3.0.4, # 3.0.1-2, 2.2.8 and 2.2.6 and the compatible vlc.py Python-VLC binding -# using 64-bit Python 3.10.0.rc1, 3.9.6, 3.9.0-1, 3.8.10, 3.8.6, 3.7.0-4, -# 3.6.4-5 and 2.7.14-18 on macOS 11.5.2 Big Sur (aka 10.16), 10.15.6 -# Catalina, 10.14.6 Mojave and 10.13.4-6 High Sierra. This player -# does not work with PyPy <https://PyPy.org> nor with Intel(R) Python -# <https://Software.Intel.com/en-us/distribution-for-python>. Python -# 3.10.0rc1, 3.9.6 and macOS' Python 2.7.16 run on Apple Silicon (C{arm64}), -# all other Python versions run on Intel (C{x86_64}) or I{emulated} Intel -# (C{"arm64_x86_64"}, see function C{pycocoa.machine}). +# using 64-bit Python 3.10.0, 3.9.6, 3.9.0-1, 3.8.10, 3.8.6, 3.7.0-4, +# 3.6.4-5 and 2.7.14-18 on macOS 12.0.1 Monterey, 11.5.2-6.1 Big Sur +# (aka 10.16), 10.15.6 Catalina, 10.14.6 Mojave and 10.13.4-6 High Sierra. +# This player does not work with PyPy <https://PyPy.org> nor with Intel(R) +# Python <https://Software.Intel.com/en-us/distribution-for-python>. + +# Python 3.10.0, 3.9.6 and macOS' Python 2.7.16 run on Apple Silicon +# (C{arm64} I{natively}), all other Python versions run on Intel (C{x86_64}) +# or I{emulated} Intel (C{"arm64_x86_64"}, see function C{pycocoa.machine}). # MIT License <https://OpenSource.org/licenses/MIT> # @@ -45,7 +46,7 @@ return 'see <https://PyPI.org/project/%s>' % (package,) __all__ = ('AppVLC',) # PYCHOK expected -__version__ = '21.08.18' +__version__ = '21.11.02' try: import vlc @@ -479,11 +480,16 @@ else: Thread(target=self._sizer).start() - def _sizer(self, secs=0.1): + def _sizer(self, secs=0.25): while True: - w, h = self.player.video_get_size(0) + p = self.player + # wiggle the video to fill the window + s = p.video_get_scale() + p.video_set_scale(0.0 if s else 1.0) + p.video_set_scale(s) # the first call(s) returns (0, 0), # subsequent calls return (w, h) + w, h = p.video_get_size(0) if h > 0 and w > 0: # window's contents' aspect ratio self.window.ratio = self.sized = w, h diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-vlc-3.0.16120/examples/psgvlc.py new/python-vlc-3.0.18121/examples/psgvlc.py --- old/python-vlc-3.0.16120/examples/psgvlc.py 1970-01-01 01:00:00.000000000 +0100 +++ new/python-vlc-3.0.18121/examples/psgvlc.py 2022-11-16 11:55:28.000000000 +0100 @@ -0,0 +1,156 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- + +u'''Bare Bones VLC Media Player Demo with Playlist. + +1 - Originally the Demo_Media_Player_VLC_Based.py duplicated from + <https://GitHub.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms> + and modified to work and showing videos on recent macOS versions. + +2 - This script uses PySimpleGUI under its LGPL3+ stipulations. + +3 - You will need to install the Python bindings for VLC, for example + using pip: python3 -m pip install python-vlc + +4 - You need the VLC player itself from <https://www.VideoLan.org>. + +5 - On macOS, you also need to get tkvlc.py from this location + <https://GitHub.com/oaubert/python-vlc/tree/master/examples> + to get video and audio. + +6 - On macOS, the video plays full-frame, overwriting the buttons. + +7 - Original <https://GitHub.com/israel-dryer/Media-Player> by Israel + Dryer, modified to be a PySimpleGUI Demo Program and a python-vlc + example for you to customize. Uses the VLC player to playback + local media files (and YouTube streams). +''' +import sys +if sys.version_info[0] < 3: # Python 3.4+ only + sys.exit('%s requires Python 3.4 or later' % (sys.argv[0],)) + # import Tkinter as tk +import PySimpleGUI as sg +import vlc + +__all__ = ('libtk',) +__version__ = '22.11.07' # mrJean1 at Gmail + +_Load_ = 'Load' +_Next_ = 'Next' +_Path_ = 'Media URL or local path:' +_Pause_ = 'Pause' +_Play_ = 'Play' +_Prev_ = 'Previous' +_Stop_ = 'Stop' + +# GUI definition & setup +sg.theme('DarkBlue') + +def Bn(name): # a PySimpleGUI "User Defined Element" (see docs) + return sg.Button(name, size=(8, 1), pad=(1, 1)) + +layout = [[sg.Input(default_text=_Path_, size=(40, 1), key='-VIDEO_PATH-'), sg.Button(_Load_)], + [sg.Frame('', [], size=(300, 170), key='-VID_OUT-')], # was [sg.Image('', ...)], + [Bn(_Prev_), Bn(_Play_), Bn(_Next_), Bn(_Pause_), Bn(_Stop_)], + [sg.Text('Load media to start', key='-MESSAGE_AREA-')]] + +window = sg.Window('PySimpleGUI VLC Player', layout, element_justification='center', finalize=True, resizable=True) + +window['-VID_OUT-'].expand(True, True) # type: sg.Element + +# Media Player Setup +inst = vlc.Instance() +list_player = inst.media_list_player_new() +media_list = inst.media_list_new([]) +list_player.set_media_list(media_list) +player = list_player.get_media_player() +# tell VLC where to render the video(s) +tk_id = window['-VID_OUT-'].Widget.winfo_id() +libtk = '' +if sg.running_linux(): + player.set_xwindow(tk_id) +elif sg.running_windows(): + player.set_hwnd(tk_id) +elif sg.running_mac(): + try: + from tkvlc import _GetNSView, libtk + ns = _GetNSView(tk_id) + except ImportError: + ns = None + libtk = 'none, install tkvlc.py from <https://GitHub.com/oaubert/python-vlc> examples' + if ns: # drawable NSview + player.set_nsobject(ns) + else: # no video, only audio + player.set_xwindow(tk_id) +else: # running trinket, etc. + player.set_hwnd(tk_id) # TBD + +if __name__ == '__main__': # MCCABE 20 + + if len(sys.argv) > 1: + if sys.argv[1].lower() in ('-v', '--version'): + # show all versions, this vlc.py, libvlc, etc. (sample output on macOS): + # ... + # % python3 ./psgvlc.py -v + # psgvlc.py: 22.11.06 + # tkinter: 8.6 + # libTk: /Library/Frameworks/Python.framework/Versions/3.11/lib/libtk8.6.dylib + # vlc.py: 3.0.12119 (Mon May 31 18:25:17 2021 3.0.12) + # libVLC: 3.0.16 Vetinari (0x3001000) + # plugins: /Applications/VLC.app/Contents/MacOS/plugins + # Python: 3.11.0 (64bit) macOS 13.0 arm64 + for t in ((sys.argv[0], __version__), (sg.tk.__name__, sg.tk.TkVersion), ('libTk', libtk)): + print('{}: {}'.format(*t)) + try: + vlc.print_version() + vlc.print_python() + except AttributeError: + pass + sys.exit(0) + + if sys.argv[1]: + media_list.add_media(sys.argv[1]) + list_player.set_media_list(media_list) + + # The Event Loop + while True: + # run with a timeout so that current location can be updated + event, values = window.read(timeout=1000) + + if event == sg.WIN_CLOSED: + break + + if event == _Pause_: + list_player.pause() + elif event == _Stop_: + list_player.stop() + elif event == _Next_: + list_player.next() + list_player.play() + elif event == _Prev_: + list_player.previous() # first call causes current video to start over + list_player.previous() # second call moves back 1 video from current + list_player.play() + elif event == _Play_: + list_player.play() + elif event == _Load_: + path = values['-VIDEO_PATH-'] + if path and _Path_ not in path: + media_list.add_media(path) + list_player.set_media_list(media_list) + window['-VIDEO_PATH-'].update(_Path_) # only add a legit submit + + # update elapsed time if a video loaded and playing + if player.is_playing(): + text = '{:02d}:{:02d}'.format(*divmod(player.get_time() // 1000, 60)) + ' / ' + \ + '{:02d}:{:02d}'.format(*divmod(player.get_length() // 1000, 60)) + if sg.running_mac(): + print('{}: {}'.format(sys.argv[0], text)) + + elif not media_list.count(): + text = 'Load media to start' + else: + text = 'Ready to play media' + window['-MESSAGE_AREA-'].update(text) + + window.close() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-vlc-3.0.16120/examples/tkvlc.py new/python-vlc-3.0.18121/examples/tkvlc.py --- old/python-vlc-3.0.16120/examples/tkvlc.py 2021-10-08 11:15:06.000000000 +0200 +++ new/python-vlc-3.0.18121/examples/tkvlc.py 2022-11-16 11:55:28.000000000 +0100 @@ -18,79 +18,119 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. # -"""A simple example for VLC python bindings using tkinter. -Requires Python 3.4 or later. +'''A simple example for VLC python bindings using tkinter. + Author: Patrick Fay Date: 23-09-2015 -""" +''' -# Tested with Python 3.7.4, tkinter/Tk 8.6.9 on macOS 10.13.6 only. -__version__ = '20.05.04' # mrJean1 at Gmail +# Tested with VLC 3.0.16, 3.0.12, 3.0.11, 3.0.10, 3.0.8 and 3.0.6 with +# the compatible vlc.py Python-VLC binding, Python 3.11.0, 3.10.0, 3.9.0 +# and 3.7.4 with tkinter/Tk 8.6.9 on macOS 13.0 (amd64 M1), 11.6.1 (10.16 +# amd64 M1), 11.0.1 (10.16 x86-64) and 10.13.6 only. +__version__ = '22.11.11' # mrJean1 at Gmail -# import external libraries -import vlc -# import standard libraries import sys -if sys.version_info[0] < 3: - import Tkinter as Tk - from Tkinter import ttk - from Tkinter.filedialog import askopenfilename - from Tkinter.tkMessageBox import showerror -else: +try: # Python 3.4+ only import tkinter as Tk - from tkinter import ttk + from tkinter import ttk # PYCHOK ttk = Tk.ttk from tkinter.filedialog import askopenfilename from tkinter.messagebox import showerror -from os.path import basename, expanduser, isfile, join as joined -from pathlib import Path + from pathlib import Path +except ImportError: + sys.exit('%s requires Python 3.4 or later' % (sys.argv[0],)) + # import Tkinter as Tk +import os import time +import vlc + +_dragging = False # use -dragging option _isMacOS = sys.platform.startswith('darwin') _isWindows = sys.platform.startswith('win') _isLinux = sys.platform.startswith('linux') -if _isMacOS: - from ctypes import c_void_p, cdll +_TKVLC_LIBTK_PATH = 'TKVLC_LIBTK_PATH' + +if _isMacOS: # MCCABE 14 + from ctypes import cdll, c_void_p + from ctypes.util import find_library as _find + # libtk = cdll.LoadLibrary(ctypes.util.find_library('tk')) - # returns the tk library /usr/lib/libtk.dylib from macOS, - # but we need the tkX.Y library bundled with Python 3+, - # to match the version number of tkinter, _tkinter, etc. + # returns (None or) the tk library /usr/lib/libtk.dylib + # from macOS, but we need the tkX.Y library bundled with + # Python 3+ or one matching the version of tkinter + + # Homebrew-built Python, Tcl/Tk, etc. are installed in + # different places, usually something like /usr/- or + # /opt/local/Cellar/tcl-tk/8.6.11_1/lib/libtk8.6.dylib, + # found by command line `find /opt -name libtk8.6.dylib` + + def _find_lib(name, *paths): + # 1. built into Python + for p in (getattr(sys, 'base_prefix', ''), sys.prefix): + if p: + yield p + '/lib/' + name + # 2. from ctypes.find_library, env variable + for p in paths: + if p: # is not None + p = os.path.expanduser(p) + yield p + if not p.endswith(name): + yield p + '/' + name + # 3. try Homebrew basement + from glob import iglob + for t in ('/opt', '/usr'): + t += '/local/Cellar/tcl-tk/*/lib/' + name + for p in iglob(t): + yield p + assert os.path.sep == '/' + try: - libtk = 'libtk%s.dylib' % (Tk.TkVersion,) - prefix = getattr(sys, 'base_prefix', sys.prefix) - libtk = joined(prefix, 'lib', libtk) - dylib = cdll.LoadLibrary(libtk) - # getNSView = dylib.TkMacOSXDrawableView is the + env = os.environ.get(_TKVLC_LIBTK_PATH, '') + lib = 'libtk%s.dylib' % (Tk.TkVersion,) + for libtk in _find_lib(lib, _find(lib), *env.split(os.pathsep)): + if libtk and lib in libtk and os.access(libtk, os.F_OK): + break + else: # not found anywhere + if env: # bad env? + t = 'no %s in %%s=%r' % (lib, env) + else: # env not set, suggest + t = 'no %s found, use %%s to set a path' % (lib,) + raise NameError(t % (_TKVLC_LIBTK_PATH,)) + + lib = cdll.LoadLibrary(libtk) + # getNSView = tklib.TkMacOSXDrawableView is the # proper function to call, but that is non-public # (in Tk source file macosx/TkMacOSXSubwindows.c) - # and dylib.TkMacOSXGetRootControl happens to call - # dylib.TkMacOSXDrawableView and return the NSView - _GetNSView = dylib.TkMacOSXGetRootControl + # Fortunately, tklib.TkMacOSXGetRootControl calls + # tklib.TkMacOSXDrawableView and returns the NSView + _GetNSView = lib.TkMacOSXGetRootControl # C signature: void *_GetNSView(void *drawable) to get # the Cocoa/Obj-C NSWindow.contentView attribute, the # drawable NSView object of the (drawable) NSWindow _GetNSView.restype = c_void_p - _GetNSView.argtypes = c_void_p, - del dylib + _GetNSView.argtypes = (c_void_p,) - except (NameError, OSError): # image or symbol not found - def _GetNSView(unused): + except (NameError, OSError) as x: # lib, image or symbol not found + libtk = str(x) # imported by psgvlc.py + + def _GetNSView(unused): # imported by psgvlc.py return None - libtk = "N/A" - C_Key = "Command-" # shortcut key modifier + del cdll, c_void_p, env, _find + C_Key = 'Command-' # shortcut key modifier else: # *nix, Xwindows and Windows, UNTESTED - libtk = "N/A" - C_Key = "Control-" # shortcut key modifier + libtk = 'N/A' + C_Key = 'Control-' # shortcut key modifier class _Tk_Menu(Tk.Menu): - '''Tk.Menu extended with .add_shortcut method. - Note, this is a kludge just to get Command-key shortcuts to - work on macOS. Other modifiers like Ctrl-, Shift- and Option- - are not handled in this code. + '''Tk.Menu extended with .add_shortcut method. Note, this is a + kludge just to get Command-key shortcuts to work on macOS. + Modifiers like Ctrl-, Shift- and Option- are not handled! ''' _shortcuts_entries = {} _shortcuts_widget = None @@ -109,7 +149,7 @@ elif _isMacOS: # keys show as upper-case, always - self.add_command(label=label, accelerator='Command-' + key, + self.add_command(label=label, accelerator=C_Key + key, command=command, **kwds) self.bind_shortcut(key, command, label) @@ -119,15 +159,15 @@ self.bind_shortcut(key, command, label) def bind_shortcut(self, key, command, label=None): - """Bind shortcut key, default modifier Command/Control. - """ + '''Bind shortcut key, default modifier Command/Control. + ''' # The accelerator modifiers on macOS are Command-, # Ctrl-, Option- and Shift-, but for .bind[_all] use # <Command-..>, <Ctrl-..>, <Option_..> and <Shift-..>, # <https://www.Tcl.Tk/man/tcl8.6/TkCmd/bind.htm#M6> if self._shortcuts_widget: if C_Key.lower() not in key.lower(): - key = "<%s%s>" % (C_Key, key.lstrip('<').rstrip('>')) + key = '<%s%s>' % (C_Key, key.lstrip('<').rstrip('>')) self._shortcuts_widget.bind(key, command) # remember the shortcut key for this menu item if label is not None: @@ -147,85 +187,100 @@ ''' self._shortcuts_widget = widget - def entryconfig(self, item, **kwds): - """Update shortcut key binding if menu entry changed. - """ + def entryconfig(self, item, **kwds): # PYCHOK signature + '''Update shortcut key binding if menu entry changed. + ''' Tk.Menu.entryconfig(self, item, **kwds) # adjust the shortcut key binding also if self._shortcuts_widget: key = self._shortcuts_entries.get(item, None) - if key is not None and "command" in kwds: - self._shortcuts_widget.bind(key, kwds["command"]) + if key is not None and 'command' in kwds: + self._shortcuts_widget.bind(key, kwds['command']) class Player(Tk.Frame): - """The main window has to deal with events. - """ - _geometry = '' - _stopped = None + '''The main window has to deal with events. + ''' + _debugs = 0 + _geometry = '' + _MIN_WIDTH = 600 + _stopped = None - def __init__(self, parent, title=None, video=''): + def __init__(self, parent, title=None, video='', debug=False): # PYCHOK called! Tk.Frame.__init__(self, parent) + self.debug = bool(debug) self.parent = parent # == root - self.parent.title(title or "tkVLCplayer") - self.video = expanduser(video) + self.parent.title(title or 'tkVLCplayer') + self.video = os.path.expanduser(video) # Menu Bar - # File Menu menubar = Tk.Menu(self.parent) self.parent.config(menu=menubar) - + # File Menu fileMenu = _Tk_Menu(menubar) fileMenu.bind_shortcuts_to(parent) # XXX must be root? - fileMenu.add_shortcut("Open...", 'o', self.OnOpen) + fileMenu.add_shortcut('Open...', 'o', self.OnOpen) + fileMenu.add_separator() + fileMenu.add_shortcut('Play', 'p', self.OnPlay) # Play/Pause + fileMenu.add_command(label='Stop', command=self.OnStop) + fileMenu.add_separator() + fileMenu.add_shortcut('Mute', 'm', self.OnMute) fileMenu.add_separator() - fileMenu.add_shortcut("Play", 'p', self.OnPlay) # Play/Pause - fileMenu.add_command(label="Stop", command=self.OnStop) + fileMenu.add_shortcut('Close', 'w' if _isMacOS else 's', self.OnClose) fileMenu.add_separator() - fileMenu.add_shortcut("Mute", 'm', self.OnMute) + fileMenu.add_shortcut('Buttons Up', 'a', self.OnAnchor) + self.anchorIndex = fileMenu.index('Buttons Up') fileMenu.add_separator() - fileMenu.add_shortcut("Close", 'w' if _isMacOS else 's', self.OnClose) - if _isMacOS: # intended for and tested on macOS - fileMenu.add_separator() - fileMenu.add_shortcut("Full Screen", 'f', self.OnFullScreen) - menubar.add_cascade(label="File", menu=fileMenu) + fileMenu.add_shortcut('Full Screen', 'f', self.OnScreen) + self.fullIndex = fileMenu.index('Full Screen') + menubar.add_cascade(label='File', menu=fileMenu) self.fileMenu = fileMenu - self.playIndex = fileMenu.index("Play") - self.muteIndex = fileMenu.index("Mute") + self.playIndex = fileMenu.index('Play') + self.muteIndex = fileMenu.index('Mute') - # first, top panel shows video - - self.videopanel = ttk.Frame(self.parent) - self.canvas = Tk.Canvas(self.videopanel) + # first, panel shows video + self.videoPanel = ttk.Frame(self.parent) + self.canvas = Tk.Canvas(self.videoPanel) self.canvas.pack(fill=Tk.BOTH, expand=1) - self.videopanel.pack(fill=Tk.BOTH, expand=1) + self.videoPanel.pack(fill=Tk.BOTH, expand=1) # panel to hold buttons - self.buttons_panel = Tk.Toplevel(self.parent) - self.buttons_panel.title("") - self.is_buttons_panel_anchor_active = False - - buttons = ttk.Frame(self.buttons_panel) - self.playButton = ttk.Button(buttons, text="Play", command=self.OnPlay) - stop = ttk.Button(buttons, text="Stop", command=self.OnStop) - self.muteButton = ttk.Button(buttons, text="Mute", command=self.OnMute) - self.playButton.pack(side=Tk.LEFT) + self.buttonsPanel = Tk.Toplevel(self.parent) + self.buttonsPanel.title('') + self.buttonsPanel_anchored = False + self.buttonsPanel_clicked = False + self.buttonsPanel_dragged = False + + buttons = ttk.Frame(self.buttonsPanel) + self.playButton = ttk.Button(buttons, text='Play', command=self.OnPlay, underline=0) + stop = ttk.Button(buttons, text='Stop', command=self.OnStop) + self.muteButton = ttk.Button(buttons, text='Mute', command=self.OnMute, underline=0) + self.playButton.pack(side=Tk.LEFT, padx=8) stop.pack(side=Tk.LEFT) - self.muteButton.pack(side=Tk.LEFT) - + self.muteButton.pack(side=Tk.LEFT, padx=8) self.volMuted = False self.volVar = Tk.IntVar() self.volSlider = Tk.Scale(buttons, variable=self.volVar, command=self.OnVolume, - from_=0, to=100, orient=Tk.HORIZONTAL, length=200, + from_=0, to=100, orient=Tk.HORIZONTAL, length=170, showvalue=0, label='Volume') - self.volSlider.pack(side=Tk.RIGHT) + self.volSlider.pack(side=Tk.LEFT) + + self.anchorButton = ttk.Button(buttons, text='Up', command=self.OnAnchor, + width=2) # in characters + self.anchorButton.pack(side=Tk.RIGHT, padx=8) buttons.pack(side=Tk.BOTTOM, fill=Tk.X) + # <https://www.PythonTutorial.net/tkinter/tkinter-window> + # <https://TkDocs.com/tutorial/windows.html> + # self.buttonsPanel.attributes('-topmost', 1) + + self.buttonsPanel.update() + self.videoPanel.update() # panel to hold player time slider - timers = ttk.Frame(self.buttons_panel) + timers = ttk.Frame(self.buttonsPanel) self.timeVar = Tk.DoubleVar() self.timeSliderLast = 0 self.timeSlider = Tk.Scale(timers, variable=self.timeVar, command=self.OnTime, @@ -235,7 +290,6 @@ self.timeSliderUpdate = time.time() timers.pack(side=Tk.BOTTOM, fill=Tk.X) - # VLC player args = [] if _isLinux: @@ -243,110 +297,149 @@ self.Instance = vlc.Instance(args) self.player = self.Instance.media_player_new() - self.parent.bind("<Configure>", self.OnConfigure) # catch window resize, etc. + self.parent.bind('<Configure>', self.OnConfigure) # catch window resize, etc. self.parent.update() # After parent.update() otherwise panel is ignored. - self.buttons_panel.overrideredirect(True) + self.buttonsPanel.overrideredirect(True) + self.buttonsPanel_anchored = True # down, under the video panel - # Estetic, to keep our video panel at least as wide as our buttons panel. - self.parent.minsize(width=502, height=0) + if _dragging: # Detect dragging of the buttons panel. + self.buttonsPanel.bind('<Button-1>', self._Button1Down) + self.buttonsPanel.bind('<B1-Motion>', self._Button1Motion) + self.buttonsPanel.bind('<ButtonRelease-1>', self._Button1Up) - if _isMacOS: - # Only tested on MacOS so far. Enable for other OS after verified tests. - self.is_buttons_panel_anchor_active = True - - # Detect dragging of the buttons panel. - self.buttons_panel.bind("<Button-1>", lambda event: setattr(self, "has_clicked_on_buttons_panel", event.y < 0)) - self.buttons_panel.bind("<B1-Motion>", self._DetectButtonsPanelDragging) - self.buttons_panel.bind("<ButtonRelease-1>", lambda _: setattr(self, "has_clicked_on_buttons_panel", False)) - self.has_clicked_on_buttons_panel = False - else: - self.is_buttons_panel_anchor_active = False + # Keep the video panel at least as wide as thebuttons panel. + self.parent.minsize(width=self._MIN_WIDTH, height=0) + + self._AnchorPanels(force=True) - self._AnchorButtonsPanel() + self.OnTick() # set up the timer - self.OnTick() # set the timer up + if self.video: # play for a second + self.OnPlay() + self.parent.after(1000, self.OnPause) + + def _Button1Down(self, *unused): # only if -dragging + self._debug(self._Button1Down) + # Left-mouse-button pressed inside the buttons + # panel, but not in and over a slider-/button. + self.buttonsPanel_clicked = True + self.buttonsPanel_dragged = False + + def _Button1Motion(self, *unused): # only if -dragging + self._debug(self._Button1Motion) + # Mouse dragged, moved with left-mouse-button down? + self.buttonsPanel_dragged = self.buttonsPanel_clicked + + def _Button1Up(self, *unused): # only if -dragging + self._debug(self._Button1Up) + # Left-mouse-button release + if self.buttonsPanel_clicked: + if self.buttonsPanel_dragged: + # If the mouse was dragged in the buttons + # panel on the background, un-/anchor it. + self.OnAnchor() +# if _dragged: +# self.buttonsPanel.unbind('<Button-1>') +# self.buttonsPanel.unbind('<B1-Motion>') +# self.buttonsPanel.unbind('<ButtonRelease-1>') + self.buttonsPanel_clicked = False + self.buttonsPanel_dragged = False + + def _debug(self, where, **kwds): + # Print where an event is are handled. + if self.debug: + self._debugs += 1 + d = dict(anchored=self.buttonsPanel_anchored, + clicked=self.buttonsPanel_clicked, + dragged=self.buttonsPanel_dragged, + playing=self.player.is_playing(), + stopped=self._stopped) + d.update(kwds) + d = ', '.join('%s=%s' % t for t in sorted(d.items())) + print('%4s: %s %s' % (self._debugs, where.__name__, d)) + + def _AnchorPanels(self, force=False): + # Un-/anchor the buttons under the video panel, at the same width. + self._debug(self._AnchorPanels) + if (force or self.buttonsPanel_anchored): + video = self.parent + h = video.winfo_height() + w = video.winfo_width() + x = video.winfo_x() # i.e. same as the video + y = video.winfo_y() + h + 32 # i.e. below the video + h = self.buttonsPanel.winfo_height() # unchanged + w = max(w, self._MIN_WIDTH) # i.e. same a video width + self.buttonsPanel.geometry('%sx%s+%s+%s' % (w, h, x, y)) + + def OnAnchor(self, *unused): + '''Toggle buttons panel anchoring. + ''' + c = self.OnAnchor + self._debug(c) + self.buttonsPanel_anchored = not self.buttonsPanel_anchored + if self.buttonsPanel_anchored: + a = 'Up' + self._AnchorPanels(force=True) + else: # move the panel to the top left corner + a = 'Down' + h = self.buttonsPanel.winfo_height() # unchanged + self.buttonsPanel.geometry('%sx%s+8+32' % (self._MIN_WIDTH, h)) + self.anchorButton.config(text=a, width=len(a)) + a = 'Buttons ' + a + self.fileMenu.entryconfig(self.anchorIndex, label=a, command=c) + # self.fileMenu.bind_shortcut('a', c) # XXX handled def OnClose(self, *unused): - """Closes the window and quit. - """ - # print("_quit: bye") + '''Closes the window and quit. + ''' + self._debug(self.OnClose) + # print('_quit: bye') self.parent.quit() # stops mainloop self.parent.destroy() # this is necessary on Windows to avoid # ... Fatal Python Error: PyEval_RestoreThread: NULL tstate - def _DetectButtonsPanelDragging(self, _): - """If our last click was on the boarder - we disable the anchor. - """ - if self.has_clicked_on_buttons_panel: - self.is_buttons_panel_anchor_active = False - self.buttons_panel.unbind("<Button-1>") - self.buttons_panel.unbind("<B1-Motion>") - self.buttons_panel.unbind("<ButtonRelease-1>") - - def _AnchorButtonsPanel(self): - video_height = self.parent.winfo_height() - panel_x = self.parent.winfo_x() - panel_y = self.parent.winfo_y() + video_height + 23 # 23 seems to put the panel just below our video. - panel_height = self.buttons_panel.winfo_height() - panel_width = self.parent.winfo_width() - self.buttons_panel.geometry("%sx%s+%s+%s" % (panel_width, panel_height, panel_x, panel_y)) - def OnConfigure(self, *unused): - """Some widget configuration changed. - """ + '''Some widget configuration changed. + ''' + self._debug(self.OnConfigure) # <https://www.Tcl.Tk/man/tcl8.6/TkCmd/bind.htm#M12> self._geometry = '' # force .OnResize in .OnTick, recursive? - - if self.is_buttons_panel_anchor_active: - self._AnchorButtonsPanel() - - def OnFullScreen(self, *unused): - """Toggle full screen, macOS only. - """ - # <https://www.Tcl.Tk/man/tcl8.6/TkCmd/wm.htm#M10> - f = not self.parent.attributes("-fullscreen") # or .wm_attributes - if f: - self._previouscreen = self.parent.geometry() - self.parent.attributes("-fullscreen", f) # or .wm_attributes - self.parent.bind("<Escape>", self.OnFullScreen) - else: - self.parent.attributes("-fullscreen", f) # or .wm_attributes - self.parent.geometry(self._previouscreen) - self.parent.unbind("<Escape>") + self._AnchorPanels() def OnMute(self, *unused): - """Mute/Unmute audio. - """ + '''Mute/Unmute audio. + ''' + self._debug(self.OnMute) + self.buttonsPanel_clicked = False # audio un/mute may be unreliable, see vlc.py docs. self.volMuted = m = not self.volMuted # self.player.audio_get_mute() self.player.audio_set_mute(m) - u = "Unmute" if m else "Mute" + u = 'Unmute' if m else 'Mute' self.fileMenu.entryconfig(self.muteIndex, label=u) self.muteButton.config(text=u) # update the volume slider text self.OnVolume() def OnOpen(self, *unused): - """Pop up a new dialow window to choose a file, then play the selected file. - """ + '''Pop up a new dialow window to choose a file, then play the selected file. + ''' # if a file is already running, then stop it. self.OnStop() # Create a file dialog opened in the current home directory, where - # you can display all kind of files, having as title "Choose a video". - video = askopenfilename(initialdir = Path(expanduser("~")), - title = "Choose a video", - filetypes = (("all files", "*.*"), - ("mp4 files", "*.mp4"), - ("mov files", "*.mov"))) + # you can display all kind of files, having as title 'Choose a video'. + video = askopenfilename(initialdir = Path(os.path.expanduser('~')), + title = 'Choose a video', + filetypes = (('all files', '*.*'), + ('mp4 files', '*.mp4'), + ('mov files', '*.mov'))) self._Play(video) def _Pause_Play(self, playing): # re-label menu item and button, adjust callbacks p = 'Pause' if playing else 'Play' - c = self.OnPlay if playing is None else self.OnPause + c = self.OnPlay if playing is None else self.OnPause # PYCHOK attr self.fileMenu.entryconfig(self.playIndex, label=p, command=c) # self.fileMenu.bind_shortcut('p', c) # XXX handled self.playButton.config(text=p, command=c) @@ -354,17 +447,17 @@ def _Play(self, video): # helper for OnOpen and OnPlay - if isfile(video): # Creation + if os.path.isfile(video): # Creation m = self.Instance.media_new(str(video)) # Path, unicode self.player.set_media(m) - self.parent.title("tkVLCplayer - %s" % (basename(video),)) + self.parent.title('tkVLCplayer - %s' % (os.path.basename(video),)) # set the window id where to render VLC's video output - h = self.videopanel.winfo_id() # .winfo_visualid()? + h = self.canvas.winfo_id() # .winfo_visualid()? if _isWindows: self.player.set_hwnd(h) elif _isMacOS: - # XXX 1) using the videopanel.winfo_id() handle + # XXX 1) using the videoPanel.winfo_id() handle # causes the video to play in the entire panel on # macOS, covering the buttons, sliders, etc. # XXX 2) .winfo_id() to return NSView on macOS? @@ -376,29 +469,33 @@ else: self.player.set_xwindow(h) # fails on Windows # FIXME: this should be made cross-platform - self.OnPlay() + self.OnPlay(None) def OnPause(self, *unused): - """Toggle between Pause and Play. - """ + '''Toggle between Pause and Play. + ''' + self._debug(self.OnPause) + self.buttonsPanel_clicked = False if self.player.get_media(): self._Pause_Play(not self.player.is_playing()) self.player.pause() # toggles def OnPlay(self, *unused): - """Play video, if none is loaded, open the dialog window. - """ + '''Play video, if not loaded, open the dialog window. + ''' + self._debug(self.OnPlay) + self.buttonsPanel_clicked = False # if there's no video to play or playing, # open a Tk.FileDialog to select a file if not self.player.get_media(): if self.video: - self._Play(expanduser(self.video)) + self._Play(os.path.expanduser(self.video)) self.video = '' else: self.OnOpen() # Try to play, if this fails display an error message elif self.player.play(): # == -1 - self.showError("Unable to play the video.") + self.showError('Unable to play the video.') else: self._Pause_Play(True) # set volume slider to audio level @@ -406,10 +503,12 @@ if vol > 0: self.volVar.set(vol) self.volSlider.set(vol) + self.OnResize() def OnResize(self, *unused): - """Adjust the window/frame to the video aspect ratio. - """ + '''Adjust the video panel to the video aspect ratio. + ''' + self._debug(self.OnResize) g = self.parent.geometry() if g != self._geometry and self.player: u, v = self.player.video_get_size() # often (0, 0) @@ -429,12 +528,36 @@ else: # ... for portrait # adjust the window width w = round(float(h) * u / v) - self.parent.geometry("%sx%s+%s+%s" % (w, h, x, y)) + self.parent.geometry('%sx%s+%s+%s' % (w, h, x, y)) self._geometry = self.parent.geometry() # actual + self._AnchorPanels() + + def OnScreen(self, *unused): + '''Toggle full/off screen. + ''' + c = self.OnScreen + self._debug(c) + # <https://www.Tcl.Tk/man/tcl8.6/TkCmd/wm.htm#M10> + f = not self.parent.attributes('-fullscreen') # or .wm_attributes + if f: + self._previouscreen = self.parent.geometry() + self.parent.attributes('-fullscreen', f) # or .wm_attributes + self.parent.bind('<Escape>', c) + f = 'Off' + else: + self.parent.attributes('-fullscreen', f) # or .wm_attributes + self.parent.geometry(self._previouscreen) + self.parent.unbind('<Escape>') + f = 'Full' + f += ' Screen' + self.fileMenu.entryconfig(self.fullIndex, label=f, command=c) + # self.fileMenu.bind_shortcut('f', c) # XXX handled def OnStop(self, *unused): - """Stop the player, resets media. - """ + '''Stop the player, resets media. + ''' + self._debug(self.OnStop) + self.buttonsPanel_clicked = False if self.player: self.player.stop() self._Pause_Play(None) @@ -448,8 +571,8 @@ # [h264 @ 0x7f84fb061200] no frame! def OnTick(self): - """Timer tick, update the time slider to the video time. - """ + '''Timer tick, update the time slider to the video time. + ''' if self.player: # since the self.player.get_length may change while # playing, re-set the timeSlider to the correct range @@ -467,19 +590,18 @@ # adjust window to video aspect ratio, done periodically # on purpose since the player.video_get_size() only # returns non-zero sizes after playing for a while - if not self._geometry: - self.OnResize() + self.OnResize() def OnTime(self, *unused): if self.player: t = self.timeVar.get() if self.timeSliderLast != int(t): - # this is a hack. The timer updates the time slider. - # This change causes this rtn (the 'slider has changed' rtn) - # to be invoked. I can't tell the difference between when - # the user has manually moved the slider and when the timer - # changed the slider. But when the user moves the slider - # tkinter only notifies this rtn about once per second and + # This is a hack. The timer updates the time slider and + # that change causes this rtn (the 'slider has changed') + # to be invoked. I can't tell the difference between the + # user moving the slider manually and the timer changing + # the slider. When the user moves the slider, tkinter + # only notifies this method about once per second and # when the slider has quit moving. # Also, the tkinter notification value has no fractional # seconds. The timer update rtn saves off the last update @@ -496,62 +618,72 @@ self.timeSliderUpdate = time.time() def OnVolume(self, *unused): - """Volume slider changed, adjust the audio volume. - """ + '''Volume slider changed, adjust the audio volume. + ''' + self._debug(self.OnVolume) + self.buttonsPanel_clicked = False + self.buttonsPanel_dragged = False vol = min(self.volVar.get(), 100) - v_M = "%d%s" % (vol, " (Muted)" if self.volMuted else '') - self.volSlider.config(label="Volume " + v_M) + v_M = '%d%s' % (vol, ' (Muted)' if self.volMuted else '') + self.volSlider.config(label='Volume ' + v_M) if self.player and not self._stopped: # .audio_set_volume returns 0 if success, -1 otherwise, # e.g. if the player is stopped or doesn't have media if self.player.audio_set_volume(vol): # and self.player.get_media(): - self.showError("Failed to set the volume: %s." % (v_M,)) + self.showError('Failed to set the volume: %s.' % (v_M,)) def showError(self, message): - """Display a simple error dialog. - """ + '''Display a simple error dialog. + ''' self.OnStop() showerror(self.parent.title(), message) -if __name__ == "__main__": +if __name__ == '__main__': # MCCABE 13 + _argv0 = sys.argv[0] + _debug = False _video = '' while len(sys.argv) > 1: arg = sys.argv.pop(1) if arg.lower() in ('-v', '--version'): - # show all versions, sample output on macOS: + # show all versions, this vlc.py, libvlc, etc. (sample output on macOS): # % python3 ./tkvlc.py -v - # tkvlc.py: 2019.07.28 (tkinter 8.6 /Library/Frameworks/Python.framework/Versions/3.7/lib/libtk8.6.dylib) - # vlc.py: 3.0.6109 (Sun Mar 31 20:14:16 2019 3.0.6) - # LibVLC version: 3.0.6 Vetinari (0x3000600) - # LibVLC compiler: clang: warning: argument unused during compilation: '-mmacosx-version-min=10.7' [-Wunused-command-line-argument] - # Plugin path: /Applications/VLC3.0.6.app/Contents/MacOS/plugins - # Python: 3.7.4 (64bit) macOS 10.13.6 - - # Print version of this vlc.py and of the libvlc - print('%s: %s (%s %s %s)' % (basename(__file__), __version__, - Tk.__name__, Tk.TkVersion, libtk)) + # tkvlc.py: 22.11.10 + # tkinter: 8.6 + # libTk: /Library/Frameworks/Python.framework/Versions/3.11/lib/libtk8.6.dylib + # vlc.py: 3.0.12119 (Mon May 31 18:25:17 2021 3.0.12) + # libVLC: 3.0.16 Vetinari (0x3001000) + # plugins: /Applications/VLC.app/Contents/MacOS/plugins + # Python: 3.11.0 (64bit) macOS 13.0.1 arm64 + for t in ((_argv0, __version__), (Tk.__name__, Tk.TkVersion), ('libTk', libtk)): + print('%s: %s' % t) try: vlc.print_version() vlc.print_python() except AttributeError: pass sys.exit(0) - + elif '-debug'.startswith(arg) and len(arg) > 2: + _debug = True + elif '-dragging'.startswith(arg) and len(arg) > 2: + _dragging = True # detect dragging in buttons panel for Buttons Up/Down elif arg.startswith('-'): - print('usage: %s [-v | --version] [<video_file_name>]' % (sys.argv[0],)) + print('usage: %s [-v | --version] [-debug] [-dragging] [<video_file_name>]' % (_argv0,)) sys.exit(1) - elif arg: # video file - _video = expanduser(arg) - if not isfile(_video): - print('%s error: no such file: %r' % (sys.argv[0], arg)) + _video = os.path.expanduser(arg) + if not os.path.isfile(_video): + print('%s error: no such file: %r' % (_argv0, arg)) sys.exit(1) # Create a Tk.App() to handle the windowing event loop root = Tk.Tk() - player = Player(root, video=_video) - root.protocol("WM_DELETE_WINDOW", player.OnClose) # XXX unnecessary (on macOS) + player = Player(root, video=_video, debug=_debug) + root.protocol('WM_DELETE_WINDOW', player.OnClose) # XXX unnecessary (on macOS) + if _isWindows: # see <https://GitHub.com/python/cpython/blob/3.11/Lib/tkinter/__init__.py> + root.iconify() + root.update() + root.deiconify() root.mainloop() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-vlc-3.0.16120/python_vlc.egg-info/PKG-INFO new/python-vlc-3.0.18121/python_vlc.egg-info/PKG-INFO --- old/python-vlc-3.0.16120/python_vlc.egg-info/PKG-INFO 2022-02-28 20:33:24.000000000 +0100 +++ new/python-vlc-3.0.18121/python_vlc.egg-info/PKG-INFO 2022-11-16 12:06:24.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: python-vlc -Version: 3.0.16120 +Version: 3.0.18121 Summary: VLC bindings for python. Home-page: http://wiki.videolan.org/PythonBinding Author: Olivier Aubert @@ -9,7 +9,6 @@ Maintainer-email: cont...@olivieraubert.net License: LGPL-2.1+ Keywords: vlc,video -Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+) @@ -31,6 +30,5 @@ player. Note that it relies on an already present install of VLC. It has been automatically generated from the include files of - vlc 3.0.16, using generator 1.20. + vlc 3.0.18, using generator 1.21. - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-vlc-3.0.16120/python_vlc.egg-info/SOURCES.txt new/python-vlc-3.0.18121/python_vlc.egg-info/SOURCES.txt --- old/python-vlc-3.0.16120/python_vlc.egg-info/SOURCES.txt 2022-02-28 20:33:24.000000000 +0100 +++ new/python-vlc-3.0.18121/python_vlc.egg-info/SOURCES.txt 2022-11-16 12:06:24.000000000 +0100 @@ -9,6 +9,7 @@ examples/gtk2vlc.py examples/gtkvlc.py examples/play_buffer.py +examples/psgvlc.py examples/pyobjcvlc.py examples/pyqt5vlc.py examples/qtvlc.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-vlc-3.0.16120/setup.py new/python-vlc-3.0.18121/setup.py --- old/python-vlc-3.0.16120/setup.py 2022-02-28 20:29:27.000000000 +0100 +++ new/python-vlc-3.0.18121/setup.py 2022-11-16 12:04:32.000000000 +0100 @@ -4,7 +4,7 @@ from setuptools import setup setup(name='python-vlc', - version = '3.0.16120', + version = '3.0.18121', author='Olivier Aubert', author_email='cont...@olivieraubert.net', maintainer='Olivier Aubert', @@ -35,5 +35,5 @@ player. Note that it relies on an already present install of VLC. It has been automatically generated from the include files of - vlc 3.0.16, using generator 1.20. + vlc 3.0.18, using generator 1.21. """) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-vlc-3.0.16120/vlc.py new/python-vlc-3.0.18121/vlc.py --- old/python-vlc-3.0.16120/vlc.py 2022-02-28 20:29:27.000000000 +0100 +++ new/python-vlc-3.0.18121/vlc.py 2022-11-16 12:04:29.000000000 +0100 @@ -51,10 +51,10 @@ import logging logger = logging.getLogger(__name__) -__version__ = "3.0.16120" -__libvlc_version__ = "3.0.16" -__generator_version__ = "1.20" -build_date = "Mon Feb 28 20:29:27 2022 3.0.16" +__version__ = "3.0.18121" +__libvlc_version__ = "3.0.18" +__generator_version__ = "1.21" +build_date = "Wed Nov 16 12:04:29 2022 3.0.18" # The libvlc doc states that filenames are expected to be in UTF8, do # not rely on sys.getfilesystemencoding() which will be confused, @@ -4915,6 +4915,18 @@ None, Instance, ctypes.c_int, ctypes.c_int, ListPOINTER(ctypes.c_char_p)) return f(p_instance, i_id, i_options, ppsz_options) +def libvlc_errmsg(): + '''A human-readable error message for the last LibVLC error in the calling + thread. The resulting string is valid until another error occurs (at least + until the next LibVLC call). + @warning + This will be None if there was no error. + ''' + f = _Cfunctions.get('libvlc_errmsg', None) or \ + _Cfunction('libvlc_errmsg', (), None, + ctypes.c_char_p) + return f() + def libvlc_clearerr(): '''Clears the LibVLC error status for the current thread. This is optional. By default, the error status is automatically overridden when a new error @@ -8361,7 +8373,7 @@ # libvlc_printerr # libvlc_set_exit_handler -# 39 function(s) not wrapped as methods: +# 40 function(s) not wrapped as methods: # libvlc_audio_equalizer_get_band_count # libvlc_audio_equalizer_get_band_frequency # libvlc_audio_equalizer_get_preset_count @@ -8378,6 +8390,7 @@ # libvlc_dialog_post_action # libvlc_dialog_post_login # libvlc_dialog_set_context +# libvlc_errmsg # libvlc_event_type_name # libvlc_free # libvlc_get_changeset @@ -8475,6 +8488,38 @@ l.extend(sorted('%s=%s' % t for t in kwds.items())) print('Debug callback (%s)' % ', '.join(l)) +def print_python(): + from platform import architecture, machine, mac_ver, uname, win32_ver + if 'intelpython' in sys.executable: + t = 'Intel-' + # elif 'PyPy ' in sys.version: + # t = 'PyPy-' + else: + t = '' + t = '%sPython: %s (%s)' % (t, sys.version.split()[0], architecture()[0]) + if win32_ver()[0]: + t = t, 'Windows', win32_ver()[0] + elif mac_ver()[0]: + t = t, ('iOS' if sys.platform == 'ios' else 'macOS'), mac_ver()[0], machine() + else: + try: + import distro # <http://GitHub.com/nir0s/distro> + t = t, bytes_to_str(distro.name()), bytes_to_str(distro.version()) + except ImportError: + t = (t,) + uname()[0:3:2] + print(' '.join(t)) + +def print_version(): + """Print version of this vlc.py and of the libvlc""" + try: + print('%s: %s (%s)' % (os.path.basename(__file__), __version__, build_date)) + print('libVLC: %s (%#x)' % (bytes_to_str(libvlc_get_version()), libvlc_hex_version())) + # print('libVLC %s' % bytes_to_str(libvlc_get_compiler())) + if plugin_path: + print('plugins: %s' % plugin_path) + except Exception: + print('Error: %s' % sys.exc_info()[1]) + if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) @@ -8506,38 +8551,6 @@ player.get_position() * 100)) sys.stdout.flush() - def print_python(): - from platform import architecture, mac_ver, uname, win32_ver - if 'intelpython' in sys.executable: - t = 'Intel-' - # elif 'PyPy ' in sys.version: - # t = 'PyPy-' - else: - t = '' - t = '%sPython: %s (%s)' % (t, sys.version.split()[0], architecture()[0]) - if win32_ver()[0]: - t = t, 'Windows', win32_ver()[0] - elif mac_ver()[0]: - t = t, ('iOS' if sys.platform == 'ios' else 'macOS'), mac_ver()[0] - else: - try: - import distro # <http://GitHub.com/nir0s/distro> - t = t, bytes_to_str(distro.name()), bytes_to_str(distro.version()) - except ImportError: - t = (t,) + uname()[0:3:2] - print(' '.join(t)) - - def print_version(): - """Print version of this vlc.py and of the libvlc""" - try: - print('%s: %s (%s)' % (os.path.basename(__file__), __version__, build_date)) - print('LibVLC version: %s (%#x)' % (bytes_to_str(libvlc_get_version()), libvlc_hex_version())) - print('LibVLC compiler: %s' % bytes_to_str(libvlc_get_compiler())) - if plugin_path: - print('Plugin path: %s' % plugin_path) - except Exception: - print('Error: %s' % sys.exc_info()[1]) - if '-h' in sys.argv[:2] or '--help' in sys.argv[:2]: print('Usage: %s [options] <movie_filename>' % sys.argv[0]) print('Once launched, type ? for help.')