Skip to content

Commit b156868

Browse files
committed
add movie diff
1 parent d4c9fcf commit b156868

2 files changed

Lines changed: 223 additions & 0 deletions

File tree

index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ <h1>Masterjun</h1>
4747
<a href="base.html">Number Base Conversion</a><br><br>
4848
<a href="Lua.html">Lua</a><br><br>
4949
<a href="Game/Game.html">Game</a><br><br>
50+
<a href="moviediff.html">Movie Diff</a><br><br>
5051
</div>
5152
<hr>
5253
<div class="div2">

moviediff.html

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
<!doctype html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8" />
6+
<meta name="viewport" content="width=device-width,initial-scale=1" />
7+
<title>Movie Diff</title>
8+
9+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css" />
10+
<style>@import url(http://fonts.googleapis.com/css?family=Inconsolata);@import url(http://fonts.googleapis.com/css?family=PT+Sans);@import url(http://fonts.googleapis.com/css?family=PT+Sans+Narrow:400,700);article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap;word-wrap:break-word}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}html{font-family:'PT Sans',sans-serif}pre,code{font-family:'Inconsolata',sans-serif}h1,h2,h3,h4,h5,h6{font-family:'PT Sans Narrow',sans-serif;font-weight:700}html{background-color:#073642;color:#839496;margin:1em}body{background-color:#002b36;margin:0 auto;max-width:23cm;border:1pt solid #586e75;padding:1em}code{background-color:#073642;padding:2px}a{color:#b58900}a:visited{color:#cb4b16}a:hover{color:#cb4b16}h1{color:#d33682}h2,h3,h4,h5,h6{color:#859900}pre{background-color:#002b36;color:#839496;border:1pt solid #586e75;padding:1em;box-shadow:5pt 5pt 8pt #073642}pre code{background-color:#002b36}h1{font-size:2.8em}h2{font-size:2.4em}h3{font-size:1.8em}h4{font-size:1.4em}h5{font-size:1.3em}h6{font-size:1.15em}.tag{background-color:#073642;color:#d33682;padding:0 .2em}.todo,.next,.done{color:#002b36;background-color:#dc322f;padding:0 .2em}.tag{-webkit-border-radius:.35em;-moz-border-radius:.35em;border-radius:.35em}.TODO{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#2aa198}.NEXT{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#268bd2}.ACTIVE{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#268bd2}.DONE{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#859900}.WAITING{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#cb4b16}.HOLD{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#d33682}.NOTE{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#d33682}.CANCELLED{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#859900}</style>
11+
<style>
12+
body {
13+
max-width: unset;
14+
display: flex;
15+
flex-direction: column;
16+
border: none;
17+
}
18+
19+
h1 {
20+
font-size: 20px;
21+
margin: 0 0 12px
22+
}
23+
24+
.panel {
25+
flex: 1;
26+
border: 1px solid #6a6a6a;
27+
border-radius: 8px;
28+
padding: 12px
29+
}
30+
31+
label {
32+
display: block;
33+
font-size: 15px;
34+
}
35+
36+
.meta {
37+
font-size: 13px;
38+
margin-top: 8px
39+
}
40+
41+
.controls {
42+
display: flex;
43+
gap: 8px;
44+
align-items: center;
45+
margin: 12px 0
46+
}
47+
48+
.btn {
49+
padding: 8px 12px;
50+
border-radius: 6px;
51+
border: 1px solid #6a6a6a;
52+
cursor: pointer
53+
}
54+
55+
.btn.primary {
56+
background: #0366d6;
57+
color: white;
58+
border-color: rgba(0, 0, 0, 0.05)
59+
}
60+
61+
.status {
62+
font-size: 13px
63+
}
64+
65+
.fileLabel {
66+
margin-bottom: 4px;
67+
}
68+
69+
.files {
70+
display: flex;
71+
gap: 12px;
72+
width: fit-content
73+
}
74+
</style>
75+
</head>
76+
77+
<body>
78+
<h1>Movie Diff</h1>
79+
<div class="files">
80+
<div class="panel" id="leftPanel">
81+
<label for="leftZip" class="fileLabel">Old Movie</label>
82+
<input id="leftZip" type="file" accept=".bk2" />
83+
</div>
84+
85+
<div class="panel" id="rightPanel">
86+
<label for="rightZip" class="fileLabel">New Movie</label>
87+
<input id="rightZip" type="file" accept=".bk2" />
88+
</div>
89+
</div>
90+
91+
<div class="controls">
92+
<button id="renderBtn" class="btn primary">Render diff</button>
93+
<label class="btn" style="display:flex;gap:8px;align-items:center"><input id="viewToggle" type="checkbox"
94+
checked="true" /> Split view</label>
95+
</div>
96+
<div class="status" id="status">Waiting for files...</div>
97+
98+
<div id="diffContainer" class="panel" style="min-height:220px"></div>
99+
100+
<script src="https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js"></script>
101+
<script src="https://cdn.jsdelivr.net/npm/diff@5.1.0/dist/diff.min.js"></script>
102+
<script src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html.min.js"></script>
103+
104+
<script>
105+
async function extractInputLogFromZip(file) {
106+
if (!file) return { error: 'No file' };
107+
try {
108+
const arrayBuffer = await file.arrayBuffer();
109+
const zip = await JSZip.loadAsync(arrayBuffer);
110+
const candidates = [];
111+
zip.forEach((relativePath, zipEntry) => {
112+
const name = relativePath.replace(/^\/+|^\.\//, '');
113+
if (name.toLowerCase() === 'input log.txt'.toLowerCase()) candidates.push(zipEntry);
114+
});
115+
if (candidates.length === 0) {
116+
zip.forEach((relativePath, zipEntry) => {
117+
const parts = relativePath.split('/');
118+
const base = parts[parts.length - 1];
119+
if (base.toLowerCase() === 'input log.txt'.toLowerCase()) candidates.push(zipEntry);
120+
});
121+
}
122+
if (candidates.length === 0) return { error: 'Input Log.txt not found in the movie' };
123+
const entry = candidates[0];
124+
const text = await entry.async('text');
125+
return { text, name: entry.name };
126+
} catch (e) {
127+
return { error: 'Failed to read movie: ' + (e && e.message ? e.message : e) };
128+
}
129+
}
130+
131+
const leftInput = document.getElementById('leftZip');
132+
const rightInput = document.getElementById('rightZip');
133+
const leftMeta = document.getElementById('leftMeta');
134+
const rightMeta = document.getElementById('rightMeta');
135+
const status = document.getElementById('status');
136+
const renderBtn = document.getElementById('renderBtn');
137+
const diffContainer = document.getElementById('diffContainer');
138+
const viewToggle = document.getElementById('viewToggle');
139+
140+
let leftFile, rightFile, leftText, rightText;
141+
142+
leftInput.addEventListener('change', async (e) => {
143+
leftFile = e.target.files[0];
144+
if (leftFile && rightFile) {
145+
status.textContent = 'Ready';
146+
}
147+
});
148+
rightInput.addEventListener('change', async (e) => {
149+
rightFile = e.target.files[0];
150+
if (leftFile && rightFile) {
151+
status.textContent = 'Ready';
152+
}
153+
});
154+
155+
async function prepareAndRender() {
156+
status.textContent = 'Reading movies...';
157+
diffContainer.innerHTML = '';
158+
159+
const leftRes = await extractInputLogFromZip(leftFile);
160+
if (leftRes.error) {
161+
status.textContent = 'Old Movie: ' + leftRes.error;
162+
return;
163+
}
164+
leftText = leftRes.text;
165+
166+
const rightRes = await extractInputLogFromZip(rightFile);
167+
if (rightRes.error) {
168+
status.textContent = 'New Movie: ' + rightRes.error;
169+
return;
170+
}
171+
rightText = rightRes.text;
172+
173+
status.textContent = 'Computing diff...';
174+
175+
const leftName = leftFile ? leftFile.name : 'left';
176+
const rightName = rightFile ? rightFile.name : 'right';
177+
const patch = Diff.createTwoFilesPatch(leftName + '/Input Log.txt', rightName + '/Input Log.txt', leftText, rightText, '', '', { context: 3 });
178+
179+
const outputFormat = viewToggle.checked ? 'side-by-side' : 'line-by-line';
180+
const diffHtml = Diff2Html.html(patch, { drawFileList: false, matching: 'lines', colorScheme: 'dark', outputFormat });
181+
diffContainer.innerHTML = diffHtml;
182+
183+
status.textContent = 'Rendered';
184+
}
185+
186+
renderBtn.addEventListener('click', async () => {
187+
if (!leftFile || !rightFile) {
188+
status.textContent = 'Select both movie files first.';
189+
return;
190+
}
191+
await prepareAndRender();
192+
});
193+
194+
viewToggle.addEventListener('change', () => {
195+
if (!leftText || !rightText) return;
196+
const leftName = leftFile ? leftFile.name : 'left';
197+
const rightName = rightFile ? rightFile.name : 'right';
198+
const patch = Diff.createTwoFilesPatch(leftName + '/Input Log.txt', rightName + '/Input Log.txt', leftText, rightText, '', '', { context: 3 });
199+
const outputFormat = viewToggle.checked ? 'side-by-side' : 'line-by-line';
200+
const diffHtml = Diff2Html.html(patch, { drawFileList: false, matching: 'lines', colorScheme: 'dark', outputFormat });
201+
diffContainer.innerHTML = diffHtml;
202+
203+
status.textContent = 'Rendered';
204+
});
205+
206+
function setupDrop(panelEl, inputEl, metaEl) {
207+
panelEl.addEventListener('dragover', (e) => { e.preventDefault(); panelEl.style.borderColor = '#bbb'; });
208+
panelEl.addEventListener('dragleave', () => { panelEl.style.borderColor = ''; });
209+
panelEl.addEventListener('drop', (e) => {
210+
e.preventDefault(); panelEl.style.borderColor = '';
211+
const f = e.dataTransfer.files[0];
212+
if (f) { inputEl.files = e.dataTransfer.files; inputEl.dispatchEvent(new Event('change')); }
213+
});
214+
}
215+
setupDrop(document.getElementById('leftPanel'), leftInput, leftMeta);
216+
setupDrop(document.getElementById('rightPanel'), rightInput, rightMeta);
217+
218+
status.textContent = 'Select two movie files.';
219+
</script>
220+
</body>
221+
222+
</html>

0 commit comments

Comments
 (0)