Yo, this is the last of these tools I am making. It’s also the best one! I like, put effort into this and shit, and I was only mildly drunk during the coding process. It is hand written code, for better or worse. Mostly worse. It’s also packaged much better… I made an .exe file. That’s high effort shit.
What does JAMR do?
JAMR is an “anchor mapping resync” script. Unlike my previous resync software, this one edits the funscript rather than the videos. Essentially it requires 3 parts to function:
- The original video
- A script that is perfectly synced to the “original video”
- A new video (a 4k upscale, new online source, FPS increase, whatever…)
It does a very complicated process known as “piecemeal anchor mapping” to match frames between the two video sources, store timetable data in an array, compare that time-data between the videos and the funscript, and finally warp the funscript to fit the new video. Basically, it is to automate the resyncing that is sometimes required when someone posts a new video source, but the script is desynced.
NERD SHIT ![]()
: Uses ffmpeg and ffprobe, and a whole slew of python libraries to convert frame data to 8bit grayscale hashes at a rate you can set (from once every .25 seconds to once every year), compares them between the two video files in a cpu-friendly-data-pipeline, and then uses a confidence value to set temporally minded anchor points (read: anchor points that only go forward on the timeline, not backward, to avoid every black screen/fadeout ruining the entire script) that are shared across each video that the script’s position values are “warped” around. The difference in time between anchor points on the new video give a delta that can be used as a modifier for the position value of every nearby funscript position value. The goal of this program is to get as many high confidence anchor points as possible as that results in high quality resyncing of the funscript files.
NOTE: This does not work to sync CFR video to VFR video! You will need to run my other tool on VFR video in order to sync it. Variable Frame-Rate Detection + Re-Sync Tool + Duplicate Frame Fix (Python) [UPDATED, AGAIN]
WARNING: It has the potential to be anywhere from mildly destructive to extremely destructive of the actual funscript output, since in order to resync, point positions MUST be moved. Don’t delete your old files, and be sure to carefully check the entire output script for syncing. This software has the potential to perfectly sync scripts to videos under ideal conditions – you will not ever get ideal conditions. But, if it saves you hours and hours of moving points because one person decided to poorly upscale a video you scripted to 4k…
What do you need to run it?
Windows, I think. GPU would be good. That’s pretty much it. I put everything else in the .exe and the folder.
Folder
Let me know what you think. Something something tip jar.
https://payhip.com/b/0bwS3
Source Code for you pussies who won't click strange exe files on the internet
import json
import subprocess
import os
import io
import sys
import imagehash
from PIL import Image
import tkinter as tk
from tkinter import filedialog
import bisect
def select_file(step_name, instruction, filetypes):
print(f"\n=== {step_name} ===“)
print(f”-> {instruction}")
root = tk.Tk()
root.withdraw()
root.attributes('-topmost', True)
file_path = filedialog.askopenfilename(title=instruction, filetypes=filetypes)
root.destroy()
return file_path
def get_precision_setting():
print(“\n— Select Precision Level —”)
print(“1. Faster (Sample every 2.0 seconds)”)
print(“2. Recommended / Balanced (Sample every 1.0 second)”)
print(“3. Custom interval”)
choice = input("\nEnter choice [1-3, default is 2]: ").strip()
if choice == '1':
return 2.0
elif choice == '3':
try:
return max(0.5, float(input("Enter custom interval in seconds, warning -- lower is not always better (minimum 0.5): ")))
except ValueError:
return 1.0
return 1.0
def get_video_duration(media_path):
try:
cmd = [
“ffprobe”, “-v”, “error”, “-show_entries”,
“format=duration”, “-of”,
“default=noprint_wrappers=1:nokey=1”, media_path
]
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
return float(result.stdout.strip())
except Exception:
return 0.0
def extract_hash_timeline_ram(media_path, sample_interval_sec=1.0):
print(f"\nExtracting and hashing: {os.path.basename(media_path)}…")
duration = get_video_duration(media_path)
total_expected_frames = int(duration / sample_interval_sec) if duration > 0 else 0
vf_filters = f"fps=1/{sample_interval_sec},scale=256:256,format=gray"
cmd = [
"ffmpeg",
"-hwaccel", "auto",
"-i", media_path,
"-vf", vf_filters,
"-f", "rawvideo",
"-pix_fmt", "gray",
"-"
]
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
timeline = []
frame_idx = 0
frame_size = 256 * 256
clown_frames = [
(
r" _.-._ ",
r" / \ ",
r"| o o | ",
r"| \v/ | ",
r" \ --- / ",
r" | / \ | ",
r" _|_/_\_|_ "
),
(
r" _.-._ ",
r" / \ ",
r"| - - | ",
r"| \v/ | ",
r" \ --- / ",
r" | / \ | ",
r" _|_/_\_|_ "
),
(
r" _.-._ ",
r" / \ ",
r"| . . | ",
r"| ~ ~ | ",
r" \ --- / ",
r" | \ / | ",
r" _|_/_\_|_ "
),
(
r" _.-._ ",
r" / \ ",
r"| v v | ",
r"| --- | ",
r" \ ___ / ",
r" | / \ | ",
r" _|_/_\_|_ "
)
]
clown_quotes = [
"Jesus Christ, we're doing this today...",
"Why is every fucking video file always broken?",
"I swear, one more day of this job, and you're going to find pieces of me everywhere...",
"Personally, I don't much care for coding...",
"All work and no play make John a dull boy...",
"Were you aware that there is nothing interesting going on here?...",
"This might be one of the most pathetic things I've ever witnessed.",
"Why are you reading this message? You can tab away you know."
]
first_print = True
while True:
raw_bytes = process.stdout.read(frame_size)
if len(raw_bytes) < frame_size:
break
try:
img = Image.frombytes('L', (256, 256), raw_bytes)
h = imagehash.phash(img, hash_size=8)
timestamp_ms = int(frame_idx * sample_interval_sec * 1000)
timeline.append((timestamp_ms, h))
frame_idx += 1
frame_art = clown_frames[(frame_idx // 3) % len(clown_frames)]
quote = clown_quotes[(frame_idx // 4) % len(clown_quotes)]
if total_expected_frames > 0:
percent = min(100.0, (frame_idx / total_expected_frames) * 100)
progress_str = f"[{percent:.1f}%]"
else:
progress_str = f"[Frame {frame_idx}]"
block = (
f" {frame_art[0]}\033[K\n"
f" {frame_art[1]}\033[K\n"
f" {frame_art[2]} {progress_str}\033[K\n"
f" {frame_art[3]}\033[K\n"
f" {frame_art[4]} \"{quote}\"\033[K\n"
f" {frame_art[5]}\033[K\n"
f" {frame_art[6]}\033[K\n"
)
if first_print:
sys.stdout.write(block)
sys.stdout.flush()
first_print = False
else:
sys.stdout.write(f"\033[7A{block}")
sys.stdout.flush()
except Exception:
pass
process.wait()
print()
return timeline
def build_anchor_map(original_timeline, desynced_timeline, max_hash_diff=10):
raw_matches =
for t_orig, h_orig in original_timeline:
best_match_t = None
lowest_diff = 999
for t_desync, h_desync in desynced_timeline:
diff = h_orig - h_desync
if diff <= max_hash_diff and diff < lowest_diff:
lowest_diff = diff
best_match_t = t_desync
if best_match_t is not None:
raw_matches.append((t_orig, best_match_t, lowest_diff))
anchors = []
anchor_diffs = []
last_t_orig = -1
last_t_desync = -1
for t_orig, t_desync, diff in raw_matches:
if t_orig > last_t_orig and t_desync > last_t_desync:
anchors.append((t_orig, t_desync))
anchor_diffs.append(diff)
last_t_orig = t_orig
last_t_desync = t_desync
return anchors, anchor_diffs
def print_sync_diagnostics(anchors, anchor_diffs, orig_len, desync_len):
print(“\n” + “=”*45)
print(" VIDEO & SYNC DIAGNOSTICS AKA HOW SHIT OF A JOB DID WE DO “)
print(”="*45)
if not anchors:
print("[ERROR] Confidence Score: ZERO (No matching anchors found, that's FUCKING BAD)")
print("-> Try increasing the frequency of your points maybe? Probably ain't worth running again.")
print("="*45)
return
avg_diff = sum(anchor_diffs) / len(anchor_diffs) if anchor_diffs else 0
gaps = []
for i in range(len(anchors) - 1):
gap_sec = (anchors[i+1][0] - anchors[i][0]) / 1000.0
gaps.append(gap_sec)
max_gap_sec = max(gaps) if gaps else 0
if avg_diff <= 8.0 and max_gap_sec < 45.0:
confidence = "[HIGH] (Somehow, this program did a REALLY good job anchoring points. Maybe it's a good video source. Maybe it's pure skill.)"
elif avg_diff <= 12.0 and max_gap_sec < 120.0:
confidence = "[MODERATE] (We did alright boss. The anchors are reasonibly well aligned. For an amateur...)"
else:
confidence = "[LOW / CAUTION] (Maybe it's okay? Sparse anchors or high visual variance. Verify manually! This is likely to be NOT GREAT. But maybe though.)"
print(f"Confidence Rating : {confidence}")
print(f"* Original Video Frames Hashed : {orig_len}")
print(f"* Desynced Video Frames Hashed : {desync_len}")
print(f"* Total Anchors Locked : {len(anchors)}")
print(f"* Avg Hamming Distance : {avg_diff:.1f} / 64 bits (Lower is tighter, and as you know, tighter is more gooder)")
print(f"* Largest Gap Without Anchor : {max_gap_sec:.1f} seconds")
if max_gap_sec > 60.0:
print("\n[WARNING] A large time gap was detected without an anchor point. That might mean nothing, that might mean something. Depends on your video ...")
print(" If your video features long black screens, static loops, or heavy cuts")
print(" consider spot-checking around those cuts or timestamps for desync.")
print("="*45 + "\n")
def warp_timestamp(t_orig, anchors):
if not anchors:
return t_orig
orig_times = [a[0] for a in anchors]
if t_orig <= orig_times[0]:
return max(0, t_orig + (anchors[0][1] - anchors[0][0]))
if t_orig >= orig_times[-1]:
return max(0, t_orig + (anchors[-1][1] - anchors[-1][0]))
idx = bisect.bisect_right(orig_times, t_orig) - 1
t_orig_start, t_desync_start = anchors[idx]
t_orig_end, t_desync_end = anchors[idx+1]
if t_orig_end == t_orig_start:
return t_desync_start
progress = (t_orig - t_orig_start) / (t_orig_end - t_orig_start)
return int(t_desync_start + progress * (t_desync_end - t_desync_start))
def main():
try:
print(“— John’s Anchor Mapping Resync —”)
print(“— J. A. M. R. —”)
sample_interval = get_precision_setting()
media_filetypes = [("Video Files", "*.mp4 *.mkv *.avi *.mov *.webm *.wmv"), ("All Files", "*.*")]
original_video_path = select_file(
step_name="STEP 1/3: ORIGINAL VIDEO",
instruction="Select the perfectly synced ORIGINAL video file",
filetypes=media_filetypes
)
if not original_video_path:
print("You didn't pick a video, dipshit.")
return
desynced_video_path = select_file(
step_name="STEP 2/3: DESYNCED VIDEO",
instruction="Select the NEW, desynced video file you want to fix",
filetypes=media_filetypes
)
if not desynced_video_path:
print("You didn't pick a video, dipshit.")
return
funscript_path = select_file(
step_name="STEP 3/3: FUNSCRIPT FILE",
instruction="Select the original .funscript file that matches the original video",
filetypes=[("Funscript", "*.funscript"), ("All Files", "*.*")]
)
if not funscript_path:
print("You didn't pick a funscript, dipshit.")
return
orig_timeline = extract_hash_timeline_ram(original_video_path, sample_interval_sec=sample_interval)
desync_timeline = extract_hash_timeline_ram(desynced_video_path, sample_interval_sec=sample_interval)
print("\n[3/4] Building anchor map and analyzing timeline...")
anchors, anchor_diffs = build_anchor_map(orig_timeline, desync_timeline)
print_sync_diagnostics(anchors, anchor_diffs, len(orig_timeline), len(desync_timeline))
if len(anchors) < 2:
print("[ERROR] Too few anchor points found to reliably warp the funscript. That means it fucked up, and that means BAD.")
return
print("[4/4] Warping funscript via binary search... Hopefully this doesn't ruin the script haha... KILL ME.")
with open(funscript_path, 'r', encoding='utf-8') as f:
data = json.load(f)
for action in data.get("actions", []):
action["at"] = warp_timestamp(action["at"], anchors)
output_path = funscript_path.rsplit('.', 1)[0] + "_resynced.funscript"
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=4)
print(f"\nHere's hoping that worked. Saved to:\n{output_path}")
except Exception as e:
print(f"\nError: {e}")
finally:
input("\nPress Enter to close...")
if name == “main”:
main()

