Initial commit

This commit is contained in:
unknown
2026-05-19 01:41:07 +02:00
commit 033d0ffcb7
19 changed files with 2532 additions and 0 deletions

666
web_files/index.js Normal file
View File

@@ -0,0 +1,666 @@
/**
* CANNIBAL GIRLS - ARCHIVE TERMINAL
* Main Application Logic
*/
(function() {
'use strict';
// ========================================
// CONFIGURATION
// ========================================
const CONFIG = {
loadDelayMin: 4000,
loadDelayMax: 9000,
audioVolume: 0.15,
audioPath: 'audio/',
terminalLines: [
{ text: 'Mounting storage volumes...', type: 'normal', delay: 0 },
{ text: 'Checking filesystem integrity...', type: 'normal', delay: 300 },
{ text: 'Scanning /archive/repository...', type: 'normal', delay: 600 },
{ text: 'Found 247 indexed files', type: 'success', delay: 1200 },
{ text: 'Verifying checksums...', type: 'normal', delay: 1500 },
{ text: 'Warning: 3 files flagged for review', type: 'warning', delay: 2200 },
{ text: 'Rebuilding file index...', type: 'normal', delay: 2500 },
{ text: 'Optimizing cache...', type: 'normal', delay: 3200 },
{ text: 'Archive mount successful', type: 'success', delay: 4000 },
{ text: 'Ready for access', type: 'success', delay: 4500 }
]
};
// ========================================
// DEMO FILE DATA
// ========================================
const DEMO_FILES = [
{ id: 1, name: 'project_venus_raw.mp4', type: 'video', size: 2847563210, date: '2024-01-15T14:23:00', checksum: 'a3f7c9d2e1b4f5a6c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1' },
{ id: 2, name: 'archive_segment_07.mkv', type: 'video', size: 1456789012, date: '2024-01-14T09:15:00', checksum: 'b4c8d0e3f2a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c2' },
{ id: 3, name: 'nocturne_stills_set.zip', type: 'archive', size: 456782345, date: '2024-01-13T16:45:00', checksum: 'c5d9e1f4a3b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d3' },
{ id: 4, name: 'soundscape_01.flac', type: 'audio', size: 124567890, date: '2024-01-12T11:30:00', checksum: 'd6e0f2a5b4c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e4' },
{ id: 5, name: 'behind_the_scenes_03.jpg', type: 'image', size: 8945678, date: '2024-01-11T08:20:00', checksum: 'e7f1a3b6c5d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f5' },
{ id: 6, name: 'production_notes_v2.pdf', type: 'document', size: 2345678, date: '2024-01-10T19:00:00', checksum: 'f8a2b4c7d6e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a6' },
{ id: 7, name: 'raw_footage_reel_12.mp4', type: 'video', size: 5678901234, date: '2024-01-09T13:10:00', checksum: 'a9b3c5d7e8f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7' },
{ id: 8, name: 'color_grade_lut.cube', type: 'document', size: 45678, date: '2024-01-08T10:45:00', checksum: 'b0c4d6e8f9a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8' },
{ id: 9, name: 'ambient_collection.zip', type: 'archive', size: 345678901, date: '2024-01-07T15:30:00', checksum: 'c1d5e7f9a0b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9' },
{ id: 10, name: 'title_sequence_v3.mov', type: 'video', size: 1234567890, date: '2024-01-06T07:00:00', checksum: 'd2e6f8a0b1c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0' },
{ id: 11, name: 'location_scout_photos.zip', type: 'archive', size: 156789012, date: '2024-01-05T12:15:00', checksum: 'e3f7a9b1c2d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1' },
{ id: 12, name: 'score_master_24bit.wav', type: 'audio', size: 890123456, date: '2024-01-04T18:30:00', checksum: 'f4a8b0c2d3e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2' },
{ id: 13, name: 'character_studies_01.png', type: 'image', size: 45678901, date: '2024-01-03T09:45:00', checksum: 'a5b9c1d3e4f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3' },
{ id: 14, name: 'edit_timeline_v4.prproj', type: 'document', size: 12345678, date: '2024-01-02T14:00:00', checksum: 'b6c0d2e4f5a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4' },
{ id: 15, name: 'promo_cut_30sec.mp4', type: 'video', size: 678901234, date: '2024-01-01T11:20:00', checksum: 'c7d1e3f5a6b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5' }
];
// ========================================
// STATE
// ========================================
let state = {
files: [],
filteredFiles: [],
selectedFile: null,
currentView: 'list',
currentFilter: 'all',
currentSort: 'date-desc',
searchQuery: '',
uptime: 0,
audioContext: null
};
// ========================================
// DOM REFERENCES
// ========================================
const els = {};
function cacheElements() {
els.loadingScreen = document.getElementById('loading-screen');
els.loadingTitle = document.getElementById('loading-title');
els.loadingDatetime = document.getElementById('loading-datetime');
els.loadBtn = document.getElementById('load-files-btn');
els.loadingPhase = document.getElementById('loading-phase');
els.terminalOutput = document.getElementById('terminal-output');
els.progressText = document.getElementById('progress-text');
els.progressPercent = document.getElementById('progress-percent');
els.progressFill = document.getElementById('progress-fill');
els.archiveBrowser = document.getElementById('archive-browser');
els.fileList = document.getElementById('file-list');
els.fileListHeader = document.getElementById('file-list-header');
els.sidebarMenu = document.getElementById('sidebar-menu');
els.sortSelect = document.getElementById('sort-select');
els.searchInput = document.getElementById('search-input');
els.previewPane = document.getElementById('preview-pane');
els.previewContent = document.getElementById('preview-content');
els.previewClose = document.getElementById('preview-close');
els.previewMeta = document.getElementById('preview-meta');
els.headerTime = document.getElementById('header-time');
els.uptime = document.getElementById('uptime');
els.fileCount = document.getElementById('file-count');
els.totalSize = document.getElementById('total-size');
els.footerStatus = document.getElementById('footer-status');
els.bgAudio = document.getElementById('bg-audio');
els.actionDownload = document.getElementById('action-download');
els.actionCopy = document.getElementById('action-copy');
}
// ========================================
// UTILITIES
// ========================================
function formatBytes(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
function formatDate(dateString) {
const date = new Date(dateString);
return date.toISOString().slice(0, 19).replace('T', ' ');
}
function formatDateShort(dateString) {
const date = new Date(dateString);
return date.toISOString().slice(0, 10);
}
function getFileIcon(type) {
const icons = {
video: '▶',
audio: '♬',
image: '■',
document: '☰',
archive: '■'
};
return icons[type] || '■';
}
function getRandomDelay() {
return Math.floor(Math.random() * (CONFIG.loadDelayMax - CONFIG.loadDelayMin + 1)) + CONFIG.loadDelayMin;
}
function generateChecksum() {
const chars = 'abcdef0123456789';
let result = '';
for (let i = 0; i < 64; i++) {
result += chars[Math.floor(Math.random() * chars.length)];
}
return result;
}
// ========================================
// TEXT SCRAMBLE EFFECT
// ========================================
class TextScramble {
constructor(el) {
this.el = el;
this.chars = '!<>-_\\/[]{}--=+*^?#________';
this.update = this.update.bind(this);
}
setText(newText) {
const oldText = this.el.innerText;
const length = Math.max(oldText.length, newText.length);
const promise = new Promise(resolve => this.resolve = resolve);
this.queue = [];
for (let i = 0; i < length; i++) {
const from = oldText[i] || '';
const to = newText[i] || '';
const start = Math.floor(Math.random() * 40);
const end = start + Math.floor(Math.random() * 40);
this.queue.push({ from, to, start, end });
}
cancelAnimationFrame(this.frameRequest);
this.frame = 0;
this.update();
return promise;
}
update() {
let output = '';
let complete = 0;
for (let i = 0, n = this.queue.length; i < n; i++) {
let { from, to, start, end, char } = this.queue[i];
if (this.frame >= end) {
complete++;
output += to;
} else if (this.frame >= start) {
if (!char || Math.random() < 0.28) {
char = this.randomChar();
this.queue[i].char = char;
}
output += `<span class="text-dim">${char}</span>`;
} else {
output += from;
}
}
this.el.innerHTML = output;
if (complete === this.queue.length) {
this.resolve();
} else {
this.frameRequest = requestAnimationFrame(this.update);
this.frame++;
}
}
randomChar() {
return this.chars[Math.floor(Math.random() * this.chars.length)];
}
}
// ========================================
// AUDIO MANAGEMENT
// ========================================
async function getAudioFiles() {
// In a real scenario, this would fetch from the server
// For now, we'll try common audio filenames or return empty
const commonNames = ['ambient.mp3', 'noise.mp3', 'static.mp3', 'drone.mp3', 'hum.mp3'];
return commonNames.map(name => CONFIG.audioPath + name);
}
async function playRandomAudio() {
try {
const audioFiles = await getAudioFiles();
if (audioFiles.length === 0) {
console.log('No audio files found in', CONFIG.audioPath);
return;
}
const randomFile = audioFiles[Math.floor(Math.random() * audioFiles.length)];
els.bgAudio.src = randomFile;
els.bgAudio.volume = CONFIG.audioVolume;
const playPromise = els.bgAudio.play();
if (playPromise !== undefined) {
playPromise.catch(err => {
console.log('Audio playback failed:', err.message);
});
}
} catch (err) {
console.log('Audio error:', err);
}
}
function stopAudio() {
els.bgAudio.pause();
els.bgAudio.currentTime = 0;
}
// ========================================
// TERMINAL OUTPUT
// ========================================
function addTerminalLine(text, type = 'normal') {
const line = document.createElement('div');
line.className = 'terminal-line';
const prompt = document.createElement('span');
prompt.className = 'line-prompt';
prompt.textContent = '>';
const content = document.createElement('span');
content.className = `line-content ${type}`;
content.textContent = text;
line.appendChild(prompt);
line.appendChild(content);
els.terminalOutput.appendChild(line);
// Auto-scroll
els.terminalOutput.scrollTop = els.terminalOutput.scrollHeight;
}
// ========================================
// LOADING SEQUENCE
// ========================================
async function startLoadingSequence() {
const delay = getRandomDelay();
// Hide loading screen, show loading phase
els.loadBtn.disabled = true;
els.loadBtn.innerHTML = '<span class="spinner"></span>';
setTimeout(() => {
els.loadingScreen.classList.add('hidden');
els.loadingPhase.classList.remove('hidden');
// Start audio
playRandomAudio();
// Start terminal output
let currentTime = 0;
CONFIG.terminalLines.forEach(line => {
setTimeout(() => {
addTerminalLine(line.text, line.type);
}, line.delay);
});
// Progress bar
const startTime = Date.now();
const updateProgress = () => {
const elapsed = Date.now() - startTime;
const progress = Math.min((elapsed / delay) * 100, 100);
els.progressFill.style.width = progress + '%';
els.progressPercent.textContent = Math.floor(progress) + '%';
// Update progress text based on percentage
if (progress < 20) els.progressText.textContent = 'MOUNTING VOLUMES...';
else if (progress < 40) els.progressText.textContent = 'VERIFYING CHECKSUMS...';
else if (progress < 60) els.progressText.textContent = 'INDEXING FILES...';
else if (progress < 80) els.progressText.textContent = 'OPTIMIZING CACHE...';
else if (progress < 100) els.progressText.textContent = 'FINALIZING...';
else els.progressText.textContent = 'COMPLETE';
if (progress < 100) {
requestAnimationFrame(updateProgress);
} else {
setTimeout(finishLoading, 500);
}
};
requestAnimationFrame(updateProgress);
}, 300);
}
function finishLoading() {
stopAudio();
els.loadingPhase.classList.add('hidden');
els.archiveBrowser.classList.remove('hidden');
els.archiveBrowser.classList.add('active');
// Initialize archive
initArchive();
}
// ========================================
// ARCHIVE INITIALIZATION
// ========================================
function initArchive() {
state.files = DEMO_FILES.map(f => ({
...f,
checksum: f.checksum || generateChecksum()
}));
updateCounts();
filterAndSortFiles();
renderFiles();
updateStats();
// Start uptime counter
setInterval(() => {
state.uptime++;
updateUptime();
}, 1000);
}
function updateCounts() {
const counts = {
all: state.files.length,
video: state.files.filter(f => f.type === 'video').length,
image: state.files.filter(f => f.type === 'image').length,
audio: state.files.filter(f => f.type === 'audio').length,
document: state.files.filter(f => f.type === 'document').length,
archive: state.files.filter(f => f.type === 'archive').length
};
Object.keys(counts).forEach(key => {
const el = document.getElementById(`count-${key}`);
if (el) el.textContent = counts[key];
});
}
function updateStats() {
els.fileCount.textContent = state.filteredFiles.length;
const totalBytes = state.filteredFiles.reduce((sum, f) => sum + f.size, 0);
els.totalSize.textContent = formatBytes(totalBytes);
}
function updateUptime() {
const hours = Math.floor(state.uptime / 3600).toString().padStart(2, '0');
const minutes = Math.floor((state.uptime % 3600) / 60).toString().padStart(2, '0');
const seconds = (state.uptime % 60).toString().padStart(2, '0');
els.uptime.textContent = `${hours}:${minutes}:${seconds}`;
}
// ========================================
// FILE FILTERING & SORTING
// ========================================
function filterAndSortFiles() {
let files = [...state.files];
// Apply filter
if (state.currentFilter !== 'all') {
files = files.filter(f => f.type === state.currentFilter);
}
// Apply search
if (state.searchQuery) {
const query = state.searchQuery.toLowerCase();
files = files.filter(f => f.name.toLowerCase().includes(query));
}
// Apply sort
files.sort((a, b) => {
switch (state.currentSort) {
case 'name-asc':
return a.name.localeCompare(b.name);
case 'name-desc':
return b.name.localeCompare(a.name);
case 'date-desc':
return new Date(b.date) - new Date(a.date);
case 'date-asc':
return new Date(a.date) - new Date(b.date);
case 'size-desc':
return b.size - a.size;
case 'size-asc':
return a.size - b.size;
case 'type':
return a.type.localeCompare(b.type) || a.name.localeCompare(b.name);
default:
return 0;
}
});
state.filteredFiles = files;
updateStats();
}
// ========================================
// FILE RENDERING
// ========================================
function renderFiles() {
els.fileList.innerHTML = '';
if (state.filteredFiles.length === 0) {
const empty = document.createElement('div');
empty.className = 'file-item';
empty.innerHTML = '<span style="color: #5A4545; grid-column: 1 / -1; text-align: center; padding: 2rem;">NO FILES FOUND</span>';
els.fileList.appendChild(empty);
return;
}
state.filteredFiles.forEach((file, index) => {
const item = document.createElement('div');
item.className = 'file-item';
if (state.selectedFile && state.selectedFile.id === file.id) {
item.classList.add('selected');
}
// Add staggered animation
item.style.animation = `fadeInUp 0.3s ease-out ${index * 0.03}s both`;
item.innerHTML = `
<div class="file-name-cell">
<span class="file-icon">${getFileIcon(file.type)}</span>
<span class="file-name" title="${file.name}">${file.name}</span>
</div>
<span class="file-type">${file.type}</span>
<span class="file-size">${formatBytes(file.size)}</span>
<span class="file-date">${formatDateShort(file.date)}</span>
`;
item.addEventListener('click', () => selectFile(file));
els.fileList.appendChild(item);
});
}
// ========================================
// FILE SELECTION & PREVIEW
// ========================================
function selectFile(file) {
state.selectedFile = file;
// Update selection in list
document.querySelectorAll('.file-item').forEach(item => {
item.classList.remove('selected');
});
const selectedEl = Array.from(els.fileList.children).find(el =>
el.querySelector('.file-name')?.textContent === file.name
);
if (selectedEl) selectedEl.classList.add('selected');
// Update preview
updatePreview(file);
// Update footer status
els.footerStatus.textContent = `SELECTED: ${file.name}`;
}
function updatePreview(file) {
// Update preview content
els.previewContent.innerHTML = `
<div class="preview-placeholder">
<span class="preview-file-icon">${getFileIcon(file.type)}</span>
<span class="placeholder-text">${file.type.toUpperCase()} FILE</span>
</div>
`;
// Update meta
document.getElementById('meta-filename').textContent = file.name;
document.getElementById('meta-type').textContent = file.type.toUpperCase();
document.getElementById('meta-size').textContent = formatBytes(file.size);
document.getElementById('meta-created').textContent = formatDate(file.date);
document.getElementById('meta-modified').textContent = formatDate(file.date);
document.getElementById('meta-checksum').textContent = file.checksum;
}
function closePreview() {
state.selectedFile = null;
document.querySelectorAll('.file-item').forEach(item => {
item.classList.remove('selected');
});
els.previewContent.innerHTML = `
<div class="preview-placeholder">
<span class="placeholder-icon">&#9632;</span>
<span class="placeholder-text">SELECT A FILE TO PREVIEW</span>
</div>
`;
document.getElementById('meta-filename').textContent = '---';
document.getElementById('meta-type').textContent = '---';
document.getElementById('meta-size').textContent = '---';
document.getElementById('meta-created').textContent = '---';
document.getElementById('meta-modified').textContent = '---';
document.getElementById('meta-checksum').textContent = '---';
els.footerStatus.textContent = 'READY';
}
// ========================================
// VIEW SWITCHING
// ========================================
function switchView(view) {
state.currentView = view;
// Update buttons
document.querySelectorAll('.view-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.view === view);
});
// Update list class
if (view === 'grid') {
els.fileList.classList.add('grid-view');
els.fileListHeader.style.display = 'none';
} else {
els.fileList.classList.remove('grid-view');
els.fileListHeader.style.display = 'grid';
}
renderFiles();
}
// ========================================
// EVENT LISTENERS
// ========================================
function bindEvents() {
// Load button
els.loadBtn.addEventListener('click', startLoadingSequence);
// Sidebar filters
els.sidebarMenu.addEventListener('click', (e) => {
const item = e.target.closest('.sidebar-item');
if (!item) return;
document.querySelectorAll('.sidebar-item').forEach(i => i.classList.remove('active'));
item.classList.add('active');
state.currentFilter = item.dataset.filter;
filterAndSortFiles();
renderFiles();
});
// View switcher
document.querySelectorAll('.view-btn').forEach(btn => {
btn.addEventListener('click', () => switchView(btn.dataset.view));
});
// Sort
els.sortSelect.addEventListener('change', (e) => {
state.currentSort = e.target.value;
filterAndSortFiles();
renderFiles();
});
// Search
els.searchInput.addEventListener('input', (e) => {
state.searchQuery = e.target.value;
filterAndSortFiles();
renderFiles();
});
// Preview close
els.previewClose.addEventListener('click', closePreview);
// Download button
els.actionDownload.addEventListener('click', () => {
if (!state.selectedFile) return;
const blob = new Blob([`File: ${state.selectedFile.name}\nSize: ${formatBytes(state.selectedFile.size)}\nChecksum: ${state.selectedFile.checksum}`], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = state.selectedFile.name + '.info.txt';
a.click();
URL.revokeObjectURL(url);
els.footerStatus.textContent = 'DOWNLOADED INFO FILE';
setTimeout(() => {
els.footerStatus.textContent = 'READY';
}, 2000);
});
// Copy hash button
els.actionCopy.addEventListener('click', () => {
if (!state.selectedFile) return;
navigator.clipboard.writeText(state.selectedFile.checksum).then(() => {
els.footerStatus.textContent = 'CHECKSUM COPIED TO CLIPBOARD';
setTimeout(() => {
els.footerStatus.textContent = 'READY';
}, 2000);
}).catch(() => {
els.footerStatus.textContent = 'COPY FAILED';
});
});
}
// ========================================
// CLOCK UPDATES
// ========================================
function updateClock() {
const now = new Date();
const dateStr = now.toISOString().slice(0, 19).replace('T', ' ');
if (els.loadingDatetime) {
els.loadingDatetime.textContent = dateStr;
}
if (els.headerTime) {
els.headerTime.textContent = dateStr;
}
}
// ========================================
// INITIALIZATION
// ========================================
function init() {
cacheElements();
// Title scramble effect
const scrambler = new TextScramble(els.loadingTitle);
setTimeout(() => {
scrambler.setText('CANNIBAL GIRLS');
}, 500);
// Clock
updateClock();
setInterval(updateClock, 1000);
// Bind events
bindEvents();
console.log('%c CANNIBAL GIRLS // ARCHIVE TERMINAL v2.4.1 ', 'background: #0F0D0D; color: #E85D75; font-family: monospace; padding: 4px 8px;');
console.log('%c SYSTEM ONLINE ', 'background: #181414; color: #4A7C59; font-family: monospace; padding: 2px 6px;');
}
// Start when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();