It’s not uncommon for coursework involving technology to be graded via the inclusion of a Youtube video, which the student (learner) uploads to show their work. Timestamps can be included in the description which makes it very easy for the person who is marking (grading) the work to see whereabouts in the video the different required features/parts of the specification are shown. By way of example, this is the approach taken by Harvard’s CS50W, where both a code submission and video upload is required for submission of coursework.
Often it is specified that a video cannot exceed a certain length. What if your video is too long though, and you already generated your timestamps? In this case you would want to do two things
- Speed up the video
- Update the timestamps that you have already generated
Speeding up the video
This can be done with the open source video editing software Shotcut
- To install:
brew install shotcut
—this assumes MacOS, adjust as approprate for your OS and package manager - Launch
shotcut
and open your video—you can adjust the speed usingProperties
and then export the video after that
Updating the timestamps
Assuming you have timestamps in a file like this timestamps.md
:
0:00 Models: Your application should have at least three models in addition to the User model: one for auction listings, one for bids, and one for comments made on auction listings
0:28 Create Listing: Users should be able to visit a page to create a new listing
1:30 Active Listings Page: The default route of your web application should let users view all of the currently active auction listings
1:51 Listing Page: Clicking on a listing should take users to a page specific to that listing
etc.
Something similar to the below (youtube_speedup.py
) can be used to update the timestamps:
# Description:
# A tool to update the timestamps of a video to reflect a given speed-up factor
# Usage:
# cat timestamps.md | python youtube_speedup.py -s 1.5 -t 6:35
import sys
import argparse
import re
def validate_input(input_: str) -> None:
len_acceptable = sum([1 for c in input_ if c.isdigit() or c == ":"])
assert len(input_) == len_acceptable, f"{input_} is not a valid input (digits, colon)"
def round_int(real: float) -> int:
return int(round(real, 0))
def timestamp_line_split(input_: str) -> tuple[int, int, str]:
splits = re.split(r"\s+", input_.strip())
time = splits[0]
rest = " ".join(splits[1:])
assert ":" in time, f"Invalid time format: {time} (should contain a colon e.g. MM:SS)"
mins_s, secs_s = time.split(":")
return (int(mins_s), int(secs_s), rest)
def speed_up_transform(mins: int, secs: int, speed_up: float) -> tuple[int, int]:
length_in_secs = mins * 60 + secs
new_length_in_secs = length_in_secs / speed_up
new_mins = new_length_in_secs // 60
new_secs = new_length_in_secs % 60
return (round_int(new_mins), round_int(new_secs))
def parse_timestamps(input_: str, speed_up: float) -> list[str]:
buffer = []
# Firstly let's isolate the timestamps and the descriptions
for timestamp_raw in input_.split("\n"):
timestamp = timestamp_raw.strip()
if timestamp:
mins, secs, description = timestamp_line_split(timestamp)
new_mins, new_secs = speed_up_transform(mins, secs, speed_up)
new_timestamp = f"{new_mins}:{new_secs} {description}"
buffer.append(new_timestamp)
return "\n".join(buffer)
def main():
# Create the parser
parser = argparse.ArgumentParser("Update the timestamp timestamps of a video to reflect a given speed-up. Pipe in the timestamp text")
# Add arguments
parser.add_argument("-s", "--speedup", type=float, help="speed-up factor as a float e.g. 1.5")
parser.add_argument("-t", "--time", type=str, help="Existing time e.g. 5:00")
# Parse the arguments
args = parser.parse_args()
# Probably a better way to do this
assert args.speedup, "A speed-up factor is required"
assert args.time, "A time is required"
# Calculate the new total time
validate_input(args.time)
mins, secs = args.time.split(":")
mins, secs = int(mins), int(secs)
new_mins, new_secs = speed_up_transform(mins, secs, args.speedup)
# Print the updated timestamps
input_ = sys.stdin.read()
print(parse_timestamps(input_, args.speedup))
# Print the new total time
print(f"{mins}:{secs} -> {new_mins}:{new_secs}")
if __name__ == "__main__":
main()