From 0fafca037c0c7db26bcdc3569793b7d8d6a40760 Mon Sep 17 00:00:00 2001 From: James W Lane Date: Mon, 9 Dec 2024 09:33:28 -0600 Subject: [PATCH 1/9] updated tubesync type directory path --- .gitignore | 3 ++ README.md | 14 ++++++++ tubesync/sync/models.py | 31 +++++++++++++++-- tubesync/sync/tests.py | 76 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index cb14cfd6..cf63cfba 100644 --- a/.gitignore +++ b/.gitignore @@ -134,3 +134,6 @@ dmypy.json Pipfile.lock .vscode/launch.json + +# Ignore Jetbrains IDE files +.idea/ \ No newline at end of file diff --git a/README.md b/README.md index b85b2b4a..f1fdb6da 100644 --- a/README.md +++ b/README.md @@ -376,7 +376,21 @@ useful if you are manually installing TubeSync in some other environment. These | HTTP_PASS | Sets the password for HTTP basic authentication | some-secure-password | | DATABASE_CONNECTION | Optional external database connection details | mysql://user:pass@host:port/database | | VIDEO_HEIGHT_CUTOFF | Smallest video height in pixels permitted to download | 240 | +| TUBESYNC_DIRECTORY_MODE | Controls how downloaded files are organized. | default | +# TubeSync Directory Mode + +Controls how downloaded files are organized. + +Values: +- default: Audio files go to `audio`, video files to `video`. +- flat: All files are placed in the root of DOWNLOAD_DIR. +- custom:,: Allows custom prefixes for audio and video directories under DOWNLOAD_DIR. + +Example: +``` +TUBESYNC_DIRECTORY_MODE=custom:music,shows +``` # Manual, non-containerised, installation diff --git a/tubesync/sync/models.py b/tubesync/sync/models.py index 2f116356..62cd8e9a 100644 --- a/tubesync/sync/models.py +++ b/tubesync/sync/models.py @@ -514,10 +514,35 @@ class Source(models.Model): @property def type_directory_path(self): - if self.source_resolution == self.SOURCE_RESOLUTION_AUDIO: - return Path(settings.DOWNLOAD_AUDIO_DIR) / self.directory + # Get the directory mode from the environment + directory_mode = os.getenv("TUBESYNC_DIRECTORY_MODE", "default") + + # Default behavior + if directory_mode == "default": + if self.source_resolution == self.SOURCE_RESOLUTION_AUDIO: + return Path(settings.DOWNLOAD_AUDIO_DIR) / self.directory + else: + return Path(settings.DOWNLOAD_VIDEO_DIR) / self.directory + + # Flat mode - Place all files in the root + elif directory_mode == "flat": + return Path(settings.DOWNLOAD_DIR) / self.directory + + # Custom mode - Parse prefixes for audio and video + elif directory_mode.startswith("custom:"): + try: + audio_prefix, video_prefix = directory_mode.split(":")[1].split(",") + except ValueError: + raise ValueError("Invalid format for TUBESYNC_DIRECTORY_MODE=custom. Expected 'custom:audio_prefix,video_prefix'.") + + if self.source_resolution == self.SOURCE_RESOLUTION_AUDIO: + return Path(settings.DOWNLOAD_DIR) / audio_prefix / self.directory + else: + return Path(settings.DOWNLOAD_DIR) / video_prefix / self.directory + + # Fallback for invalid modes else: - return Path(settings.DOWNLOAD_VIDEO_DIR) / self.directory + raise ValueError(f"Unsupported TUBESYNC_DIRECTORY_MODE: {directory_mode}") def make_directory(self): return os.makedirs(self.directory_path, exist_ok=True) diff --git a/tubesync/sync/tests.py b/tubesync/sync/tests.py index 72411894..0029a0e9 100644 --- a/tubesync/sync/tests.py +++ b/tubesync/sync/tests.py @@ -6,7 +6,9 @@ import logging +import os from datetime import datetime, timedelta +from pathlib import Path from urllib.parse import urlsplit from xml.etree import ElementTree from django.conf import settings @@ -1714,3 +1716,77 @@ class TasksTestCase(TestCase): self.assertEqual(src1.media_source.all().count(), 3) self.assertEqual(src2.media_source.all().count(), 2) self.assertEqual(Media.objects.filter(pk=m22.pk).exists(), False) + +class TypeDirectoryPathTestCase(TestCase): + def setUp(self): + # Mock settings for testing + self.audio_dir = Path("/mock/audio/dir") + self.video_dir = Path("/mock/video/dir") + self.download_dir = Path("/mock/download/dir") + settings.DOWNLOAD_AUDIO_DIR = self.audio_dir + settings.DOWNLOAD_VIDEO_DIR = self.video_dir + settings.DOWNLOAD_DIR = self.download_dir + + # Create a source object for testing + self.source = Source( + directory="test_directory", + source_resolution=Source.SOURCE_RESOLUTION_AUDIO, + ) + + def test_default_mode_audio(self): + """ + Test default mode for audio resolution. + """ + os.environ["TUBESYNC_DIRECTORY_MODE"] = "default" + expected_path = self.audio_dir / self.source.directory + self.assertEqual(self.source.type_directory_path, expected_path) + + def test_default_mode_video(self): + """ + Test default mode for video resolution. + """ + os.environ["TUBESYNC_DIRECTORY_MODE"] = "default" + self.source.source_resolution = Source.SOURCE_RESOLUTION_1080P + expected_path = self.video_dir / self.source.directory + self.assertEqual(self.source.type_directory_path, expected_path) + + def test_flat_mode(self): + """ + Test flat mode places files in the root download directory. + """ + os.environ["TUBESYNC_DIRECTORY_MODE"] = "flat" + expected_path = self.download_dir / self.source.directory + self.assertEqual(self.source.type_directory_path, expected_path) + + def test_custom_mode_audio(self): + """ + Test custom mode with prefixes for audio. + """ + os.environ["TUBESYNC_DIRECTORY_MODE"] = "custom:audio_prefix,video_prefix" + expected_path = self.download_dir / "audio_prefix" / self.source.directory + self.assertEqual(self.source.type_directory_path, expected_path) + + def test_custom_mode_video(self): + """ + Test custom mode with prefixes for video. + """ + os.environ["TUBESYNC_DIRECTORY_MODE"] = "custom:audio_prefix,video_prefix" + self.source.source_resolution = Source.SOURCE_RESOLUTION_1080P + expected_path = self.download_dir / "video_prefix" / self.source.directory + self.assertEqual(self.source.type_directory_path, expected_path) + + def test_custom_mode_invalid_format(self): + """ + Test custom mode with an invalid format. + """ + os.environ["TUBESYNC_DIRECTORY_MODE"] = "custom:only_audio_prefix" + with self.assertRaises(ValueError): + _ = self.source.type_directory_path + + def test_invalid_mode(self): + """ + Test unsupported directory mode raises an error. + """ + os.environ["TUBESYNC_DIRECTORY_MODE"] = "unsupported_mode" + with self.assertRaises(ValueError): + _ = self.source.type_directory_path \ No newline at end of file From 279a01f9bc541b9af503468a43b88db830802af9 Mon Sep 17 00:00:00 2001 From: tcely Date: Mon, 9 Dec 2024 14:06:35 -0500 Subject: [PATCH 2/9] directory mode on settings --- tubesync/tubesync/settings.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tubesync/tubesync/settings.py b/tubesync/tubesync/settings.py index d67d577a..c43bf15c 100644 --- a/tubesync/tubesync/settings.py +++ b/tubesync/tubesync/settings.py @@ -112,6 +112,15 @@ DOWNLOAD_VIDEO_DIR = 'video' DOWNLOAD_AUDIO_DIR = 'audio' SASS_PROCESSOR_ROOT = STATIC_ROOT +directory_mode = os.getenv("TUBESYNC_DIRECTORY_MODE", "default") +if directory_mode == 'flat': + DOWNLOAD_VIDEO_DIR = '.' + DOWNLOAD_AUDIO_DIR = '.' +elif directory_mode.startswith("custom:"): + custom_value = directory_mode.split(":")[1] + if str(',') in custom_value: + DOWNLOAD_AUDIO_DIR, DOWNLOAD_VIDEO_DIR = custom_value.split(',') + ROBOTS = ''' User-agent: * From a02fd5bde925662a7a2cf88ea9b30f016df44722 Mon Sep 17 00:00:00 2001 From: tcely Date: Mon, 9 Dec 2024 14:21:55 -0500 Subject: [PATCH 3/9] maxsplit and quoting --- tubesync/tubesync/settings.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tubesync/tubesync/settings.py b/tubesync/tubesync/settings.py index c43bf15c..27af1d04 100644 --- a/tubesync/tubesync/settings.py +++ b/tubesync/tubesync/settings.py @@ -112,14 +112,14 @@ DOWNLOAD_VIDEO_DIR = 'video' DOWNLOAD_AUDIO_DIR = 'audio' SASS_PROCESSOR_ROOT = STATIC_ROOT -directory_mode = os.getenv("TUBESYNC_DIRECTORY_MODE", "default") +directory_mode = os.getenv('TUBESYNC_DIRECTORY_MODE', 'default') if directory_mode == 'flat': DOWNLOAD_VIDEO_DIR = '.' DOWNLOAD_AUDIO_DIR = '.' -elif directory_mode.startswith("custom:"): - custom_value = directory_mode.split(":")[1] +elif directory_mode.startswith('custom:'): + custom_value = directory_mode.split(':', maxsplit=1)[1] if str(',') in custom_value: - DOWNLOAD_AUDIO_DIR, DOWNLOAD_VIDEO_DIR = custom_value.split(',') + DOWNLOAD_AUDIO_DIR, DOWNLOAD_VIDEO_DIR = custom_value.split(',', maxsplit=1) ROBOTS = ''' From 233d384e25ddd44d5c8261d01a7d1d90ccd48729 Mon Sep 17 00:00:00 2001 From: tcely Date: Mon, 9 Dec 2024 14:56:41 -0500 Subject: [PATCH 4/9] Removed unnecessary str --- tubesync/tubesync/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tubesync/tubesync/settings.py b/tubesync/tubesync/settings.py index 27af1d04..5eb7a8dc 100644 --- a/tubesync/tubesync/settings.py +++ b/tubesync/tubesync/settings.py @@ -118,7 +118,7 @@ if directory_mode == 'flat': DOWNLOAD_AUDIO_DIR = '.' elif directory_mode.startswith('custom:'): custom_value = directory_mode.split(':', maxsplit=1)[1] - if str(',') in custom_value: + if ',' in custom_value: DOWNLOAD_AUDIO_DIR, DOWNLOAD_VIDEO_DIR = custom_value.split(',', maxsplit=1) From 2a63e3a360d262b20239abf36dcf621a1048d444 Mon Sep 17 00:00:00 2001 From: tcely Date: Mon, 9 Dec 2024 15:26:12 -0500 Subject: [PATCH 5/9] Reset tubesync/sync/models.py (#16) --- tubesync/sync/models.py | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/tubesync/sync/models.py b/tubesync/sync/models.py index 62cd8e9a..2f116356 100644 --- a/tubesync/sync/models.py +++ b/tubesync/sync/models.py @@ -514,35 +514,10 @@ class Source(models.Model): @property def type_directory_path(self): - # Get the directory mode from the environment - directory_mode = os.getenv("TUBESYNC_DIRECTORY_MODE", "default") - - # Default behavior - if directory_mode == "default": - if self.source_resolution == self.SOURCE_RESOLUTION_AUDIO: - return Path(settings.DOWNLOAD_AUDIO_DIR) / self.directory - else: - return Path(settings.DOWNLOAD_VIDEO_DIR) / self.directory - - # Flat mode - Place all files in the root - elif directory_mode == "flat": - return Path(settings.DOWNLOAD_DIR) / self.directory - - # Custom mode - Parse prefixes for audio and video - elif directory_mode.startswith("custom:"): - try: - audio_prefix, video_prefix = directory_mode.split(":")[1].split(",") - except ValueError: - raise ValueError("Invalid format for TUBESYNC_DIRECTORY_MODE=custom. Expected 'custom:audio_prefix,video_prefix'.") - - if self.source_resolution == self.SOURCE_RESOLUTION_AUDIO: - return Path(settings.DOWNLOAD_DIR) / audio_prefix / self.directory - else: - return Path(settings.DOWNLOAD_DIR) / video_prefix / self.directory - - # Fallback for invalid modes + if self.source_resolution == self.SOURCE_RESOLUTION_AUDIO: + return Path(settings.DOWNLOAD_AUDIO_DIR) / self.directory else: - raise ValueError(f"Unsupported TUBESYNC_DIRECTORY_MODE: {directory_mode}") + return Path(settings.DOWNLOAD_VIDEO_DIR) / self.directory def make_directory(self): return os.makedirs(self.directory_path, exist_ok=True) From a487e64550282d7d704824ace1fd3af87aeb9b5a Mon Sep 17 00:00:00 2001 From: tcely Date: Mon, 9 Dec 2024 15:40:16 -0500 Subject: [PATCH 6/9] Raise an error for an unsupported value --- tubesync/tubesync/settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tubesync/tubesync/settings.py b/tubesync/tubesync/settings.py index 5eb7a8dc..1fd8499c 100644 --- a/tubesync/tubesync/settings.py +++ b/tubesync/tubesync/settings.py @@ -120,6 +120,8 @@ elif directory_mode.startswith('custom:'): custom_value = directory_mode.split(':', maxsplit=1)[1] if ',' in custom_value: DOWNLOAD_AUDIO_DIR, DOWNLOAD_VIDEO_DIR = custom_value.split(',', maxsplit=1) +elif directory_mode not in ('', 'default'): + raise ValueError(f"Unsupported TUBESYNC_DIRECTORY_MODE: {directory_mode}") ROBOTS = ''' From d33be55b51857afe8d97ee9b22ea784d0c9130e9 Mon Sep 17 00:00:00 2001 From: tcely Date: Mon, 9 Dec 2024 15:44:32 -0500 Subject: [PATCH 7/9] Raise an error for an invalid custom value --- tubesync/tubesync/settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tubesync/tubesync/settings.py b/tubesync/tubesync/settings.py index 1fd8499c..220f93d4 100644 --- a/tubesync/tubesync/settings.py +++ b/tubesync/tubesync/settings.py @@ -120,6 +120,8 @@ elif directory_mode.startswith('custom:'): custom_value = directory_mode.split(':', maxsplit=1)[1] if ',' in custom_value: DOWNLOAD_AUDIO_DIR, DOWNLOAD_VIDEO_DIR = custom_value.split(',', maxsplit=1) + else: + raise ValueError("Invalid format for TUBESYNC_DIRECTORY_MODE=custom. Expected 'custom:audio_prefix,video_prefix'.") elif directory_mode not in ('', 'default'): raise ValueError(f"Unsupported TUBESYNC_DIRECTORY_MODE: {directory_mode}") From aa3bc7e6af4b7fa8839cff4d01ba86aff3d42116 Mon Sep 17 00:00:00 2001 From: James W Lane Date: Mon, 9 Dec 2024 22:23:32 -0600 Subject: [PATCH 8/9] fixed default test --- tubesync/sync/tests.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tubesync/sync/tests.py b/tubesync/sync/tests.py index 0029a0e9..1975ba08 100644 --- a/tubesync/sync/tests.py +++ b/tubesync/sync/tests.py @@ -1719,15 +1719,9 @@ class TasksTestCase(TestCase): class TypeDirectoryPathTestCase(TestCase): def setUp(self): - # Mock settings for testing - self.audio_dir = Path("/mock/audio/dir") - self.video_dir = Path("/mock/video/dir") - self.download_dir = Path("/mock/download/dir") - settings.DOWNLOAD_AUDIO_DIR = self.audio_dir - settings.DOWNLOAD_VIDEO_DIR = self.video_dir - settings.DOWNLOAD_DIR = self.download_dir - - # Create a source object for testing + self.audio_dir = Path(settings.DOWNLOAD_AUDIO_DIR) + self.video_dir = Path(settings.DOWNLOAD_VIDEO_DIR) + self.download_dir = Path(settings.DOWNLOAD_ROOT) self.source = Source( directory="test_directory", source_resolution=Source.SOURCE_RESOLUTION_AUDIO, From ff8d00f5bd8a0af5ccc079bebe09b46a7175478b Mon Sep 17 00:00:00 2001 From: James W Lane Date: Tue, 10 Dec 2024 20:04:42 -0600 Subject: [PATCH 9/9] updating code per comments --- README.md | 14 +++----- tubesync/sync/tests.py | 64 +++++++---------------------------- tubesync/tubesync/settings.py | 13 ++----- 3 files changed, 18 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index f1fdb6da..9a565c29 100644 --- a/README.md +++ b/README.md @@ -362,7 +362,7 @@ There are a number of other environment variables you can set. These are, mostly useful if you are manually installing TubeSync in some other environment. These are: | Name | What | Example | -| --------------------------- | ------------------------------------------------------------ | ------------------------------------ | +| --------------------------- | ------------------------------------------------------------ |--------------------------------------| | DJANGO_SECRET_KEY | Django's SECRET_KEY | YJySXnQLB7UVZw2dXKDWxI5lEZaImK6l | | DJANGO_URL_PREFIX | Run TubeSync in a sub-URL on the web server | /somepath/ | | TUBESYNC_DEBUG | Enable debugging | True | @@ -376,21 +376,15 @@ useful if you are manually installing TubeSync in some other environment. These | HTTP_PASS | Sets the password for HTTP basic authentication | some-secure-password | | DATABASE_CONNECTION | Optional external database connection details | mysql://user:pass@host:port/database | | VIDEO_HEIGHT_CUTOFF | Smallest video height in pixels permitted to download | 240 | -| TUBESYNC_DIRECTORY_MODE | Controls how downloaded files are organized. | default | +| TUBESYNC_DIRECTORY_PREFIX | Controls how downloaded files are organized. | true | # TubeSync Directory Mode Controls how downloaded files are organized. Values: -- default: Audio files go to `audio`, video files to `video`. -- flat: All files are placed in the root of DOWNLOAD_DIR. -- custom:,: Allows custom prefixes for audio and video directories under DOWNLOAD_DIR. - -Example: -``` -TUBESYNC_DIRECTORY_MODE=custom:music,shows -``` +- true: Audio files go to `audio`, video files to `video`. +- false: All files are placed in the root of DOWNLOAD_DIR. # Manual, non-containerised, installation diff --git a/tubesync/sync/tests.py b/tubesync/sync/tests.py index 1975ba08..86a39366 100644 --- a/tubesync/sync/tests.py +++ b/tubesync/sync/tests.py @@ -1719,68 +1719,28 @@ class TasksTestCase(TestCase): class TypeDirectoryPathTestCase(TestCase): def setUp(self): - self.audio_dir = Path(settings.DOWNLOAD_AUDIO_DIR) - self.video_dir = Path(settings.DOWNLOAD_VIDEO_DIR) - self.download_dir = Path(settings.DOWNLOAD_ROOT) self.source = Source( directory="test_directory", source_resolution=Source.SOURCE_RESOLUTION_AUDIO, ) - def test_default_mode_audio(self): + def test_directory_prefix_default(self): """ - Test default mode for audio resolution. + Test that default directory prefix exist. """ - os.environ["TUBESYNC_DIRECTORY_MODE"] = "default" - expected_path = self.audio_dir / self.source.directory - self.assertEqual(self.source.type_directory_path, expected_path) + os.environ['TUBESYNC_DIRECTORY_PREFIX'] = '' + self.assertEqual(self.source.type_directory_path, Path(settings.DOWNLOAD_AUDIO_DIR) / 'test_directory') - def test_default_mode_video(self): + def test_directory_prefix_true(self): """ - Test default mode for video resolution. + Test that when TUBESYNC_DIRECTORY_PREFIX is set to true the directory prefix exist. """ - os.environ["TUBESYNC_DIRECTORY_MODE"] = "default" - self.source.source_resolution = Source.SOURCE_RESOLUTION_1080P - expected_path = self.video_dir / self.source.directory - self.assertEqual(self.source.type_directory_path, expected_path) + os.environ['TUBESYNC_DIRECTORY_PREFIX'] = 'true' + self.assertEqual(self.source.type_directory_path, Path(settings.DOWNLOAD_AUDIO_DIR) / 'test_directory') - def test_flat_mode(self): + def test_directory_prefix_false(self): """ - Test flat mode places files in the root download directory. + Test that when TUBESYNC_DIRECTORY_PREFIX is set to false the directory prefix does not exist. """ - os.environ["TUBESYNC_DIRECTORY_MODE"] = "flat" - expected_path = self.download_dir / self.source.directory - self.assertEqual(self.source.type_directory_path, expected_path) - - def test_custom_mode_audio(self): - """ - Test custom mode with prefixes for audio. - """ - os.environ["TUBESYNC_DIRECTORY_MODE"] = "custom:audio_prefix,video_prefix" - expected_path = self.download_dir / "audio_prefix" / self.source.directory - self.assertEqual(self.source.type_directory_path, expected_path) - - def test_custom_mode_video(self): - """ - Test custom mode with prefixes for video. - """ - os.environ["TUBESYNC_DIRECTORY_MODE"] = "custom:audio_prefix,video_prefix" - self.source.source_resolution = Source.SOURCE_RESOLUTION_1080P - expected_path = self.download_dir / "video_prefix" / self.source.directory - self.assertEqual(self.source.type_directory_path, expected_path) - - def test_custom_mode_invalid_format(self): - """ - Test custom mode with an invalid format. - """ - os.environ["TUBESYNC_DIRECTORY_MODE"] = "custom:only_audio_prefix" - with self.assertRaises(ValueError): - _ = self.source.type_directory_path - - def test_invalid_mode(self): - """ - Test unsupported directory mode raises an error. - """ - os.environ["TUBESYNC_DIRECTORY_MODE"] = "unsupported_mode" - with self.assertRaises(ValueError): - _ = self.source.type_directory_path \ No newline at end of file + os.environ['TUBESYNC_DIRECTORY_PREFIX'] = 'false' + self.assertEqual(self.source.type_directory_path, Path('.') / 'test_directory') \ No newline at end of file diff --git a/tubesync/tubesync/settings.py b/tubesync/tubesync/settings.py index 220f93d4..7bb4b2a2 100644 --- a/tubesync/tubesync/settings.py +++ b/tubesync/tubesync/settings.py @@ -112,19 +112,10 @@ DOWNLOAD_VIDEO_DIR = 'video' DOWNLOAD_AUDIO_DIR = 'audio' SASS_PROCESSOR_ROOT = STATIC_ROOT -directory_mode = os.getenv('TUBESYNC_DIRECTORY_MODE', 'default') -if directory_mode == 'flat': +directory_prefix = os.getenv('TUBESYNC_DIRECTORY_PREFIX', 'true') +if directory_prefix == 'false': DOWNLOAD_VIDEO_DIR = '.' DOWNLOAD_AUDIO_DIR = '.' -elif directory_mode.startswith('custom:'): - custom_value = directory_mode.split(':', maxsplit=1)[1] - if ',' in custom_value: - DOWNLOAD_AUDIO_DIR, DOWNLOAD_VIDEO_DIR = custom_value.split(',', maxsplit=1) - else: - raise ValueError("Invalid format for TUBESYNC_DIRECTORY_MODE=custom. Expected 'custom:audio_prefix,video_prefix'.") -elif directory_mode not in ('', 'default'): - raise ValueError(f"Unsupported TUBESYNC_DIRECTORY_MODE: {directory_mode}") - ROBOTS = ''' User-agent: *