Skip to content

Commit 2552df6

Browse files
authored
Merge pull request #1 from Morpheus2018/update-added-xml
Update added xml
2 parents fb832c9 + 667beff commit 2552df6

File tree

6 files changed

+265
-96
lines changed

6 files changed

+265
-96
lines changed

README.md

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
# Video Chapter Creator
2-
Ein einfaches Python Program zum Erstellen von Kapiteldateien als TXT für Videos.
3-
Verwendung zB. mit [MKVToolNix Chapters](https://mkvtoolnix.download/doc/mkvmerge.html#mkvmerge.chapters)
2+
Ein einfaches Python Program zum Erstellen von Kapiteldateien als TXT, XML für Videos.
3+
Basieren auf [MKVToolNix Chapters](https://mkvtoolnix.download/doc/mkvmerge.html#mkvmerge.chapters) unterstützten Typen.
44

55
```
66
python chapter_creator.py
77
```
8-
![preview.gif](https://github.com/Morpheus2018/Video-Chapter-Creator/blob/main/preview.gif)
8+
![preview_new.gif](https://github.com/Morpheus2018/Video-Chapter-Creator/blob/main/preview_new.gif)
99

10-
#### Output
10+
#### Output Types
11+
- [TXT output example](https://github.com/Morpheus2018/Video-Chapter-Creator/blob/main/test_chapters.txt)
12+
- [XML output example](https://github.com/Morpheus2018/Video-Chapter-Creator/blob/main/test_chapters.xml)
13+
#### Hotkeys
14+
```
15+
┌───────────────────────────────────────────┐
16+
│ 's' = Speichern | 'x' = Zurück / Löschen │
17+
│ 'l' = Liste | 'q' = Beenden 'STRG + C'│
18+
└───────────────────────────────────────────┘
19+
```
20+
#### Output TXT
1121

1222
```
1323
CHAPTER01=00:05:24.000
@@ -19,11 +29,4 @@ CHAPTER03NAME=Chapter 03
1929
CHAPTER04=00:43:50.000
2030
CHAPTER04NAME=Ending
2131
...
22-
```
23-
24-
#### Hotkeys
25-
26-
- **q**
27-
- Beenden durch Benutzereingabe q
28-
- **CTRL + C**
29-
- Beenden durch Benutzereingabe CTRL + C
32+
```

chapter_creator.py

Lines changed: 0 additions & 84 deletions
This file was deleted.

chapter_creatorv2.py

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
import re
2+
3+
# Farb-Codes
4+
RED = "\033[91m"
5+
GREEN = "\033[92m"
6+
YELLOW = "\033[93m"
7+
CYAN = "\033[96m"
8+
RESET = "\033[0m"
9+
10+
11+
def format_timecode(input_str):
12+
digits = re.sub(r'\D', '', input_str)
13+
if len(digits) != 6:
14+
return None
15+
hh, mm, ss = int(digits[:2]), int(digits[2:4]), int(digits[4:6])
16+
if mm > 59 or ss > 59:
17+
return None
18+
return f"{hh:02}:{mm:02}:{ss:02}.000"
19+
20+
21+
def input_loop(prompt, allow_empty=False):
22+
while True:
23+
value = input(prompt).strip()
24+
if value.lower() == 'q':
25+
print(f"{YELLOW}Beendet durch 'q'{RESET}")
26+
return 'q'
27+
elif value.lower() == 'x':
28+
return 'x'
29+
elif not value and not allow_empty:
30+
continue
31+
return value
32+
33+
34+
def main():
35+
chapters = []
36+
step_stack = ['chapters'] # Speicher für "Zurück"-Navigation
37+
chapter_counter = 1
38+
39+
print(f"""{YELLOW}
40+
┌───────────────────────────────────────────┐
41+
│ 's' = Speichern | 'x' = Zurück / Löschen │
42+
│ 'l' = Liste | 'q' = Beenden 'STRG + C'│
43+
└───────────────────────────────────────────┘
44+
{RESET}""")
45+
46+
format_choice = None
47+
filename_input = None
48+
lang_input = None
49+
full_length_formatted = None
50+
51+
try:
52+
while True:
53+
current_step = step_stack[-1]
54+
55+
if current_step == 'chapters':
56+
user_input = input(f"Zeitstempel {chapter_counter} eingeben: ").strip().lower()
57+
58+
if user_input == 'q':
59+
print(f"{YELLOW}Beendet durch Benutzereingabe 'q'{RESET}")
60+
return
61+
if user_input == 's':
62+
if not chapters:
63+
print(f"{YELLOW}ℹ️ Keine Kapitel vorhanden!{RESET}")
64+
continue
65+
step_stack.append('choose_format')
66+
continue
67+
if user_input == 'x':
68+
if chapters:
69+
last_time, last_name = chapters.pop()
70+
chapter_counter -= 1
71+
print(f"{YELLOW}⏪ Kapitel {chapter_counter} gelöscht: [{last_time} | {last_name}]{RESET}")
72+
else:
73+
print(f"{YELLOW}ℹ️ Keine Kapitel zum Zurückgehen vorhanden!{RESET}")
74+
continue
75+
if user_input == 'l':
76+
if not chapters:
77+
print(f"{YELLOW}ℹ️ Keine Kapitel vorhanden!{RESET}")
78+
else:
79+
print(f"{GREEN}🔖 Kapitelübersicht:{RESET}")
80+
for idx, (timecode, name) in enumerate(chapters, start=1):
81+
print(f"{CYAN}CHAPTER{idx:02}={timecode}{RESET}")
82+
print(f"{CYAN}CHAPTER{idx:02}NAME={name}{RESET}")
83+
continue
84+
85+
formatted = format_timecode(user_input)
86+
if not formatted:
87+
print(f"{RED}⚠️ Ungültiger Zeitstempel: {user_input}{RESET}")
88+
continue
89+
90+
while True:
91+
name_input = input(f"Kapitel Name {chapter_counter} eingeben: ").strip()
92+
if name_input.lower() == 'q':
93+
print(f"{YELLOW}Beendet durch 'q'{RESET}")
94+
return
95+
if name_input.lower() == 's':
96+
print(f"{YELLOW}ℹ️ Du brauchst einen Kapitelname um Speicher zu können!{RESET}")
97+
continue
98+
if name_input.lower() == 'x':
99+
print(f"{YELLOW}⏪ Zurück zu Zeitstempel {chapter_counter}...{RESET}")
100+
break
101+
if name_input.lower() == 'l':
102+
if not chapters:
103+
print(f"{YELLOW}ℹ️ Keine Kapitel zum Anzeigen vorhanden!{RESET}")
104+
else:
105+
print(f"{GREEN}🔖 Kapitelübersicht:{RESET}")
106+
for idx, (timecode, name) in enumerate(chapters, start=1):
107+
print(f"{CYAN}CHAPTER{idx:02}={timecode}{RESET}")
108+
print(f"{CYAN}CHAPTER{idx:02}NAME={name}{RESET}")
109+
continue
110+
if not name_input:
111+
name_input = f"Chapter {chapter_counter:02}"
112+
113+
chapters.append((formatted, name_input))
114+
chapter_counter += 1
115+
break
116+
117+
elif current_step == 'choose_format':
118+
user_input = input("Welches Format? TXT | XML | 'b' Beide: ").strip().lower()
119+
if user_input == 'q':
120+
print(f"{YELLOW}Beendet durch 'q'{RESET}")
121+
return
122+
if user_input == 'x':
123+
step_stack.pop()
124+
continue
125+
if user_input in ['txt', 'xml', 'b']:
126+
format_choice = user_input
127+
step_stack.append('choose_filename')
128+
else:
129+
print(f"{RED}⚠️ Ungültige Eingabe: {user_input}{RESET}")
130+
131+
elif current_step == 'choose_filename':
132+
filename_input = input_loop("Dateiname für die Kapitelliste: ", allow_empty=True)
133+
if filename_input == 'q':
134+
return
135+
if filename_input == 'x':
136+
step_stack.pop()
137+
continue
138+
if not filename_input:
139+
filename_input = "Kapitel"
140+
if format_choice in ['xml', 'b']:
141+
step_stack.append('xml_lang')
142+
else:
143+
step_stack.append('save')
144+
145+
elif current_step == 'xml_lang':
146+
while True:
147+
lang_input = input_loop("XML-Sprache (zB. eng, deu): ", allow_empty=True)
148+
if lang_input == 'q':
149+
return
150+
if lang_input == 'x':
151+
step_stack.pop()
152+
break
153+
if not lang_input:
154+
lang_input = "eng"
155+
if not (lang_input.isalpha() and len(lang_input) == 3):
156+
print(f"{RED}⚠️ Ungültiger Sprache! {lang_input}{RESET}")
157+
continue
158+
step_stack.append('xml_length')
159+
break
160+
161+
elif current_step == 'xml_length':
162+
full_length = input_loop("Volle Videolänge (zB. 01:23:45): ", allow_empty=True)
163+
if full_length == 'q':
164+
return
165+
if full_length == 'x':
166+
step_stack.pop()
167+
continue
168+
full_length_formatted = format_timecode(full_length)
169+
if not full_length_formatted:
170+
print(f"{RED}⚠️ Ungültiger Videolänge: {full_length}{RESET}")
171+
continue
172+
step_stack.append('save')
173+
174+
elif current_step == 'save':
175+
# TXT speichern
176+
if format_choice in ['txt', 'b']:
177+
path_txt = f"{filename_input}_chapters.txt"
178+
with open(path_txt, "w", encoding="utf-8") as f:
179+
for idx, (tc, name) in enumerate(chapters, start=1):
180+
f.write(f"CHAPTER{idx:02}={tc}\n")
181+
f.write(f"CHAPTER{idx:02}NAME={name}\n")
182+
print(f"{GREEN}TXT gespeichert: {path_txt}{RESET}")
183+
184+
# XML speichern
185+
if format_choice in ['xml', 'b']:
186+
path_xml = f"{filename_input}_chapters.xml"
187+
with open(path_xml, "w", encoding="utf-8") as f:
188+
f.write('<?xml version="1.0" encoding="ISO-8859-1"?>\n')
189+
f.write('<!DOCTYPE Chapters SYSTEM "matroskachapters.dtd">\n')
190+
f.write('<Chapters>\n <EditionEntry>\n')
191+
for i in range(len(chapters)):
192+
start = chapters[i][0]
193+
end = chapters[i + 1][0] if i + 1 < len(chapters) else full_length_formatted
194+
name = chapters[i][1]
195+
f.write(" <ChapterAtom>\n")
196+
f.write(f" <ChapterTimeStart>{start}</ChapterTimeStart>\n")
197+
f.write(f" <ChapterTimeEnd>{end}</ChapterTimeEnd>\n")
198+
f.write(" <ChapterDisplay>\n")
199+
f.write(f" <ChapterString>{name}</ChapterString>\n")
200+
f.write(f" <ChapterLanguage>{lang_input}</ChapterLanguage>\n")
201+
f.write(" </ChapterDisplay>\n")
202+
f.write(" </ChapterAtom>\n")
203+
f.write(" </EditionEntry>\n</Chapters>\n")
204+
print(f"{GREEN}XML gespeichert: {path_xml}{RESET}")
205+
return
206+
207+
except KeyboardInterrupt:
208+
print(f"\n{YELLOW}Beendet durch STRG+C{RESET}")
209+
210+
211+
if __name__ == "__main__":
212+
main()

preview.gif

-268 KB
Binary file not shown.

preview_new.gif

271 KB
Loading

test_chapters.xml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?xml version="1.0" encoding="ISO-8859-1"?>
2+
<!DOCTYPE Chapters SYSTEM "matroskachapters.dtd">
3+
<Chapters>
4+
<EditionEntry>
5+
<ChapterAtom>
6+
<ChapterTimeStart>00:05:24.000</ChapterTimeStart>
7+
<ChapterTimeEnd>00:18:27.000</ChapterTimeEnd>
8+
<ChapterDisplay>
9+
<ChapterString>Intro</ChapterString>
10+
<ChapterLanguage>deu</ChapterLanguage>
11+
</ChapterDisplay>
12+
</ChapterAtom>
13+
<ChapterAtom>
14+
<ChapterTimeStart>00:18:27.000</ChapterTimeStart>
15+
<ChapterTimeEnd>00:37:01.000</ChapterTimeEnd>
16+
<ChapterDisplay>
17+
<ChapterString>Chapter 02</ChapterString>
18+
<ChapterLanguage>deu</ChapterLanguage>
19+
</ChapterDisplay>
20+
</ChapterAtom>
21+
<ChapterAtom>
22+
<ChapterTimeStart>00:37:01.000</ChapterTimeStart>
23+
<ChapterTimeEnd>00:43:50.000</ChapterTimeEnd>
24+
<ChapterDisplay>
25+
<ChapterString>Chapter 03</ChapterString>
26+
<ChapterLanguage>deu</ChapterLanguage>
27+
</ChapterDisplay>
28+
</ChapterAtom>
29+
<ChapterAtom>
30+
<ChapterTimeStart>00:43:50.000</ChapterTimeStart>
31+
<ChapterTimeEnd>01:23:45.000</ChapterTimeEnd>
32+
<ChapterDisplay>
33+
<ChapterString>Ending</ChapterString>
34+
<ChapterLanguage>deu</ChapterLanguage>
35+
</ChapterDisplay>
36+
</ChapterAtom>
37+
</EditionEntry>
38+
</Chapters>

0 commit comments

Comments
 (0)