210 lines
7.1 KiB
Python
210 lines
7.1 KiB
Python
# Copyright 2021 (c) Anna Schumaker.
|
|
import lib
|
|
from gi.repository import GObject
|
|
from gi.repository import Gio
|
|
from gi.repository import Gtk
|
|
|
|
class PlaylistModel(GObject.GObject, Gio.ListModel):
|
|
def __init__(self):
|
|
GObject.GObject.__init__(self)
|
|
self.playlist = None
|
|
self.tracks = [ ]
|
|
|
|
def do_get_item_type(self): return GObject.TYPE_PYOBJECT
|
|
def do_get_n_items(self): return len(self.tracks)
|
|
def do_get_item(self, n):
|
|
return self.tracks[n] if n < len(self.tracks) else None
|
|
|
|
def get_playlist(self): return self.playlist
|
|
def set_playlist(self, playlist):
|
|
if self.playlist:
|
|
self.playlist.disconnect_by_func(self.refreshed)
|
|
self.playlist.disconnect_by_func(self.track_added)
|
|
self.playlist.disconnect_by_func(self.track_removed)
|
|
self.playlist = playlist
|
|
self.refreshed(playlist)
|
|
self.playlist.connect("refreshed", self.refreshed)
|
|
self.playlist.connect("track-added", self.track_added)
|
|
self.playlist.connect("track-removed", self.track_removed)
|
|
|
|
def track_added(self, plist, track):
|
|
index = plist.get_track_index(track)
|
|
self.tracks.insert(index, track)
|
|
self.emit("items-changed", index, 0, 1)
|
|
|
|
def track_removed(self, plist, track, adjusted_current):
|
|
index = self.tracks.index(track)
|
|
del self.tracks[index]
|
|
self.emit("items-changed", index, 1, 0)
|
|
|
|
def refreshed(self, plist):
|
|
rm = len(self.tracks)
|
|
self.tracks = plist.get_tracks()
|
|
self.emit("items-changed", 0, rm, len(self.tracks))
|
|
|
|
|
|
class Filter(lib.filter.Regex):
|
|
def do_match(self, track):
|
|
fields = [ f"disc={track.disc.number}",
|
|
f"track={track.number}",
|
|
f"title={track.title}",
|
|
f"artist={track.artist.name}",
|
|
f"album={track.album.name}",
|
|
f"subtitle={track.disc.subtitle}",
|
|
f"year={track.year.year}" ]
|
|
return self.search(' & '.join(fields))
|
|
PlaylistFilter = Filter()
|
|
|
|
|
|
class FilterPlaylistModel(Gtk.FilterListModel):
|
|
def __init__(self):
|
|
Gtk.FilterListModel.__init__(self)
|
|
self.set_model(PlaylistModel())
|
|
self.set_filter(PlaylistFilter)
|
|
|
|
def get_playlist(self): return self.get_model().get_playlist()
|
|
def set_playlist(self, plist):
|
|
self.set_incremental(plist.get_n_tracks() > 1000)
|
|
self.get_model().set_playlist(plist)
|
|
|
|
def get_runtime(self):
|
|
n = self.get_n_items()
|
|
return sum([ int(self.get_item(i).length) for i in range(n) ])
|
|
|
|
|
|
class PlaylistSelection(Gtk.MultiSelection):
|
|
def __init__(self):
|
|
Gtk.MultiSelection.__init__(self)
|
|
self.set_model(FilterPlaylistModel())
|
|
|
|
def get_filter_model(self): return self.get_model()
|
|
def get_playlist(self): return self.get_model().get_playlist()
|
|
def set_playlist(self, plist): return self.get_model().set_playlist(plist)
|
|
|
|
|
|
class SortPlaylistModel(Gtk.StringList):
|
|
def __init__(self):
|
|
Gtk.StringList.__init__(self)
|
|
self.playlist = None
|
|
self.connect("items-changed", self.order_changed)
|
|
|
|
def get_index(self, string):
|
|
fields = [ self.get_string(i).split()[0] for i in range(self.get_n_items()) ]
|
|
return fields.index(string) if string in fields else None
|
|
|
|
def get_direction(self, string):
|
|
return self.get_string(self.get_index(string)).split()[1]
|
|
|
|
def set_playlist(self, plist):
|
|
self.handler_block_by_func(self.order_changed)
|
|
self.playlist = plist
|
|
order = [ f for f in plist.sort if f.split()[0] not in ( "discs.number", "tracks.trackid") ]
|
|
self.splice(0, self.get_n_items(), order)
|
|
self.handler_unblock_by_func(self.order_changed)
|
|
|
|
def append(self, field):
|
|
super().append(f"{field} ASC")
|
|
|
|
def remove(self, field):
|
|
super().remove(self.get_index(field))
|
|
|
|
def move_up(self, field):
|
|
if (index := self.get_index(field)) > 0:
|
|
self.splice(index - 1, 2, [ self.get_string(index),
|
|
self.get_string(index - 1) ])
|
|
|
|
def move_down(self, field):
|
|
if (index := self.get_index(field)) < (self.get_n_items() - 1):
|
|
self.splice(index, 2, [ self.get_string(index + 1),
|
|
self.get_string(index) ])
|
|
|
|
def reverse(self, field):
|
|
index = self.get_index(field)
|
|
dir = 'DESC' if self.get_direction(field) == 'ASC' else 'ASC'
|
|
self.splice(index, 1, [ f"{field} {dir}" ])
|
|
|
|
def order_changed(self, model, pos, rm, add):
|
|
order = [ self.get_string(i) for i in range(self.get_n_items()) ]
|
|
if i := self.get_index("tracks.number"):
|
|
order.insert(i, f"discs.number {self.get_direction('tracks.number')}")
|
|
self.playlist.sort = order
|
|
|
|
|
|
class SortOptionsModel(Gtk.StringList):
|
|
def __init__(self):
|
|
Gtk.StringList.__init__(self)
|
|
self.append("tracks.number ASC")
|
|
self.append("tracks.title ASC"),
|
|
self.append("tracks.length ASC"),
|
|
self.append("artists.sort ASC"),
|
|
self.append("albums.sort ASC"),
|
|
self.append("discs.subtitle ASC"),
|
|
self.append("albums.release ASC"),
|
|
self.append("tracks.playcount ASC"),
|
|
self.append("tracks.lastplayed ASC")
|
|
|
|
|
|
class DisabledOptionsFilter(Gtk.Filter):
|
|
def __init__(self):
|
|
Gtk.Filter.__init__(self)
|
|
self.playlist = None
|
|
|
|
def changed(self):
|
|
super().changed(Gtk.FilterChange.DIFFERENT)
|
|
|
|
def do_match(self, item):
|
|
if self.playlist == None: return True
|
|
field = item.get_string().split()[0]
|
|
return field not in [ f.split()[0] for f in self.playlist.sort ]
|
|
|
|
def set_playlist(self, plist):
|
|
self.playlist = plist
|
|
self.changed()
|
|
|
|
|
|
class DisabledOptionsModel(Gtk.FilterListModel):
|
|
def __init__(self):
|
|
Gtk.FilterListModel.__init__(self)
|
|
self.set_model(SortOptionsModel())
|
|
self.set_filter(DisabledOptionsFilter())
|
|
|
|
def set_playlist(self, plist):
|
|
self.get_filter().set_playlist(plist)
|
|
|
|
|
|
class SortModelsModel(GObject.GObject, Gio.ListModel):
|
|
def __init__(self):
|
|
GObject.GObject.__init__(self)
|
|
self.models = [ SortPlaylistModel(), DisabledOptionsModel() ]
|
|
|
|
def do_get_item_type(self): return Gio.ListModel
|
|
def do_get_n_items(self): return len(self.models)
|
|
def do_get_item(self, n): return self.models[n]
|
|
|
|
def set_playlist(self, plist):
|
|
for model in self.models:
|
|
model.set_playlist(plist)
|
|
|
|
|
|
class FlatSortModel(Gtk.FlattenListModel):
|
|
def __init__(self):
|
|
Gtk.FlattenListModel.__init__(self)
|
|
self.set_model(SortModelsModel())
|
|
|
|
def set_playlist(self, plist):
|
|
self.get_model().set_playlist(plist)
|
|
|
|
def get_n_enabled(self):
|
|
return self.get_enabled_model().get_n_items()
|
|
|
|
def get_enabled_model(self):
|
|
return self.get_model().get_item(0)
|
|
|
|
def enable(self, field):
|
|
self.get_model().get_item(0).append(field)
|
|
self.get_model().get_item(1).get_filter().changed()
|
|
|
|
def disable(self, field):
|
|
self.get_model().get_item(0).remove(field)
|
|
self.get_model().get_item(1).get_filter().changed()
|