Remove old rind/ directory

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2021-07-26 10:24:56 -04:00
parent 08d866b047
commit 0aa31586a3
25 changed files with 0 additions and 2694 deletions

View File

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

View File

@ -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()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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()

View File

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

View File

@ -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()

View File

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

View File

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

View File

@ -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())

View File

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

View File

@ -1,6 +0,0 @@
# Copyright 2019 (c) Anna Schumaker
from . import model
from . import view
Model = model.PlaylistModel
View = view.TreeView

View File

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

View File

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

View File

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

View File

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

View File

@ -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()

View File

@ -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())

View File

@ -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()