示例#1
0
 def root_directory(self):
     if not self._media_dirs:
         return None
     elif len(self._media_dirs) == 1:
         uri = path.path_to_uri(self._media_dirs[0]["path"])
     else:
         uri = "file:root"
     return models.Ref.directory(name="Files", uri=uri)
示例#2
0
文件: library.py 项目: connrs/mopidy
 def root_directory(self):
     if not self._media_dirs:
         return None
     elif len(self._media_dirs) == 1:
         uri = path.path_to_uri(self._media_dirs[0]['path'])
     else:
         uri = 'file:root'
     return models.Ref.directory(name='Files', uri=uri)
示例#3
0
 def root_directory(self):
     if not self._media_dirs:
         return None
     elif len(self._media_dirs) == 1:
         uri = path.path_to_uri(self._media_dirs[0]['path'])
     else:
         uri = 'file:root'
     return models.Ref.directory(name='Files', uri=uri)
示例#4
0
 def scan(self, paths):
     scanner = scan.Scanner()
     for path in paths:
         uri = path_to_uri(path)
         try:
             self.result[path] = scanner.scan(uri)
         except exceptions.ScannerError as error:
             self.errors[path] = error
示例#5
0
 def scan(self, paths):
     scanner = scan.Scanner()
     for path in paths:
         uri = path_lib.path_to_uri(path)
         key = uri[len('file://'):]
         try:
             self.result[key] = scanner.scan(uri)
         except exceptions.ScannerError as error:
             self.errors[key] = error
示例#6
0
 def scan(self, paths):
     scanner = scan.Scanner()
     for path in paths:
         uri = path_lib.path_to_uri(path)
         key = uri[len('file://'):]
         try:
             self.result[key] = scanner.scan(uri)
         except exceptions.ScannerError as error:
             self.errors[key] = error
示例#7
0
class BaseTest(unittest.TestCase):
    config = {
        'audio': {
            'buffer_time': None,
            'mixer': 'fakemixer track_max_volume=65536',
            'mixer_track': None,
            'mixer_volume': None,
            'output': 'testoutput',
            'visualizer': None,
        }
    }

    uris = [
        path.path_to_uri(path_to_data_dir('song1.wav')),
        path.path_to_uri(path_to_data_dir('song2.wav'))
    ]

    audio_class = audio.Audio

    def setUp(self):  # noqa: N802
        config = {
            'audio': {
                'buffer_time': None,
                'mixer': 'foomixer',
                'mixer_volume': None,
                'output': 'testoutput',
                'visualizer': None,
            },
            'proxy': {
                'hostname': '',
            },
        }
        self.song_uri = path.path_to_uri(path_to_data_dir('song1.wav'))
        self.audio = self.audio_class.start(config=config, mixer=None).proxy()

    def tearDown(self):  # noqa
        pykka.ActorRegistry.stop_all()

    def possibly_trigger_fake_playback_error(self, uri):
        pass

    def possibly_trigger_fake_about_to_finish(self):
        pass
示例#8
0
class BaseTest(unittest.TestCase):
    config = {
        "audio": {
            "buffer_time": None,
            "mixer": "fakemixer track_max_volume=65536",
            "mixer_track": None,
            "mixer_volume": None,
            "output": "testoutput",
            "visualizer": None,
        }
    }

    uris = [
        path.path_to_uri(path_to_data_dir("song1.wav")),
        path.path_to_uri(path_to_data_dir("song2.wav")),
    ]

    audio_class = audio.Audio

    def setUp(self):  # noqa: N802
        config = {
            "audio": {
                "buffer_time": None,
                "mixer": "foomixer",
                "mixer_volume": None,
                "output": "testoutput",
                "visualizer": None,
            },
            "proxy": {"hostname": ""},
        }
        self.song_uri = path.path_to_uri(path_to_data_dir("song1.wav"))
        self.audio = self.audio_class.start(config=config, mixer=None).proxy()

    def tearDown(self):  # noqa
        pykka.ActorRegistry.stop_all()

    def possibly_trigger_fake_playback_error(self, uri):
        pass

    def possibly_trigger_fake_about_to_finish(self):
        pass
