I am trying to make a player which will automatically play the next song from a list when the current one ends. Whatever I do, when I add a call to Load() in the on_song_end method of my class binded both to wx.media.EVT_MEDIA_STOP or wx.media.EVT_MEDIA_FINISHED, it hangs or just iterates through all the items indefinitely. What am I doing wrong?
import os
import wx
from wx.media import MediaCtrl
from wx.adv import Sound
import wx.lib.mixins.listctrl as listmix
import eyed3
import glob
APP_TITLE = "Crazy ID3 tag editor"
class Window(wx.Frame, listmix.ColumnSorterMixin):
def __init__(self, *args, **kwargs):
super(Window, self).__init__(*args, **kwargs)
self.InitUI()
self.create_menu()
self.update_mp3_listing("D:\\YT")
self.itemDataMap = self.row_data
listmix.ColumnSorterMixin.__init__(self, 4)
def InitUI(self):
self.row_data = {}
self.row_obj_dict = {}
self.now_playing_index = -1
self.panel = wx.Panel(self)
self.panel.SetLabel(APP_TITLE)
self.mp3_list_label = wx.StaticText(self.panel, label="List of mp3's")
self.mp3_list = MyListCtrl(self.panel, id=wx.ID_ANY, size=(-1, -1), style=wx.LC_REPORT)
self.mp3_list.InsertColumn(0, "Artist", width=140)
self.mp3_list.InsertColumn(1, "Title", width=140)
self.mp3_list.InsertColumn(2, "Album", width=200)
self.mp3_list.InsertColumn(3, "Filename", width=200)
# self.mp3_list.InsertColumn(3, "Year", width=200)
self.mp3_list.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.on_list_item_activate)
self.mp3_list.Bind(wx.EVT_LIST_ITEM_FOCUSED, self.on_item_focused)
self.mp3_list.Bind(wx.EVT_LIST_COL_CLICK, self.on_col_click)
self.edit_button = wx.Button(self.panel, label="&Edit")
self.edit_button.Bind(wx.EVT_BUTTON, self.on_edit)
self.refresh_button = wx.Button(self.panel)
self.refresh_button.Bind(wx.EVT_BUTTON, self.refresh_mp3_listing)
self.refresh_button.Hide()
self.stop_button = wx.Button(self.panel)
self.stop_button.Bind(wx.EVT_BUTTON, self.stop_song)
self.stop_button.Hide()
self.focus_now_playing_button = wx.Button(self.panel)
self.focus_now_playing_button.Bind(wx.EVT_BUTTON, self.focus_now_playing)
self.focus_now_playing_button.Hide()
self.sizer = wx.GridBagSizer(5, 5)
self.sizer.Add(self.mp3_list_label, pos=(0, 0), span=(1, 2))
self.sizer.Add(self.mp3_list, pos=(1, 0), span=(1, 1), flag=wx.EXPAND|wx.ALL)
self.sizer.Add(self.edit_button, pos=(1, 1), span=(1, 1), flag=wx.ALL|wx.ALIGN_CENTER_HORIZONTAL)
self.sizer.AddGrowableRow(1)
self.sizer.AddGrowableCol(0, proportion=3)
self.sizer.AddGrowableCol(1, proportion=1)
self.panel.SetSizerAndFit(self.sizer)
self.Fit()
self.Center()
self.accelerator_table = wx.AcceleratorTable([(wx.ACCEL_NORMAL, wx.WXK_F5, self.refresh_button.GetId()), (wx.ACCEL_ALT, wx.WXK_CONTROL_S, self.stop_button.GetId()), (wx.ACCEL_NORMAL, wx.WXK_MEDIA_STOP, self.stop_button.GetId()), (wx.ACCEL_CTRL, wx.WXK_CONTROL_P, self.focus_now_playing_button.GetId())])
self.SetAcceleratorTable(self.accelerator_table)
self.Show(True)
def create_menu(self):
self.menu_bar = wx.MenuBar()
self.file_menu = wx.Menu()
self.open_folder_menu_item = self.file_menu.Append(wx.ID_ANY, "Op&en folder", "Open a folder with MP3s")
self.menu_bar.Append(self.file_menu, "&File")
self.Bind(wx.EVT_MENU, self.on_open_folder, self.open_folder_menu_item)
self.SetMenuBar(self.menu_bar)
def on_open_folder(self, event):
title = "Choose a directory: "
dlg = wx.DirDialog(self, title, style=wx.DD_DEFAULT_STYLE)
if dlg.ShowModal() == wx.ID_OK:
self.update_mp3_listing(dlg.GetPath())
dlg.Destroy()
def on_edit(self, event):
focused_item = self.mp3_list.GetFocusedItem()
selected_items_count = self.mp3_list.GetSelectedItemCount()
if focused_item >= 0:
if selected_items_count == 1:
if focused_item == self.now_playing_index:
self.stop_song(event=None)
mp3 = self.row_obj_dict[self.mp3_list.GetItemData(focused_item)]
dlg = EditDialog(mp3)
dlg.ShowModal()
self.update_mp3_listing(self.current_folder_path)
dlg.Destroy()
self.mp3_list.Focus(focused_item)
else:
mp3s_to_edit = []
item = self.mp3_list.GetFirstSelected()
while item != -1:
mp3 = self.row_obj_dict[self.mp3_list.GetItemData(item)]
mp3s_to_edit.append(mp3)
item = self.mp3_list.GetNextSelected(item)
dlg = EditArtistDialog(mp3s_to_edit)
dlg.ShowModal()
self.update_mp3_listing(self.current_folder_path)
dlg.Destroy()
self.mp3_list.Focus(focused_item)
def update_mp3_listing(self, folder_path):
self.current_folder_path = folder_path
self.mp3_list.ClearAll()
self.mp3_list.InsertColumn(0, "Artist", width=140)
self.mp3_list.InsertColumn(1, "Title", width=200)
self.mp3_list.InsertColumn(2, "Album", width=140)
self.mp3_list.InsertColumn(3, "Filename", width=200)
# self.mp3_list.InsertColumn(3, "Year", width=200)
mp3s = glob.glob(folder_path + "/*.mp3")
mp3_objects = []
index = 0
for mp3 in mp3s:
mp3_object = eyed3.load(mp3)
self.mp3_list.InsertItem(index, mp3_object.tag.artist or " ")
self.mp3_list.SetItem(index, 1, mp3_object.tag.title or " ")
self.mp3_list.SetItem(index, 2, mp3_object.tag.album or " ")
self.mp3_list.SetItem(index, 3, mp3)
self.mp3_list.SetItemData(index, index)
# self.mp3_list.SetItemData(index, [mp3_object.tag.artist or " ", mp3_object.tag.title or " ", mp3_object.tag.album or " ", mp3])
mp3_objects.append(mp3_object)
self.row_data[index] = (mp3_object.tag.artist or " ", mp3_object.tag.title or " ", mp3_object.tag.album or " ", mp3_object.tag.file_info.name)
self.row_obj_dict[index] = mp3_object
index += 1
def refresh_mp3_listing(self, event):
self.update_mp3_listing(self.current_folder_path)
def on_loaded(self, event):
self.player.Play()
# self.now_playing_index = self.mp3_list.GetItemData(self.mp3_list.GetFocusedItem()+1)
def on_list_item_activate(self, event):
focused_item = self.mp3_list.GetFocusedItem()
mp3 = self.row_obj_dict[self.mp3_list.GetItemData(focused_item)]
self.player = MediaCtrl(parent=self, szBackend=wx.media.MEDIABACKEND_WMP10)
self.player.Load(mp3.tag.file_info.name) # tried wx.CallAfter without success
self.player.SetVolume(0.25)
self.now_playing_index = focused_item
self.Bind(wx.media.EVT_MEDIA_LOADED, self.on_loaded, self.player)
self.Bind(wx.media.EVT_MEDIA_STOP, self.on_song_end, self.player)
if mp3.tag.artist and mp3.tag.title:
self.SetTitle(f"{APP_TITLE + ': ' + mp3.tag.artist + ' - ' + mp3.tag.title}")
else:
self.SetTitle(f"{APP_TITLE + ': ' + os.path.split(os.path.splitext(mp3.tag.file_info.name)[0])[1]} (no tags)")
def on_song_end(self, event):
self.player.Stop()
now = self.now_playing_index
if now < self.mp3_list.GetItemCount()-1:
song = self.row_obj_dict[self.mp3_list.GetItemData(now+1)]
self.now_playing_index = now+1
else:
song = self.row_obj_dict[self.mp3_list.GetItemData(0)]
self.now_playing_index = 0
print(song.tag.title)
#self.player.Stop()
wx.CallAfter(self.player.Load, song.tag.file_info.name)
"""
if song.tag.artist and song.tag.title:
self.SetTitle(f"{APP_TITLE + ': ' + song.tag.artist + ' - ' + song.tag.title}")
else:
self.SetTitle(f"{APP_TITLE + ': ' + os.path.split(os.path.splitext(song.tag.file_info.name)[0])[1]} (no tags)")
"""
def stop_song(self, event):
self.player.Pause()
self.now_playing_index = -1
self.SetTitle(APP_TITLE)
def focus_now_playing(self, event):
if self.now_playing_index >= 0:
self.mp3_list.Focus(self.now_playing_index)
self.mp3_list.Select(self.now_playing_index, on=1)
else:
Sound().PlaySound("c:/windows/media/Windows Ding.wav", wx.adv.SOUND_ASYNC)
# mixin methods override
def GetListCtrl(self):
return self.mp3_list
def on_col_click(self, event):
old_order = self.mp3_list.GetColumnsOrder()
self.mp3_list.SortItems(1)
new_order = old_order
clicked_column = new_order.pop(self.mp3_list.GetColumnIndexFromOrder(event.GetColumn()))
new_order.insert(0, clicked_column)
self.mp3_list.SetColumnsOrder(new_order)
self.mp3_list.RefreshItems(0, self.mp3_list.GetItemCount()-1)
# event.Skip()
def on_item_focused(self, event):
# print(self.mp3_list.GetItemData(self.mp3_list.GetFocusedItem()))
pass
class MyListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
def __init__(self, parent, id=wx.ID_ANY, autoSizeColumn="LAST", itemTextCallable=None, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0):
if itemTextCallable is not None:
if not isinstance(itemTextCallable, Callable):
raise TypeError("itemTextCallable should be None or a callable")
self._itemTextCallable = itemTextCallable
else:
self._itemTextCallable = self._super_itemTextCallable
wx.ListCtrl.__init__(self, parent, id=id, pos=pos, size=size, style=style)
listmix.ListCtrlAutoWidthMixin.__init__(self)
self.setResizeColumn(autoSizeColumn)
self.Bind(wx.EVT_WINDOW_DESTROY, source=self, id=self.GetId, handler=self._onDestroy)
def _onDestroy(self, evt):
evt.Skip()
self._itemTextCallable = None
def _super_itemTextCallable(self, item, column):
return super(MyListCtrl, self).OnGetItemText(item, column)
def OnGetItemText(self, item, column):
return self._itemTextCallable(item, column)
def sendListItemFocusedEvent(self, index):
evt = wx.ListEvent(wx.wxEVT_LIST_ITEM_FOCUSED, self.Id)
evt.EventObject = self
evt.Index = index
self.ProcessEvent(evt)
app = wx.App()
window = Window(None, title=APP_TITLE)
app.MainLoop()