1# This program is free software; you can redistribute it and/or modify 2# it under the terms of the GNU General Public License as published by 3# the Free Software Foundation; either version 2 of the License, or 4# (at your option) any later version. 5 6from gi.repository import Gtk 7from quodlibet import _, app 8from quodlibet.plugins import PluginConfigMixin 9from quodlibet.plugins.songshelpers import has_bookmark 10from quodlibet.plugins.events import EventPlugin 11from quodlibet.qltk import Icons 12from quodlibet.qltk.entry import UndoEntry 13from quodlibet.qltk.tracker import TimeTracker 14 15 16class SeekPointsPlugin(EventPlugin, PluginConfigMixin): 17 """The plugin class.""" 18 19 PLUGIN_ID = "Seekpoints" 20 PLUGIN_NAME = _("Seekpoint Bookmarks") 21 PLUGIN_ICON = Icons.GO_JUMP 22 PLUGIN_CONFIG_SECTION = __name__ 23 PLUGIN_DESC = _( 24 "Store Seekpoints A and/or B for tracks. " 25 "Skip to time A and stop after time B when track is played.\n" 26 "Note that changing the names of the points below does not " 27 "update the actual bookmark names, it only changes which " 28 "bookmark names the plugin looks for when deciding whether to seek.") 29 30 CFG_SEEKPOINT_A_TEXT = "A" 31 CFG_SEEKPOINT_B_TEXT = "B" 32 DEFAULT_A_TEXT = "A" 33 DEFAULT_B_TEXT = "B" 34 35 def enabled(self): 36 self._seekpoint_A, self._seekpoint_B = self._get_seekpoints() 37 self._tracker = TimeTracker(app.player) 38 self._tracker.connect('tick', self._on_tick) 39 40 def disabled(self): 41 self._tracker.destroy() 42 43 def plugin_on_song_started(self, song): 44 """Seeks to point A if it exists, and also fetches the bookmarks of the 45 current track that matches the seekpoint names set in config. 46 """ 47 self._seekpoint_A, self._seekpoint_B = self._get_seekpoints() 48 if not self._seekpoint_A: 49 return 50 self._seek(self._seekpoint_A) 51 52 def _on_tick(self, tracker): 53 """Checks whether the current position is past point B if it exists, 54 and if it is -- seek to the end of the track. 55 """ 56 if not self._seekpoint_B: 57 return 58 59 time = app.player.get_position() // 1000 60 if self._seekpoint_B <= time: 61 self._seek(app.player.info("~#length")) 62 63 def _get_seekpoints(self): 64 """Reads seekpoint-names from config, which are compared to the 65 bookmark-names of the current track to get timestamps (if any). 66 """ 67 if not app.player.song: 68 return None, None 69 70 marks = [] 71 if has_bookmark(app.player.song): 72 marks = app.player.song.bookmarks 73 74 seekpoint_A = None 75 seekpoint_B = None 76 seekpoint_A_name = self.config_get(self.CFG_SEEKPOINT_A_TEXT, 77 self.DEFAULT_A_TEXT) 78 seekpoint_B_name = self.config_get(self.CFG_SEEKPOINT_B_TEXT, 79 self.DEFAULT_B_TEXT) 80 for time, mark in marks: 81 if mark == seekpoint_A_name: 82 seekpoint_A = time 83 elif mark == seekpoint_B_name: 84 seekpoint_B = time 85 86 # if seekpoints are not properly ordered (or identical), the track 87 # will likely endlessly seek when looping tracks, so discard B 88 # (maybe raise an exception for the plugin list?). 89 if (seekpoint_A is not None) and (seekpoint_B is not None): 90 if seekpoint_A >= seekpoint_B: 91 return seekpoint_A, None 92 93 return seekpoint_A, seekpoint_B 94 95 def _seek(self, seconds): 96 app.player.seek(seconds * 1000) 97 98 def PluginPreferences(self, parent): 99 vb = Gtk.VBox(spacing=12) 100 101 # Bookmark name to use for point A 102 hb = Gtk.HBox(spacing=6) 103 entry = UndoEntry() 104 entry.set_text(self.config_get(self.CFG_SEEKPOINT_A_TEXT, 105 self.DEFAULT_A_TEXT)) 106 entry.connect('changed', self.config_entry_changed, 107 self.CFG_SEEKPOINT_A_TEXT) 108 lbl = Gtk.Label(label=_("Bookmark name for point A")) 109 entry.set_tooltip_markup(_("Bookmark name to check for when " 110 "a track is started, and if found the player seeks to that " 111 "timestamp")) 112 lbl.set_mnemonic_widget(entry) 113 hb.pack_start(lbl, False, True, 0) 114 hb.pack_start(entry, True, True, 0) 115 vb.pack_start(hb, True, True, 0) 116 117 # Bookmark name to use for point B 118 hb = Gtk.HBox(spacing=6) 119 entry = UndoEntry() 120 entry.set_text(self.config_get(self.CFG_SEEKPOINT_B_TEXT, 121 self.DEFAULT_B_TEXT)) 122 entry.connect('changed', self.config_entry_changed, 123 self.CFG_SEEKPOINT_B_TEXT) 124 lbl = Gtk.Label(label=_("Bookmark name for point B")) 125 entry.set_tooltip_markup(_("Bookmark name to use each tick during " 126 "play of a track if it exist. If the current position exceeds " 127 "the timestamp, seek to the end of the track.")) 128 lbl.set_mnemonic_widget(entry) 129 hb.pack_start(lbl, False, True, 0) 130 hb.pack_start(entry, True, True, 0) 131 vb.pack_start(hb, True, True, 0) 132 133 return vb 134