示例#9
0
 def setUp(self):  # noqa: N802
     config = {
         "audio": {
             "buffer_time": None,
             "mixer": "foomixer",
             "mixer_volume": None,
             "output": "testoutput",
             "visualizer": None,
         },
         "proxy": {"hostname": ""},
     }
     self.song_uri = path.path_to_uri(path_to_data_dir("song1.wav"))
     self.audio = self.audio_class.start(config=config, mixer=None).proxy()
示例#10
0
    def browse(self, uri):
        logger.debug("Browsing files at: %s", uri)
        result = []
        local_path = path.uri_to_path(uri)

        if str(local_path) == "root":
            return list(self._get_media_dirs_refs())

        if not self._is_in_basedir(local_path):
            logger.warning(
                "Rejected attempt to browse path (%s) outside dirs defined "
                "in file/media_dirs config.",
                uri,
            )
            return []
        if path.uri_to_path(uri).is_file():
            logger.error("Rejected attempt to browse file (%s)", uri)
            return []

        for dir_entry in local_path.iterdir():
            child_path = dir_entry.resolve()
            uri = path.path_to_uri(child_path)

            if not self._show_dotfiles and dir_entry.name.startswith("."):
                continue

            if (self._excluded_file_extensions
                    and dir_entry.suffix in self._excluded_file_extensions):
                continue

            if child_path.is_symlink() and not self._follow_symlinks:
                logger.debug("Ignoring symlink: %s", uri)
                continue

            if not self._is_in_basedir(child_path):
                logger.debug("Ignoring symlink to outside base dir: %s", uri)
                continue

            if child_path.is_dir():
                result.append(
                    models.Ref.directory(name=dir_entry.name, uri=uri))
            elif child_path.is_file():
                result.append(models.Ref.track(name=dir_entry.name, uri=uri))

        def order(item):
            return (item.type != models.Ref.DIRECTORY, item.name)

        result.sort(key=order)

        return result
示例#11
0
 def setUp(self):  # noqa: N802
     config = {
         'audio': {
             'mixer': 'foomixer',
             'mixer_volume': None,
             'output': 'testoutput',
             'visualizer': None,
         },
         'proxy': {
             'hostname': '',
         },
     }
     self.song_uri = path.path_to_uri(path_to_data_dir('song1.wav'))
     self.audio = self.audio_class.start(config=config, mixer=None).proxy()
示例#12
0
 def setUp(self):  # noqa: N802
     config = {
         'audio': {
             'mixer': 'foomixer',
             'mixer_volume': None,
             'output': 'testoutput',
             'visualizer': None,
         },
         'proxy': {
             'hostname': '',
         },
     }
     self.song_uri = path.path_to_uri(path_to_data_dir('song1.wav'))
     self.audio = self.audio_class.start(config=config, mixer=None).proxy()
示例#13
0
    def browse(self, uri):
        logger.debug('Browsing files at: %s', uri)
        result = []
        local_path = path.uri_to_path(uri)

        if local_path == 'root':
            return list(self._get_media_dirs_refs())

        if not self._is_in_basedir(os.path.realpath(local_path)):
            logger.warning(
                'Rejected attempt to browse path (%s) outside dirs defined '
                'in file/media_dirs config.', uri)
            return []

        for dir_entry in os.listdir(local_path):
            child_path = os.path.join(local_path, dir_entry)
            uri = path.path_to_uri(child_path)

            if not self._show_dotfiles and dir_entry.startswith(b'.'):
                continue

            if (self._excluded_file_extensions
                    and dir_entry.endswith(self._excluded_file_extensions)):
                continue

            if os.path.islink(child_path) and not self._follow_symlinks:
                logger.debug('Ignoring symlink: %s', uri)
                continue

            if not self._is_in_basedir(os.path.realpath(child_path)):
                logger.debug('Ignoring symlink to outside base dir: %s', uri)
                continue

            name = dir_entry.decode(FS_ENCODING, 'replace')
            if os.path.isdir(child_path):
                result.append(models.Ref.directory(name=name, uri=uri))
            elif os.path.isfile(child_path):
                result.append(models.Ref.track(name=name, uri=uri))

        def order(item):
            return (item.type != models.Ref.DIRECTORY, item.name)

        result.sort(key=order)

        return result
