Skip to content

Commit d62d5d6

Browse files
Merge pull request #9 from JaimeFlorian27/multiplatform
feat: removed findstr dependency, added linux and mac support.
2 parents 7567164 + 3919c9b commit d62d5d6

File tree

9 files changed

+171
-72
lines changed

9 files changed

+171
-72
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ __pycache__/
1010
.Python
1111
build/
1212
bin/
13+
bin_linux/
14+
bin_mac/
15+
bin_windows/
1316
develop-eggs/
1417
dist/
1518
downloads/

README.md

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,45 @@
11
# Reference Importer
2-
## Description
3-
Script that automates the importing of video reference into Maya, automatically creating an image sequence from a video file and creating an image plane with the sequence.
4-
# Installation
5-
## 1. Download the correct version from the Releases tab
6-
>If Using Maya 2021 or under, download ***ReferenceImporter.zip***.
7-
8-
>If Using Maya 2022 or higher, download ***ReferenceImporter_Python3.zip***.
9-
## 2. Copy the contents of the zip to you Maya scripts folder
10-
>found under 'Documents/maya/***InsertMayaVerion***/scripts'
11-
## 2. Run the following command in Maya
12-
import reference_importer
13-
reference_importer.run()
14-
make sure to run it using Python
15-
16-
# Usage
17-
18-
## for a demonstration of usage please watch the video :
19-
20-
[![Script Walkthrough Video](https://img.youtube.com/vi/ObX9NU2BmZo/0.jpg)](https://www.youtube.com/watch?v=ObX9NU2BmZo "Script Walkthrough Video")
21-
### 1. Choose a video file
22-
### 2. Set the desired trimming
23-
> Use format HH:MM:SS.ms for the timecode
24-
### 3. Set output sequence name and format
25-
### 4. Set output directory for the sequence
26-
### 5. (Optional) auto creation of image plane in Maya for the sequence
27-
### 6. Import the video!!
2+
Tool to help artists import videos as image sequence in Maya without going through a
3+
video editing package. Supports multiple file formats, length trimming and specifying a
4+
target frame rate.
5+
6+
## Features
7+
- Video to image sequence conversion, for improved Reference Plane performance.
8+
- Automatic frame rate conversion (Forget about using Premiere Pro to change the frame rate of your reference!).
9+
- Automatic creation of a Reference Plane in Maya.
10+
- Multiplatform support (Windows, Mac and Linux).
11+
- [Ffmpeg](https://ffmpeg.org/) as the backend engine for video conversion.
12+
- [Qt.py](https://github.com/mottosso/Qt.py) abstraction for multiple Maya versions support (2022+).
13+
14+
## Installation
15+
1. Find the latest version under the [Releases](https://github.com/JaimeFlorian27/reference-importer/releases) section.
16+
2. Download the right distribution for your platform.
17+
- Distributions use the following naming pattern: `reference_importer_<version>_<platform>.zip`
18+
3. Extract the zip file as a folder named `reference_importer` in your `$MAYA_APP_DIR/<MAYA_VERSION>/scripts` folder.
19+
- ie: `/home/jaime/Maya/2024/scripts/reference_importer`
20+
- [What is my MAYA_APP_DIR?](https://help.autodesk.com/view/MAYAUL/2024/ENU/?guid=GUID-228CCA33-4AFE-4380-8C3D-18D23F7EAC72)
21+
22+
## Usage
23+
24+
To launch Reference Importer, run the following Python script:
25+
```
26+
import reference_importer
27+
reference_importer.run()
28+
```
29+
30+
### Workflow
31+
32+
1. Select a video file in the UI.
33+
2. Set the desired start and end timecode.
34+
- End timecode will be populated by default to the end of the video.
35+
> Use HH:MM:SS.ms as the timecode format.
36+
3. Set output image sequence name.
37+
4. Set the output format.
38+
4. Select the output directory for the sequence.
39+
4. Set the desired target frame rate.
40+
5. (Optional) Enable creation of an Image Plane in Maya for the sequence.
41+
6. Import the video!
42+
43+
#### Demonstration Video:
44+
45+
[![Reference Importer demonstration video](https://img.youtube.com/vi/ObX9NU2BmZo/0.jpg)](https://www.youtube.com/watch?v=ObX9NU2BmZo "Script Walkthrough Video")

ReferenceImporterMain.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@
33
# Video Demonstration: https://www.youtube.com/watch?v=ObX9NU2BmZo
44

55
import sys
6+
import os
7+
import platform
68
from pathlib import Path
79

10+
# Allow standalone to show in macOS
11+
if platform.system() == "Darwin":
12+
os.environ["QT_MAC_WANTS_LAYER"] = "1"
13+
814
VENDOR_PATH = Path(__file__).parent.resolve() / "vendor"
915
sys.path.insert(0, str(VENDOR_PATH))
1016

11-
import os
1217
import re
1318
from Qt import QtCore,QtWidgets
1419
from Qt.QtCompat import wrapInstance
@@ -21,7 +26,7 @@
2126
except ImportError:
2227
IN_MAYA = False
2328

24-
from .reference_importer import ImageSequencer, Ui
29+
from .reference_importer_main import ImageSequencer, Ui
2530

2631

2732

@@ -94,7 +99,7 @@ def SetInput(self):
9499
filename = filename[0]
95100

96101
if filename != "":
97-
duration = self.imageSequencer.getDuration(filename)
102+
duration = self.imageSequencer.get_duration(filename)
98103
self.ui.lineEdit_end_trim.setText(duration)
99104
self.ui.lineEdit.setText(filename)
100105
def SetOutput(self):
@@ -155,7 +160,7 @@ def CreateImageSequence(self):
155160
output_name = self.ui.lineEdit_2.text()+"_%03d"+output_ext
156161
output_file = os.path.join(output_dir, output_name)
157162

158-
self.imageSequencer.createSequence(input_file,frameRate,
163+
self.imageSequencer.create_sequence(input_file,frameRate,
159164
trim_start,trim_end,
160165
output_file)
161166

reference_importer/imageSequence.py

Lines changed: 0 additions & 41 deletions
This file was deleted.
File renamed without changes.
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
from __future__ import annotations
2+
import re
3+
import shutil
4+
import subprocess
5+
import platform
6+
from pathlib import Path
7+
8+
TIMECODE_REGEX = r"Duration: (\d{2}:\d{2}:\d{2}.\d)"
9+
10+
11+
class FfmpegError(Exception):
12+
...
13+
14+
class MissingFfmpeg(FfmpegError):
15+
...
16+
17+
18+
class ImageSequencer:
19+
def __init__(
20+
self,
21+
video_file: str = "",
22+
output_name: str = "",
23+
padding: str = "%03d",
24+
output_frame_rate: int = 24,
25+
) -> None:
26+
self.output_frame_rate = output_frame_rate
27+
self.video_file = video_file
28+
self.output_name = output_name
29+
self.padding = padding
30+
self.trim_start = 0
31+
self.trim_end = 0
32+
self.ffmpeg_path = self.get_ffmpeg_path()
33+
34+
@classmethod
35+
def get_ffmpeg_path(cls) -> Path:
36+
"""Resolves the path to ffmpeg.
37+
38+
The script first attempts to use the ffmpeg available in the bin folder,
39+
otherwise it fallbacks to the first match of ffmpeg avaiable in Path.
40+
41+
Raises:
42+
MissingFfmpeg: If no ffmpeg binaries are available.
43+
44+
"""
45+
_ffmpeg_binary = f"ffmpeg{'.exe' if platform.system() == 'Windows' else ''}"
46+
ffmpeg_executable = Path(__file__).parent.parent.resolve() / "bin" / _ffmpeg_binary
47+
if not ffmpeg_executable.exists():
48+
ffmpeg_executable = shutil.which("ffmpeg")
49+
50+
if not ffmpeg_executable:
51+
raise MissingFfmpeg()
52+
return Path(ffmpeg_executable)
53+
54+
def get_duration(self, video_file: str) -> str:
55+
"""Gets the duration of the video from the output of ffmpeg -i.
56+
57+
Args:
58+
video_file: Video file.
59+
60+
Raises:
61+
FfmpegError: If ffmpeg errors out and does not yield an output.
62+
ValueError: If there is no match for the duration in ffmpeg's output.
63+
64+
Returns:
65+
Duration of the video in timecode.
66+
"""
67+
command = f'"{self.ffmpeg_path}" -i "{video_file}"'
68+
try:
69+
output = str(
70+
subprocess.run(
71+
command,
72+
shell=True,
73+
stderr=subprocess.STDOUT,
74+
stdout=subprocess.PIPE,
75+
check=False,
76+
),
77+
)
78+
79+
except subprocess.CalledProcessError as e:
80+
_except_msg: str = "Unable to get ffmpeg output"
81+
raise FfmpegError(_except_msg) from e
82+
83+
# re.search searches across the whole string (multiline output).
84+
match: re.Match | None = re.search(TIMECODE_REGEX, output)
85+
86+
if not match:
87+
_except_msg: str = f"Unable to find Duration for video {video_file}"
88+
raise ValueError(_except_msg)
89+
90+
# the timecode is the first group of the match.
91+
duration: str = str(match.groups(0)[0])
92+
return duration
93+
94+
def create_sequence(
95+
self,
96+
input_file: str,
97+
frame_rate: int,
98+
start_trim: str,
99+
end_trim: str,
100+
output_file: str,
101+
):
102+
command = (
103+
f'"{self.ffmpeg_path}" -i "{input_file}" -r {frame_rate}'
104+
f" -vf scale=1280:-1 -q:v 3 -ss {start_trim} -to {end_trim} "
105+
f'"{output_file}"'
106+
)
107+
try:
108+
subprocess.run(command, shell=True, check=False)
109+
except subprocess.CalledProcessError as e:
110+
raise FfmpegError from e
111+
112+
113+
if __name__ == "__main__":
114+
pass
File renamed without changes.

reference_importer/ui/main_dialog_ui.py renamed to reference_importer_main/ui/main_dialog_ui.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def __init__(self, Dialog):
148148
# setupUi
149149

150150
def setTexts(self, Dialog):
151-
Dialog.setWindowTitle(u"Reference Importer v1.1.2")
151+
Dialog.setWindowTitle(u"Reference Importer v1.2.0")
152152
self.groupBox_input.setTitle(u"Input")
153153
self.label_video_file.setText(u"Video File")
154154
self.pushButton_fileExplorer_input.setText(u"Open...")

0 commit comments

Comments
 (0)