Remove old rind/ directory
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
parent
08d866b047
commit
0aa31586a3
|
@ -1,8 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import audio
|
||||
from . import gtk
|
||||
from . import library
|
||||
from . import node
|
||||
from . import playlist
|
||||
|
||||
Application = gtk.Application
|
|
@ -1,22 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
import gi
|
||||
import sys
|
||||
|
||||
gi.require_version("Gst", "1.0")
|
||||
from gi.repository import Gst
|
||||
Gst.init(sys.argv)
|
||||
|
||||
from . import playbin
|
||||
from . import autopause
|
||||
from . import widgets
|
||||
|
||||
Playbin = playbin.Playbin
|
||||
|
||||
|
||||
def play_track(track):
|
||||
playbin.set_uri(track.filepath, Gst.State.PLAYING)
|
||||
|
||||
def reset():
|
||||
playbin.reset()
|
||||
autopause.reset()
|
||||
widgets.reset()
|
|
@ -1,49 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from .. import gtk
|
||||
from gi.repository import GdkPixbuf, Gtk, GLib
|
||||
|
||||
Default = Gtk.IconTheme.get_default().load_icon("applications-multimedia-symbolic", 512, 0)
|
||||
IdleCount = 0
|
||||
Image = gtk.Builder.get_object("album_artwork")
|
||||
Paned = gtk.Builder.get_object("sidebar_pane")
|
||||
Pixbuf = Default
|
||||
|
||||
def reset():
|
||||
global Pixbuf
|
||||
Pixbuf = Default
|
||||
set_scaled_image_idle()
|
||||
|
||||
def resize(*args):
|
||||
if Pixbuf != None:
|
||||
set_scaled_image()
|
||||
Paned.connect_after("notify::position", resize)
|
||||
|
||||
def set_image(data):
|
||||
global Pixbuf
|
||||
|
||||
loader = GdkPixbuf.PixbufLoader()
|
||||
loader.write(data)
|
||||
Pixbuf = loader.get_pixbuf()
|
||||
loader.close()
|
||||
set_scaled_image_idle()
|
||||
|
||||
def set_scaled_image():
|
||||
pos = Paned.get_position()
|
||||
new_h = pos * 0.9
|
||||
new_w = min((Pixbuf.get_width() * new_h) / Pixbuf.get_height(), pos)
|
||||
if new_h > 0:
|
||||
pix = Pixbuf.scale_simple(new_w, new_h, GdkPixbuf.InterpType.BILINEAR)
|
||||
Image.set_from_pixbuf(pix)
|
||||
set_scaled_image()
|
||||
|
||||
def __set_scaled_image_idle():
|
||||
global IdleCount
|
||||
if (IdleCount := IdleCount - 1) == 0:
|
||||
set_scaled_image()
|
||||
return GLib.SOURCE_REMOVE
|
||||
return GLib.SOURCE_CONTINUE
|
||||
|
||||
def set_scaled_image_idle():
|
||||
global IdleCount
|
||||
if (IdleCount := IdleCount + 1) == 1:
|
||||
GLib.idle_add(__set_scaled_image_idle, priority=GLib.PRIORITY_LOW)
|
|
@ -1,137 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import playbin
|
||||
from .. import gtk
|
||||
import curds
|
||||
import re
|
||||
from gi.repository import GLib, Gtk, Gst
|
||||
|
||||
MINIMUM = -1
|
||||
MAXIMUM = 99
|
||||
|
||||
Box = gtk.Builder.get_object("pause_box")
|
||||
Count = MINIMUM
|
||||
Down = gtk.Builder.get_object("pause_down")
|
||||
Entry = gtk.Builder.get_object("pause_entry")
|
||||
Icon = Gtk.EntryIconPosition.PRIMARY
|
||||
Label = gtk.Builder.get_object("pause_estimate")
|
||||
Popover = gtk.Builder.get_object("pause_popover")
|
||||
Up = gtk.Builder.get_object("pause_up")
|
||||
|
||||
Playing = False
|
||||
PopCB = None
|
||||
|
||||
|
||||
def __changed__():
|
||||
Entry.set_text(__text__())
|
||||
Entry.set_icon_from_icon_name(Icon, __icon__())
|
||||
Up.set_sensitive(Count < MAXIMUM)
|
||||
Down.set_sensitive(Count > MINIMUM)
|
||||
|
||||
def __estimate__():
|
||||
tracks = curds.playlist.peek(Count)
|
||||
length = sum([ t.tags["length"] for t in tracks if t is not None ])
|
||||
length += playbin.remaining() / Gst.SECOND
|
||||
(m, s) = divmod(round(length / 5.0) * 5, 60)
|
||||
res = [ ]
|
||||
if m > 0: res.append(f"{m} minute{'s' if m != 1 else ''}")
|
||||
if s > 0: res.append(f"{s} seconds")
|
||||
return f"About {' '.join(res)}"
|
||||
|
||||
def __text__():
|
||||
if Count == -1:
|
||||
if Playing == True:
|
||||
return "Keep Playing"
|
||||
return "Paused"
|
||||
elif Count == 0:
|
||||
return "This Track"
|
||||
elif Count == 1:
|
||||
return "Next Track"
|
||||
return f"{Count} Tracks"
|
||||
|
||||
def __icon__():
|
||||
if Count == -1 and Playing == True:
|
||||
return "media-playback-start"
|
||||
return "media-playback-pause"
|
||||
|
||||
def __popup__():
|
||||
global PopCB
|
||||
if PopCB != None:
|
||||
GLib.source_remove(PopCB)
|
||||
if Count == MINIMUM:
|
||||
__popdown__()
|
||||
return
|
||||
Label.set_text(__estimate__())
|
||||
Popover.popup()
|
||||
PopCB = GLib.timeout_add_seconds(5, __popdown__)
|
||||
|
||||
def __popdown__():
|
||||
global PopCB
|
||||
PopCB = None
|
||||
Popover.popdown()
|
||||
return GLib.SOURCE_REMOVE
|
||||
|
||||
|
||||
def on_up_clicked(button):
|
||||
global Count
|
||||
Count = min(MAXIMUM, Count + 1)
|
||||
__changed__()
|
||||
__popup__()
|
||||
|
||||
Up.connect("clicked", on_up_clicked)
|
||||
|
||||
|
||||
def decrement():
|
||||
global Count
|
||||
old = Count
|
||||
Count = max(MINIMUM, Count - 1)
|
||||
__changed__()
|
||||
if Count != old and Count == MINIMUM:
|
||||
return Gst.State.PAUSED
|
||||
return Gst.State.PLAYING
|
||||
|
||||
def on_down_clicked(button):
|
||||
decrement()
|
||||
__popup__()
|
||||
|
||||
Down.connect("clicked", on_down_clicked)
|
||||
|
||||
|
||||
def on_activate_entry(entry):
|
||||
global Count
|
||||
match = re.search("-?\d+", entry.get_text())
|
||||
if match:
|
||||
Count = max(-1, min(99, int(match.group(0))))
|
||||
__changed__()
|
||||
__popup__()
|
||||
|
||||
Entry.connect("activate", on_activate_entry)
|
||||
|
||||
|
||||
def on_show_more(visible):
|
||||
Box.set_visible(visible)
|
||||
|
||||
curds.notify.register("show-more", on_show_more)
|
||||
|
||||
|
||||
def state_changed(state):
|
||||
global Playing
|
||||
Playing = (state == Gst.State.PLAYING)
|
||||
__changed__()
|
||||
|
||||
def about_to_pause():
|
||||
return Count == (MINIMUM + 1)
|
||||
|
||||
|
||||
def reset():
|
||||
global Count
|
||||
global Playing
|
||||
global PopCB
|
||||
Count = -1
|
||||
Playing = False
|
||||
__changed__()
|
||||
on_show_more(False)
|
||||
Popover.hide()
|
||||
if PopCB != None:
|
||||
GLib.source_remove(PopCB)
|
||||
PopCB = None
|
||||
curds.notify.register("show-more", on_show_more)
|
|
@ -1,76 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import artwork
|
||||
from gi.repository import GLib, Gst
|
||||
|
||||
Playbin = Gst.ElementFactory.make("playbin")
|
||||
Fakesink = Gst.ElementFactory.make("fakesink")
|
||||
Bus = Playbin.get_bus()
|
||||
|
||||
Bus.add_signal_watch()
|
||||
|
||||
|
||||
def get_state():
|
||||
(ret, state, pending) = Playbin.get_state(Gst.CLOCK_TIME_NONE)
|
||||
if ret == Gst.StateChangeReturn.SUCCESS:
|
||||
return state
|
||||
return ret
|
||||
|
||||
def get_property(key):
|
||||
return Playbin.get_property(key)
|
||||
|
||||
def set_property(key, value):
|
||||
Playbin.set_property(key, value)
|
||||
|
||||
set_property("video-sink", Fakesink)
|
||||
|
||||
|
||||
def set_uri(uri, state=None):
|
||||
uri = GLib.filename_to_uri(str(uri.absolute()))
|
||||
if state != None:
|
||||
Playbin.set_state(Gst.State.NULL)
|
||||
artwork.reset()
|
||||
set_property("uri", uri)
|
||||
if state != None:
|
||||
Playbin.set_state(state)
|
||||
|
||||
def set_volume(volume):
|
||||
set_property("volume", volume / 100.0)
|
||||
|
||||
|
||||
def position():
|
||||
(res, pos) = Playbin.query_position(Gst.Format.TIME)
|
||||
return pos if res == True else 0
|
||||
|
||||
def duration():
|
||||
(res, dur) = Playbin.query_duration(Gst.Format.TIME)
|
||||
return dur if res == True else 0
|
||||
|
||||
def remaining():
|
||||
return duration() - position()
|
||||
|
||||
def progress():
|
||||
dur = duration()
|
||||
return (position() * 100) / dur if dur > 0 else 0
|
||||
|
||||
|
||||
def play():
|
||||
return Playbin.set_state(Gst.State.PLAYING)
|
||||
|
||||
def pause():
|
||||
return Playbin.set_state(Gst.State.PAUSED)
|
||||
|
||||
def seek_percent(percent):
|
||||
pos = (duration() * percent) / 100.0
|
||||
return Playbin.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, pos)
|
||||
|
||||
def seek_step(step):
|
||||
pos = position() + (step * Gst.SECOND)
|
||||
if pos < 0:
|
||||
pos = 0
|
||||
return Playbin.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, pos)
|
||||
|
||||
|
||||
def reset():
|
||||
Playbin.set_state(Gst.State.NULL)
|
||||
seek_percent(0)
|
||||
set_volume(100)
|
|
@ -1,58 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import artwork
|
||||
from . import playbin
|
||||
from . import widgets
|
||||
from .. import gtk
|
||||
import os
|
||||
import pathlib
|
||||
import unittest
|
||||
from gi.repository import GdkPixbuf, Gtk, Gst
|
||||
|
||||
test_track = pathlib.Path("./trier/Test Album/01 - Test Track.ogg")
|
||||
|
||||
class TestAudioArtwork(unittest.TestCase):
|
||||
def setUp(self):
|
||||
playbin.reset()
|
||||
widgets.reset()
|
||||
|
||||
def test_artwork_init(self):
|
||||
self.assertIsInstance(artwork.Default, GdkPixbuf.Pixbuf)
|
||||
self.assertIsInstance(artwork.Image, Gtk.Image)
|
||||
self.assertIsInstance(artwork.Paned, Gtk.Paned)
|
||||
self.assertEqual(artwork.IdleCount, 0)
|
||||
self.assertEqual(artwork.Pixbuf, artwork.Default)
|
||||
self.assertTrue(artwork.Image.is_visible())
|
||||
|
||||
def test_artwork_load(self):
|
||||
artwork.Paned.set_position(150)
|
||||
playbin.set_uri(test_track, Gst.State.PAUSED)
|
||||
self.assertEqual(artwork.IdleCount, 1)
|
||||
while (artwork.IdleCount > 0):
|
||||
gtk.main_loop()
|
||||
|
||||
self.assertTrue(artwork.Image.is_visible())
|
||||
self.assertEqual(artwork.Image.get_storage_type(), Gtk.ImageType.PIXBUF)
|
||||
self.assertEqual(artwork.Pixbuf.get_height(), 512)
|
||||
self.assertEqual(artwork.Pixbuf.get_width(), 512)
|
||||
|
||||
pbuf = artwork.Image.get_pixbuf()
|
||||
self.assertEqual(pbuf.get_height(), 135)
|
||||
self.assertEqual(pbuf.get_width(), 135)
|
||||
|
||||
playbin.set_uri(test_track)
|
||||
self.assertEqual(artwork.Pixbuf, artwork.Default)
|
||||
|
||||
def test_artwork_resize(self):
|
||||
artwork.Paned.set_position(150)
|
||||
playbin.set_uri(test_track, Gst.State.PAUSED)
|
||||
while (artwork.IdleCount > 0):
|
||||
gtk.main_loop()
|
||||
|
||||
pbuf = artwork.Image.get_pixbuf()
|
||||
self.assertEqual(pbuf.get_height(), 135)
|
||||
self.assertEqual(pbuf.get_width(), 135)
|
||||
|
||||
artwork.Paned.set_position(140)
|
||||
pbuf = artwork.Image.get_pixbuf()
|
||||
self.assertEqual(pbuf.get_height(), 126)
|
||||
self.assertEqual(pbuf.get_width(), 126)
|
|
@ -1,196 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import autopause
|
||||
from . import playbin
|
||||
from .. import gtk
|
||||
import curds
|
||||
import os
|
||||
import pathlib
|
||||
import time
|
||||
import unittest
|
||||
from gi.repository import Gtk, Gst
|
||||
|
||||
test_album = pathlib.Path("./trier/Test Library/Test Artist 01/Test Album 1")
|
||||
test_track = test_album / "10 - Test Track 10.ogg"
|
||||
|
||||
class TestAutoPause(unittest.TestCase):
|
||||
def setUp(self):
|
||||
curds.reset()
|
||||
playbin.reset()
|
||||
autopause.reset()
|
||||
|
||||
def test_init(self):
|
||||
self.assertEqual(autopause.MINIMUM, -1)
|
||||
self.assertEqual(autopause.MAXIMUM, 99)
|
||||
|
||||
self.assertIsInstance(autopause.Box, Gtk.Box)
|
||||
self.assertIsInstance(autopause.Down, Gtk.Button)
|
||||
self.assertIsInstance(autopause.Entry, Gtk.Entry)
|
||||
self.assertIsInstance(autopause.Label, Gtk.Label)
|
||||
self.assertIsInstance(autopause.Popover, Gtk.Popover)
|
||||
self.assertIsInstance(autopause.Up, Gtk.Button)
|
||||
|
||||
self.assertEqual(autopause.Count, autopause.MINIMUM)
|
||||
self.assertEqual(autopause.Icon, Gtk.EntryIconPosition.PRIMARY)
|
||||
|
||||
self.assertFalse(autopause.Playing)
|
||||
self.assertTrue( autopause.Up.is_sensitive())
|
||||
self.assertFalse(autopause.Down.is_sensitive())
|
||||
|
||||
def test_show_more(self):
|
||||
self.assertFalse(autopause.Box.is_visible())
|
||||
|
||||
curds.notify.notify("show-more", True)
|
||||
self.assertTrue(autopause.Box.is_visible())
|
||||
|
||||
curds.notify.notify("show-more", False)
|
||||
self.assertFalse(autopause.Box.is_visible())
|
||||
|
||||
def test_pause_up_clicked(self):
|
||||
autopause.Up.clicked()
|
||||
self.assertEqual(autopause.Count, 0)
|
||||
self.assertTrue(autopause.Up.is_sensitive())
|
||||
self.assertEqual(autopause.Entry.get_text(), "This Track")
|
||||
self.assertTrue( autopause.Popover.is_visible())
|
||||
|
||||
autopause.Up.clicked()
|
||||
self.assertEqual(autopause.Count, 1)
|
||||
self.assertTrue(autopause.Up.is_sensitive())
|
||||
self.assertEqual(autopause.Entry.get_text(), "Next Track")
|
||||
|
||||
autopause.Count = 99
|
||||
autopause.Up.clicked()
|
||||
self.assertEqual(autopause.Count, 99)
|
||||
self.assertFalse(autopause.Up.is_sensitive())
|
||||
self.assertEqual(autopause.Entry.get_text(), "99 Tracks")
|
||||
|
||||
def test_pause_down_clicked(self):
|
||||
autopause.Count = 99
|
||||
autopause.Down.clicked()
|
||||
self.assertEqual(autopause.Count, 98)
|
||||
self.assertTrue(autopause.Down.is_sensitive())
|
||||
self.assertEqual(autopause.Entry.get_text(), "98 Tracks")
|
||||
self.assertTrue( autopause.Popover.is_visible())
|
||||
|
||||
autopause.Down.clicked()
|
||||
self.assertEqual(autopause.Count, 97)
|
||||
self.assertTrue(autopause.Down.is_sensitive())
|
||||
self.assertEqual(autopause.Entry.get_text(), "97 Tracks")
|
||||
|
||||
autopause.Count = -1
|
||||
autopause.Down.clicked()
|
||||
self.assertEqual(autopause.Count, -1)
|
||||
self.assertFalse(autopause.Down.is_sensitive())
|
||||
self.assertEqual(autopause.Entry.get_text(), "Paused")
|
||||
|
||||
def test_pause_decrement(self):
|
||||
self.assertFalse(autopause.about_to_pause())
|
||||
|
||||
autopause.Count = 2
|
||||
self.assertFalse(autopause.about_to_pause())
|
||||
self.assertEqual(autopause.decrement(), Gst.State.PLAYING)
|
||||
self.assertEqual(autopause.Count, 1)
|
||||
|
||||
self.assertFalse(autopause.about_to_pause())
|
||||
self.assertEqual(autopause.decrement(), Gst.State.PLAYING)
|
||||
self.assertEqual(autopause.Count, 0)
|
||||
|
||||
self.assertTrue( autopause.about_to_pause())
|
||||
self.assertEqual(autopause.decrement(), Gst.State.PAUSED)
|
||||
self.assertEqual(autopause.Count, -1)
|
||||
|
||||
self.assertFalse(autopause.about_to_pause())
|
||||
self.assertEqual(autopause.decrement(), Gst.State.PLAYING)
|
||||
self.assertEqual(autopause.Count, -1)
|
||||
|
||||
def test_pause_entry(self):
|
||||
autopause.Entry.set_text("3")
|
||||
autopause.Entry.activate()
|
||||
self.assertEqual(autopause.Count, 3)
|
||||
self.assertTrue( autopause.Popover.is_visible())
|
||||
|
||||
autopause.Entry.set_text("100")
|
||||
autopause.Entry.activate()
|
||||
self.assertEqual(autopause.Count, 99)
|
||||
|
||||
autopause.Entry.set_text("-5")
|
||||
autopause.Entry.activate()
|
||||
self.assertEqual(autopause.Count, -1)
|
||||
|
||||
def test_pause_state_changed(self):
|
||||
autopause.Playing = False
|
||||
autopause.state_changed(Gst.State.PLAYING)
|
||||
self.assertEqual(autopause.Playing, True)
|
||||
self.assertEqual(autopause.Entry.get_text(), "Keep Playing")
|
||||
self.assertEqual(autopause.Entry.get_icon_name(autopause.Icon), "media-playback-start")
|
||||
|
||||
autopause.state_changed(Gst.State.PAUSED)
|
||||
self.assertEqual(autopause.Playing, False)
|
||||
self.assertEqual(autopause.Entry.get_icon_name(autopause.Icon), "media-playback-pause")
|
||||
|
||||
def test_pause_text(self):
|
||||
self.assertEqual(autopause.__text__(), "Paused")
|
||||
|
||||
autopause.Playing = True
|
||||
self.assertEqual(autopause.__text__(), "Keep Playing")
|
||||
|
||||
autopause.Count = 0
|
||||
self.assertEqual(autopause.__text__(), "This Track")
|
||||
|
||||
autopause.Count = 1
|
||||
self.assertEqual(autopause.__text__(), "Next Track")
|
||||
|
||||
autopause.Count = 2
|
||||
self.assertEqual(autopause.__text__(), "2 Tracks")
|
||||
|
||||
def test_pause_icon(self):
|
||||
self.assertEqual(autopause.__icon__(), "media-playback-pause")
|
||||
|
||||
autopause.Playing = True
|
||||
self.assertEqual(autopause.__icon__(), "media-playback-start")
|
||||
|
||||
autopause.Count = 0
|
||||
self.assertEqual(autopause.__icon__(), "media-playback-pause")
|
||||
|
||||
def test_pause_estimate(self):
|
||||
curds.playlist.lookup("Libraries").lookup(test_album)
|
||||
curds.playlist.library.join()
|
||||
|
||||
playbin.set_uri(test_track, Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
|
||||
autopause.Count = 0
|
||||
self.assertEqual(autopause.__estimate__(), "About 10 seconds")
|
||||
playbin.seek_step(6)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(autopause.__estimate__(), "About 5 seconds")
|
||||
|
||||
autopause.Count = 10
|
||||
self.assertEqual(autopause.__estimate__(), "About 1 minute")
|
||||
autopause.Count = 20
|
||||
self.assertEqual(autopause.__estimate__(), "About 1 minute 55 seconds")
|
||||
autopause.Count = 30
|
||||
self.assertEqual(autopause.__estimate__(), "About 2 minutes 50 seconds")
|
||||
|
||||
def test_pause_popover(self):
|
||||
playbin.set_uri(test_track, Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
|
||||
autopause.Popover.show()
|
||||
autopause.__popup__()
|
||||
gtk.main_loop(iteration_delay=0.02)
|
||||
self.assertFalse(autopause.Popover.is_visible())
|
||||
|
||||
autopause.Count = 0
|
||||
autopause.__popup__()
|
||||
self.assertTrue(autopause.Popover.is_visible())
|
||||
self.assertEqual(autopause.Label.get_text(), "About 10 seconds")
|
||||
|
||||
self.assertIsNotNone(autopause.PopCB)
|
||||
gtk.timeout_loop(5.75, iteration_delay=0.05)
|
||||
self.assertIsNone(autopause.PopCB)
|
||||
self.assertFalse(autopause.Popover.is_visible())
|
||||
|
||||
autopause.__popup__()
|
||||
cb_id = autopause.PopCB
|
||||
autopause.__popup__()
|
||||
self.assertNotEqual(autopause.PopCB, cb_id)
|
|
@ -1,106 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import playbin
|
||||
from .. import node
|
||||
import curds
|
||||
import os
|
||||
import pathlib
|
||||
import unittest
|
||||
from gi.repository import GLib, Gst
|
||||
|
||||
test_album = pathlib.Path("./trier/Test Library/Test Artist 01/Test Album 1")
|
||||
test_track = test_album / "10 - Test Track 10.ogg"
|
||||
|
||||
class TestPlaybin(unittest.TestCase):
|
||||
def setUp(self):
|
||||
curds.reset()
|
||||
playbin.reset()
|
||||
|
||||
def test_playbin_init(self):
|
||||
self.assertIsInstance(playbin.Playbin, Gst.Element)
|
||||
self.assertIsInstance(playbin.Fakesink, Gst.Element)
|
||||
self.assertIsInstance(playbin.Bus, Gst.Bus)
|
||||
|
||||
self.assertEqual(playbin.get_property("video-sink"), playbin.Fakesink)
|
||||
|
||||
def test_playbin_set_uri(self):
|
||||
playbin.set_uri(test_track)
|
||||
uri = GLib.filename_to_uri(str(test_track.absolute()))
|
||||
self.assertEqual(playbin.get_property("uri"), uri)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.NULL)
|
||||
|
||||
playbin.set_uri(test_track, Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
|
||||
def test_playbin_volume(self):
|
||||
self.assertEqual(playbin.get_property("volume"), 1.0)
|
||||
playbin.set_volume(50)
|
||||
self.assertEqual(playbin.get_property("volume"), 0.5)
|
||||
playbin.set_volume(0)
|
||||
self.assertEqual(playbin.get_property("volume"), 0.0)
|
||||
|
||||
def test_playbin_play(self):
|
||||
playbin.set_uri(test_track)
|
||||
self.assertEqual(playbin.play(), Gst.StateChangeReturn.ASYNC)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PLAYING)
|
||||
|
||||
def test_playbin_pause(self):
|
||||
playbin.set_uri(test_track)
|
||||
self.assertEqual(playbin.pause(), Gst.StateChangeReturn.ASYNC)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
|
||||
def test_playbin_duration(self):
|
||||
self.assertEqual(playbin.duration(), 0)
|
||||
playbin.set_uri(test_track, Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.duration(), 10 * Gst.SECOND)
|
||||
|
||||
def test_playbin_seek_percent(self):
|
||||
self.assertEqual(playbin.position(), 0)
|
||||
self.assertEqual(playbin.progress(), 0)
|
||||
|
||||
playbin.set_uri(test_track, Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.position(), 0)
|
||||
self.assertEqual(playbin.progress(), 0)
|
||||
|
||||
self.assertTrue(playbin.seek_percent(50))
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.position(), 5 * Gst.SECOND)
|
||||
self.assertEqual(playbin.progress(), 50)
|
||||
|
||||
self.assertTrue(playbin.seek_percent(75))
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.position(), 7.5 * Gst.SECOND)
|
||||
self.assertEqual(playbin.progress(), 75)
|
||||
|
||||
self.assertTrue(playbin.seek_percent(25))
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.position(), 2.5 * Gst.SECOND)
|
||||
self.assertEqual(playbin.progress(), 25)
|
||||
|
||||
def test_playbin_seek_step(self):
|
||||
self.assertEqual(playbin.remaining(), 0)
|
||||
|
||||
playbin.set_uri(test_track, Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.remaining(), 10 * Gst.SECOND)
|
||||
|
||||
self.assertTrue(playbin.seek_step(5))
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.remaining(), 5 * Gst.SECOND)
|
||||
|
||||
self.assertTrue(playbin.seek_step(2.5))
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.remaining(), 2.5 * Gst.SECOND)
|
||||
|
||||
self.assertTrue(playbin.seek_step(-5))
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.remaining(), 7.5 * Gst.SECOND)
|
||||
|
||||
self.assertTrue(playbin.seek_step(-15))
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.remaining(), 10 * Gst.SECOND)
|
||||
|
||||
self.assertTrue(playbin.seek_step(15))
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.remaining(), 0)
|
|
@ -1,228 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import autopause
|
||||
from . import playbin
|
||||
from . import widgets
|
||||
from .. import gtk
|
||||
import curds
|
||||
import os
|
||||
import pathlib
|
||||
import unittest
|
||||
from gi.repository import Gdk, Gtk, Gst
|
||||
|
||||
test_album = pathlib.Path("./trier/Test Library/Test Artist 01/Test Album 1")
|
||||
test_track = test_album / "10 - Test Track 10.ogg"
|
||||
|
||||
class TestAudioWidgets(unittest.TestCase):
|
||||
def setUp(self):
|
||||
curds.reset()
|
||||
autopause.reset()
|
||||
playbin.reset()
|
||||
widgets.reset()
|
||||
|
||||
def test_widgets_init(self):
|
||||
self.assertIsInstance(widgets.Artist, Gtk.Label)
|
||||
self.assertIsInstance(widgets.Next, Gtk.Button)
|
||||
self.assertIsInstance(widgets.Pause, Gtk.Button)
|
||||
self.assertIsInstance(widgets.Play, Gtk.Button)
|
||||
self.assertIsInstance(widgets.Position, Gtk.Label)
|
||||
self.assertIsInstance(widgets.Previous, Gtk.Button)
|
||||
self.assertIsInstance(widgets.Progress, Gtk.Scale)
|
||||
self.assertIsInstance(widgets.Remaining, Gtk.Label)
|
||||
self.assertIsInstance(widgets.Title, Gtk.Label)
|
||||
self.assertIsInstance(widgets.Volume, Gtk.VolumeButton)
|
||||
|
||||
self.assertFalse(widgets.Pause.is_visible())
|
||||
self.assertTrue( widgets.Play.is_visible())
|
||||
|
||||
self.assertEqual(widgets.SEEK_STEP, 10)
|
||||
|
||||
def test_volume_button(self):
|
||||
self.assertEqual(playbin.get_property("volume"), 1.0)
|
||||
self.assertEqual(widgets.Volume.get_value(), 100.0)
|
||||
|
||||
widgets.Volume.set_value(50)
|
||||
self.assertEqual(playbin.get_property("volume"), 0.5)
|
||||
self.assertEqual(widgets.Volume.get_value(), 50.0)
|
||||
|
||||
widgets.Volume.set_value(0)
|
||||
self.assertEqual(playbin.get_property("volume"), 0.0)
|
||||
self.assertEqual(widgets.Volume.get_value(), 0.0)
|
||||
|
||||
def test_play_pause_buttons(self):
|
||||
playbin.set_uri(test_track)
|
||||
|
||||
widgets.Play.clicked()
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PLAYING)
|
||||
gtk.main_loop()
|
||||
self.assertFalse(widgets.Play.is_visible())
|
||||
self.assertTrue( widgets.Pause.is_visible())
|
||||
self.assertTrue( autopause.Playing)
|
||||
|
||||
widgets.Pause.clicked()
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
gtk.main_loop()
|
||||
self.assertTrue( widgets.Play.is_visible())
|
||||
self.assertFalse(widgets.Pause.is_visible())
|
||||
self.assertFalse( autopause.Playing)
|
||||
|
||||
def test_next_previous_buttons(self):
|
||||
widgets.Next.clicked()
|
||||
self.assertEqual(playbin.get_state(), Gst.State.NULL)
|
||||
self.assertEqual(curds.playlist.Track, None)
|
||||
|
||||
widgets.Previous.clicked()
|
||||
self.assertEqual(playbin.get_state(), Gst.State.NULL)
|
||||
self.assertEqual(curds.playlist.Track, None)
|
||||
|
||||
curds.playlist.lookup("Libraries").lookup(test_album)
|
||||
curds.playlist.library.join()
|
||||
gtk.notify_loop()
|
||||
|
||||
widgets.Next.clicked()
|
||||
self.assertNotEqual(curds.playlist.Track, None)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PLAYING)
|
||||
track = curds.playlist.Track
|
||||
|
||||
widgets.Next.clicked()
|
||||
gtk.main_loop(delay=0.1)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PLAYING)
|
||||
self.assertNotEqual(curds.playlist.Track, track)
|
||||
track2 = curds.playlist.Track
|
||||
|
||||
widgets.Previous.clicked()
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PLAYING)
|
||||
self.assertNotEqual(curds.playlist.Track, track2)
|
||||
|
||||
track3 = curds.playlist.next()
|
||||
widgets.NEXT_TRACK = track3
|
||||
widgets.Next.clicked()
|
||||
gtk.main_loop(delay=0.2)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PLAYING)
|
||||
self.assertEqual(curds.playlist.Track, track3)
|
||||
|
||||
def test_seek_scale(self):
|
||||
playbin.set_uri(test_track, Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.position(), 0.0)
|
||||
|
||||
widgets.Progress.emit("change-value", Gtk.ScrollType.JUMP, 50)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.position(), 5 * Gst.SECOND)
|
||||
|
||||
def test_seek_step(self):
|
||||
widgets.SEEK_STEP = 5
|
||||
event = Gdk.EventKey.new(Gdk.EventType.KEY_PRESS)
|
||||
|
||||
playbin.set_uri(test_track, Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.position(), 0.0)
|
||||
|
||||
event.keyval = Gdk.keyval_from_name("Right")
|
||||
gtk.Window.emit("key-press-event", event)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.position(), 0.0)
|
||||
|
||||
event.state |= Gdk.ModifierType.CONTROL_MASK
|
||||
gtk.Window.emit("key-press-event", event)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.position(), 5 * Gst.SECOND)
|
||||
|
||||
widgets.SEEK_STEP = 2
|
||||
event.keyval = Gdk.keyval_from_name("Left")
|
||||
gtk.Window.emit("key-press-event", event)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.position(), 3 * Gst.SECOND)
|
||||
|
||||
def test_timestr(self):
|
||||
self.assertEqual(widgets.timestr( 2 * Gst.SECOND), "00:02")
|
||||
self.assertEqual(widgets.timestr( 42 * Gst.SECOND), "00:42")
|
||||
self.assertEqual(widgets.timestr( 162 * Gst.SECOND), "02:42")
|
||||
self.assertEqual(widgets.timestr(2562 * Gst.SECOND), "42:42")
|
||||
|
||||
def test_position(self):
|
||||
playbin.set_uri(test_track, Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
|
||||
gtk.main_loop(delay=0.1)
|
||||
self.assertEqual(widgets.Position.get_text(), "00:00")
|
||||
self.assertEqual(widgets.Remaining.get_text(), "00:10")
|
||||
self.assertEqual(widgets.Progress.get_adjustment().get_value(), 0.0)
|
||||
|
||||
playbin.seek_step(3)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
gtk.main_loop(delay=0.1)
|
||||
|
||||
self.assertEqual(widgets.Position.get_text(), "00:03")
|
||||
self.assertEqual(widgets.Remaining.get_text(), "00:07")
|
||||
self.assertEqual(widgets.Progress.get_adjustment().get_value(), 30.0)
|
||||
|
||||
def test_tags(self):
|
||||
self.assertEqual(widgets.Title.get_text(), "Emmental")
|
||||
self.assertEqual(widgets.Artist.get_text(), "The Cheesy Music Player")
|
||||
|
||||
playbin.set_uri(test_track, Gst.State.PAUSED)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
gtk.main_loop()
|
||||
|
||||
self.assertEqual(widgets.Title.get_text(), "Test Track 10")
|
||||
self.assertEqual(widgets.Artist.get_text(), "by Test Artist 01")
|
||||
|
||||
playbin.Playbin.set_state(Gst.State.READY)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.READY)
|
||||
gtk.main_loop()
|
||||
|
||||
self.assertEqual(widgets.Title.get_text(), "Emmental")
|
||||
self.assertEqual(widgets.Artist.get_text(), "The Cheesy Music Player")
|
||||
|
||||
def on_stream_start(self):
|
||||
self.stream_start += 1
|
||||
|
||||
def test_stream_start(self):
|
||||
self.stream_start = 0
|
||||
widgets.NEXT_TRACK = 42
|
||||
curds.notify.register("stream-start", self.on_stream_start)
|
||||
|
||||
playbin.set_uri(test_track, Gst.State.PLAYING)
|
||||
gtk.main_loop(delay=0.1)
|
||||
self.assertEqual(self.stream_start, 1)
|
||||
self.assertIsNone(widgets.NEXT_TRACK)
|
||||
|
||||
def test_eos(self):
|
||||
curds.playlist.lookup("Libraries").lookup(test_album)
|
||||
curds.playlist.library.join()
|
||||
|
||||
track = curds.playlist.next()
|
||||
widgets.on_message(playbin.Bus, Gst.Message.new_eos())
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PLAYING)
|
||||
self.assertNotEqual(curds.playlist.Track, track)
|
||||
|
||||
track = curds.playlist.Track
|
||||
autopause.Count = 0
|
||||
widgets.on_message(playbin.Bus, Gst.Message.new_eos())
|
||||
self.assertEqual(autopause.Count, -1)
|
||||
self.assertEqual(playbin.get_state(), Gst.State.PAUSED)
|
||||
self.assertNotEqual(curds.playlist.Track, track)
|
||||
|
||||
def test_about_to_finish(self):
|
||||
curds.playlist.lookup("Libraries").lookup(test_album)
|
||||
curds.playlist.library.join()
|
||||
|
||||
track = curds.playlist.next()
|
||||
playbin.set_uri(track.filepath, Gst.State.PLAYING)
|
||||
|
||||
widgets.on_about_to_finish(playbin.Playbin)
|
||||
self.assertNotEqual( widgets.NEXT_TRACK, track)
|
||||
self.assertIsNotNone(widgets.NEXT_TRACK)
|
||||
|
||||
track = widgets.NEXT_TRACK
|
||||
autopause.Count = 1
|
||||
widgets.on_about_to_finish(playbin.Playbin)
|
||||
self.assertEqual(autopause.Count, 0)
|
||||
self.assertNotEqual( widgets.NEXT_TRACK, track)
|
||||
self.assertIsNotNone(widgets.NEXT_TRACK)
|
||||
|
||||
track = widgets.NEXT_TRACK
|
||||
widgets.NEXT_TRACK = None
|
||||
widgets.on_about_to_finish(playbin.Playbin)
|
||||
self.assertEqual(autopause.Count, 0)
|
||||
self.assertIsNone(widgets.NEXT_TRACK)
|
|
@ -1,161 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import artwork
|
||||
from . import autopause
|
||||
from . import playbin
|
||||
from .. import gtk
|
||||
import curds
|
||||
from gi.repository import Gdk, GLib, Gst
|
||||
|
||||
NEXT_TRACK = None
|
||||
SEEK_STEP = 10
|
||||
|
||||
|
||||
def on_next(button):
|
||||
track = NEXT_TRACK
|
||||
if track == None:
|
||||
track = curds.playlist.next()
|
||||
if track != None:
|
||||
playbin.set_uri(track.filepath, Gst.State.PLAYING)
|
||||
|
||||
Next = gtk.Builder.get_object("next_button")
|
||||
Next.connect("clicked", on_next)
|
||||
Next.connect("can-activate-accel", gtk.can_activate_accel)
|
||||
|
||||
def on_previous(button):
|
||||
track = curds.playlist.previous()
|
||||
if track != None:
|
||||
playbin.set_uri(track.filepath, Gst.State.PLAYING)
|
||||
|
||||
Previous = gtk.Builder.get_object("previous_button")
|
||||
Previous.connect("clicked", on_previous)
|
||||
Previous.connect("can-activate-accel", gtk.can_activate_accel)
|
||||
|
||||
def on_play(button):
|
||||
playbin.play()
|
||||
|
||||
Play = gtk.Builder.get_object("play_button")
|
||||
Play.connect("clicked", on_play)
|
||||
Play.connect("can-activate-accel", gtk.can_activate_accel)
|
||||
|
||||
def on_pause(button):
|
||||
playbin.pause()
|
||||
|
||||
Pause = gtk.Builder.get_object("pause_button")
|
||||
Pause.connect("clicked", on_pause)
|
||||
Pause.connect("can-activate-accel", gtk.can_activate_accel)
|
||||
|
||||
def on_seek(range, scroll, value):
|
||||
playbin.seek_percent(value)
|
||||
|
||||
Progress = gtk.Builder.get_object("progress_scale")
|
||||
Progress.connect("change-value", on_seek)
|
||||
|
||||
def on_key_press(widget, event):
|
||||
ctrl = event.state & Gdk.ModifierType.CONTROL_MASK
|
||||
key = Gdk.keyval_name(event.keyval)
|
||||
if ctrl and key in ("Left", "Right"):
|
||||
step = SEEK_STEP if key == "Right" else -SEEK_STEP
|
||||
playbin.seek_step(step)
|
||||
return True
|
||||
return False
|
||||
|
||||
gtk.Window.connect("key-press-event", on_key_press)
|
||||
|
||||
def on_volume_changed(button, value):
|
||||
playbin.set_volume(value)
|
||||
|
||||
Volume = gtk.Builder.get_object("volume_button")
|
||||
Volume.connect("value-changed", on_volume_changed)
|
||||
|
||||
|
||||
Title = gtk.Builder.get_object("title")
|
||||
Artist = gtk.Builder.get_object("artist")
|
||||
|
||||
def on_eos(message):
|
||||
track = curds.playlist.next()
|
||||
if track != None:
|
||||
state = autopause.decrement()
|
||||
playbin.set_uri(track.filepath, state)
|
||||
|
||||
def on_state_change(message):
|
||||
if message.src == playbin.Playbin:
|
||||
(old, new, pending) = message.parse_state_changed()
|
||||
Play.set_visible( new != Gst.State.PLAYING)
|
||||
Pause.set_visible(new == Gst.State.PLAYING)
|
||||
if new != Gst.State.PLAYING and new != Gst.State.PAUSED:
|
||||
set_tag_markup(Title, "Emmental")
|
||||
set_tag_markup(Artist, "The Cheesy Music Player")
|
||||
autopause.state_changed(new)
|
||||
|
||||
def on_stream_start(message):
|
||||
global NEXT_TRACK
|
||||
NEXT_TRACK = None
|
||||
curds.notify.notify("stream-start")
|
||||
|
||||
def set_tag_markup(label, text):
|
||||
markup = GLib.markup_escape_text(text)
|
||||
label.set_markup(f"<big>{markup}</big>")
|
||||
|
||||
def on_each_tag(taglist, name):
|
||||
if name == "image":
|
||||
(res, sample) = taglist.get_sample(name)
|
||||
buffer = sample.get_buffer()
|
||||
(res, map) = buffer.map(Gst.MapFlags.READ)
|
||||
artwork.set_image(map.data)
|
||||
buffer.unmap(map)
|
||||
elif name in [ "artist", "title" ]:
|
||||
(valid, value) = taglist.get_string(name)
|
||||
if valid and name == "artist":
|
||||
set_tag_markup(Artist, f"by {value}")
|
||||
elif valid and name == "title":
|
||||
set_tag_markup(Title, value)
|
||||
|
||||
def on_message(bus, message):
|
||||
if message.type == Gst.MessageType.EOS:
|
||||
on_eos(message)
|
||||
elif message.type == Gst.MessageType.STATE_CHANGED:
|
||||
on_state_change(message)
|
||||
elif message.type == Gst.MessageType.STREAM_START:
|
||||
on_stream_start(message)
|
||||
elif message.type == Gst.MessageType.TAG:
|
||||
message.parse_tag().foreach(on_each_tag)
|
||||
|
||||
playbin.Bus.connect("message", on_message)
|
||||
|
||||
|
||||
def on_about_to_finish(pbin):
|
||||
global NEXT_TRACK
|
||||
if not autopause.about_to_pause():
|
||||
NEXT_TRACK = curds.playlist.next()
|
||||
if NEXT_TRACK:
|
||||
playbin.set_uri(NEXT_TRACK.filepath)
|
||||
autopause.decrement()
|
||||
|
||||
playbin.Playbin.connect("about-to-finish", on_about_to_finish)
|
||||
|
||||
|
||||
Position = gtk.Builder.get_object("position")
|
||||
Remaining = gtk.Builder.get_object("remaining")
|
||||
|
||||
def timestr(nanoseconds):
|
||||
(m, s) = divmod(round(nanoseconds / Gst.SECOND), 60)
|
||||
return f"{m:02}:{s:02}"
|
||||
|
||||
def on_timeout():
|
||||
Position.set_text( timestr(playbin.position()))
|
||||
Remaining.set_text(timestr(playbin.remaining()))
|
||||
Progress.get_adjustment().set_value(playbin.progress())
|
||||
return GLib.SOURCE_CONTINUE
|
||||
GLib.timeout_add(100, on_timeout)
|
||||
|
||||
|
||||
def reset():
|
||||
global NEXT_TRACK
|
||||
global SEEK_STEP
|
||||
set_tag_markup(Title, "Emmental")
|
||||
set_tag_markup(Artist, "The Cheesy Music Player")
|
||||
Play.show()
|
||||
Pause.hide()
|
||||
Volume.set_value(100)
|
||||
NEXT_TRACK = None
|
||||
SEEK_STEP = 10
|
76
rind/gtk.py
76
rind/gtk.py
|
@ -1,76 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
import curds
|
||||
import gi
|
||||
import time
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk, GLib
|
||||
|
||||
Builder = Gtk.Builder()
|
||||
Builder.add_from_file("emmental.ui")
|
||||
|
||||
UpArrow = Builder.get_object("up_arrow")
|
||||
DownArrow = Builder.get_object("down_arrow")
|
||||
ShowMore = Builder.get_object("show_more")
|
||||
Window = Builder.get_object("window")
|
||||
|
||||
class EmmentalApplication(Gtk.Application):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, application_id="org.gtk.emmental", **kwargs)
|
||||
self.window = None
|
||||
self.idle_id = None
|
||||
|
||||
def do_activate(self):
|
||||
if self.window == None:
|
||||
self.window = Window
|
||||
self.add_window(self.window)
|
||||
self.window.present()
|
||||
|
||||
def do_shutdown(self):
|
||||
Gtk.Application.do_shutdown(self)
|
||||
if self.idle_id != None:
|
||||
GLib.source_remove(self.idle_id)
|
||||
self.idle_id = None
|
||||
curds.stop()
|
||||
|
||||
def do_startup(self):
|
||||
Gtk.Application.do_startup(self)
|
||||
|
||||
|
||||
def can_activate_accel(widget, signal):
|
||||
widget.stop_emission_by_name("can-activate-accel")
|
||||
return widget.is_visible() and widget.is_sensitive() and \
|
||||
not type_focused(Gtk.Entry)
|
||||
|
||||
def can_activate_entry(widget, signal):
|
||||
widget.stop_emission_by_name("can-activate-accel")
|
||||
active = not widget.is_focus()
|
||||
ShowMore.set_active(active)
|
||||
return active
|
||||
|
||||
def main_loop(delay=0.0, iteration_delay=0.0):
|
||||
time.sleep(delay)
|
||||
while Gtk.events_pending():
|
||||
time.sleep(iteration_delay)
|
||||
Gtk.main_iteration_do(True)
|
||||
|
||||
def notify_loop(delay=0.0, iteration_delay=0.0):
|
||||
main_loop(delay, iteration_delay)
|
||||
|
||||
def timeout_loop(timeout, iteration_delay=0.0):
|
||||
timeout = time.time() + timeout
|
||||
while time.time() <= timeout:
|
||||
time.sleep(iteration_delay)
|
||||
main_loop()
|
||||
|
||||
def type_focused(type):
|
||||
return isinstance(Window.get_focus(), type)
|
||||
|
||||
def show_more_toggled(self, *args):
|
||||
active = ShowMore.get_active()
|
||||
UpArrow.set_visible(active)
|
||||
DownArrow.set_visible(not active)
|
||||
curds.notify.notify("show-more", active)
|
||||
|
||||
ShowMore.connect("toggled", show_more_toggled)
|
||||
|
||||
Application = EmmentalApplication()
|
|
@ -1,32 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import gtk
|
||||
import curds
|
||||
import os
|
||||
|
||||
Chooser = gtk.Builder.get_object("library_chooser")
|
||||
Popover = gtk.Builder.get_object("library_popover")
|
||||
|
||||
|
||||
def add_clicked(button):
|
||||
folder = os.path.join(os.path.expanduser("~"), "Music")
|
||||
Chooser.set_filename(folder)
|
||||
Popover.popup()
|
||||
|
||||
Add = gtk.Builder.get_object("library_add")
|
||||
Add.connect("clicked", add_clicked)
|
||||
|
||||
|
||||
def cancel_clicked(button):
|
||||
Popover.popdown()
|
||||
|
||||
Cancel = gtk.Builder.get_object("library_cancel")
|
||||
Cancel.connect("clicked", cancel_clicked)
|
||||
|
||||
|
||||
def ok_clicked(button):
|
||||
Popover.popdown()
|
||||
path = Chooser.get_filename()
|
||||
curds.playlist.lookup("Libraries").lookup(path)
|
||||
|
||||
Ok = gtk.Builder.get_object("library_ok")
|
||||
Ok.connect("clicked", ok_clicked)
|
|
@ -1,9 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker
|
||||
from . import model
|
||||
from . import view
|
||||
|
||||
Model = model.NodeTreeModel
|
||||
View = view.TreeView
|
||||
|
||||
def reset():
|
||||
view.reset()
|
|
@ -1,107 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from .. import gtk
|
||||
from .. import playlist
|
||||
import curds
|
||||
import os
|
||||
from gi.repository import GObject, Gtk, GLib
|
||||
|
||||
Treeview = gtk.Builder.get_object("node_treeview")
|
||||
|
||||
|
||||
class NodeTreeModel(GObject.GObject, Gtk.TreeModel):
|
||||
def __init__(self):
|
||||
GObject.GObject.__init__(self)
|
||||
self.height = 0
|
||||
self.reset()
|
||||
|
||||
def do_get_column_type(self, col):
|
||||
if col == 2:
|
||||
return int
|
||||
return str
|
||||
|
||||
def do_get_iter(self, path):
|
||||
plist = curds.playlist.Root.lookup_path(path.get_indices())
|
||||
if plist == None or plist.parent == None:
|
||||
return (False, None)
|
||||
return (True, self.playlist_iter(plist))
|
||||
|
||||
def do_get_n_columns(self):
|
||||
return 3
|
||||
|
||||
def do_get_path(self, iter):
|
||||
plist = self.iter_playlist(iter)
|
||||
return Gtk.TreePath(plist.get_path())
|
||||
|
||||
def do_get_node_height(self, plist, column):
|
||||
area = Treeview.get_cell_area(Gtk.TreePath(0), Treeview.get_column(1))
|
||||
self.height = max(self.height, area.height)
|
||||
return self.height
|
||||
|
||||
def do_get_value(self, iter, column):
|
||||
plist = self.iter_playlist(iter)
|
||||
if column == 0:
|
||||
return plist.icon
|
||||
elif column == 1:
|
||||
if plist == curds.playlist.current():
|
||||
return f"<b>{plist.get_markup()}</b>"
|
||||
return plist.get_markup()
|
||||
|
||||
if isinstance(plist, curds.Playlist):
|
||||
return -1
|
||||
return self.do_get_node_height(plist, column)
|
||||
|
||||
def do_iter_children(self, parent):
|
||||
return self.do_iter_nth_child(parent, 0)
|
||||
|
||||
def do_iter_has_child(self, iter):
|
||||
return self.iter_n_children(iter) > 0
|
||||
|
||||
def do_iter_n_children(self, iter):
|
||||
if (plist := self.iter_playlist(iter)):
|
||||
return plist.n_children()
|
||||
return 0
|
||||
|
||||
def do_iter_nth_child(self, iter, n):
|
||||
if (plist := self.iter_playlist(iter)) != None:
|
||||
if (child := plist.nth_child(n)) != None:
|
||||
return (True, self.playlist_iter(child))
|
||||
return (False, None)
|
||||
|
||||
def do_iter_next(self, iter):
|
||||
if (plist := self.iter_playlist(iter)) != None:
|
||||
if (next := plist.next_child()) != None:
|
||||
iter.user_data = id(next)
|
||||
return (True, iter)
|
||||
return (False, None)
|
||||
|
||||
def do_iter_parent(self, child):
|
||||
plist = self.iter_playlist(child)
|
||||
if plist != None and plist.parent != None:
|
||||
if plist.parent != curds.playlist.Root:
|
||||
return (True, self.playlist_iter(plist.parent))
|
||||
return (False, None)
|
||||
|
||||
def iter_playlist(self, iter):
|
||||
if iter and iter.user_data > 0:
|
||||
return curds.playlist.Root.lookup_byid(iter.user_data)
|
||||
return curds.playlist.Root
|
||||
|
||||
def on_first_child(self, parent, path):
|
||||
if parent.parent != None:
|
||||
self.row_has_child_toggled(Gtk.TreePath(path), self.playlist_iter(parent))
|
||||
|
||||
def on_playlist_changed(self, plist):
|
||||
if plist.parent:
|
||||
path = Gtk.TreePath(plist.get_path())
|
||||
self.row_changed(path, self.playlist_iter(plist))
|
||||
|
||||
def playlist_iter(self, plist):
|
||||
if plist == None or plist.parent == None:
|
||||
return None
|
||||
iter = Gtk.TreeIter()
|
||||
iter.user_data = id(plist)
|
||||
return iter
|
||||
|
||||
def reset(self):
|
||||
curds.notify.register("first-child", self.on_first_child, idle=True)
|
||||
curds.notify.register("playlist-changed", self.on_playlist_changed, idle=True)
|
|
@ -1,169 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import model
|
||||
from .. import gtk
|
||||
from .. import playlist
|
||||
import curds
|
||||
import os
|
||||
import pathlib
|
||||
import time
|
||||
import trackdb
|
||||
import unittest
|
||||
from gi.repository import Gtk, GObject, GLib
|
||||
|
||||
test_album = pathlib.Path("./trier/Test Library/Test Artist 02/")
|
||||
|
||||
class TestNodeTreeModel(unittest.TestCase):
|
||||
def setUp(self):
|
||||
curds.reset()
|
||||
|
||||
self.model = model.NodeTreeModel()
|
||||
self.artist = curds.playlist.lookup("Artists")
|
||||
self.decade = curds.playlist.lookup("Decades")
|
||||
self.genre = curds.playlist.lookup("Genres")
|
||||
self.lib = trackdb.add_path(test_album)
|
||||
self.lib.add_track("Test Album 1", "01 - Test Track 01.ogg")
|
||||
self.lib.add_track("Test Album 2", "01 - Test Track 01.ogg")
|
||||
|
||||
def tearDown(self):
|
||||
curds.notify.cancel("first-child", self.model.on_first_child)
|
||||
curds.notify.cancel("playlist-changed", self.model.on_playlist_changed)
|
||||
|
||||
def tearDownClass():
|
||||
curds.stop()
|
||||
|
||||
def test_model_init(self):
|
||||
self.assertEqual(model.Treeview, gtk.Builder.get_object("node_treeview"))
|
||||
|
||||
self.assertEqual(self.model.get_n_columns(), 3)
|
||||
self.assertEqual(self.model.get_flags(), 0)
|
||||
|
||||
self.assertEqual(self.model.get_column_type(0), GObject.GType(str))
|
||||
self.assertEqual(self.model.get_column_type(1), GObject.GType(str))
|
||||
self.assertEqual(self.model.get_column_type(2), GObject.GType(int))
|
||||
|
||||
def test_model_get_iter(self):
|
||||
path = Gtk.TreePath.new_from_indices([ curds.playlist.Root.n_children() ])
|
||||
self.assertRaises(ValueError, self.model.get_iter, path)
|
||||
|
||||
for node in list(curds.playlist.Root.walk()):
|
||||
path = Gtk.TreePath(node.get_path())
|
||||
iter = self.model.get_iter(path)
|
||||
self.assertEqual(iter.user_data, id(node))
|
||||
|
||||
def test_model_get_path(self):
|
||||
iter = Gtk.TreeIter()
|
||||
|
||||
for node in list(curds.playlist.Root.walk()):
|
||||
iter.user_data = id(node)
|
||||
path = self.model.get_path(iter)
|
||||
self.assertEqual(path.get_indices(), node.get_path())
|
||||
|
||||
def test_model_get_value(self):
|
||||
col = model.Treeview.get_column(1)
|
||||
rect = model.Treeview.get_cell_area(Gtk.TreePath(0), col)
|
||||
iter = Gtk.TreeIter()
|
||||
|
||||
for child in curds.playlist.Root.walk():
|
||||
iter.user_data = id(child)
|
||||
text = child.get_markup()
|
||||
height = rect.height
|
||||
if child == curds.playlist.Current[0]:
|
||||
text = f"<b>{text}</b>"
|
||||
if isinstance(child, curds.Playlist):
|
||||
height = -1
|
||||
|
||||
self.assertEqual(self.model.get_value(iter, 0), child.icon)
|
||||
self.assertEqual(self.model.get_value(iter, 1), text)
|
||||
self.assertEqual(self.model.get_value(iter, 2), height)
|
||||
|
||||
def test_model_iter_children(self):
|
||||
iter = self.model.iter_children(None)
|
||||
self.assertEqual(iter.user_data, id(curds.playlist.Root.nth_child(0)))
|
||||
|
||||
for node in list(curds.playlist.Root.walk()):
|
||||
iter.user_data = id(node)
|
||||
if node.n_children() == 0:
|
||||
self.assertIsNone(self.model.iter_children(iter))
|
||||
else:
|
||||
child = self.model.iter_children(iter)
|
||||
self.assertEqual(child.user_data, id(node.nth_child(0)))
|
||||
|
||||
iter.user_data = 42
|
||||
self.assertIsNone(self.model.iter_children(iter))
|
||||
|
||||
def test_model_iter_has_child(self):
|
||||
iter = Gtk.TreeIter()
|
||||
|
||||
for node in list(curds.playlist.Root.walk()):
|
||||
iter.user_data = id(node)
|
||||
if node.n_children() > 0:
|
||||
self.assertTrue(self.model.iter_has_child(iter))
|
||||
else:
|
||||
self.assertFalse(self.model.iter_has_child(iter))
|
||||
|
||||
iter.user_data = 42
|
||||
self.assertFalse(self.model.iter_has_child(iter))
|
||||
|
||||
def test_model_iter_n_children(self):
|
||||
iter = Gtk.TreeIter()
|
||||
self.assertEqual(self.model.iter_n_children(None), curds.playlist.Root.n_children())
|
||||
|
||||
for node in list(curds.playlist.Root.walk()):
|
||||
iter.user_data = id(node)
|
||||
self.assertEqual(self.model.iter_n_children(iter), node.n_children())
|
||||
|
||||
iter.user_data = 42
|
||||
self.assertEqual(self.model.iter_n_children(iter), 0)
|
||||
|
||||
def test_model_iter_nth_child(self):
|
||||
iter = Gtk.TreeIter()
|
||||
|
||||
for node in [ curds.playlist.Root ] + list(curds.playlist.Root.walk()):
|
||||
iter = self.model.playlist_iter(node)
|
||||
for i, child in enumerate(node.children):
|
||||
citer = self.model.iter_nth_child(iter, i)
|
||||
self.assertEqual(citer.user_data, id(child))
|
||||
self.assertIsNone(self.model.iter_nth_child(iter, node.n_children()))
|
||||
|
||||
def test_model_iter_next(self):
|
||||
iter = Gtk.TreeIter()
|
||||
|
||||
for node in list(curds.playlist.Root.walk()):
|
||||
iter.user_data = id(node)
|
||||
next = self.model.iter_next(iter)
|
||||
if node.next_child() != None:
|
||||
self.assertEqual(next.user_data, id(node.next_child()))
|
||||
else:
|
||||
self.assertIsNone(next)
|
||||
|
||||
def test_model_iter_parent(self):
|
||||
iter = Gtk.TreeIter()
|
||||
|
||||
for node in curds.playlist.Root.walk():
|
||||
iter.user_data = id(node)
|
||||
if node.parent != curds.playlist.Root:
|
||||
parent = self.model.iter_parent(iter)
|
||||
self.assertEqual(parent.user_data, id(node.parent))
|
||||
else:
|
||||
self.assertIsNone(self.model.iter_parent(iter))
|
||||
|
||||
def test_model_iter_playlist(self):
|
||||
iter = Gtk.TreeIter()
|
||||
|
||||
self.assertEqual(self.model.iter_playlist(None), curds.playlist.Root)
|
||||
self.assertEqual(self.model.iter_playlist(iter), curds.playlist.Root)
|
||||
|
||||
for node in curds.playlist.Root.walk():
|
||||
iter.user_data = id(node)
|
||||
self.assertEqual(self.model.iter_playlist(iter), node)
|
||||
|
||||
iter.user_data = 42
|
||||
self.assertIsNone(self.model.iter_playlist(iter))
|
||||
|
||||
def test_model_playlist_iter(self):
|
||||
self.assertIsNone(self.model.playlist_iter(None))
|
||||
self.assertIsNone(self.model.playlist_iter(curds.playlist.Root))
|
||||
|
||||
for node in curds.playlist.Root.walk():
|
||||
iter = self.model.playlist_iter(node)
|
||||
self.assertEqual(iter.user_data, id(node))
|
|
@ -1,174 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import model
|
||||
from . import view
|
||||
from .. import gtk
|
||||
from .. import playlist
|
||||
import curds
|
||||
import os
|
||||
import re
|
||||
import unittest
|
||||
from gi.repository import Gtk, GObject
|
||||
|
||||
class TestNodeTreeView(unittest.TestCase):
|
||||
def setUpClass():
|
||||
curds.reset()
|
||||
view.reset()
|
||||
|
||||
def tearDownClass():
|
||||
view.reset()
|
||||
|
||||
def test_init(self):
|
||||
self.assertIsInstance(view.Entry, Gtk.SearchEntry)
|
||||
self.assertIsInstance(view.Error, Gtk.Label)
|
||||
self.assertIsInstance(view.Filter, Gtk.TreeModelFilter)
|
||||
self.assertIsInstance(view.Model, model.NodeTreeModel)
|
||||
self.assertIsInstance(view.Pattern, re.Pattern)
|
||||
self.assertIsInstance(view.Popover, Gtk.Popover)
|
||||
self.assertIsInstance(view.TreeView, Gtk.TreeView)
|
||||
self.assertIsInstance(view.Selection, Gtk.TreeSelection)
|
||||
self.assertIsInstance(view.Separator, Gtk.Separator)
|
||||
|
||||
self.assertEqual(view.TreeView.get_model(), view.Filter)
|
||||
self.assertEqual(view.Pattern.pattern, "")
|
||||
|
||||
def test_show_more_accel(self):
|
||||
signal = GObject.signal_lookup("grab-focus", Gtk.SearchEntry)
|
||||
|
||||
self.assertFalse(view.Entry.is_visible())
|
||||
self.assertTrue( view.Entry.can_activate_accel(signal))
|
||||
self.assertTrue( view.Entry.is_visible())
|
||||
self.assertTrue( view.Separator.is_visible())
|
||||
|
||||
view.Entry.grab_focus()
|
||||
self.assertFalse(view.Entry.can_activate_accel(signal))
|
||||
self.assertFalse(view.Entry.is_visible())
|
||||
self.assertFalse(view.Separator.is_visible())
|
||||
|
||||
def test_show_more_notification(self):
|
||||
curds.notify.notify("show-more", True)
|
||||
self.assertTrue(view.Entry.is_visible())
|
||||
self.assertTrue(view.Separator.is_visible())
|
||||
|
||||
curds.notify.notify("show-more", False)
|
||||
self.assertFalse(view.Entry.is_visible())
|
||||
self.assertFalse(view.Separator.is_visible())
|
||||
|
||||
def test_child_inserted(self):
|
||||
path = os.path.abspath("./trier/Test Library/Test Artist 01")
|
||||
library = curds.playlist.lookup("Libraries")
|
||||
user = curds.playlist.lookup("Playlists")
|
||||
row = Gtk.TreePath(library.get_path())
|
||||
|
||||
crow = Gtk.TreePath(user.get_path())
|
||||
row = view.Filter.convert_child_path_to_path(crow)
|
||||
view.TreeView.collapse_row(row)
|
||||
|
||||
for child in curds.playlist.Root.children:
|
||||
crow = Gtk.TreePath(child.get_path())
|
||||
row = view.Filter.convert_child_path_to_path(crow)
|
||||
self.assertFalse(view.TreeView.row_expanded(row))
|
||||
|
||||
library.lookup(path)
|
||||
curds.playlist.library.join()
|
||||
gtk.main_loop()
|
||||
|
||||
for child in curds.playlist.Root.children:
|
||||
crow = Gtk.TreePath(child.get_path())
|
||||
row = view.Filter.convert_child_path_to_path(crow)
|
||||
if child == library:
|
||||
self.assertTrue(view.TreeView.row_expanded(row))
|
||||
else:
|
||||
self.assertFalse(view.TreeView.row_expanded(row))
|
||||
|
||||
def test_can_select_path(self):
|
||||
root = curds.playlist.Root
|
||||
view.TreeView.expand_all()
|
||||
|
||||
for child in list(root.walk()):
|
||||
path = Gtk.TreePath(child.get_path())
|
||||
view.Selection.select_path(path)
|
||||
if isinstance(child, curds.Playlist):
|
||||
self.assertTrue(view.Selection.path_is_selected(path))
|
||||
else:
|
||||
self.assertFalse(view.Selection.path_is_selected(path))
|
||||
|
||||
def test_select_path(self):
|
||||
collection = curds.playlist.lookup("Collection")
|
||||
previous = curds.playlist.lookup("Previous")
|
||||
|
||||
self.assertNotEqual(playlist.view.Model.playlist, previous)
|
||||
|
||||
path = Gtk.TreePath(previous.get_path())
|
||||
view.Selection.select_path(path)
|
||||
self.assertEqual(playlist.view.Model.playlist, previous)
|
||||
|
||||
path = Gtk.TreePath(collection.get_path())
|
||||
view.Selection.select_path(path)
|
||||
self.assertEqual(playlist.view.Model.playlist, collection)
|
||||
|
||||
view.Selection.unselect_all()
|
||||
self.assertEqual(playlist.view.Model.playlist, collection)
|
||||
|
||||
def test_row_activate(self):
|
||||
collection = curds.playlist.lookup("Collection")
|
||||
playlists = curds.playlist.lookup("Playlists")
|
||||
column = view.TreeView.get_column(1)
|
||||
|
||||
self.assertEqual(curds.playlist.current(), collection)
|
||||
|
||||
path = Gtk.TreePath(curds.playlist.Starred.get_path())
|
||||
path = view.Filter.convert_child_path_to_path(path)
|
||||
view.TreeView.row_activated(path, column)
|
||||
self.assertEqual(curds.playlist.current(), curds.playlist.Starred)
|
||||
|
||||
path = Gtk.TreePath(playlists.get_path())
|
||||
path = view.Filter.convert_child_path_to_path(path)
|
||||
view.TreeView.row_activated(path, column)
|
||||
self.assertEqual(curds.playlist.current(), curds.playlist.Starred)
|
||||
|
||||
def test_filter_error(self):
|
||||
view.Entry.set_text("*v")
|
||||
view.Entry.emit("search-changed")
|
||||
|
||||
self.assertTrue(view.Entry.get_style_context().has_class("warning"))
|
||||
self.assertTrue(view.Popover.is_visible())
|
||||
self.assertEqual(view.Error.get_text(), "Nothing to repeat at position 0")
|
||||
|
||||
view.Entry.set_text("")
|
||||
view.Entry.emit("search-changed")
|
||||
gtk.main_loop(iteration_delay=0.01)
|
||||
|
||||
self.assertFalse(view.Entry.get_style_context().has_class("warning"))
|
||||
self.assertFalse(view.Popover.is_visible())
|
||||
|
||||
def test_filter_nodes(self):
|
||||
root = curds.playlist.Root
|
||||
user = root.lookup("Playlists")
|
||||
star = user.lookup("Starred")
|
||||
stär = curds.Playlist("Stärred (Semi)", "semi-starred")
|
||||
iter = None
|
||||
|
||||
user.insert_child(stär)
|
||||
self.assertEqual(user.n_children(), 3)
|
||||
|
||||
self.assertEqual(view.Filter.iter_n_children(None), root.n_children())
|
||||
view.Entry.set_text("star")
|
||||
view.Entry.emit("search-changed")
|
||||
|
||||
self.assertEqual(view.Filter.iter_n_children(iter), 1)
|
||||
iter = view.Filter.iter_nth_child(iter, 0)
|
||||
child = view.Filter.convert_iter_to_child_iter(iter)
|
||||
self.assertEqual(view.Model.iter_playlist(child), user)
|
||||
|
||||
self.assertEqual(view.Filter.iter_n_children(iter), 2)
|
||||
iter = view.Filter.iter_nth_child(iter, 0)
|
||||
child = view.Filter.convert_iter_to_child_iter(iter)
|
||||
self.assertEqual(view.Model.iter_playlist(child), star)
|
||||
|
||||
iter = view.Filter.iter_next(iter)
|
||||
child = view.Filter.convert_iter_to_child_iter(iter)
|
||||
self.assertEqual(view.Model.iter_playlist(child), stär)
|
||||
|
||||
view.Entry.set_text("")
|
||||
view.Entry.emit("search-changed")
|
||||
self.assertEqual(view.Filter.iter_n_children(None), root.n_children())
|
|
@ -1,102 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import model
|
||||
from .. import gtk
|
||||
from .. import playlist
|
||||
import curds
|
||||
import re
|
||||
from gi.repository import Gtk
|
||||
|
||||
Entry = gtk.Builder.get_object("node_search")
|
||||
Error = gtk.Builder.get_object("node_regex_error")
|
||||
Model = model.NodeTreeModel()
|
||||
Filter = Model.filter_new()
|
||||
Pattern = re.compile("")
|
||||
Popover = gtk.Builder.get_object("node_regex_popover")
|
||||
TreeView = gtk.Builder.get_object("node_treeview")
|
||||
Selection = TreeView.get_selection()
|
||||
Separator = gtk.Builder.get_object("node_separator")
|
||||
|
||||
Entry.connect("can-activate-accel", gtk.can_activate_entry)
|
||||
TreeView.set_model(Filter)
|
||||
|
||||
|
||||
def can_select_path(selection, model, path, current):
|
||||
child = model.convert_path_to_child_path(path)
|
||||
plist = curds.playlist.Root.lookup_path(child)
|
||||
return isinstance(plist, curds.Playlist)
|
||||
|
||||
Selection.set_select_function(can_select_path)
|
||||
|
||||
|
||||
def on_selection_changed(selection):
|
||||
(model, rows) = selection.get_selected_rows()
|
||||
if len(rows) > 0:
|
||||
child = model.convert_path_to_child_path(rows[0])
|
||||
plist = curds.playlist.Root.lookup_path(child)
|
||||
playlist.view.switch(plist)
|
||||
|
||||
Selection.connect("changed", on_selection_changed)
|
||||
|
||||
|
||||
def on_row_activated(treeview, path, column):
|
||||
child = Filter.convert_path_to_child_path(path)
|
||||
plist = curds.playlist.Root.lookup_path(child)
|
||||
if isinstance(plist, curds.Playlist):
|
||||
curds.playlist.select(plist)
|
||||
|
||||
TreeView.connect("row-activated", on_row_activated)
|
||||
|
||||
|
||||
def on_search_changed(entry):
|
||||
global Pattern
|
||||
try:
|
||||
Pattern = re.compile(entry.get_text(), re.I)
|
||||
Filter.refilter()
|
||||
Entry.get_style_context().remove_class("warning")
|
||||
Popover.popdown()
|
||||
except re.error as e:
|
||||
Entry.get_style_context().add_class("warning")
|
||||
Error.set_text(str(e).capitalize())
|
||||
Popover.popup()
|
||||
|
||||
Entry.connect("search-changed", on_search_changed)
|
||||
|
||||
|
||||
def visible_func(model, iter, data):
|
||||
plist = Model.iter_playlist(iter)
|
||||
for n in plist.walk():
|
||||
if Pattern.search(n.name) != None:
|
||||
return True
|
||||
if Pattern.search(curds.sort.normalize(n.name)) != None:
|
||||
return True
|
||||
return False
|
||||
|
||||
Filter.set_visible_func(visible_func)
|
||||
|
||||
|
||||
def on_child_inserted(child, path):
|
||||
if isinstance(child, curds.playlist.Library):
|
||||
fpath = Filter.convert_child_path_to_path(Gtk.TreePath(path))
|
||||
TreeView.expand_to_path(fpath)
|
||||
|
||||
curds.notify.register("child-inserted", on_child_inserted, idle=True)
|
||||
|
||||
|
||||
def on_show_more(visible):
|
||||
Entry.set_visible(visible)
|
||||
Separator.set_visible(visible)
|
||||
|
||||
curds.notify.register("show-more", on_show_more)
|
||||
|
||||
|
||||
def reset():
|
||||
global Pattern
|
||||
TreeView.set_model(None)
|
||||
Entry.set_text("")
|
||||
Pattern = re.compile("")
|
||||
Model.reset()
|
||||
Filter.clear_cache()
|
||||
on_show_more(False)
|
||||
TreeView.set_model(Filter)
|
||||
curds.notify.register("show-more", on_show_more)
|
||||
curds.notify.register("child-inserted", on_child_inserted, idle=True)
|
|
@ -1,6 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker
|
||||
from . import model
|
||||
from . import view
|
||||
|
||||
Model = model.PlaylistModel
|
||||
View = view.TreeView
|
|
@ -1,111 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from .. import gtk
|
||||
import curds
|
||||
from gi.repository import GObject, Gtk
|
||||
|
||||
cols = [ "starred", "upnext", "tracknumber", "title", "length",
|
||||
"artist", "album", "discnumber", "year", "genre" ]
|
||||
|
||||
class PlaylistModel(GObject.GObject, Gtk.TreeModel, Gtk.TreeDragSource, Gtk.TreeDragDest):
|
||||
def __init__(self, playlist):
|
||||
GObject.GObject.__init__(self)
|
||||
self.playlist = playlist
|
||||
self.playlist.visible = True
|
||||
|
||||
def __del__(self):
|
||||
self.playlist.visible = False
|
||||
|
||||
def do_drag_data_delete(self, path):
|
||||
return path[0] < len(self.playlist)
|
||||
|
||||
def do_drag_data_get(self, path, selection):
|
||||
return Gtk.tree_set_row_drag_data(selection, self, path)
|
||||
|
||||
def do_drag_data_received(self, target, selection):
|
||||
(ret, model, path) = Gtk.tree_get_row_drag_data(selection)
|
||||
if ret == True:
|
||||
self.move_track(path, target)
|
||||
return ret
|
||||
|
||||
def do_get_column_type(self, col):
|
||||
return str
|
||||
|
||||
def do_get_flags(self):
|
||||
return Gtk.TreeModelFlags.LIST_ONLY
|
||||
|
||||
def do_get_iter(self, path):
|
||||
return self.do_iter_nth_child(None, path.get_indices()[0])
|
||||
|
||||
def do_get_n_columns(self):
|
||||
return len(cols)
|
||||
|
||||
def do_get_path(self, iter):
|
||||
return Gtk.TreePath((iter.user_data))
|
||||
|
||||
def do_get_value(self, iter, column):
|
||||
colname = cols[column]
|
||||
track = self.iter_track(iter)
|
||||
if track == None:
|
||||
return ""
|
||||
if colname == "starred":
|
||||
if curds.playlist.Starred.contains(track):
|
||||
return "starred"
|
||||
return "non-starred"
|
||||
elif colname == "upnext":
|
||||
if curds.playlist.UpNext.contains(track):
|
||||
return "edit-redo"
|
||||
return "edit-redo-symbolic"
|
||||
elif colname == "discnumber":
|
||||
return f"Disc {track.tags['discnumber']}"
|
||||
return track[colname]
|
||||
|
||||
def do_iter_children(self, parent):
|
||||
return (False, None)
|
||||
|
||||
def do_iter_has_child(self, iter):
|
||||
return False
|
||||
|
||||
def do_iter_n_children(self, iter):
|
||||
if iter != None:
|
||||
return 0
|
||||
return len(self.playlist)
|
||||
|
||||
def do_iter_nth_child(self, iter, n):
|
||||
if iter or n >= len(self.playlist):
|
||||
return (False, None)
|
||||
iter = Gtk.TreeIter()
|
||||
iter.user_data = n
|
||||
return (True, iter)
|
||||
|
||||
def do_iter_next(self, iter):
|
||||
iter.user_data += 1
|
||||
if iter.user_data >= len(self.playlist):
|
||||
return (False, None)
|
||||
return (True, iter)
|
||||
|
||||
def do_row_draggable(self, path):
|
||||
return path[0] < len(self.playlist)
|
||||
|
||||
def do_row_drop_possible(self, path, selection):
|
||||
return path[0] < len(self.playlist)
|
||||
|
||||
def move_track(self, old, new):
|
||||
self.playlist.move(self.path_track(old), new[0])
|
||||
|
||||
for i in range(min(old[0], new[0]), max(old[0], new[0]) + 1):
|
||||
path = Gtk.TreePath(i)
|
||||
self.row_changed(path, self.get_iter(path))
|
||||
|
||||
def iter_track(self, iter):
|
||||
return self.playlist[iter.user_data]
|
||||
|
||||
def path_track(self, path):
|
||||
return self.playlist[path[0]]
|
||||
|
||||
def track_iter(self, track):
|
||||
index = self.playlist.index(track)
|
||||
if index == None:
|
||||
return None
|
||||
iter = Gtk.TreeIter()
|
||||
iter.user_data = index
|
||||
return iter
|
|
@ -1,170 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import model
|
||||
import curds
|
||||
import os
|
||||
import pathlib
|
||||
import trackdb
|
||||
import unittest
|
||||
from gi.repository import Gtk, GObject
|
||||
|
||||
test_album = pathlib.Path("./trier/Test Library/Test Artist 01/Test Album 1")
|
||||
|
||||
class TestPlaylistModel(unittest.TestCase):
|
||||
def setUp(self):
|
||||
curds.reset()
|
||||
|
||||
self.playlist = curds.Playlist("Test Playlist")
|
||||
self.track1 = trackdb.track.Track(0, test_album / "01 - Test Track 01.ogg")
|
||||
self.track2 = trackdb.track.Track(1, test_album / "02 - Test Track 02.ogg")
|
||||
self.playlist.add(self.track1)
|
||||
self.playlist.add(self.track2)
|
||||
|
||||
self.model = model.PlaylistModel(self.playlist)
|
||||
|
||||
def test_model_init(self):
|
||||
self.assertEqual(self.model.playlist, self.playlist)
|
||||
self.assertTrue( self.model.playlist.visible)
|
||||
|
||||
self.assertEqual(self.model.get_n_columns(), 10)
|
||||
self.assertEqual(self.model.get_flags(), Gtk.TreeModelFlags.LIST_ONLY)
|
||||
for col in range(self.model.get_n_columns()):
|
||||
self.assertEqual(self.model.get_column_type(col), GObject.GType(str))
|
||||
|
||||
def test_model_del(self):
|
||||
plist = curds.Playlist("Test Playlist Two")
|
||||
self.assertTrue( self.playlist.visible)
|
||||
self.model = model.PlaylistModel(plist)
|
||||
self.assertTrue(plist.visible)
|
||||
self.assertFalse(self.playlist.visible)
|
||||
|
||||
def test_model_drag_data_delete(self):
|
||||
self.assertTrue( self.model.drag_data_delete(Gtk.TreePath(0)))
|
||||
self.assertEqual(len(self.model), 2)
|
||||
self.assertTrue( self.model.drag_data_delete(Gtk.TreePath(1)))
|
||||
self.assertEqual(len(self.model), 2)
|
||||
self.assertFalse(self.model.drag_data_delete(Gtk.TreePath(2)))
|
||||
|
||||
def test_model_drag_data_get(self):
|
||||
# There is currently no way to manually allocate the
|
||||
# Gtk.SelectionData that we need to test this function
|
||||
pass
|
||||
|
||||
def on_row_changed(self, model, path, iter):
|
||||
self.row_changed += 1
|
||||
|
||||
def test_model_drag_data_received(self):
|
||||
cb = self.model.connect("row-changed", self.on_row_changed)
|
||||
self.row_changed = 0
|
||||
|
||||
self.model.move_track(Gtk.TreePath(0), Gtk.TreePath(1))
|
||||
self.assertEqual(self.playlist[0], self.track2)
|
||||
self.assertEqual(self.playlist[1], self.track1)
|
||||
self.assertEqual(self.row_changed, 2)
|
||||
|
||||
self.model.move_track(Gtk.TreePath(1), Gtk.TreePath(0))
|
||||
self.assertEqual(self.playlist[0], self.track1)
|
||||
self.assertEqual(self.playlist[1], self.track2)
|
||||
self.assertEqual(self.row_changed, 4)
|
||||
|
||||
self.model.disconnect(cb)
|
||||
|
||||
def test_model_get_iter(self):
|
||||
iter = self.model.get_iter(Gtk.TreePath(0))
|
||||
self.assertEqual(iter.user_data, 0)
|
||||
iter = self.model.get_iter(Gtk.TreePath(1))
|
||||
self.assertEqual(iter.user_data, 1)
|
||||
self.assertRaises(ValueError, self.model.get_iter, Gtk.TreePath(2))
|
||||
|
||||
def test_model_get_path(self):
|
||||
iter = Gtk.TreeIter()
|
||||
iter.user_data = 0
|
||||
self.assertEqual(self.model.get_path(iter), Gtk.TreePath(0))
|
||||
iter.user_data = 1
|
||||
self.assertEqual(self.model.get_path(iter), Gtk.TreePath(1))
|
||||
|
||||
def test_model_get_value(self):
|
||||
iter = self.model.track_iter(self.track1)
|
||||
self.assertEqual(self.model.get_value(iter, 0), "non-starred")
|
||||
self.assertEqual(self.model.get_value(iter, 1), "edit-redo-symbolic")
|
||||
self.assertEqual(self.model.get_value(iter, 2), "01")
|
||||
self.assertEqual(self.model.get_value(iter, 3), "Test Track 01")
|
||||
self.assertEqual(self.model.get_value(iter, 4), "0:01")
|
||||
self.assertEqual(self.model.get_value(iter, 5), "Test Artist 01")
|
||||
self.assertEqual(self.model.get_value(iter, 6), "Test Album 1")
|
||||
self.assertEqual(self.model.get_value(iter, 7), "Disc 1")
|
||||
self.assertEqual(self.model.get_value(iter, 8), "1973")
|
||||
self.assertEqual(self.model.get_value(iter, 9).title(), "Test Genre 1")
|
||||
|
||||
iter = self.model.track_iter(self.track2)
|
||||
curds.playlist.Starred.add(self.track2)
|
||||
curds.playlist.UpNext.add(self.track2)
|
||||
|
||||
self.assertEqual(self.model.get_value(iter, 0), "starred")
|
||||
self.assertEqual(self.model.get_value(iter, 1), "edit-redo")
|
||||
self.assertEqual(self.model.get_value(iter, 2), "02")
|
||||
self.assertEqual(self.model.get_value(iter, 3), "Test Track 02")
|
||||
self.assertEqual(self.model.get_value(iter, 4), "0:02")
|
||||
self.assertEqual(self.model.get_value(iter, 5), "Test Artist 01")
|
||||
self.assertEqual(self.model.get_value(iter, 6), "Test Album 1")
|
||||
self.assertEqual(self.model.get_value(iter, 7), "Disc 1")
|
||||
self.assertEqual(self.model.get_value(iter, 8), "1973")
|
||||
self.assertEqual(self.model.get_value(iter, 9).title(), "Test Genre 1")
|
||||
|
||||
def test_model_iter_children(self):
|
||||
iter = self.model.track_iter(self.track1)
|
||||
self.assertIsNone(self.model.iter_children(iter))
|
||||
|
||||
def test_model_has_child(self):
|
||||
iter = self.model.track_iter(self.track1)
|
||||
self.assertFalse(self.model.iter_has_child(iter))
|
||||
|
||||
def test_model_n_children(self):
|
||||
iter = self.model.track_iter(self.track1)
|
||||
self.assertEqual(self.model.iter_n_children(None), 2)
|
||||
self.assertEqual(self.model.iter_n_children(iter), 0)
|
||||
|
||||
def test_model_iter_nth_child(self):
|
||||
iter = self.model.iter_nth_child(None, 0)
|
||||
self.assertEqual(iter.user_data, 0)
|
||||
iter = self.model.iter_nth_child(None, 1)
|
||||
self.assertEqual(iter.user_data, 1)
|
||||
self.assertIsNone(self.model.iter_nth_child(None, 2))
|
||||
self.assertIsNone(self.model.iter_nth_child(iter, 0))
|
||||
|
||||
def test_model_iter_next(self):
|
||||
iter = self.model.iter_nth_child(None, 0)
|
||||
self.assertEqual(iter.user_data, 0)
|
||||
iter = self.model.iter_next(iter)
|
||||
self.assertEqual(iter.user_data, 1)
|
||||
self.assertIsNone(self.model.iter_next(iter))
|
||||
|
||||
def test_model_row_draggable(self):
|
||||
self.assertTrue( self.model.row_draggable(Gtk.TreePath(0)))
|
||||
self.assertTrue( self.model.row_draggable(Gtk.TreePath(1)))
|
||||
self.assertFalse(self.model.row_draggable(Gtk.TreePath(2)))
|
||||
|
||||
def test_model_row_drop_possible(self):
|
||||
# There is currently no way to manually allocate the
|
||||
# Gtk.SelectionData that we need to test this function
|
||||
pass
|
||||
|
||||
def test_model_iter_track(self):
|
||||
iter = Gtk.TreeIter()
|
||||
iter.user_data = 0
|
||||
self.assertEqual(self.model.iter_track(iter), self.track1)
|
||||
iter.user_data = 1
|
||||
self.assertEqual(self.model.iter_track(iter), self.track2)
|
||||
iter.user_data = 2
|
||||
self.assertEqual(self.model.iter_track(iter), None)
|
||||
|
||||
def test_model_path_track(self):
|
||||
self.assertEqual(self.model.path_track(Gtk.TreePath(0)), self.track1)
|
||||
self.assertEqual(self.model.path_track(Gtk.TreePath(1)), self.track2)
|
||||
self.assertEqual(self.model.path_track(Gtk.TreePath(2)), None)
|
||||
|
||||
def test_model_track_iter(self):
|
||||
iter = self.model.track_iter(self.track1)
|
||||
self.assertEqual(iter.user_data, 0)
|
||||
iter = self.model.track_iter(self.track2)
|
||||
self.assertEqual(iter.user_data, 1)
|
||||
self.assertEqual(self.model.track_iter(None), None)
|
|
@ -1,330 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import model
|
||||
from . import view
|
||||
from .. import audio
|
||||
from .. import gtk
|
||||
import curds
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import trackdb
|
||||
import unittest
|
||||
from gi.repository import Gdk, GObject, Gtk, Gst
|
||||
|
||||
test_album = pathlib.Path("./trier/Test Library/Test Artist 01/Test Album 1")
|
||||
|
||||
class TestPlaylistView(unittest.TestCase):
|
||||
def setUp(self):
|
||||
curds.reset()
|
||||
audio.reset()
|
||||
view.reset()
|
||||
|
||||
self.plist = curds.Playlist("Test Playlist")
|
||||
view.switch(self.plist)
|
||||
|
||||
def tearDownClass():
|
||||
view.TreeView.set_model(None)
|
||||
|
||||
def add_track(self, trackno):
|
||||
path = test_album / f"{trackno:02} - Test Track {trackno:02}.ogg"
|
||||
track = trackdb.track.Track(trackno, path)
|
||||
self.plist.add(track)
|
||||
gtk.notify_loop()
|
||||
|
||||
def test_init(self):
|
||||
self.assertIsInstance(view.Entry, Gtk.Entry)
|
||||
self.assertIsInstance(view.Error, Gtk.Label)
|
||||
self.assertIsInstance(view.Filter, Gtk.TreeModelFilter)
|
||||
self.assertIsInstance(view.Loop, Gtk.ToggleButton)
|
||||
self.assertIsInstance(view.Model, model.PlaylistModel)
|
||||
self.assertIsInstance(view.Pattern, re.Pattern)
|
||||
self.assertIsInstance(view.Popover, Gtk.Popover)
|
||||
self.assertIsInstance(view.Random, Gtk.ToggleButton)
|
||||
self.assertIsInstance(view.TreeView, Gtk.TreeView)
|
||||
self.assertIsInstance(view.Selection, Gtk.TreeSelection)
|
||||
self.assertIsInstance(view.Separator, Gtk.Separator)
|
||||
|
||||
view.reset()
|
||||
self.assertEqual(view.Model.playlist, curds.playlist.lookup("Collection"))
|
||||
self.assertEqual(view.NextPos, 0)
|
||||
self.assertEqual(view.TreeView.get_model(), view.Filter)
|
||||
self.assertEqual(view.Pattern.pattern, "")
|
||||
|
||||
def test_show_more_accel(self):
|
||||
signal = GObject.signal_lookup("grab-focus", Gtk.SearchEntry)
|
||||
|
||||
self.assertFalse(view.Entry.is_visible())
|
||||
self.assertTrue( view.Entry.can_activate_accel(signal))
|
||||
self.assertTrue( view.Entry.is_visible())
|
||||
self.assertTrue( view.Separator.is_visible())
|
||||
|
||||
view.Entry.grab_focus()
|
||||
self.assertFalse(view.Entry.can_activate_accel(signal))
|
||||
self.assertFalse(view.Entry.is_visible())
|
||||
self.assertFalse(view.Separator.is_visible())
|
||||
|
||||
def test_show_more_notification(self):
|
||||
curds.notify.notify("show-more", True)
|
||||
self.assertTrue(view.Entry.is_visible())
|
||||
self.assertTrue(view.Separator.is_visible())
|
||||
|
||||
curds.notify.notify("show-more", False)
|
||||
self.assertFalse(view.Entry.is_visible())
|
||||
self.assertFalse(view.Separator.is_visible())
|
||||
|
||||
def test_switch(self):
|
||||
plist = curds.Playlist("Test Playlist 2")
|
||||
view.switch(plist)
|
||||
self.assertEqual(view.Model.playlist, plist)
|
||||
self.assertEqual(view.Filter.get_model(), view.Model)
|
||||
|
||||
view.NextPos = 42
|
||||
model = view.Model
|
||||
view.switch(plist)
|
||||
self.assertEqual(view.Model, model)
|
||||
|
||||
view.switch(plist, force=True)
|
||||
self.assertNotEqual(view.Model, model)
|
||||
self.assertEqual(view.NextPos, 0)
|
||||
|
||||
def test_loop_toggled(self):
|
||||
self.assertFalse(self.plist.loop)
|
||||
view.Loop.set_active(True)
|
||||
self.assertTrue(self.plist.loop)
|
||||
view.Loop.set_active(False)
|
||||
self.assertFalse(self.plist.loop)
|
||||
|
||||
self.plist.can_loop = False
|
||||
view.Loop.set_active(True)
|
||||
self.assertFalse(self.plist.loop)
|
||||
self.assertFalse(view.Loop.get_active())
|
||||
|
||||
def test_random_toggled(self):
|
||||
self.assertFalse(self.plist.random)
|
||||
view.Random.set_active(True)
|
||||
self.assertTrue(self.plist.random)
|
||||
view.Random.set_active(False)
|
||||
self.assertFalse(self.plist.random)
|
||||
|
||||
self.plist.can_random = False
|
||||
view.Random.set_active(True)
|
||||
self.assertFalse(self.plist.random)
|
||||
self.assertFalse(view.Random.get_active())
|
||||
|
||||
def test_switch_toggle_buttons(self):
|
||||
self.plist.can_random = False
|
||||
self.plist.can_loop = False
|
||||
view.switch(self.plist, force=True)
|
||||
self.assertFalse(view.Loop.is_sensitive())
|
||||
self.assertFalse(view.Random.is_sensitive())
|
||||
|
||||
self.plist.can_random = True
|
||||
self.plist.can_loop = True
|
||||
self.plist.random = True
|
||||
self.plist.loop = True
|
||||
view.switch(self.plist, force=True)
|
||||
self.assertTrue(view.Loop.is_sensitive())
|
||||
self.assertTrue(view.Loop.get_active())
|
||||
self.assertTrue(view.Random.is_sensitive())
|
||||
self.assertTrue(view.Random.get_active())
|
||||
|
||||
def test_column_clicks(self):
|
||||
self.plist.sort_order = [ ]
|
||||
self.assertFalse(view.TreeView.get_columns()[0].get_clickable())
|
||||
|
||||
for i, col in enumerate(view.TreeView.get_columns()[2:]):
|
||||
self.assertTrue(col.get_clickable())
|
||||
|
||||
col.clicked()
|
||||
self.assertEqual(self.plist.sort_order, [ model.cols[i+2] ])
|
||||
self.assertEqual(col.get_sort_indicator(), True)
|
||||
self.assertEqual(col.get_sort_order(), Gtk.SortType.ASCENDING)
|
||||
|
||||
col.clicked()
|
||||
self.assertEqual(self.plist.sort_order, [ ])
|
||||
self.assertEqual(col.get_sort_order(), False)
|
||||
|
||||
def on_row_inserted(self, model, path, iter):
|
||||
self.row_inserted += 1
|
||||
|
||||
def on_row_deleted(self, model, path):
|
||||
self.row_deleted += 1
|
||||
|
||||
def test_add_remove_track(self):
|
||||
cb1 = view.Model.connect("row-inserted", self.on_row_inserted)
|
||||
cb2 = view.Model.connect("row-deleted", self.on_row_deleted)
|
||||
self.row_inserted = 0
|
||||
self.row_deleted = 0
|
||||
|
||||
for i in range(1, 11):
|
||||
track = trackdb.track.Track(i, test_album / f"{i:02} - Test Track {i:02}.ogg")
|
||||
self.plist.add(track)
|
||||
gtk.notify_loop()
|
||||
self.assertEqual(self.row_inserted, i)
|
||||
self.assertEqual(view.NextPos, i)
|
||||
|
||||
for i in range(1, 11):
|
||||
self.plist.remove(self.plist[0])
|
||||
gtk.notify_loop()
|
||||
self.assertEqual(self.row_deleted, i)
|
||||
self.assertEqual(view.NextPos, len(self.plist))
|
||||
|
||||
view.Model.disconnect(cb1)
|
||||
view.Model.disconnect(cb2)
|
||||
|
||||
def test_playlist_changed(self):
|
||||
for i in range(1, 11):
|
||||
self.add_track(i)
|
||||
self.assertEqual(view.Runtime.get_text(), f" {self.plist.runtime()} ")
|
||||
|
||||
view.switch(curds.playlist.Starred)
|
||||
self.assertEqual(view.Runtime.get_text(), "")
|
||||
|
||||
view.switch(self.plist)
|
||||
self.assertEqual(view.Runtime.get_text(), f" {self.plist.runtime()} ")
|
||||
|
||||
def test_stream_start(self):
|
||||
curds.playlist.select(self.plist)
|
||||
|
||||
for i in range(1, 11):
|
||||
self.add_track(i)
|
||||
|
||||
for i in range(-1, 10):
|
||||
self.plist.current = i
|
||||
curds.notify.notify("stream-start")
|
||||
if i == -1:
|
||||
self.assertEqual(view.Selection.count_selected_rows(), 0)
|
||||
else:
|
||||
self.assertEqual(view.Selection.count_selected_rows(), 1)
|
||||
self.assertTrue( view.Selection.path_is_selected(Gtk.TreePath(i)))
|
||||
view.Selection.unselect_all()
|
||||
|
||||
def test_scroll_to(self):
|
||||
for i in range(1, 11):
|
||||
self.add_track(i)
|
||||
|
||||
for i in range(-1, 10):
|
||||
view.scroll_to(i)
|
||||
if i == -1:
|
||||
self.assertEqual(view.Selection.count_selected_rows(), 0)
|
||||
else:
|
||||
self.assertEqual(view.Selection.count_selected_rows(), 1)
|
||||
self.assertTrue( view.Selection.path_is_selected(Gtk.TreePath(i)))
|
||||
|
||||
def test_switch_scroll(self):
|
||||
for i in range(1, 11):
|
||||
self.add_track(i)
|
||||
|
||||
for i in range(-1, 10):
|
||||
self.plist.current = i
|
||||
view.switch(self.plist, force=True)
|
||||
if i == -1:
|
||||
self.assertEqual(view.Selection.count_selected_rows(), 0)
|
||||
else:
|
||||
self.assertEqual(view.Selection.count_selected_rows(), 1)
|
||||
self.assertTrue( view.Selection.path_is_selected(Gtk.TreePath(i)))
|
||||
|
||||
def test_row_activate(self):
|
||||
column = view.TreeView.get_column(1)
|
||||
|
||||
for i in range(1, 11):
|
||||
self.add_track(i)
|
||||
|
||||
for i in range(10):
|
||||
path = Gtk.TreePath(i)
|
||||
view.TreeView.row_activated(path, column)
|
||||
|
||||
self.assertEqual(self.plist.current, i)
|
||||
self.assertEqual(audio.playbin.get_state(), Gst.State.PLAYING)
|
||||
self.assertEqual(audio.playbin.duration(), (i + 1) * Gst.SECOND)
|
||||
|
||||
def test_add_remove_starred(self):
|
||||
event = Gdk.EventButton.new(Gdk.EventType.BUTTON_PRESS)
|
||||
event.x = 5
|
||||
column = view.TreeView.get_column(0)
|
||||
|
||||
for i in range(1, 11):
|
||||
self.add_track(i)
|
||||
|
||||
for i in range(10):
|
||||
area = view.TreeView.get_cell_area(Gtk.TreePath(i), column)
|
||||
event.y = area.y + 5
|
||||
|
||||
event.x = area.x + area.width + 5
|
||||
view.TreeView.emit("button-press-event", event)
|
||||
self.assertNotIn(self.plist[i], curds.playlist.Starred.list)
|
||||
|
||||
event.x = area.x + 5
|
||||
view.TreeView.emit("button-press-event", event)
|
||||
self.assertIn(self.plist[i], curds.playlist.Starred.list)
|
||||
view.TreeView.emit("button-press-event", event)
|
||||
self.assertNotIn(self.plist[i], curds.playlist.Starred.list)
|
||||
|
||||
view.Selection.select_all()
|
||||
view.TreeView.emit("button-press-event", event)
|
||||
for track in self.plist.list:
|
||||
self.assertIn(track, curds.playlist.Starred.list)
|
||||
view.TreeView.emit("button-press-event", event)
|
||||
for track in self.plist.list:
|
||||
self.assertNotIn(track, curds.playlist.Starred.list)
|
||||
|
||||
def test_add_remove_up_next(self):
|
||||
event = Gdk.EventButton.new(Gdk.EventType.BUTTON_PRESS)
|
||||
event.x = 5
|
||||
column = view.TreeView.get_column(1)
|
||||
|
||||
for i in range(1, 11):
|
||||
self.add_track(i)
|
||||
|
||||
for i in range(10):
|
||||
curds.playlist.select(curds.playlist.Starred)
|
||||
area = view.TreeView.get_cell_area(Gtk.TreePath(i), column)
|
||||
event.y = area.y + 5
|
||||
|
||||
event.x = area.x + area.width + 5
|
||||
view.TreeView.emit("button-press-event", event)
|
||||
self.assertNotIn(self.plist[i], curds.playlist.UpNext.list)
|
||||
self.assertNotEqual(curds.playlist.Current[0], curds.playlist.UpNext)
|
||||
|
||||
event.x = area.x + 5
|
||||
view.TreeView.emit("button-press-event", event)
|
||||
self.assertIn(self.plist[i], curds.playlist.UpNext.list)
|
||||
self.assertEqual(curds.playlist.Current[0], curds.playlist.UpNext)
|
||||
view.TreeView.emit("button-press-event", event)
|
||||
self.assertNotIn(self.plist[i], curds.playlist.UpNext.list)
|
||||
|
||||
view.Selection.select_all()
|
||||
view.TreeView.emit("button-press-event", event)
|
||||
for track in self.plist.list:
|
||||
self.assertIn(track, curds.playlist.UpNext.list)
|
||||
view.TreeView.emit("button-press-event", event)
|
||||
for track in self.plist.list:
|
||||
self.assertNotIn(track, curds.playlist.UpNext.list)
|
||||
|
||||
def test_filter_error(self):
|
||||
view.Entry.set_text("*v")
|
||||
view.Entry.emit("search-changed")
|
||||
|
||||
self.assertTrue(view.Entry.get_style_context().has_class("warning"))
|
||||
self.assertTrue(view.Popover.is_visible())
|
||||
self.assertEqual(view.Error.get_text(), "Nothing to repeat at position 0")
|
||||
|
||||
view.Entry.set_text("")
|
||||
view.Entry.emit("search-changed")
|
||||
gtk.main_loop(iteration_delay=0.01)
|
||||
|
||||
self.assertFalse(view.Entry.get_style_context().has_class("warning"))
|
||||
self.assertFalse(view.Popover.is_visible())
|
||||
|
||||
def test_filter_tracks(self):
|
||||
for i in range(1, 11):
|
||||
self.add_track(i)
|
||||
self.assertEqual(view.Filter.iter_n_children(None), 10)
|
||||
|
||||
view.Entry.set_text("Test Track 01")
|
||||
view.Entry.emit("search-changed")
|
||||
self.assertEqual(view.Filter.iter_n_children(None), 1)
|
||||
|
||||
view.Entry.set_text("")
|
||||
view.Entry.emit("search-changed")
|
||||
self.assertEqual(view.Filter.iter_n_children(None), 10)
|
|
@ -1,201 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import model
|
||||
from .. import audio
|
||||
from .. import gtk
|
||||
import curds
|
||||
import re
|
||||
from gi.repository import Gtk
|
||||
|
||||
Entry = gtk.Builder.get_object("track_search")
|
||||
Error = gtk.Builder.get_object("track_regex_error")
|
||||
Filter = None
|
||||
Loop = gtk.Builder.get_object("loop_button")
|
||||
Model = None
|
||||
NextPos = 0
|
||||
Pattern = re.compile("")
|
||||
Popover = gtk.Builder.get_object("track_regex_popover")
|
||||
Random = gtk.Builder.get_object("random_button")
|
||||
Runtime = gtk.Builder.get_object("runtime")
|
||||
TreeView = gtk.Builder.get_object("playlist_treeview")
|
||||
Selection = TreeView.get_selection()
|
||||
Separator = gtk.Builder.get_object("track_separator")
|
||||
|
||||
Entry.connect("can-activate-accel", gtk.can_activate_entry)
|
||||
|
||||
|
||||
def on_loop_toggled(button):
|
||||
active = button.get_active()
|
||||
button.set_active(Model.playlist.set_loop(active))
|
||||
|
||||
Loop.connect("toggled", on_loop_toggled)
|
||||
|
||||
|
||||
def on_random_toggled(button):
|
||||
active = button.get_active()
|
||||
button.set_active(Model.playlist.set_random(active))
|
||||
|
||||
Random.connect("toggled", on_random_toggled)
|
||||
|
||||
|
||||
def on_row_activated(treeview, path, column):
|
||||
child = Filter.convert_path_to_child_path(path)
|
||||
track = Model.path_track(child)
|
||||
Model.playlist.current = child[0]
|
||||
audio.play_track(track)
|
||||
|
||||
TreeView.connect("row-activated", on_row_activated)
|
||||
|
||||
|
||||
def get_selected_rows():
|
||||
(model, rows) = Selection.get_selected_rows()
|
||||
return [ Filter.convert_path_to_child_path(p) for p in rows ]
|
||||
|
||||
def on_add_remove(plist, path):
|
||||
rows = get_selected_rows()
|
||||
add_rm = plist.remove if plist.contains(Model.path_track(path)) else plist.add
|
||||
|
||||
if len(rows) == 0 or path[0] >= rows[-1][0]:
|
||||
rows = rows + [ path ]
|
||||
elif len(rows) > 0 and path[0] < rows[0][0]:
|
||||
rows = [ path ] + rows
|
||||
|
||||
for row in rows:
|
||||
add_rm(Model.path_track(row))
|
||||
TreeView.queue_draw()
|
||||
return add_rm == plist.add
|
||||
|
||||
def on_button_press(treeview, event):
|
||||
(path, col, x, y) = TreeView.get_path_at_pos(event.x, event.y)
|
||||
path = Filter.convert_path_to_child_path(path)
|
||||
if col == TreeView.get_column(0):
|
||||
on_add_remove(curds.playlist.Starred, path)
|
||||
return True
|
||||
elif col == TreeView.get_column(1):
|
||||
if on_add_remove(curds.playlist.UpNext, path) == True:
|
||||
curds.playlist.select(curds.playlist.UpNext)
|
||||
return True
|
||||
return False
|
||||
|
||||
TreeView.connect("button-press-event", on_button_press)
|
||||
|
||||
|
||||
def on_column_clicked(column, index):
|
||||
valid = Model.playlist.sort(model.cols[index])
|
||||
column.set_sort_indicator(valid)
|
||||
|
||||
for i, column in enumerate(TreeView.get_columns()):
|
||||
column.connect("clicked", on_column_clicked, i)
|
||||
|
||||
|
||||
def on_search_changed(entry):
|
||||
global Pattern
|
||||
try:
|
||||
Pattern = re.compile(entry.get_text(), re.I)
|
||||
Filter.refilter()
|
||||
Entry.get_style_context().remove_class("warning")
|
||||
Popover.popdown()
|
||||
except re.error as e:
|
||||
Entry.get_style_context().add_class("warning")
|
||||
Error.set_text(str(e).capitalize())
|
||||
Popover.popup()
|
||||
|
||||
Entry.connect("search-changed", on_search_changed)
|
||||
|
||||
|
||||
def visible_func(plist_model, iter, data):
|
||||
track = plist_model.iter_track(iter)
|
||||
for col in model.cols[2:]:
|
||||
text = track[col]
|
||||
if Pattern.search(text) != None:
|
||||
return True
|
||||
if col in [ "title", "artist", "album", "genre" ]:
|
||||
if Pattern.search(curds.sort.normalize(text)) != None:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def on_add_track(plist, track, index):
|
||||
global NextPos
|
||||
if plist == Model.playlist:
|
||||
iter = Gtk.TreeIter()
|
||||
iter.user_data = NextPos
|
||||
Model.row_inserted(Gtk.TreePath(NextPos), iter)
|
||||
NextPos += 1
|
||||
|
||||
curds.notify.register("add-track", on_add_track, idle=True)
|
||||
|
||||
|
||||
def on_remove_track(plist, track, index):
|
||||
global NextPos
|
||||
if plist == Model.playlist:
|
||||
Model.row_deleted(Gtk.TreePath(index))
|
||||
NextPos -= 1
|
||||
elif plist == curds.playlist.UpNext and Model.playlist.contains(track):
|
||||
TreeView.queue_draw()
|
||||
|
||||
curds.notify.register("remove-track", on_remove_track, idle=True)
|
||||
|
||||
|
||||
def set_runtime(text):
|
||||
if text != "":
|
||||
text = f" {text} "
|
||||
Runtime.set_text(text)
|
||||
|
||||
def on_playlist_changed(plist):
|
||||
if plist == Model.playlist:
|
||||
set_runtime(plist.runtime())
|
||||
|
||||
curds.notify.register("playlist-changed", on_playlist_changed, idle=True)
|
||||
|
||||
|
||||
def scroll_to(index):
|
||||
Selection.unselect_all()
|
||||
if index >= 0:
|
||||
path = Gtk.TreePath(index)
|
||||
Selection.select_path(path)
|
||||
TreeView.scroll_to_cell(path, None, True, 0.25, 0)
|
||||
|
||||
def on_stream_start():
|
||||
plist = Model.playlist
|
||||
if plist == curds.playlist.current():
|
||||
scroll_to(plist.current)
|
||||
|
||||
curds.notify.register("stream-start", on_stream_start)
|
||||
|
||||
|
||||
def on_show_more(visible):
|
||||
Entry.set_visible(visible)
|
||||
Separator.set_visible(visible)
|
||||
|
||||
curds.notify.register("show-more", on_show_more)
|
||||
|
||||
|
||||
def configure_toggle(toggle, sensitive, active):
|
||||
toggle.set_active(active)
|
||||
toggle.set_sensitive(sensitive)
|
||||
|
||||
def switch(plist, force=False):
|
||||
global Filter
|
||||
global Model
|
||||
global NextPos
|
||||
if not Model or Model.playlist != plist or force == True:
|
||||
Model = model.PlaylistModel(plist)
|
||||
Filter = Model.filter_new()
|
||||
NextPos = len(plist)
|
||||
TreeView.set_model(Filter)
|
||||
Filter.set_visible_func(visible_func)
|
||||
configure_toggle(Random, plist.can_random, plist.random)
|
||||
configure_toggle(Loop, plist.can_loop, plist.loop)
|
||||
set_runtime(plist.runtime())
|
||||
scroll_to(plist.current)
|
||||
|
||||
switch(curds.playlist.lookup("Collection"))
|
||||
|
||||
|
||||
def reset():
|
||||
switch(curds.playlist.lookup("Collection"))
|
||||
curds.notify.register("add-track", on_add_track, idle=True)
|
||||
curds.notify.register("remove-track", on_remove_track, idle=True)
|
||||
curds.notify.register("playlist-changed", on_playlist_changed, idle=True)
|
||||
curds.notify.register("stream-start", on_stream_start)
|
||||
curds.notify.register("show-more", on_show_more)
|
|
@ -1,21 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import audio
|
||||
import curds
|
||||
import os
|
||||
import pathlib
|
||||
import trackdb
|
||||
import unittest
|
||||
from gi.repository import Gst
|
||||
|
||||
test_album = pathlib.Path("./trier/Test Library/Test Artist 01/Test Album 1")
|
||||
|
||||
class TestAudio(unittest.TestCase):
|
||||
def test_play_track(self):
|
||||
path = test_album / "01 - Test Track 01.ogg"
|
||||
track = trackdb.track.Track(1, path)
|
||||
|
||||
self.assertEqual(audio.playbin.get_state(), Gst.State.NULL)
|
||||
audio.play_track(track)
|
||||
self.assertEqual(audio.playbin.get_state(), Gst.State.PLAYING)
|
||||
|
||||
audio.reset()
|
|
@ -1,91 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import gtk
|
||||
import curds
|
||||
import time
|
||||
import unittest
|
||||
from gi.repository import Gtk, GObject, GLib
|
||||
|
||||
class TestGtk(unittest.TestCase):
|
||||
def setUp(self):
|
||||
curds.reset()
|
||||
|
||||
def test_builder(self):
|
||||
self.assertIsInstance(gtk.Builder, Gtk.Builder)
|
||||
self.assertIsInstance(gtk.Builder.get_object("window"), Gtk.ApplicationWindow)
|
||||
|
||||
def fake_idle(self):
|
||||
return GLib.SOURCE_CONTINUE
|
||||
|
||||
def test_window(self):
|
||||
app = gtk.EmmentalApplication()
|
||||
window = gtk.Builder.get_object("window")
|
||||
thread = curds.ThreadQueue()
|
||||
|
||||
self.assertIsInstance(window, Gtk.ApplicationWindow)
|
||||
self.assertIsNone(app.window)
|
||||
|
||||
thread.push(app.run)
|
||||
gtk.main_loop(delay=0.1)
|
||||
self.assertTrue(window.is_visible())
|
||||
|
||||
app.idle_id = GLib.idle_add(self.fake_idle)
|
||||
app.quit()
|
||||
gtk.main_loop(delay=0.1)
|
||||
thread.stop()
|
||||
self.assertEqual(app.idle_id, None)
|
||||
|
||||
def test_application(self):
|
||||
self.assertIsInstance(gtk.Application, Gtk.Application)
|
||||
|
||||
def on_show_more(self, visible):
|
||||
self.show_more = visible
|
||||
|
||||
def test_show_more(self):
|
||||
self.show_more = None
|
||||
curds.notify.register("show-more", self.on_show_more)
|
||||
|
||||
self.assertIsInstance(gtk.ShowMore, Gtk.ToggleButton)
|
||||
self.assertIsInstance(gtk.UpArrow, Gtk.Image)
|
||||
self.assertIsInstance(gtk.DownArrow, Gtk.Image)
|
||||
|
||||
self.assertFalse(gtk.ShowMore.get_active())
|
||||
self.assertFalse(gtk.UpArrow.is_visible())
|
||||
self.assertTrue( gtk.DownArrow.is_visible())
|
||||
|
||||
gtk.ShowMore.set_active(True)
|
||||
self.assertTrue(self.show_more)
|
||||
self.assertTrue( gtk.ShowMore.get_active())
|
||||
self.assertTrue( gtk.UpArrow.is_visible())
|
||||
self.assertFalse(gtk.DownArrow.is_visible())
|
||||
|
||||
gtk.ShowMore.set_active(False)
|
||||
self.assertFalse(self.show_more)
|
||||
self.assertFalse(gtk.ShowMore.get_active())
|
||||
self.assertFalse(gtk.UpArrow.is_visible())
|
||||
self.assertTrue( gtk.DownArrow.is_visible())
|
||||
|
||||
def test_accel(self):
|
||||
play_button = gtk.Builder.get_object("play_button")
|
||||
track_entry = gtk.Builder.get_object("track_search")
|
||||
|
||||
self.assertFalse(gtk.type_focused(Gtk.Entry))
|
||||
track_entry.grab_focus()
|
||||
self.assertTrue(gtk.type_focused(Gtk.Entry))
|
||||
gtk.Window.grab_focus()
|
||||
self.assertFalse(gtk.type_focused(Gtk.Entry))
|
||||
|
||||
signal = GObject.signal_lookup("activate", Gtk.Button)
|
||||
self.assertTrue(play_button.can_activate_accel(signal))
|
||||
track_entry.grab_focus()
|
||||
self.assertFalse(play_button.can_activate_accel(signal))
|
||||
|
||||
gtk.Window.grab_focus()
|
||||
self.assertFalse(gtk.ShowMore.get_active())
|
||||
signal = GObject.signal_lookup("grab-focus", Gtk.SearchEntry)
|
||||
|
||||
self.assertTrue(track_entry.can_activate_accel(signal))
|
||||
self.assertTrue(gtk.ShowMore.get_active())
|
||||
|
||||
track_entry.grab_focus()
|
||||
self.assertFalse(track_entry.can_activate_accel(signal))
|
||||
self.assertFalse(gtk.ShowMore.get_active())
|
|
@ -1,54 +0,0 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import gtk
|
||||
from . import library
|
||||
import curds
|
||||
import os
|
||||
import unittest
|
||||
from gi.repository import Gtk
|
||||
|
||||
test_album = os.path.abspath("./trier/Test Library/Test Artist 02/Test Album 1")
|
||||
|
||||
class TestLibrary(unittest.TestCase):
|
||||
def setUp(self):
|
||||
curds.reset()
|
||||
|
||||
def tearDownClass():
|
||||
curds.stop()
|
||||
|
||||
def test_init(self):
|
||||
self.assertIsInstance(library.Add, Gtk.Button)
|
||||
self.assertIsInstance(library.Cancel, Gtk.Button)
|
||||
self.assertIsInstance(library.Chooser, Gtk.FileChooser)
|
||||
self.assertIsInstance(library.Ok, Gtk.Button)
|
||||
self.assertIsInstance(library.Popover, Gtk.Popover)
|
||||
|
||||
def test_add_cancel(self):
|
||||
self.assertFalse(library.Popover.is_visible())
|
||||
library.Add.clicked()
|
||||
gtk.main_loop()
|
||||
self.assertTrue(library.Popover.is_visible())
|
||||
|
||||
library.Cancel.clicked()
|
||||
gtk.main_loop(iteration_delay=0.01)
|
||||
self.assertFalse(library.Popover.is_visible())
|
||||
self.assertEqual(curds.playlist.lookup("Libraries").n_children(), 0)
|
||||
|
||||
def test_add_ok(self):
|
||||
music_dir = os.path.join(os.path.expanduser("~"), "Music")
|
||||
|
||||
self.assertFalse(library.Popover.is_visible())
|
||||
library.Add.clicked()
|
||||
gtk.main_loop()
|
||||
self.assertTrue(library.Popover.is_visible())
|
||||
self.assertEqual(library.Chooser.get_filename(), music_dir)
|
||||
|
||||
library.Chooser.set_filename(test_album)
|
||||
gtk.main_loop(delay=0.1)
|
||||
self.assertEqual(library.Chooser.get_filename(), test_album)
|
||||
|
||||
library.Ok.clicked()
|
||||
gtk.main_loop(iteration_delay=0.01)
|
||||
self.assertEqual(curds.playlist.lookup("Libraries").n_children(), 1)
|
||||
self.assertFalse(library.Popover.is_visible())
|
||||
|
||||
curds.playlist.library.join()
|
Loading…
Reference in New Issue