示例#14
0
文件: library.py 项目: mopidy/mopidy
    def browse(self, uri):
        logger.debug('Browsing files at: %s', uri)
        result = []
        local_path = path.uri_to_path(uri)

        if local_path == 'root':
            return list(self._get_media_dirs_refs())

        if not self._is_in_basedir(os.path.realpath(local_path)):
            logger.warning(
                'Rejected attempt to browse path (%s) outside dirs defined '
                'in file/media_dirs config.', uri)
            return []

        for dir_entry in os.listdir(local_path):
            child_path = os.path.join(local_path, dir_entry)
            uri = path.path_to_uri(child_path)

            if not self._show_dotfiles and dir_entry.startswith(b'.'):
                continue

            if (self._excluded_file_extensions and
                    dir_entry.endswith(self._excluded_file_extensions)):
                continue

            if os.path.islink(child_path) and not self._follow_symlinks:
                logger.debug('Ignoring symlink: %s', uri)
                continue

            if not self._is_in_basedir(os.path.realpath(child_path)):
                logger.debug('Ignoring symlink to outside base dir: %s', uri)
                continue

            name = dir_entry.decode(FS_ENCODING, 'replace')
            if os.path.isdir(child_path):
                result.append(models.Ref.directory(name=name, uri=uri))
            elif os.path.isfile(child_path):
                result.append(models.Ref.track(name=name, uri=uri))

        def order(item):
            return (item.type != models.Ref.DIRECTORY, item.name)
        result.sort(key=order)

        return result
示例#15
0
    def test_space_in_path(self):
        result = path.path_to_uri("/tmp/test this")

        assert result == "file:///tmp/test%20this"
示例#16
0
def track_uri():
    return path.path_to_uri(path_to_data_dir("song1.wav"))
示例#17
0
        timeout = timeout_ms - (int(time.time() * 1000) - start)

    raise exceptions.ScannerError('Timeout after %dms' % timeout_ms)


if __name__ == '__main__':
    import os
    import sys

    from mopidy.internal import path

    logging.basicConfig(format='%(asctime)-15s %(levelname)s %(message)s',
                        level=log.TRACE_LOG_LEVEL)

    scanner = Scanner(5000)
    for uri in sys.argv[1:]:
        if not Gst.uri_is_valid(uri):
            uri = path.path_to_uri(os.path.abspath(uri))
        try:
            result = scanner.scan(uri)
            for key in ('uri', 'mime', 'duration', 'playable', 'seekable'):
                print('%-20s   %s' % (key, getattr(result, key)))
            print('tags')
            for tag, value in result.tags.items():
                line = '%-20s   %s' % (tag, value)
                if len(line) > 77:
                    line = line[:77] + '...'
                print(line)
        except exceptions.ScannerError as error:
            print('%s: %s' % (uri, error))
示例#18
0
def path_to_file_uri(abspath):
    """Convert absolute path to file URI."""
    # Re-export internal method for use by Mopidy-Local-* extensions.
    return path.path_to_uri(abspath)
示例#19
0
def track_uri():
    return path.path_to_uri(path_to_data_dir('song1.wav'))
示例#20
0
 def test_utf8_in_path(self):
     result = path.path_to_uri('/tmp/æøå'.encode('utf-8'))
     self.assertEqual(result, 'file:///tmp/%C3%A6%C3%B8%C3%A5')
示例#21
0
 def test_space_in_path(self):
     result = path.path_to_uri('/tmp/test this')
     self.assertEqual(result, 'file:///tmp/test%20this')
示例#22
0
def path_to_file_uri(abspath):
    """Convert absolute path to file URI."""
    # Re-export internal method for use by Mopidy-Local-* extensions.
    return path.path_to_uri(abspath)
示例#23
0
    def test_unicode_in_path(self):
        result = path.path_to_uri("/tmp/æøå")

        assert result == "file:///tmp/%C3%A6%C3%B8%C3%A5"
