forked from jima80525/mp3splitter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
splitter.py
executable file
·125 lines (114 loc) · 4.2 KB
/
splitter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/env python3
import eyed3
import pathlib
import platform
import subprocess
import sys
import xml.etree.ElementTree as ET
def convert_time(time_secs):
fraction = int((time_secs % 1) * 1000)
seconds = int(time_secs)
min, sec = divmod(seconds, 60)
hour, min = divmod(min, 60)
return f"{hour:02}:{min:02}:{sec:02}.{fraction:03}"
def build_segments(filename):
audio = eyed3.load(filename)
end_time = convert_time(audio.info.time_secs)
x = audio.tag.user_text_frames
xmltext = x.get("OverDrive MediaMarkers").text
markers = ET.fromstring(xmltext)
base_chapter = "invalid I hope I never have chapters like this"
chapter_section = 0
segments = []
for marker in markers:
# chapters can be split into several shorter sections and end up being
# shown like this:
# Chapter 1 00:00.000
# Chapter 1 (03:21) 03:21.507
# Chapter 1 (06:27) 06:27.227
# Use the chapter name with a counter to create:
# Chapter 1-00 00:00.000
# Chapter 1-01 03:21.507
# Chapter 1-02 06:27.227
name = marker[0].text.strip()
if not name.startswith(base_chapter):
base_chapter = name
chapter_section = 0
name = f"{base_chapter}_{chapter_section:02}"
chapter_section += 1
start_time = marker[1].text
times = start_time.split(":")
# ffmpeg really doesn't like times with minute field > 60, but I've
# found some books that have this.
m, s = start_time.split(":", 1)
m = int(m)
h = 0
while m > 59:
h += 1
m -= 60
if h != 0:
start_time = "{0:02}:{1:02}:{2}".format(h, m, s)
name = name.replace(" ", "_")
segments.append((name, start_time, base_chapter))
return end_time, segments
def complete_segments(segments, final_time):
new_segments = []
for index, segment in enumerate(segments):
if index < len(segments) - 1:
end_time = segments[index + 1][1]
else:
end_time = final_time
new_segments.append((segment[0], segment[1], end_time, segment[2]))
return new_segments
def split_file(filename, segments, starting_track=1):
fn = pathlib.Path(filename)
subdir = pathlib.Path(fn.stem)
subdir.mkdir()
segs = []
for index, segment in enumerate(segments):
segname = f"{subdir}/{fn.stem}_{index:03}_{segment[0]}{fn.suffix}"
command = ["ffmpeg",
"-i",
"" + filename + "",
"-acodec",
"copy",
"-metadata",
f"title={segment[3]}",
"-metadata",
f"track={starting_track}",
"-ss",
f"{segment[1]}",
"-to",
f"{segment[2]}",
"" + segname + "",
]
starting_track = starting_track + 1
try:
is_win = 'Win' in platform.system()
# ffmpeg requires an output file and so it errors when it does not
# get one so we need to capture stderr, not stdout.
output = subprocess.check_output(command, stderr=subprocess.STDOUT,
universal_newlines=True,
shell=is_win)
except Exception as e:
print("exception", e)
else:
print(f"Created {segname}")
# the following can be handy for debugging ffmpeg issues
# for line in output.splitlines():
# print(f"Got line: {line}")
segs.append(segname)
return segs, starting_track
allsegs = []
starting_track = 1
for filename in sys.argv[1:]:
print(f"Processing:{filename}")
end_time, segments = build_segments(filename)
segments = complete_segments(segments, end_time)
segs, starting_track = split_file(filename, segments, starting_track)
allsegs.extend(segs)
with open("copyit.sh", "w") as ff:
print("#!/bin/sh", file=ff)
print("mkdir /media/usb0/book", file=ff)
for seg in allsegs:
print(f"cp {seg} /media/usb0/book/", file=ff)