diff --git a/config.sample b/config.sample index 7b2b548..c980c0f 100644 --- a/config.sample +++ b/config.sample @@ -8,6 +8,9 @@ ; Optional, restrict scanner to these extensions. Default: none ;scanner_extensions = mp3 ogg +; Should the scanner follow symbolic links? Default: no +follow_symlinks = no + [webapp] ; Optional cache directory. Default: /tmp/supysonic cache_dir = /var/supysonic/cache diff --git a/docs/configuration.md b/docs/configuration.md index 806df69..b1b8196 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -61,6 +61,10 @@ restricted to. Useful if you have multiple audio formats in your library but only want to serve some. If left empty, the scanner will try to read every file it finds. +`follow_symlinks`: if set to `yes`, allows the scanner to follow symbolic links. +Disabled by default, enable it only if you trust your file system as nothing is +done to handle broken links or loops. + ```ini [base] ; A database URI. See the 'schema' folder for schema creation scripts @@ -71,6 +75,9 @@ database_uri = sqlite:////var/supysonic/supysonic.db ; Optional, restrict scanner to these extensions. Default: none scanner_extensions = mp3 ogg + +; Should the scanner follow symbolic links? Default: no +follow_symlinks = no ``` ## `[webapp]` section diff --git a/supysonic/cli.py b/supysonic/cli.py index 540ed65..69b0d58 100755 --- a/supysonic/cli.py +++ b/supysonic/cli.py @@ -251,6 +251,7 @@ class SupysonicCLI(cmd.Cmd): scanner = Scanner( force=force, extensions=extensions, + follow_symlinks=self.__config.BASE["follow_symlinks"], progress=TimedProgressDisplay(self.stdout), on_folder_start=self.__unwatch_folder, on_folder_end=self.__watch_folder, diff --git a/supysonic/config.py b/supysonic/config.py index dade606..a83616d 100644 --- a/supysonic/config.py +++ b/supysonic/config.py @@ -30,6 +30,7 @@ class DefaultConfig(object): BASE = { "database_uri": "sqlite:///" + os.path.join(tempdir, "supysonic.db"), "scanner_extensions": None, + "follow_symlinks": False, } WEBAPP = { "cache_dir": tempdir, diff --git a/supysonic/daemon/server.py b/supysonic/daemon/server.py index 1f1bf31..c6d45cc 100644 --- a/supysonic/daemon/server.py +++ b/supysonic/daemon/server.py @@ -82,6 +82,7 @@ class Daemon(object): self.__scanner = Scanner( force=force, extensions=extensions, + follow_symlinks=self.__config.BASE["follow_symlinks"], on_folder_start=self.__unwatch, on_folder_end=self.__watch, ) diff --git a/supysonic/frontend/folder.py b/supysonic/frontend/folder.py index 6e5578a..3bef1cb 100644 --- a/supysonic/frontend/folder.py +++ b/supysonic/frontend/folder.py @@ -7,9 +7,6 @@ # # Distributed under terms of the GNU AGPLv3 license. -import os.path -import uuid - from flask import current_app, flash, redirect, render_template, request, url_for from pony.orm import ObjectNotFound @@ -17,7 +14,6 @@ from ..daemon.client import DaemonClient from ..daemon.exceptions import DaemonUnavailableError from ..db import Folder from ..managers.folder import FolderManager -from ..scanner import Scanner from . import admin_only, frontend diff --git a/supysonic/scanner.py b/supysonic/scanner.py index 0b5c302..e2ab933 100644 --- a/supysonic/scanner.py +++ b/supysonic/scanner.py @@ -59,6 +59,7 @@ class Scanner(Thread): self, force=False, extensions=None, + follow_symlinks=False, progress=None, on_folder_start=None, on_folder_end=None, @@ -71,6 +72,7 @@ class Scanner(Thread): self.__force = force self.__extensions = extensions + self.__follow_symlinks = follow_symlinks self.__progress = progress self.__on_folder_start = on_folder_start @@ -131,8 +133,7 @@ class Scanner(Thread): for entry in scandir(path): if entry.name.startswith("."): continue - # TODO add config setting to allow following symlinks - if entry.is_symlink(): + if entry.is_symlink() and not self.__follow_symlinks: continue elif entry.is_dir(): to_scan.append(entry.path) diff --git a/tests/base/test_scanner.py b/tests/base/test_scanner.py index 9cecdee..4659949 100644 --- a/tests/base/test_scanner.py +++ b/tests/base/test_scanner.py @@ -45,7 +45,7 @@ class ScannerTestCase(unittest.TestCase): yield tf def __scan(self, force=False): - self.scanner = Scanner(force) + self.scanner = Scanner(force=force) self.scanner.queue_folder("folder") self.scanner.run()