示例#24
0
    def run(self, args, config):
        media_dir = config['local']['media_dir']
        scan_timeout = config['local']['scan_timeout']
        flush_threshold = config['local']['scan_flush_threshold']
        excluded_file_extensions = config['local']['excluded_file_extensions']
        excluded_file_extensions = tuple(
            bytes(file_ext.lower()) for file_ext in excluded_file_extensions)

        library = _get_library(args, config)
        if library is None:
            return 1

        file_mtimes, file_errors = path.find_mtimes(
            media_dir, follow=config['local']['scan_follow_symlinks'])

        logger.info('Found %d files in media_dir.', len(file_mtimes))

        if file_errors:
            logger.warning('Encountered %d errors while scanning media_dir.',
                           len(file_errors))
        for name in file_errors:
            logger.debug('Scan error %r for %r', file_errors[name], name)

        num_tracks = library.load()
        logger.info('Checking %d tracks from library.', num_tracks)

        uris_to_update = set()
        uris_to_remove = set()
        uris_in_library = set()

        for track in library.begin():
            abspath = translator.local_track_uri_to_path(track.uri, media_dir)
            mtime = file_mtimes.get(abspath)
            if mtime is None:
                logger.debug('Missing file %s', track.uri)
                uris_to_remove.add(track.uri)
            elif mtime > track.last_modified or args.force:
                uris_to_update.add(track.uri)
            uris_in_library.add(track.uri)

        logger.info('Removing %d missing tracks.', len(uris_to_remove))
        for uri in uris_to_remove:
            library.remove(uri)

        for abspath in file_mtimes:
            relpath = os.path.relpath(abspath, media_dir)
            uri = translator.path_to_local_track_uri(relpath)

            if b'/.' in relpath:
                logger.debug('Skipped %s: Hidden directory/file.', uri)
            elif relpath.lower().endswith(excluded_file_extensions):
                logger.debug('Skipped %s: File extension excluded.', uri)
            elif uri not in uris_in_library:
                uris_to_update.add(uri)

        logger.info('Found %d tracks which need to be updated.',
                    len(uris_to_update))
        logger.info('Scanning...')

        uris_to_update = sorted(uris_to_update, key=lambda v: v.lower())
        uris_to_update = uris_to_update[:args.limit]

        scanner = scan.Scanner(scan_timeout)
        progress = _Progress(flush_threshold, len(uris_to_update))

        for uri in uris_to_update:
            try:
                relpath = translator.local_track_uri_to_path(uri, media_dir)
                file_uri = path.path_to_uri(os.path.join(media_dir, relpath))
                result = scanner.scan(file_uri)
                tags, duration = result.tags, result.duration
                if not result.playable:
                    logger.warning('Failed %s: No audio found in file.', uri)
                elif duration < MIN_DURATION_MS:
                    logger.warning('Failed %s: Track shorter than %dms', uri,
                                   MIN_DURATION_MS)
                else:
                    mtime = file_mtimes.get(os.path.join(media_dir, relpath))
                    track = utils.convert_tags_to_track(tags).replace(
                        uri=uri, length=duration, last_modified=mtime)
                    if library.add_supports_tags_and_duration:
                        library.add(track, tags=tags, duration=duration)
                    else:
                        library.add(track)
                    logger.debug('Added %s', track.uri)
            except exceptions.ScannerError as error:
                logger.warning('Failed %s: %s', uri, error)

            if progress.increment():
                progress.log()
                if library.flush():
                    logger.debug('Progress flushed.')

        progress.log()
        library.close()
        logger.info('Done scanning.')
        return 0
示例#25
0
 def test_simple_path(self):
     result = path.path_to_uri('/etc/fstab')
     self.assertEqual(result, 'file:///etc/fstab')
示例#26
0
def test_lookup_respects_blacklist_globbing(audio, config, track_uri):
    blacklist_glob = path.path_to_uri(path_to_data_dir('')) + '*'
    config['stream']['metadata_blacklist'].append(blacklist_glob)
    backend = actor.StreamBackend(audio=audio, config=config)

    assert backend.library.lookup(track_uri) == [Track(uri=track_uri)]
示例#27
0
 def test_unicode_in_path(self):
     result = path.path_to_uri('/tmp/æøå')
     self.assertEqual(result, 'file:///tmp/%C3%A6%C3%B8%C3%A5')
示例#28
0
import os
import tempfile
import unittest

from mopidy.internal import path
from mopidy.m3u import translator
from mopidy.models import Track

from tests import path_to_data_dir

data_dir = path_to_data_dir('')
song1_path = path_to_data_dir('song1.mp3')
song2_path = path_to_data_dir('song2.mp3')
song3_path = path_to_data_dir('φοο.mp3')
encoded_path = path_to_data_dir('æøå.mp3')
song1_uri = path.path_to_uri(song1_path)
song2_uri = path.path_to_uri(song2_path)
song3_uri = path.path_to_uri(song3_path)
encoded_uri = path.path_to_uri(encoded_path)
song1_track = Track(uri=song1_uri)
song2_track = Track(uri=song2_uri)
song3_track = Track(uri=song3_uri)
encoded_track = Track(uri=encoded_uri)
song1_ext_track = song1_track.replace(name='song1')
song2_ext_track = song2_track.replace(name='song2', length=60000)
encoded_ext_track = encoded_track.replace(name='æøå')


