diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..5c98b42
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,2 @@
+# Default ignored files
+/workspace.xml
\ No newline at end of file
diff --git a/.idea/dictionaries/pragma.xml b/.idea/dictionaries/pragma.xml
new file mode 100644
index 0000000..fcb7a84
--- /dev/null
+++ b/.idea/dictionaries/pragma.xml
@@ -0,0 +1,9 @@
+
+
+
+ idct
+ mpeg
+ yuvrgb
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..d1e22ec
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..42f10a7
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/pyd2v.iml b/.idea/pyd2v.iml
new file mode 100644
index 0000000..9b31378
--- /dev/null
+++ b/.idea/pyd2v.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..27fecb3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,85 @@
+# pyd2v
+
+A Python Parser for DGMPGDec's D2V Project Files
+
+`pip install pyd2v`
+
+
+
+
+
+
+
+
+
+
+## Documentation
+
+### Quick Example
+
+```
+# pip install pyd2v
+from pyd2v import D2V
+
+# ...
+
+# Choose Input File
+input_file = "/home/user/Desktop/video.d2v"
+# Parse Input File
+d2v = D2V(filename=input_file)
+# Print D2V Version and Settings Options, which will be shown with the accessible variable names.
+print(d2v)
+# Print Input Video Files
+print(d2v.videos)
+# Print Frame Rate
+print(d2v.settings["Frame_Rate"])
+# Print first frame data
+print(d2v.data[0])
+```
+
+#### Accessible Variables
+
+A successful D2V parse will result in the following options accessible from the D2V object.
+
+- version: D2V version, `16` is currently the latest for the original DGIndex which was last updated in 2010.
+- videos: List of the video file paths that were indexed by DGIndex. It will be just a filename if "Use Full Paths" was disabled in DGIndex.
+- settings: Will return various user-provided and auto-evaluated settings based on input data. More information on Settings below.
+- data: Indexing data of the MPEG video stream, Each entry is of an I frame which will describe the following non-I frames up to the next I frame.
+- data_type: What type of video is most previlent, e.g. `88.4% FILM`, `PAL`, `99.9% NTSC`.
+
+#### Settings
+
+| Auto-evaluated Settings | Possible Values | Description |
+| ----------------------- | -------------------------------------- | ---------------------------------------------------------------------------------- |
+| Stream_Type | 0=Elementary Stream | Defines the type of MPEG stream. |
+| | 1=Program Stream | |
+| | 2=Transport Stream | |
+| | 3=PVA Stream | |
+| Transport_Packet_Size | [188, 204] | Specifies the size in bytes of the transport packets. Used only for Stream_Type=2. |
+| MPEG_Type | 1=MPEG-1, 2=MPEG-2 | Defines the type of MPEG stream. |
+| Aspect_Ratio | MPEG-2: "1:1", "4:3", "16:9", "2.21:1" | Defines the Aspect Ratio of the video specified in the MPEG stream. |
+| | MPEG-1: "1:1", 0.6735, ["16:9", 625], | |
+| | 0.7615, 0.8055, ["16:9", 525], 0.8935, | |
+| | ["4:3", 625], 0.9815, 1.0255, 1.0695, | |
+| | ["4:3", 525], 1.575, 1.2015 | |
+| Picture_Size | [width, height] | Defines the size of the video _after_ clipping has been applied. |
+| Frame_Rate | rate [num, den] | 'rate' defines output framerate \* 1000. |
+
+| User-specified Settings | Possible Values | Description |
+| ----------------------- | ----------------------------------------- | ------------------------------------------------------------------------ |
+| MPEG2_Transport_PID | {Video, Audio, RCR} | Selects the video/audio PIDs to be decoded. Used only for Stream_Type=2. |
+| iDCT_Algorithm | 1=32-bit MMX | Defines the iDCT DGDecode will use to decode this video |
+| | 2=32-bit SSEMMX | |
+| | 3=32-bit SSE2MMX | |
+| | 4=64-bit Floating Point | |
+| | 5=64-bit IEEE-1180 Reference | |
+| | 6=32-bit SSEMMX (Skal) | |
+| | 7=32-bit Simple MMX (XviD) | |
+| YUVRGB_Scale | 0=TV Scale | Defines the range DGDecode will use if RGB conversion is requested. |
+| | 1=PC Scale | |
+| Luminance_Filter | {Gamma, Offset} (range of +/- 256) | Defines values for DGIndex's Luminance_Filter. |
+| Clipping | [ClipLeft,ClipRight,ClipTop,ClipBottom] | Defines values for Cropping lines of video. |
+| Field_Operation | 0=Honor Pulldown Flags | Defines values for Field Operation. |
+| | 1=Force Film | |
+| | 2=Ignore Pulldown Flags | |
+| Location | {StartFile,StartOffset,EndFile,EndOffset} | Defines start and end points for the video selection range. |
diff --git a/pyd2v/__init__.py b/pyd2v/__init__.py
new file mode 100644
index 0000000..210150b
--- /dev/null
+++ b/pyd2v/__init__.py
@@ -0,0 +1,97 @@
+class D2V:
+ """
+ Object containing information on D2V Project Files
+ """
+ def __init__(self, filename):
+ """
+ Parse a d2v file
+ :param str filename: path to the d2v file to be parsed
+ :rtype: :class:`D2V`.
+ :raises ValueError: if parsing fails
+ """
+ # Header
+ self.version = None
+ self.videos = None
+ # Settings
+ self.settings = None
+ # Data
+ self.data = None
+ self.data_type = None
+ with open(filename, mode="r", encoding="utf-8") as f:
+ # Version
+ self.version = f.readline().strip()
+ if not self.version.startswith("DGIndexProjectFile"):
+ raise ValueError(f"Expected Version Header, received:\n\t{self.version}")
+ self.version = self.version[18:] # strip "DGIndexProjectFile"
+ # Videos
+ self.videos = []
+ for n in range(int(f.readline().strip())):
+ self.videos.append(f.readline().strip())
+ # Headers Terminate Check
+ if len(f.readline().strip()) > 0:
+ raise ValueError("Unexpected data after reading Header's Video List.")
+ # Settings
+ self.settings = {}
+ int_list = ["Stream_Type", "MPEG_Type", "iDCT_Algorithm", "YUVRGB_Scale", "Field_Operation"]
+ while True:
+ line = f.readline().strip()
+ # Settings Terminate Check
+ if len(line) == 0:
+ break
+ line = line.split("=", maxsplit=2)
+ self.settings[line[0]] = int(line[1]) if line[0] in int_list else line[1]
+ if self.settings["Stream_Type"] == 2:
+ self.settings["MPEG2_Transport_PID"] = self.settings["MPEG2_Transport_PID"].split(",")
+ self.settings["MPEG2_Transport_PID"] = {
+ "Video": float(self.settings["MPEG2_Transport_PID"][0]),
+ "Audio": float(self.settings["MPEG2_Transport_PID"][1]),
+ "PCR": float(self.settings["MPEG2_Transport_PID"][2])
+ }
+ self.settings["Transport_Packet_Size"] = self.settings["Transport_Packet_Size"].split(",")
+ self.settings["Luminance_Filter"] = self.settings["Luminance_Filter"].split(",")
+ self.settings["Luminance_Filter"] = {
+ "Gamma": float(self.settings["Luminance_Filter"][0]),
+ "Offset": float(self.settings["Luminance_Filter"][1])
+ }
+ self.settings["Clipping"] = [int(x) for x in self.settings["Clipping"].split(",")]
+ if "," in self.settings["Aspect_Ratio"]:
+ self.settings["Aspect_Ratio"] = [
+ (x if ":" in x else float(x)) for x in self.settings["Aspect_Ratio"].split(",")
+ ]
+ elif ":" not in self.settings["Aspect_Ratio"]:
+ self.settings["Aspect_Ratio"] = float(self.settings["Aspect_Ratio"])
+ self.settings["Picture_Size"] = [int(x) for x in self.settings["Picture_Size"].split("x")]
+ self.settings["Frame_Rate"] = self.settings["Frame_Rate"].split(" ")
+ self.settings["Frame_Rate"] = [
+ self.settings["Frame_Rate"][0],
+ [int(x) for x in self.settings["Frame_Rate"][1].strip("()").split("/")]
+ ]
+ self.settings["Location"] = self.settings["Location"].split(",")
+ self.settings["Location"] = {
+ "StartFile": int(self.settings["Location"][0]),
+ "StartOffset": int(self.settings["Location"][1]),
+ "EndFile": int(self.settings["Location"][2]),
+ "EndOffset": int(self.settings["Location"][3])
+ }
+ # Data
+ self.data = []
+ while True:
+ line = f.readline().strip()
+ # Data Terminate Check
+ if len(line) == 0:
+ break
+ line = line.split(" ", maxsplit=7)
+ self.data.append({
+ "info": line[0],
+ "matrix": line[1],
+ "file": line[2],
+ "position": line[3],
+ "skip": line[4],
+ "vob": line[5],
+ "cell": line[6],
+ "flags": line[7].split(" ")
+ })
+ self.data_type = f.readline()[10:]
+
+ def __repr__(self):
+ return f""
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..f185d77
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,24 @@
+from setuptools import setup, find_packages
+
+with open("README.md", "r") as f:
+ readme = f.read()
+
+setup(
+ name="pyd2v",
+ version="1.0.0-post2",
+ author="PHOENiX",
+ author_email="pragma.exe@gmail.com",
+ description="A Python Parser for DGMPGDec's D2V Project Files",
+ license="MIT",
+ long_description=readme,
+ long_description_content_type="text/markdown",
+ url="https://github.com/rlaPHOENiX/pyd2v",
+ packages=find_packages(),
+ install_requires=[],
+ classifiers=[
+ "Programming Language :: Python :: 3",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+ ],
+ python_requires=">=3.6",
+)