This file is indexed.

/usr/share/beets/beetsplug/bpd/gstplayer.py is in beets 1.4.6-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# -*- coding: utf-8 -*-
# This file is part of beets.
# Copyright 2016, Adrian Sampson.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.

"""A wrapper for the GStreamer Python bindings that exposes a simple
music player.
"""

from __future__ import division, absolute_import, print_function

import six
import sys
import time
from six.moves import _thread
import os
import copy
from six.moves import urllib
from beets import ui

import gi
gi.require_version('Gst', '1.0')
from gi.repository import GLib, Gst  # noqa: E402


Gst.init(None)


class QueryError(Exception):
    pass


class GstPlayer(object):
    """A music player abstracting GStreamer's Playbin element.

    Create a player object, then call run() to start a thread with a
    runloop. Then call play_file to play music. Use player.playing
    to check whether music is currently playing.

    A basic play queue is also implemented (just a Python list,
    player.queue, whose last element is next to play). To use it,
    just call enqueue() and then play(). When a track finishes and
    another is available on the queue, it is played automatically.
    """

    def __init__(self, finished_callback=None):
        """Initialize a player.

        If a finished_callback is provided, it is called every time a
        track started with play_file finishes.

        Once the player has been created, call run() to begin the main
        runloop in a separate thread.
        """

        # Set up the Gstreamer player. From the pygst tutorial:
        # http://pygstdocs.berlios.de/pygst-tutorial/playbin.html
        ####
        # Updated to GStreamer 1.0 with:
        # https://wiki.ubuntu.com/Novacut/GStreamer1.0
        self.player = Gst.ElementFactory.make("playbin", "player")

        if self.player is None:
            raise ui.UserError("Could not create playbin")

        fakesink = Gst.ElementFactory.make("fakesink", "fakesink")

        if fakesink is None:
            raise ui.UserError("Could not create fakesink")

        self.player.set_property("video-sink", fakesink)
        bus = self.player.get_bus()
        bus.add_signal_watch()
        bus.connect("message", self._handle_message)

        # Set up our own stuff.
        self.playing = False
        self.finished_callback = finished_callback
        self.cached_time = None
        self._volume = 1.0

    def _get_state(self):
        """Returns the current state flag of the playbin."""
        # gst's get_state function returns a 3-tuple; we just want the
        # status flag in position 1.
        return self.player.get_state(Gst.CLOCK_TIME_NONE)[1]

    def _handle_message(self, bus, message):
        """Callback for status updates from GStreamer."""
        if message.type == Gst.MessageType.EOS:
            # file finished playing
            self.player.set_state(Gst.State.NULL)
            self.playing = False
            self.cached_time = None
            if self.finished_callback:
                self.finished_callback()

        elif message.type == Gst.MessageType.ERROR:
            # error
            self.player.set_state(Gst.State.NULL)
            err, debug = message.parse_error()
            print(u"Error: {0}".format(err))
            self.playing = False

    def _set_volume(self, volume):
        """Set the volume level to a value in the range [0, 1.5]."""
        # And the volume for the playbin.
        self._volume = volume
        self.player.set_property("volume", volume)

    def _get_volume(self):
        """Get the volume as a float in the range [0, 1.5]."""
        return self._volume

    volume = property(_get_volume, _set_volume)

    def play_file(self, path):
        """Immediately begin playing the audio file at the given
        path.
        """
        self.player.set_state(Gst.State.NULL)
        if isinstance(path, six.text_type):
            path = path.encode('utf-8')
        uri = 'file://' + urllib.parse.quote(path)
        self.player.set_property("uri", uri)
        self.player.set_state(Gst.State.PLAYING)
        self.playing = True

    def play(self):
        """If paused, resume playback."""
        if self._get_state() == Gst.State.PAUSED:
            self.player.set_state(Gst.State.PLAYING)
            self.playing = True

    def pause(self):
        """Pause playback."""
        self.player.set_state(Gst.State.PAUSED)

    def stop(self):
        """Halt playback."""
        self.player.set_state(Gst.State.NULL)
        self.playing = False
        self.cached_time = None

    def run(self):
        """Start a new thread for the player.

        Call this function before trying to play any music with
        play_file() or play().
        """

        # If we don't use the MainLoop, messages are never sent.

        def start():
            loop = GLib.MainLoop()
            loop.run()

        _thread.start_new_thread(start, ())

    def time(self):
        """Returns a tuple containing (position, length) where both
        values are integers in seconds. If no stream is available,
        returns (0, 0).
        """
        fmt = Gst.Format(Gst.Format.TIME)
        try:
            posq = self.player.query_position(fmt)
            if not posq[0]:
                raise QueryError("query_position failed")
            pos = posq[1] // (10 ** 9)

            lengthq = self.player.query_duration(fmt)
            if not lengthq[0]:
                raise QueryError("query_duration failed")
            length = lengthq[1] // (10 ** 9)

            self.cached_time = (pos, length)
            return (pos, length)

        except QueryError:
            # Stream not ready. For small gaps of time, for instance
            # after seeking, the time values are unavailable. For this
            # reason, we cache recent.
            if self.playing and self.cached_time:
                return self.cached_time
            else:
                return (0, 0)

    def seek(self, position):
        """Seeks to position (in seconds)."""
        cur_pos, cur_len = self.time()
        if position > cur_len:
            self.stop()
            return

        fmt = Gst.Format(Gst.Format.TIME)
        ns = position * 10 ** 9  # convert to nanoseconds
        self.player.seek_simple(fmt, Gst.SeekFlags.FLUSH, ns)

        # save new cached time
        self.cached_time = (position, cur_len)

    def block(self):
        """Block until playing finishes."""
        while self.playing:
            time.sleep(1)


def play_simple(paths):
    """Play the files in paths in a straightforward way, without
    using the player's callback function.
    """
    p = GstPlayer()
    p.run()
    for path in paths:
        p.play_file(path)
        p.block()


def play_complicated(paths):
    """Play the files in the path one after the other by using the
    callback function to advance to the next song.
    """
    my_paths = copy.copy(paths)

    def next_song():
        my_paths.pop(0)
        p.play_file(my_paths[0])

    p = GstPlayer(next_song)
    p.run()
    p.play_file(my_paths[0])
    while my_paths:
        time.sleep(1)


if __name__ == '__main__':
    # A very simple command-line player. Just give it names of audio
    # files on the command line; these are all played in sequence.
    paths = [os.path.abspath(os.path.expanduser(p))
             for p in sys.argv[1:]]
    # play_simple(paths)
    play_complicated(paths)