# FIXME use mock instead of tempfile.NamedTemporaryFile

class M3UToUriTest(unittest.TestCase):
示例#29
0
 def test_latin1_in_path(self):
     result = path.path_to_uri('/tmp/æøå'.encode('latin-1'))
     self.assertEqual(result, 'file:///tmp/%E6%F8%E5')
示例#30
0
 def test_simple_path(self):
     result = path.path_to_uri('/etc/fstab')
     self.assertEqual(result, 'file:///etc/fstab')
示例#31
0
def test_lookup_respects_blacklist_globbing(backend, track_uri):
    blacklist = [path.path_to_uri(path_to_data_dir('')) + '*']
    library = actor.StreamLibraryProvider(backend, blacklist)

    assert library.lookup(track_uri) == [Track(uri=track_uri)]
示例#32
0
 def test_space_in_path(self):
     result = path.path_to_uri('/tmp/test this')
     self.assertEqual(result, 'file:///tmp/test%20this')
示例#33
0
文件: scan.py 项目: connrs/mopidy
        timeout = timeout_ms - (int(time.time() * 1000) - start)

        # workaround for https://bugzilla.gnome.org/show_bug.cgi?id=763553:
        # if we got what we want then stop playing (and wait for ASYNC_DONE)
        if tags and duration is not None:
            pipeline.set_state(Gst.State.PAUSED)

    raise exceptions.ScannerError('Timeout after %dms' % timeout_ms)


if __name__ == '__main__':
    import os
    import sys

    from mopidy.internal import path

    scanner = Scanner(5000)
    for uri in sys.argv[1:]:
        if not Gst.uri_is_valid(uri):
            uri = path.path_to_uri(os.path.abspath(uri))
        try:
            result = scanner.scan(uri)
            for key in ('uri', 'mime', 'duration', 'playable', 'seekable'):
                print('%-20s   %s' % (key, getattr(result, key)))
            print('tags')
            for tag, value in result.tags.items():
                print('%-20s   %s' % (tag, value))
        except exceptions.ScannerError as error:
            print('%s: %s' % (uri, error))
示例#34
0
 def test_unicode_in_path(self):
     result = path.path_to_uri('/tmp/æøå')
     self.assertEqual(result, 'file:///tmp/%C3%A6%C3%B8%C3%A5')
示例#35
0
文件: library.py 项目: connrs/mopidy
 def _get_media_dirs_refs(self):
     for media_dir in self._media_dirs:
         yield models.Ref.directory(
             name=media_dir['name'],
             uri=path.path_to_uri(media_dir['path']))
示例#36
0
 def test_utf8_in_path(self):
     result = path.path_to_uri('/tmp/æøå'.encode('utf-8'))
     self.assertEqual(result, 'file:///tmp/%C3%A6%C3%B8%C3%A5')
示例#37
0
 def _get_media_dirs_refs(self):
     for media_dir in self._media_dirs:
         yield models.Ref.directory(name=media_dir['name'],
                                    uri=path.path_to_uri(media_dir['path']))
示例#38
0
 def test_latin1_in_path(self):
     result = path.path_to_uri('/tmp/æøå'.encode('latin-1'))
     self.assertEqual(result, 'file:///tmp/%E6%F8%E5')
