-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathxmp2lrc.py
143 lines (130 loc) · 4.99 KB
/
xmp2lrc.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
"""Convert XMP track data to an LRC file for Plex"""
#!/usr/bin/python3
# Program: xmp2lrc.py
# Purpose: Convert XMP chapter marker data from Adobe Audition into an LRC file with
# track names in it
# Author: s0ph0s
# Creation Date: 2017-04-14
# Needed for arguments
import sys
# needed for floor
import math
# XMP is xml, regrettably.
from bs4 import BeautifulSoup
# JSON files
import json
def ms_to_minsec(msec):
"""Convert milliseconds to mm:ss.ss"""
# Convert the milliseconds of time to seconds
allsecs = msec / 1000
# Calculate the minute length of the provided time
mins = math.floor(allsecs / 60)
# Calculate how many seconds over the minute length it is
secs = allsecs % 60
# Format it as a string. 2 digit minutes; 5 character wide, 0-padded, 2 decimal seconds
return "%02d:%05.2f" % (mins, secs)
class XMPTracklist:
"""An XMP tracklist parser"""
def __init__(self, xml_fp):
"""Initialize this tracklist parser"""
self.xml_fp = xml_fp
self.soup = None
self.framerate = None
self.tracklist = []
self.make_soup()
self.find_framerate()
self.read_tracks()
def make_soup(self):
"""Make a BeautifulSoup object out of the provided XMP stuff"""
self.soup = BeautifulSoup(self.xml_fp, 'lxml-xml')
self.xml_fp.close()
def find_framerate(self):
"""Locate the framerate tag in the XMP soup
Necessary for converting times from difficult-to-use frame numbers to
easily-applicable millisecond counts"""
tracks_tag = self.soup.find_all("Tracks")[0]
frame_str = tracks_tag.find_all("frameRate")[0].contents[0]
frame_list = frame_str.split("f")
self.framerate = float(frame_list[1]) / 1000.0
def find_track_holder(self):
"""Find the tag that holds the track markers"""
return self.soup.find_all("Tracks")[0].find_all("Seq")[0]
def number_normalizer(self, number_as_str):
"""Normalizes the weird frame numbers into milliseconds."""
if number_as_str.isnumeric() is False:
return number_as_str
return int(round(float(number_as_str) / self.framerate))
def read_tracks(self):
"""Parse and pull out the tracks"""
# Each track is a bs4 Tag object
track_soup = self.find_track_holder()
data_attrs = ["startTime", "duration", "name"]
for track in track_soup.children:
# Initialize data storage
data_keys = []
data_vals = []
if track.name is None:
continue
# For each of the child elements in the track,
for child in track.children:
# If the name isn't None (emptystr) and the name starts with
# "xmpdm:", the prefix on all of the data tags,
if child.name is not None and child.name in data_attrs:
# Append the name (minus the prefix) to the keys list
data_keys.append(child.name.lower())
# Append the value to the values list
data_vals.append(
self.number_normalizer(child.contents[0])
)
# if child.name == "xmpdm:name":
# print("Reading %s..." % child.contents[0])
# This looks like
# {
# 'name':'Wolfgun - Road to Jupiter',
# 'starttime':10300,
# 'duration':347000
# }
data = dict(zip(data_keys, data_vals))
self.tracklist.append(data)
# Somewhat-useful usage message
if len(sys.argv) != 3:
print("Usage: xmp2lrc.py (infile.xmp|infile.json) outfile.lrc")
exit(1)
# Read the show number from the terminal
SHOWNUM = input("Episode number: ")
# Read the show title from the terminal
SHOWNAME = input("Show title: ")
# The artist will pretty much always be XBN
ARTIST = "..::XANA Creations::.."
# And the album will always be this
ALBUM = "The Friday Night Tech Podcast"
IS_JSON = False
# Open the input and output files
infile = open(sys.argv[1], "r")
outfile = open(sys.argv[2], "w")
# Parse the input file into a tracklist
tracklist = None
if sys.argv[1][-3:] == "xmp":
tracklist = XMPTracklist(infile).tracklist
IS_JSON = False
elif sys.argv[1][-4:] == "json":
tracklist = json.load(infile)
IS_JSON = True
infile.close()
# Title tag for LRC format
outfile.write("[ti:FNT-%s %s]\n" % (SHOWNUM, SHOWNAME))
# Artist tag for LRC format
outfile.write("[ar:%s]\n" % (ARTIST,))
# Album tag for LRC format
outfile.write("[al:%s]\n" % (ALBUM,))
# Loop over all of the tracks
for track in tracklist:
# Write the track to the output file
outfile.write("[%s]%s\n" % (
# Convert the millisecond time to minutes and seconds
ms_to_minsec(track['starttime']) if not IS_JSON else ms_to_minsec(track[1]),
# Just the track name
track['name'] if not IS_JSON else track[0]
))
# Close the output file
outfile.close()