2023-03-14 13:27:51 +01:00

300 lines
10 KiB
Python

from io import StringIO
from time import sleep
from datetime import datetime
import streamlit as st
from bs4 import BeautifulSoup
def find_start_seconds(region):
"""Returns start second for region."""
if len(region["Start"]) > 9:
start_time = datetime.strptime(region["Start"], "%H:%M:%S.%f")
elif len(region["Start"]) in [8, 9]:
start_time = datetime.strptime(region["Start"], "%M:%S.%f")
elif len(region["Start"]) < 8:
start_time = datetime.strptime(region["Start"], "%S.%f")
t = start_time.time()
return (t.hour * 60 + t.minute) * 60 + t.second
def find_lenght_seconds(region):
"""Returns lenght of region in seconds."""
if len(region["Length"]) > 9:
lenght = datetime.strptime(region["Length"], "%H:%M:%S.%f")
elif len(region["Length"]) in [8, 9]:
lenght = datetime.strptime(region["Length"], "%M:%S.%f")
elif len(region["Length"]) < 8:
lenght = datetime.strptime(region["Length"], "%S.%f")
t = lenght.time()
return (t.hour * 60 + t.minute) * 60 + t.second
def colorize(soup):
# Get colors from clips on timeline.
colors = {}
timeline = soup.find("Tracks")
for region in timeline.find_all("Region"):
try:
colors[str(region["Ref"])] = int(region["Colour"])
except:
pass
soup = colorize_clipboard(soup, colors)
soup = colorize_timeline(soup, colors)
return soup
def colorize_clipboard(soup, colors):
"""Give colors to clips in clipboard."""
clipboard = soup.find("Clipboard")
for group in clipboard.find_all("Group"):
# new_group_tag = soup.new_tag('Group', {'Caption':group['Caption'], 'IsExpanded':group['IsExpanded']})
new_group_tag = group
# Go over clips (nested regions).
for clip in group.find_all("Clip"):
# new_clip_tag = soup.new_tag('Clip', {'Name':clip['Name'], 'Start':clip['Start'], 'Length':clip['Length']})
new_clip_tag = clip
for region in clip.find_all("Region"):
new_region_tag = region
try:
new_region_tag["Colour"] = colors[region["Ref"]]
except KeyError:
new_region_tag["Colour"] = "0"
new_clip_tag.append(new_region_tag)
new_group_tag.append(new_clip_tag)
# Go over regions.
for region in group.find_all("Region"):
if region.parent.name == "Clip":
continue
new_region_tag = region
try:
new_region_tag["Colour"] = colors[region["Ref"]]
except KeyError:
new_region_tag["Colour"] = "0"
new_group_tag.append(new_region_tag)
new_clipboard_tag.append(new_group_tag)
soup.Session.Clipboard.replace_with(new_clipboard_tag)
return soup
def colorize_timeline(soup, colors):
print(colors)
# Give colors to clips on timeline.
new_tracks_tag = soup.find("Tracks")
tracks = soup.find("Tracks")
for track in tracks.find_all("Track"):
new_track_tag = track
for region in track.find_all("Region"):
new_region_tag = region
try:
new_region_tag["Colour"] = colors[region["Ref"]]
except KeyError:
print(region)
new_region_tag["Colour"] = "0"
new_track_tag.append(new_region_tag)
new_tracks_tag.append(new_track_tag)
soup.Session.Tracks.replace_with(new_tracks_tag)
return soup
def print_analyzing(soup):
regions = soup.find_all("Region")
n_regions = len(regions)
for region in regions:
try:
with s0:
st.markdown(f':green[{region["Name"]}]')
sleep(1 / (n_regions * 2))
except KeyError:
pass
def colorize_groups(soup):
"""Give color to regions based on groups they are in."""
groups = soup.find_all("Group")
ids = []
ids_groups = []
for group in reversed(groups):
print(group["Caption"])
regions = []
# try:
for region in group.find_all("Region"):
if region["Ref"] not in ids: # Exclude if already in another group.
regions.append(region["Ref"])
# except:
# pass
ids += regions
ids_groups.append(regions)
colors = {}
for group in ids_groups:
group_color = (ids_groups.index(group) * 50) % 360
for ref in group:
colors[ref] = group_color
soup = colorize_clipboard(soup, colors)
soup = colorize_timeline(soup, colors)
return soup
def make_rainbow(soup):
"""Returns a tracks tag colored like a rainbow."""
new_tracks_tag = soup.find("Tracks")
tracks = soup.find("Tracks")
regions = tracks.find_all("Region")
min_start = min([find_start_seconds(i) for i in regions])
max_end = max(
[int(find_start_seconds(i) + find_lenght_seconds(i)) for i in regions]
)
lenght = int(max_end - min_start)
# Give colors to clips on timeline.
new_tracks_tag = soup.find("Tracks")
tracks = soup.find("Tracks")
for track in tracks.find_all("Track"):
new_track_tag = track
for region in track.find_all("Region"):
new_region_tag = region
try:
middle = find_start_seconds(region) + find_lenght_seconds(region) / 2
new_region_tag["Colour"] = int(((middle) / lenght) * 360)
except KeyError:
new_region_tag["Colour"] = "0"
new_track_tag.append(new_region_tag)
new_tracks_tag.append(new_track_tag)
soup.Session.Tracks.replace_with(new_tracks_tag)
return soup
st.title(":green[Colorize] your :red[Hindenburg] project")
t1, t2, t3 = st.tabs(['From timeline', 'From clipboard', 'As rainbow 🌈'])
with t1:
st.markdown(
"""
1. Set colors for a few clips on your timeline, ideally at least one clip from
each recording.
2. Upload your Hindenburg project file (ending with *.nhsx*) below to add the same color
to other clips originating from the same recording.
3. Choose *Colors from timeline* to download the project file **and put it in the same folder as your original project file**.
"""
)
with st.expander(label="Unclear? See an example here.", expanded=False):
st.write("*Put colors on clips like this...*")
st.image("before.png")
st.write("*...and you get a file that looks like this*")
st.image("after.png")
with t2:
st.markdown('''
This alternative can be good if you have created clipboards for each person/interview as all clips in the same clipboard will
be given the same color.
If you have clips originating from the same recording in different clipboards it will be given the
color for the last clipbord where it is included.
Choose *Colors from clipboard* to download the project file **and put it in the same folder as your original project file**.
''')
with t3:
st.markdown('''
This alternative makes your timeline into a beautiful rainbow! Lots of love but maybe not where you will start a longer project.
Choose *Download rainbow project!* to download the project file **and put it in the same folder as your original project file**.
''')
st.markdown('No data is saved anywhere. Made by [Lasse Edfast](https://lasseedfast.se). You need to be using Hindenburg 2.0 to use colors.')
# Ask for file.
uploaded_file = st.file_uploader(
"*Upload your project file*",
label_visibility="hidden",
type="nhsx",
accept_multiple_files=False,
)
if uploaded_file:
if "soup" not in st.session_state: # Make soup.
stringio = StringIO(uploaded_file.getvalue().decode("utf-8"))
soup = BeautifulSoup(stringio.read(), "xml")
st.session_state["soup"] = soup
soup = st.session_state["soup"]
project_name = soup.find("AudioPool")["Path"].replace(" Files", "")
new_clipboard_tag = soup.new_tag("Clipboard")
s0 = st.empty()
with s0:
if 'analyzed' not in st.session_state:
print_analyzing(soup)
sleep(1)
st.session_state['analyzed'] = True
st.markdown("Choose your colors:")
sleep(0.3)
# Area to print things.
c1, c2, c3 = st.columns(3)
with c1:
if "soup1" not in st.session_state:
soup1 = colorize(soup)
st.session_state["soup1"] = soup1
soup1 = st.session_state["soup1"]
# Allow the user to download file.
st.download_button(
"🎨 Colors from timeline",
soup1.encode("utf-8"),
file_name=f"{project_name}_colors.nhsx",
)
with c2:
if "soup2" not in st.session_state:
soup2 = colorize_groups(soup)
st.session_state["soup2"] = soup2
soup2 = st.session_state["soup2"]
st.download_button(
"🎨 Colors from clipboard",
soup2.encode("utf-8"),
file_name=f"{project_name}_colors.nhsx",
)
with c3:
if "soup3" not in st.session_state:
soup3 = make_rainbow(soup)
st.session_state["soup3"] = soup3
else:
soup3 = st.session_state["soup3"]
s3 = st.empty()
with s3:
rainbow = st.button("🌈 Rainbow timeline!")
if rainbow:
for _ in range(0, 4):
with s3:
st.markdown("🌧️")
sleep(0.4)
with s3:
st.markdown("")
sleep(0.15)
with s3:
st.markdown("🌦️")
sleep(1)
with s3:
rainbows = []
for _ in range(0, 10):
rainbows.append("🌈")
st.markdown("".join(rainbows))
sleep(0.09)
with s3:
st.download_button(
"🌈 Download rainbow project! 🌈",
soup3.encode("utf-8"),
file_name=f"{project_name}_rainbow.nhsx",
)