示例#39
0
def parse_m3u(file_path, media_dir=None):
    r"""
    Convert M3U file list to list of tracks

    Example M3U data::

        # This is a comment
        Alternative\Band - Song.mp3
        Classical\Other Band - New Song.mp3
        Stuff.mp3
        D:\More Music\Foo.mp3
        http://www.example.com:8000/Listen.pls
        http://www.example.com/~user/Mine.mp3

    Example extended M3U data::

        #EXTM3U
        #EXTINF:123, Sample artist - Sample title
        Sample.mp3
        #EXTINF:321,Example Artist - Example title
        Greatest Hits\Example.ogg
        #EXTINF:-1,Radio XMP
        http://mp3stream.example.com:8000/

    - Relative paths of songs should be with respect to location of M3U.
    - Paths are normally platform specific.
    - Lines starting with # are ignored, except for extended M3U directives.
    - Track.name and Track.length are set from extended M3U directives.
    - m3u files are latin-1.
    - m3u8 files are utf-8
    """
    # TODO: uris as bytes
    file_encoding = 'utf-8' if file_path.endswith(b'.m3u8') else 'latin1'

    tracks = []
    try:
        with codecs.open(file_path, 'rb', file_encoding, 'replace') as m3u:
            contents = m3u.readlines()
    except IOError as error:
        logger.warning('Couldn\'t open m3u: %s', encoding.locale_decode(error))
        return tracks

    if not contents:
        return tracks

    # Strip newlines left by codecs
    contents = [line.strip() for line in contents]

    extended = contents[0].startswith('#EXTM3U')

    track = Track()
    for line in contents:
        if line.startswith('#'):
            if extended and line.startswith('#EXTINF'):
                track = m3u_extinf_to_track(line)
            continue

        if urlparse.urlsplit(line).scheme:
            tracks.append(track.replace(uri=line))
        elif os.path.normpath(line) == os.path.abspath(line):
            uri = path.path_to_uri(line)
            tracks.append(track.replace(uri=uri))
        elif media_dir is not None:
            uri = path.path_to_uri(os.path.join(media_dir, line))
            tracks.append(track.replace(uri=uri))

        track = Track()
    return tracks
示例#40
0
def parse_m3u(file_path, media_dir=None):
    r"""
    Convert M3U file list to list of tracks

    Example M3U data::

        # This is a comment
        Alternative\Band - Song.mp3
        Classical\Other Band - New Song.mp3
        Stuff.mp3
        D:\More Music\Foo.mp3
        http://www.example.com:8000/Listen.pls
        http://www.example.com/~user/Mine.mp3

    Example extended M3U data::

        #EXTM3U
        #EXTINF:123, Sample artist - Sample title
        Sample.mp3
        #EXTINF:321,Example Artist - Example title
        Greatest Hits\Example.ogg
        #EXTINF:-1,Radio XMP
        http://mp3stream.example.com:8000/

    - Relative paths of songs should be with respect to location of M3U.
    - Paths are normally platform specific.
    - Lines starting with # are ignored, except for extended M3U directives.
    - Track.name and Track.length are set from extended M3U directives.
    - m3u files are latin-1.
    - m3u8 files are utf-8
    """
    # TODO: uris as bytes
    file_encoding = 'utf-8' if file_path.endswith(b'.m3u8') else 'latin1'

    tracks = []
    try:
        with codecs.open(file_path, 'rb', file_encoding, 'replace') as m3u:
            contents = m3u.readlines()
    except IOError as error:
        logger.warning('Couldn\'t open m3u: %s', encoding.locale_decode(error))
        return tracks

    if not contents:
        return tracks

    # Strip newlines left by codecs
    contents = [line.strip() for line in contents]

    extended = contents[0].startswith('#EXTM3U')

    track = Track()
    for line in contents:
        if line.startswith('#'):
            if extended and line.startswith('#EXTINF'):
                track = m3u_extinf_to_track(line)
            continue

        if urlparse.urlsplit(line).scheme:
            tracks.append(track.replace(uri=line))
        elif os.path.normpath(line) == os.path.abspath(line):
            uri = path.path_to_uri(line)
            tracks.append(track.replace(uri=uri))
        elif media_dir is not None:
            uri = path.path_to_uri(os.path.join(media_dir, line))
            tracks.append(track.replace(uri=uri))

        track = Track()
    return tracks
