fix(api): file upload with library (#3213)
Some checks failed
Container / meta (analyzer) (push) Waiting to run
Container / meta (api) (push) Waiting to run
Container / meta (legacy) (push) Waiting to run
Container / meta (nginx) (push) Waiting to run
Container / meta (playout) (push) Waiting to run
Container / meta (worker) (push) Waiting to run
Container / build (push) Blocked by required conditions
Project / pre-commit (push) Waiting to run
Project / test-tools (push) Waiting to run
Release-Please / release-please (push) Waiting to run
API schema / check (push) Has been cancelled
API schema / dispatch (push) Has been cancelled
API / lint (push) Has been cancelled
API / test-with-database (bullseye) (push) Has been cancelled
API / test-with-database (focal) (push) Has been cancelled
API / test-with-database (jammy) (push) Has been cancelled
Legacy / lint (7.4) (push) Has been cancelled
Legacy / test (7.4) (push) Has been cancelled
Legacy / locale (push) Has been cancelled

### Description

Uploads with bulk_import defining --library fail with error 400 in the
REST API

The primary key of the track_type was changed from chars to a numerical
ID, and the data model expects this now in the REST endpoint. However
the bulk import still populates this field with the Char Tag.

### Testing Notes

**What I did:**

`libretime-api bulk_import --path /home/libretime/upload/ --library POD
--allowed-extensions mp3`

**How you can replicate my testing:**

see above

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Thomas Göttgens 2025-09-19 12:38:04 +02:00 committed by GitHub
parent 6ff6de7124
commit 643504edc9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 31 additions and 16 deletions

View File

@ -85,7 +85,7 @@ class Importer:
return File.objects.filter(md5=file_md5).exists()
def _upload_file(self, filepath: Path, library: Optional[str]) -> None:
def _upload_file(self, filepath: Path, library_id: Optional[int]) -> None:
try:
resp = requests.post(
f"{self.url}/rest/media",
@ -94,7 +94,11 @@ class Importer:
("file", (filepath.name, filepath.open("rb"))),
],
timeout=30,
cookies={"tt_upload": library} if library is not None else {},
cookies=(
{"tt_upload": str(library_id)}
if library_id not in (None, 0)
else {}
),
)
resp.raise_for_status()
@ -105,7 +109,7 @@ class Importer:
logger.info("deleting %s", filepath)
filepath.unlink()
def _handle_file(self, filepath: Path, library: Optional[str]) -> None:
def _handle_file(self, filepath: Path, library_id: Optional[int]) -> None:
logger.debug("handling file %s", filepath)
if not filepath.is_file():
@ -117,7 +121,7 @@ class Importer:
self._delete_file(filepath)
return
self._upload_file(filepath, library)
self._upload_file(filepath, library_id)
if self.delete_after_upload:
self._delete_file(filepath)
@ -125,7 +129,7 @@ class Importer:
def _walk_dir(
self,
path: Path,
library: Optional[str],
library_id: Optional[int],
allowed_extensions: List[str],
) -> None:
if not path.is_dir():
@ -133,13 +137,13 @@ class Importer:
for sub_path in path.iterdir():
if sub_path.is_dir():
self._walk_dir(sub_path, library, allowed_extensions)
self._walk_dir(sub_path, library_id, allowed_extensions)
continue
if sub_path.suffix.lower() not in allowed_extensions:
continue
self._handle_file(sub_path.resolve(), library)
self._handle_file(sub_path.resolve(), library_id)
def _check_library(self, library: str) -> bool:
return Library.objects.filter(code=library).exists()
@ -153,8 +157,16 @@ class Importer:
if library is not None and not self._check_library(library):
raise ValueError(f"provided library {library} does not exist")
if library:
try:
library_id = Library.objects.get(code=library).id
except Library.DoesNotExist as exc:
raise ValueError(f"provided library {library} does not exist") from exc
else:
library_id = 0
allowed_extensions = [
(x if x.startswith(".") else "." + x) for x in allowed_extensions
]
self._walk_dir(path, library, allowed_extensions)
self._walk_dir(path, library_id, allowed_extensions)

View File

@ -22,6 +22,8 @@ class Library(models.Model):
db_column="analyze_cue_points",
)
id = models.AutoField(primary_key=True)
class Meta:
managed = False
db_table = "cc_track_types"

View File

@ -30,6 +30,7 @@ def _import_paths(tmp_path: Path):
def _library():
return baker.make(
"storage.Library",
id=1,
code="MUS",
name="Music",
description="Some music",
@ -62,8 +63,8 @@ def test_importer(
):
importer.import_dir(import_paths[0], library.code, [".mp3"])
importer._handle_file.assert_called_with(import_paths[1], library.code)
importer._upload_file.assert_called_with(import_paths[1], library.code)
importer._handle_file.assert_called_with(import_paths[1], library.id)
importer._upload_file.assert_called_with(import_paths[1], library.id)
importer._delete_file.assert_not_called()
@ -76,8 +77,8 @@ def test_importer_and_delete(
importer.delete_after_upload = True
importer.import_dir(import_paths[0], library.code, [".mp3"])
importer._handle_file.assert_called_with(import_paths[1], library.code)
importer._upload_file.assert_called_with(import_paths[1], library.code)
importer._handle_file.assert_called_with(import_paths[1], library.id)
importer._upload_file.assert_called_with(import_paths[1], library.id)
importer._delete_file.assert_called_with(import_paths[1])
@ -87,11 +88,11 @@ def test_importer_existing_file(
importer: MockImporter,
library,
):
baker.make("storage.File", md5="46305a7cf42ee53976c88d337e47e940")
baker.make("storage.File", id=1, md5="46305a7cf42ee53976c88d337e47e940")
importer.import_dir(import_paths[0], library.code, [".mp3"])
importer._handle_file.assert_called_with(import_paths[1], library.code)
importer._handle_file.assert_called_with(import_paths[1], library.id)
importer._upload_file.assert_not_called()
importer._delete_file.assert_not_called()
@ -102,12 +103,12 @@ def test_importer_existing_file_and_delete(
importer: MockImporter,
library,
):
baker.make("storage.File", md5="46305a7cf42ee53976c88d337e47e940")
baker.make("storage.File", id=1, md5="46305a7cf42ee53976c88d337e47e940")
importer.delete_if_exists = True
importer.import_dir(import_paths[0], library.code, [".mp3"])
importer._handle_file.assert_called_with(import_paths[1], library.code)
importer._handle_file.assert_called_with(import_paths[1], library.id)
importer._upload_file.assert_not_called()
importer._delete_file.assert_called_with(import_paths[1])