Repository: AcademySoftwareFoundation/OpenTimelineIO (python)

Issue:
otioconvert to OTIOZ fails with relative media target_urls
## Description

otioconvert fails to convert OTIO to OTIOZ if there are media target_urls which start with "./"

Related bug: https://github.com/AcademySoftwareFoundation/OpenTimelineIO/issues/1783

## Reproduction Steps

1. Download the attached OTIO and media file.
2. Run this command:

```bash
% uvx --from opentimelineio otioconvert -i time_warp_test.otio -o time_warp_test.otioz
```

## Log output if appropriate

```
Traceback (most recent call last):
  File "/Users/jminor/.cache/uv/archive-v0/egUCtOWX8VKGzLpjlRQw-/bin/otioconvert", line 10, in <module>
    sys.exit(main())
  File "/Users/jminor/.cache/uv/archive-v0/egUCtOWX8VKGzLpjlRQw-/lib/python3.8/site-packages/opentimelineio/console/otioconvert.py", line 273, in main
    otio.adapters.write_to_file(
  File "/Users/jminor/.cache/uv/archive-v0/egUCtOWX8VKGzLpjlRQw-/lib/python3.8/site-packages/opentimelineio/adapters/__init__.py", line 203, in write_to_file
    return adapter.write_to_file(
  File "/Users/jminor/.cache/uv/archive-v0/egUCtOWX8VKGzLpjlRQw-/lib/python3.8/site-packages/opentimelineio/adapters/adapter.py", line 189, in write_to_file
    result = self._execute_function(
  File "/Users/jminor/.cache/uv/archive-v0/egUCtOWX8VKGzLpjlRQw-/lib/python3.8/site-packages/opentimelineio/plugins/python_plugin.py", line 144, in _execute_function
    return (getattr(self.module(), func_name)(**kwargs))
  File "/Users/jminor/.cache/uv/archive-v0/egUCtOWX8VKGzLpjlRQw-/lib/python3.8/site-packages/opentimelineio/adapters/otioz.py", line 93, in write_to_file
    result_otio, path_to_mr_map = utils._prepped_otio_for_bundle_and_manifest(
  File "/Users/jminor/.cache/uv/archive-v0/egUCtOWX8VKGzLpjlRQw-/lib/python3.8/site-packages/opentimelineio/adapters/file_bundle_utils.py", line 132, in _prepped_otio_for_bundle_and_manifest
    target_file = os.path.abspath(url_utils.filepath_from_url(target_url))
  File "/Users/jminor/.cache/uv/archive-v0/egUCtOWX8VKGzLpjlRQw-/lib/python3.8/site-packages/opentimelineio/url_utils.py", line 75, in filepath_from_url
    elif PureWindowsPath(filepath.parts[1]).drive:
IndexError: tuple index out of range
```

## Sample data and other attachements

[otioconvert_relative_paths_to_otioz_bug_repro.zip](https://github.com/user-attachments/files/18043730/otioconvert_relative_paths_to_otioz_bug_repro.zip)


Pull request description:
Fix bug w/ url_utils.filepath_from_url + rel paths with a single "/"
* Fix traceback when using relative paths with one "/" and not two "/" were triggering a traceback - ie "./blah" but not "./blah/blah" in url_utils. 
* url_utils is used by the otioz adapter (Which is where this was originally reported)

Fixes #1817 


Patch:
diff --git a/src/py-opentimelineio/opentimelineio/url_utils.py b/src/py-opentimelineio/opentimelineio/url_utils.py
index a67ad52..d99cc9b 100644
--- a/src/py-opentimelineio/opentimelineio/url_utils.py
+++ b/src/py-opentimelineio/opentimelineio/url_utils.py
@@ -77,7 +77,11 @@ def filepath_from_url(urlstr):
         filepath = pathlib.PurePosixPath(filepath.drive, *filepath.parts[1:])
 
     # If the specified index is a windows drive, then offset the path
-    elif pathlib.PureWindowsPath(filepath.parts[1]).drive:
+    elif (
+        # relative paths may not have a parts[1]
+        len(filepath.parts) > 1
+        and pathlib.PureWindowsPath(filepath.parts[1]).drive
+    ):
         # Remove leading "/" if/when `request.url2pathname` yields
         # "/S:/path/file.ext"
         filepath = pathlib.PurePosixPath(*filepath.parts[1:])


Test patch:
diff --git a/tests/test_url_conversions.py b/tests/test_url_conversions.py
index 2e51901..3adff3c 100644
--- a/tests/test_url_conversions.py
+++ b/tests/test_url_conversions.py
@@ -79,6 +79,14 @@ class TestConversions(unittest.TestCase):
             processed_url = otio.url_utils.filepath_from_url(url)
             self.assertEqual(processed_url, POSIX_PATH)
 
+    def test_relative_url(self):
+        # see github issue #1817 - when a relative URL has only one name after
+        # the "." (ie ./blah but not ./blah/blah)
+        self.assertEqual(
+            otio.url_utils.filepath_from_url(os.path.join(".", "docs")),
+            "docs",
+        )
+
 
 if __name__ == "__main__":
     unittest.main()