示例#41
0
    def run(self, args, config):
        media_dir = config['local']['media_dir']
        scan_timeout = config['local']['scan_timeout']
        flush_threshold = config['local']['scan_flush_threshold']
        excluded_file_extensions = config['local']['excluded_file_extensions']
        excluded_file_extensions = tuple(
            bytes(file_ext.lower()) for file_ext in excluded_file_extensions)

        library = _get_library(args, config)
        if library is None:
            return 1

        file_mtimes, file_errors = path.find_mtimes(
            media_dir, follow=config['local']['scan_follow_symlinks'])

        logger.info('Found %d files in media_dir.', len(file_mtimes))

        if file_errors:
            logger.warning('Encountered %d errors while scanning media_dir.',
                           len(file_errors))
        for name in file_errors:
            logger.debug('Scan error %r for %r', file_errors[name], name)

        num_tracks = library.load()
        logger.info('Checking %d tracks from library.', num_tracks)

        uris_to_update = set()
        uris_to_remove = set()
        uris_in_library = set()

        for track in library.begin():
            abspath = translator.local_track_uri_to_path(track.uri, media_dir)
            mtime = file_mtimes.get(abspath)
            if mtime is None:
                logger.debug('Missing file %s', track.uri)
                uris_to_remove.add(track.uri)
            elif mtime > track.last_modified or args.force:
                uris_to_update.add(track.uri)
            uris_in_library.add(track.uri)

        logger.info('Removing %d missing tracks.', len(uris_to_remove))
        for uri in uris_to_remove:
            library.remove(uri)

        for abspath in file_mtimes:
            relpath = os.path.relpath(abspath, media_dir)
            uri = translator.path_to_local_track_uri(relpath)

            if b'/.' in relpath or relpath.startswith(b'.'):
                logger.debug('Skipped %s: Hidden directory/file.', uri)
            elif relpath.lower().endswith(excluded_file_extensions):
                logger.debug('Skipped %s: File extension excluded.', uri)
            elif uri not in uris_in_library:
                uris_to_update.add(uri)

        logger.info(
            'Found %d tracks which need to be updated.', len(uris_to_update))
        logger.info('Scanning...')

        uris_to_update = sorted(uris_to_update, key=lambda v: v.lower())
        uris_to_update = uris_to_update[:args.limit]

        scanner = scan.Scanner(scan_timeout)
        progress = _Progress(flush_threshold, len(uris_to_update))

        for uri in uris_to_update:
            try:
                relpath = translator.local_track_uri_to_path(uri, media_dir)
                file_uri = path.path_to_uri(os.path.join(media_dir, relpath))
                result = scanner.scan(file_uri)
                if not result.playable:
                    logger.warning('Failed %s: No audio found in file.', uri)
                elif result.duration < MIN_DURATION_MS:
                    logger.warning('Failed %s: Track shorter than %dms',
                                   uri, MIN_DURATION_MS)
                else:
                    mtime = file_mtimes.get(os.path.join(media_dir, relpath))
                    track = tags.convert_tags_to_track(result.tags).replace(
                        uri=uri, length=result.duration, last_modified=mtime)
                    if library.add_supports_tags_and_duration:
                        library.add(
                            track, tags=result.tags, duration=result.duration)
                    else:
                        library.add(track)
                    logger.debug('Added %s', track.uri)
            except exceptions.ScannerError as error:
                logger.warning('Failed %s: %s', uri, error)

            if progress.increment():
                progress.log()
                if library.flush():
                    logger.debug('Progress flushed.')

        progress.log()
        library.close()
        logger.info('Done scanning.')
        return 0
示例#42
0
    def test_simple_path(self):
        result = path.path_to_uri("/etc/fstab")

        assert result == "file:///etc/fstab"
示例#43
0
def test_lookup_respects_blacklist_globbing(backend, track_uri):
    blacklist = [path.path_to_uri(path_to_data_dir("")) + "*"]
    library = actor.StreamLibraryProvider(backend, blacklist)

    assert library.lookup(track_uri) == [Track(uri=track_uri)]
示例#44
0
import os
import tempfile
import unittest

from mopidy.internal import path
from mopidy.m3u import translator
from mopidy.models import Track

from tests import path_to_data_dir

data_dir = path_to_data_dir('')
song1_path = path_to_data_dir('song1.mp3')
song2_path = path_to_data_dir('song2.mp3')
song3_path = path_to_data_dir('φοο.mp3')
encoded_path = path_to_data_dir('æøå.mp3')
song1_uri = path.path_to_uri(song1_path)
song2_uri = path.path_to_uri(song2_path)
song3_uri = path.path_to_uri(song3_path)
encoded_uri = path.path_to_uri(encoded_path)
song1_track = Track(uri=song1_uri)
song2_track = Track(uri=song2_uri)
song3_track = Track(uri=song3_uri)
encoded_track = Track(uri=encoded_uri)
song1_ext_track = song1_track.replace(name='song1')
song2_ext_track = song2_track.replace(name='song2', length=60000)
encoded_ext_track = encoded_track.replace(name='æøå')

# FIXME use mock instead of tempfile.NamedTemporaryFile


class M3UToUriTest(unittest.TestCase):