Synced files: - Quote wizard frontend (all components, hooks, types, config) - API updates (config, models, routers, schemas, services) - Client work (bg-builders, gurushow) - Scripts (BGB Lesley termination, CIPP, Datto, migration) - Temp files (Bardach contacts, VWP investigation, misc) - Credentials and session logs - Email service, PHP API, session logs Machine: ACG-M-L5090 Timestamp: 2026-03-10 19:11:00 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1566 lines
48 KiB
HTML
1566 lines
48 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>The Computer Guru Show - Archive</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
|
<style>
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
:root {
|
|
--blue: #428fcb;
|
|
--blue-hover: #5aa3d9;
|
|
--blue-dark: #2d6a9e;
|
|
--dark: #1d1d1d;
|
|
--dark-card: #262626;
|
|
--dark-hover: #2e2e2e;
|
|
--dark-border: #3a3a3a;
|
|
--text-light: #ffffff;
|
|
--text-muted: #a0a0a0;
|
|
--text-dark: #000000;
|
|
--player-height: 90px;
|
|
--header-height: 200px;
|
|
}
|
|
|
|
html { scroll-behavior: smooth; }
|
|
|
|
body {
|
|
font-family: 'Roboto', sans-serif;
|
|
background: var(--dark);
|
|
color: var(--text-light);
|
|
min-height: 100vh;
|
|
padding-bottom: calc(var(--player-height) + 20px);
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
|
|
/* ===== HEADER ===== */
|
|
.site-header {
|
|
background: linear-gradient(135deg, var(--dark) 0%, #0d2137 50%, var(--dark) 100%);
|
|
border-bottom: 2px solid var(--blue);
|
|
padding: 30px 20px;
|
|
text-align: center;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.site-header::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0; left: 0; right: 0; bottom: 0;
|
|
background: radial-gradient(ellipse at 50% 0%, rgba(66,143,203,0.15) 0%, transparent 70%);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.header-content {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 24px;
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.show-artwork {
|
|
width: 120px;
|
|
height: 120px;
|
|
border-radius: 12px;
|
|
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
|
|
flex-shrink: 0;
|
|
object-fit: cover;
|
|
background: var(--dark-card);
|
|
}
|
|
|
|
.header-text { text-align: left; }
|
|
|
|
.header-text h1 {
|
|
font-size: 2rem;
|
|
font-weight: 700;
|
|
letter-spacing: -0.5px;
|
|
line-height: 1.1;
|
|
}
|
|
|
|
.header-text h1 span { color: var(--blue); }
|
|
|
|
.header-text .subtitle {
|
|
font-size: 1rem;
|
|
color: var(--text-muted);
|
|
font-weight: 300;
|
|
margin-top: 6px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 3px;
|
|
}
|
|
|
|
.header-text .meta {
|
|
font-size: 0.85rem;
|
|
color: var(--text-muted);
|
|
margin-top: 10px;
|
|
}
|
|
|
|
/* ===== NAVIGATION TABS ===== */
|
|
.nav-section {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
background: var(--dark);
|
|
border-bottom: 1px solid var(--dark-border);
|
|
box-shadow: 0 2px 12px rgba(0,0,0,0.4);
|
|
}
|
|
|
|
.nav-tabs {
|
|
display: flex;
|
|
overflow-x: auto;
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 0 10px;
|
|
scrollbar-width: none;
|
|
}
|
|
|
|
.nav-tabs::-webkit-scrollbar { display: none; }
|
|
|
|
.nav-tab {
|
|
flex-shrink: 0;
|
|
padding: 14px 20px;
|
|
cursor: pointer;
|
|
border: none;
|
|
background: none;
|
|
color: var(--text-muted);
|
|
font-family: 'Roboto', sans-serif;
|
|
font-size: 0.9rem;
|
|
font-weight: 500;
|
|
border-bottom: 3px solid transparent;
|
|
transition: all 0.2s;
|
|
white-space: nowrap;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 2px;
|
|
}
|
|
|
|
.nav-tab:hover {
|
|
color: var(--text-light);
|
|
background: var(--dark-hover);
|
|
}
|
|
|
|
.nav-tab.active {
|
|
color: var(--blue);
|
|
border-bottom-color: var(--blue);
|
|
}
|
|
|
|
.nav-tab .tab-count {
|
|
font-size: 0.7rem;
|
|
color: var(--text-muted);
|
|
font-weight: 300;
|
|
}
|
|
|
|
.nav-tab.active .tab-count { color: var(--blue); opacity: 0.7; }
|
|
|
|
/* ===== MAIN CONTENT ===== */
|
|
.main-content {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 1.4rem;
|
|
font-weight: 700;
|
|
margin-bottom: 16px;
|
|
padding-bottom: 8px;
|
|
border-bottom: 1px solid var(--dark-border);
|
|
}
|
|
|
|
.section-title span { color: var(--blue); }
|
|
|
|
/* ===== EPISODE LIST ===== */
|
|
.episode-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
}
|
|
|
|
.episode-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 12px 16px;
|
|
background: var(--dark-card);
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
transition: background 0.15s;
|
|
position: relative;
|
|
}
|
|
|
|
.episode-item:hover { background: var(--dark-hover); }
|
|
|
|
.episode-item.playing {
|
|
background: rgba(66,143,203,0.12);
|
|
border-left: 3px solid var(--blue);
|
|
padding-left: 13px;
|
|
}
|
|
|
|
.episode-play-btn {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 50%;
|
|
background: var(--blue);
|
|
border: none;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
transition: background 0.15s, transform 0.1s;
|
|
}
|
|
|
|
.episode-play-btn:hover {
|
|
background: var(--blue-hover);
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
.episode-play-btn svg {
|
|
width: 16px;
|
|
height: 16px;
|
|
fill: white;
|
|
}
|
|
|
|
.episode-info { flex: 1; min-width: 0; }
|
|
|
|
.episode-title {
|
|
font-size: 0.95rem;
|
|
font-weight: 500;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.episode-meta {
|
|
font-size: 0.8rem;
|
|
color: var(--text-muted);
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.episode-parts {
|
|
display: flex;
|
|
gap: 6px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.part-btn {
|
|
padding: 8px 14px;
|
|
font-size: 0.8rem;
|
|
font-family: 'Roboto', sans-serif;
|
|
border: 1px solid var(--dark-border);
|
|
border-radius: 4px;
|
|
background: transparent;
|
|
color: var(--text-muted);
|
|
cursor: pointer;
|
|
transition: all 0.15s;
|
|
}
|
|
|
|
.part-btn:hover {
|
|
border-color: var(--blue);
|
|
color: var(--blue);
|
|
}
|
|
|
|
.part-btn.active-part {
|
|
background: var(--blue);
|
|
border-color: var(--blue);
|
|
color: white;
|
|
}
|
|
|
|
.episode-download {
|
|
display: flex;
|
|
gap: 4px;
|
|
flex-shrink: 0;
|
|
margin-left: 4px;
|
|
}
|
|
|
|
.dl-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
padding: 8px 12px;
|
|
font-size: 0.78rem;
|
|
font-family: 'Roboto', sans-serif;
|
|
border: 1px solid var(--dark-border);
|
|
border-radius: 4px;
|
|
background: transparent;
|
|
color: var(--text-muted);
|
|
cursor: pointer;
|
|
text-decoration: none;
|
|
transition: all 0.15s;
|
|
}
|
|
|
|
.dl-btn:hover {
|
|
border-color: var(--blue);
|
|
color: var(--blue);
|
|
}
|
|
|
|
.dl-btn svg {
|
|
width: 12px;
|
|
height: 12px;
|
|
fill: currentColor;
|
|
}
|
|
|
|
.bumper-dl {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-left: auto;
|
|
padding: 4px;
|
|
border: none;
|
|
background: transparent;
|
|
color: var(--text-muted);
|
|
cursor: pointer;
|
|
text-decoration: none;
|
|
border-radius: 4px;
|
|
transition: color 0.15s;
|
|
}
|
|
|
|
.bumper-dl:hover { color: var(--blue); }
|
|
|
|
.bumper-dl svg { width: 14px; height: 14px; fill: currentColor; }
|
|
|
|
/* ===== BUMPERS SECTION ===== */
|
|
.bumpers-section {
|
|
margin-top: 30px;
|
|
padding-top: 20px;
|
|
border-top: 1px solid var(--dark-border);
|
|
}
|
|
|
|
.bumper-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
gap: 8px;
|
|
}
|
|
|
|
.bumper-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 10px 14px;
|
|
background: var(--dark-card);
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
transition: background 0.15s;
|
|
}
|
|
|
|
.bumper-item:hover { background: var(--dark-hover); }
|
|
|
|
.bumper-play {
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 50%;
|
|
background: var(--blue-dark);
|
|
border: none;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
transition: background 0.15s;
|
|
}
|
|
|
|
.bumper-play:hover { background: var(--blue); }
|
|
|
|
.bumper-play svg { width: 12px; height: 12px; fill: white; }
|
|
|
|
.bumper-name {
|
|
font-size: 0.82rem;
|
|
color: var(--text-muted);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
/* ===== FIXED BOTTOM PLAYER ===== */
|
|
.player-bar {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: var(--player-height);
|
|
background: #111111;
|
|
border-top: 1px solid var(--dark-border);
|
|
z-index: 200;
|
|
display: flex;
|
|
flex-direction: column;
|
|
box-shadow: 0 -4px 20px rgba(0,0,0,0.5);
|
|
transition: transform 0.3s;
|
|
}
|
|
|
|
.player-bar.hidden { transform: translateY(100%); }
|
|
|
|
.player-progress-wrap {
|
|
width: 100%;
|
|
height: 4px;
|
|
background: #333;
|
|
cursor: pointer;
|
|
position: relative;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.player-progress-wrap:hover { height: 6px; }
|
|
|
|
.player-progress-fill {
|
|
height: 100%;
|
|
background: var(--blue);
|
|
width: 0%;
|
|
transition: width 0.1s linear;
|
|
position: relative;
|
|
}
|
|
|
|
.player-progress-fill::after {
|
|
content: '';
|
|
position: absolute;
|
|
right: -6px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
background: var(--blue);
|
|
opacity: 0;
|
|
transition: opacity 0.15s;
|
|
}
|
|
|
|
.player-progress-wrap:hover .player-progress-fill::after { opacity: 1; }
|
|
|
|
.player-main {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 16px;
|
|
gap: 16px;
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
width: 100%;
|
|
}
|
|
|
|
.player-now-playing {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
}
|
|
|
|
.player-now-title {
|
|
font-size: 0.9rem;
|
|
font-weight: 500;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.player-now-sub {
|
|
font-size: 0.75rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.player-controls {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.player-btn {
|
|
background: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
color: var(--text-muted);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 6px;
|
|
border-radius: 50%;
|
|
transition: color 0.15s, background 0.15s;
|
|
}
|
|
|
|
.player-btn:hover {
|
|
color: var(--text-light);
|
|
background: rgba(255,255,255,0.08);
|
|
}
|
|
|
|
.player-btn svg { width: 20px; height: 20px; fill: currentColor; }
|
|
|
|
.player-btn-main {
|
|
width: 42px;
|
|
height: 42px;
|
|
border-radius: 50%;
|
|
background: var(--blue);
|
|
color: white;
|
|
border: none;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
transition: background 0.15s, transform 0.1s;
|
|
}
|
|
|
|
.player-btn-main:hover {
|
|
background: var(--blue-hover);
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
.player-btn-main svg { width: 20px; height: 20px; fill: white; }
|
|
|
|
.player-time {
|
|
font-size: 0.75rem;
|
|
color: var(--text-muted);
|
|
white-space: nowrap;
|
|
flex-shrink: 0;
|
|
min-width: 90px;
|
|
text-align: center;
|
|
}
|
|
|
|
.player-volume {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.player-volume svg { width: 18px; height: 18px; fill: var(--text-muted); }
|
|
|
|
.volume-slider {
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
width: 80px;
|
|
height: 4px;
|
|
background: #333;
|
|
border-radius: 2px;
|
|
outline: none;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.volume-slider::-webkit-slider-thumb {
|
|
-webkit-appearance: none;
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
background: var(--blue);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.volume-slider::-moz-range-thumb {
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
background: var(--blue);
|
|
cursor: pointer;
|
|
border: none;
|
|
}
|
|
|
|
/* ===== EMPTY STATE ===== */
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: 60px 20px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.empty-state svg { width: 48px; height: 48px; fill: var(--dark-border); margin-bottom: 16px; }
|
|
|
|
.empty-state p { font-size: 1rem; }
|
|
|
|
/* ===== RESPONSIVE ===== */
|
|
@media (max-width: 768px) {
|
|
.header-content { flex-direction: column; gap: 16px; }
|
|
.header-text { text-align: center; }
|
|
.header-text h1 { font-size: 1.5rem; }
|
|
.show-artwork { width: 100px; height: 100px; }
|
|
|
|
.episode-item { padding: 10px 12px; gap: 10px; flex-wrap: wrap; }
|
|
.episode-title { white-space: normal; }
|
|
.episode-parts { flex-direction: column; gap: 3px; }
|
|
.part-btn { padding: 10px 14px; font-size: 0.82rem; min-height: 44px; }
|
|
.episode-download { gap: 3px; }
|
|
.dl-btn { padding: 10px 12px; font-size: 0.78rem; min-height: 44px; }
|
|
.dl-btn svg { width: 12px; height: 12px; }
|
|
|
|
.bumper-play { width: 44px; height: 44px; }
|
|
.bumper-play svg { width: 16px; height: 16px; }
|
|
.bumper-dl { padding: 10px; min-width: 44px; min-height: 44px; justify-content: center; }
|
|
.bumper-dl svg { width: 18px; height: 18px; }
|
|
|
|
.player-progress-wrap { height: 12px; }
|
|
.player-progress-wrap:hover { height: 12px; }
|
|
.player-main { padding: 0 10px; gap: 10px; }
|
|
.player-volume { display: none; }
|
|
.player-time { min-width: 70px; font-size: 0.7rem; }
|
|
.player-now-title { font-size: 0.8rem; }
|
|
|
|
.bumper-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
.nav-tab { padding: 12px 14px; font-size: 0.82rem; }
|
|
.player-btn-main { width: 36px; height: 36px; }
|
|
.player-btn-main svg { width: 16px; height: 16px; }
|
|
.player-controls { gap: 6px; }
|
|
}
|
|
|
|
/* ===== SCROLLBAR ===== */
|
|
::-webkit-scrollbar { width: 8px; }
|
|
::-webkit-scrollbar-track { background: var(--dark); }
|
|
::-webkit-scrollbar-thumb { background: #444; border-radius: 4px; }
|
|
::-webkit-scrollbar-thumb:hover { background: #555; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<header class="site-header">
|
|
<div class="header-content">
|
|
<img class="show-artwork" src="../itunes.jpg" alt="The Computer Guru Show">
|
|
<div class="header-text">
|
|
<h1>The <span>Computer Guru</span> Show</h1>
|
|
<div class="subtitle">Podcast Archive</div>
|
|
<div class="meta">Seasons 1-10 · 2010-2018 · Radio Show & Podcast</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<nav class="nav-section">
|
|
<div class="nav-tabs" id="navTabs"></div>
|
|
</nav>
|
|
|
|
<main class="main-content" id="mainContent">
|
|
<div class="empty-state">
|
|
<svg viewBox="0 0 24 24"><path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55C7.79 13 6 14.79 6 17s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/></svg>
|
|
<p>Select a year to browse episodes</p>
|
|
</div>
|
|
</main>
|
|
|
|
<div class="player-bar hidden" id="playerBar">
|
|
<div class="player-progress-wrap" id="progressWrap">
|
|
<div class="player-progress-fill" id="progressFill"></div>
|
|
</div>
|
|
<div class="player-main">
|
|
<div class="player-now-playing">
|
|
<div class="player-now-title" id="playerTitle">--</div>
|
|
<div class="player-now-sub" id="playerSub">--</div>
|
|
</div>
|
|
<div class="player-controls">
|
|
<button class="player-btn" id="btnPrev" title="Previous">
|
|
<svg viewBox="0 0 24 24"><path d="M6 6h2v12H6zm3.5 6 8.5 6V6z"/></svg>
|
|
</button>
|
|
<button class="player-btn-main" id="btnPlayPause" title="Play/Pause">
|
|
<svg id="iconPlay" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>
|
|
<svg id="iconPause" viewBox="0 0 24 24" style="display:none"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
|
|
</button>
|
|
<button class="player-btn" id="btnNext" title="Next">
|
|
<svg viewBox="0 0 24 24"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></svg>
|
|
</button>
|
|
</div>
|
|
<div class="player-time" id="playerTime">0:00 / 0:00</div>
|
|
<div class="player-volume">
|
|
<svg viewBox="0 0 24 24"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/></svg>
|
|
<input type="range" class="volume-slider" id="volumeSlider" min="0" max="100" value="80">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<audio id="audioEl" preload="metadata"></audio>
|
|
|
|
<script>
|
|
(function() {
|
|
'use strict';
|
|
|
|
// ===== SVG ICON TEMPLATES =====
|
|
var SVG_PLAY = '<svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>';
|
|
var SVG_PAUSE = '<svg viewBox="0 0 24 24"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>';
|
|
var SVG_DOWNLOAD = '<svg viewBox="0 0 24 24"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>';
|
|
|
|
// ===== EPISODE DATA =====
|
|
// Structure: { year, title, subtitle, files: [{label, path}] }
|
|
// files[0] is default play target; multi-part episodes have HR1, HR2
|
|
|
|
var ARCHIVE = [];
|
|
|
|
// Helper to add episodes
|
|
function addEp(year, title, subtitle, files) {
|
|
ARCHIVE.push({ year: year, title: title, subtitle: subtitle, files: files });
|
|
}
|
|
|
|
function enc(p) { return p.replace(/ /g, '%20'); }
|
|
|
|
// ===== 2010 =====
|
|
// Root files
|
|
addEp(2010, 'May 8, 2010', 'Hour 1 + Hour 2', [
|
|
{ label: 'HR 1', path: enc('2010/COMPUTER GURU 5-8-10 hour 1.mp3') },
|
|
{ label: 'HR 2', path: enc('2010/COMPUTER GURU 5-8-10 hour 2.mp3') }
|
|
]);
|
|
addEp(2010, 'May 15, 2010', 'Hour 1 + Hour 2', [
|
|
{ label: 'HR 1', path: enc('2010/COMPUTER GURU 5-15-10 hour 1.mp3') },
|
|
{ label: 'HR 2', path: enc('2010/COMPUTER GURU 5-15-10 hour 2.mp3') }
|
|
]);
|
|
addEp(2010, 'May 29, 2010', 'Hour 2 only', [
|
|
{ label: 'HR 2', path: enc('2010/Computer Guru 5-29-10 hour 2.mp3') }
|
|
]);
|
|
addEp(2010, 'June 5, 2010', 'Hour 1 + Hour 2', [
|
|
{ label: 'HR 1', path: enc('2010/Computer Guru 06-05-10 hour 1.mp3') },
|
|
{ label: 'HR 2', path: enc('2010/Computer Guru 06-05-10 hour2.mp3') }
|
|
]);
|
|
addEp(2010, 'July 3, 2010', 'Hour 1 + Hour 2', [
|
|
{ label: 'HR 1', path: enc('2010/Computer Guru Hour 1 7-3-10.mp3') },
|
|
{ label: 'HR 2', path: enc('2010/Computer Guru Hour 2 7-3-10.mp3') }
|
|
]);
|
|
addEp(2010, 'August 7, 2010', 'Hour 1 + Hour 2', [
|
|
{ label: 'HR 1', path: enc('2010/ComputerGuruHour1_8-7-2010.mp3') },
|
|
{ label: 'HR 2', path: enc('2010/ComputerGuruHour2_8-7-2010.mp3') }
|
|
]);
|
|
addEp(2010, 'August 28, 2010', 'Hour 1 + Hour 2', [
|
|
{ label: 'HR 1', path: enc('2010/ComputerGuruHour1_8-28-10.mp3') },
|
|
{ label: 'HR 2', path: enc('2010/ComputerGuruHour2_8-28-10.mp3') }
|
|
]);
|
|
addEp(2010, 'September 4, 2010', 'Hour 1 + Hour 2', [
|
|
{ label: 'HR 1', path: enc('2010/ComputerGuru_Hour1_9-4-10.mp3') },
|
|
{ label: 'HR 2', path: enc('2010/ComputerGuru_Hour2_9-4-10.mp3') }
|
|
]);
|
|
addEp(2010, 'September 18, 2010', 'Hour 1 + Hour 2', [
|
|
{ label: 'HR 1', path: enc('2010/ComputerGuru_Hour1_9-18-10.mp3') },
|
|
{ label: 'HR 2', path: enc('2010/ComputerGuru_Hour2_9-18-10.mp3') }
|
|
]);
|
|
addEp(2010, 'September 25, 2010', 'Hour 1 + Hour 2', [
|
|
{ label: 'HR 1', path: enc('2010/ComputerGuru_Hour1_9-25-10.mp3') },
|
|
{ label: 'HR 2', path: enc('2010/ComputerGuru_Hour2_09-25-10.mp3') }
|
|
]);
|
|
// October
|
|
['10-02-10','10-09-10','10-16-10','10-23-10','10-30-10'].forEach(function(d) {
|
|
var parts = d.split('-');
|
|
var month = parseInt(parts[0]), day = parseInt(parts[1]);
|
|
addEp(2010, 'October ' + day + ', 2010', 'Hour 1 + Hour 2', [
|
|
{ label: 'HR 1', path: enc('2010/10 - October/' + d + ' HR 1.mp3') },
|
|
{ label: 'HR 2', path: enc('2010/10 - October/' + d + ' HR 2.mp3') }
|
|
]);
|
|
});
|
|
// November
|
|
['11-06-10','11-13-10','11-20-10','11-27-10'].forEach(function(d) {
|
|
var day = parseInt(d.split('-')[1]);
|
|
addEp(2010, 'November ' + day + ', 2010', 'Hour 1 + Hour 2', [
|
|
{ label: 'HR 1', path: enc('2010/11 - November/' + d + ' HR 1.mp3') },
|
|
{ label: 'HR 2', path: enc('2010/11 - November/' + d + ' HR 2.mp3') }
|
|
]);
|
|
});
|
|
// December
|
|
['12-4-10','12-11-10','12-18-10'].forEach(function(d) {
|
|
var day = parseInt(d.split('-')[1]);
|
|
addEp(2010, 'December ' + day + ', 2010', 'Hour 1 + Hour 2', [
|
|
{ label: 'HR 1', path: enc('2010/12 - December/' + d + ' HR 1.mp3') },
|
|
{ label: 'HR 2', path: enc('2010/12 - December/' + d + ' HR 2.mp3') }
|
|
]);
|
|
});
|
|
|
|
// ===== 2011 (month subdirs only) =====
|
|
var months2011 = [
|
|
{ num: '1', name: 'January', eps: [
|
|
{ d: '1-1-11', hr1: '1-1-11 HR 1.mp3', hr2: '1-1-11 HR 2.mp3' },
|
|
{ d: '1-15-11', hr1: '1-15-1 HR 1.mp3', hr2: '1-15-11 HR 2.mp3' },
|
|
{ d: '1-23-11', hr1: '1-23-11 HR 1.mp3', hr2: '1-23-11 HR 2.mp3' },
|
|
{ d: '1-29-11', hr1: '1-29-11 HR 1.mp3', hr2: '1-29-11 HR 2.mp3' }
|
|
]},
|
|
{ num: '2', name: 'February', eps: [
|
|
{ d: '2-05-11', hr1: '2-05-11 HR 1.mp3', hr2: '2-05-11 HR 2.mp3' },
|
|
{ d: '2-12-11', hr1: '2-12-11 HR 1.mp3', hr2: '2-12-11 HR 2.mp3' },
|
|
{ d: '2-19-11', hr1: '2-19-11 HR 1.mp3', hr2: '2-19-11 HR 2.mp3' },
|
|
{ d: '2-26-11', hr1: '2-26-11 HR 1.mp3', hr2: '2-26-11 HR 2.mp3' }
|
|
]},
|
|
{ num: '3', name: 'March', eps: [
|
|
{ d: '3-05-11', hr1: '3-05-11 HR 1.mp3', hr2: '3-05-11 HR 2.mp3' },
|
|
{ d: '3-12-11', hr1: '3-12-11 HR 1.mp3', hr2: '3-12-11 HR 2.mp3' },
|
|
{ d: '3-19-11', hr1: '3-19-11 HR 1.mp3', hr2: '3-19-11 HR 2.mp3' },
|
|
{ d: '3-26-11', hr1: '3-26-11 HR 1.mp3', hr2: '3-26-11 HR 2.mp3' }
|
|
]},
|
|
{ num: '4', name: 'April', eps: [
|
|
{ d: '4-02-11', hr1: '4-02-11 HR 1.mp3', hr2: '4-02-11 HR 2.mp3' },
|
|
{ d: '4-09-11', hr1: '4-09-11 HR 1.mp3', hr2: '4-09-11 HR 2.mp3' },
|
|
{ d: '4-16-11', hr1: '4-16-11 HR 1.mp3', hr2: '4-16-11 HR 2.mp3' },
|
|
{ d: '4-23-11', hr1: '4-23-11 HR 1.mp3', hr2: '4-23-11 HR 2.mp3' },
|
|
{ d: '4-30-11', hr1: '4-30-11 HR 1.mp3', hr2: '4-30-11 HR 2.mp3' }
|
|
]},
|
|
{ num: '5', name: 'May', eps: [
|
|
{ d: '5-07-11', hr1: '5-07-11 HR 1.mp3', hr2: '5-07-11 HR 2.mp3' },
|
|
{ d: '5-14-11', hr1: '5-14-11 HR 1.mp3', hr2: '5-14-11 HR 2.mp3' },
|
|
{ d: '5-21-11', hr1: '5-21-11 HR 1.mp3', hr2: '5-21-11 HR 2.mp3' },
|
|
{ d: '5-28-11', hr1: '5-28-11 HR 1.mp3', hr2: '5-28-11 HR 2.mp3' }
|
|
]},
|
|
{ num: '6', name: 'June', eps: [
|
|
{ d: '6-04-11', hr1: '6-04-11 HR 1.mp3', hr2: '6-04-11 HR 2.mp3' },
|
|
{ d: '6-11-11', hr1: '6-11-11 HR 1.mp3', hr2: '6-11-11 HR 2.mp3' },
|
|
{ d: '6-18-11', hr1: '6-18-11 HR 1.mp3', hr2: '6-18-11 HR 2.mp3' }
|
|
]},
|
|
{ num: '7', name: 'July', eps: [
|
|
{ d: '7-02-11', hr1: '7-02-11 HR 1.mp3', hr2: '7-02-11 HR 2.mp3' },
|
|
{ d: '7-09-11', hr1: '7-09-11 HR 1.mp3', hr2: '7-09-11 HR 2.mp3' },
|
|
{ d: '7-16-11', hr1: '7-16-11 HR 1.mp3', hr2: '7-16-11 HR 2.mp3' },
|
|
{ d: '7-23-11', hr1: '7-23-11 HR 1.mp3', hr2: '7-23-11 HR 2.mp3' },
|
|
{ d: '7-30-11', hr1: '7-30-11 HR 1.mp3', hr2: '7-30-11 HR 2.mp3' }
|
|
]},
|
|
{ num: '8', name: 'August', eps: [
|
|
{ d: '8-06-11', hr1: '8-06-11 HR 1.mp3', hr2: '8-06-11 HR 2.mp3' },
|
|
{ d: '8-13-11', hr1: '8-13-11 HR 1.mp3', hr2: '8-13-11 HR 2.mp3' },
|
|
{ d: '8-20-11', hr1: '8-20-11 HR 1.mp3', hr2: '8-20-11 HR 2.mp3' },
|
|
{ d: '8-27-11', hr1: '8-27-11 HR 1.mp3', hr2: '8-27-11 HR 2.mp3' }
|
|
]},
|
|
{ num: '9', name: 'September', eps: [
|
|
{ d: '9-03-11', hr1: '9-03-11 HR 1.mp3', hr2: '9-03-11 HR 2.mp3' },
|
|
{ d: '9-10-11', hr1: '9-10-11 HR 1.mp3', hr2: '9-10-11 HR 2.mp3' },
|
|
{ d: '9-24-11', hr1: '9-24-11 HR 1.mp3', hr2: '9-24-11 HR 2.mp3' }
|
|
]},
|
|
{ num: '10', name: 'October', eps: [
|
|
{ d: '10-01-11', hr1: '10-01-11 HR 1.mp3', hr2: '10-01-11 HR 2.mp3' },
|
|
{ d: '10-08-11', hr1: '10-08-11 HR 1.mp3', hr2: '10-08-11 HR 2.mp3' },
|
|
{ d: '10-15-11', hr1: '10-15-11 HR 1.mp3', hr2: '10-15-11 HR 2.mp3' },
|
|
{ d: '10-22-11', hr1: '10-22-11 HR 1.mp3', hr2: '10-22-11 HR 2.mp3' },
|
|
{ d: '10-29-11', hr1: '10-29-11 HR 1.mp3', hr2: '10-29-11 HR 2.mp3' }
|
|
]},
|
|
{ num: '11', name: 'November', eps: [
|
|
{ d: '11-05-11', hr1: '11-05-11 HR 1.mp3', hr2: '11-05-11 HR 2.mp3' },
|
|
{ d: '11-12-11', hr1: '11-12-11 HR 1.mp3', hr2: '11-12-11 HR 2.mp3' },
|
|
{ d: '11-19-11', hr1: '11-19-11 HR 1.mp3', hr2: '11-19-11 HR 2.mp3' },
|
|
{ d: '11-26-11', hr1: '11-26-11 HR 1.mp3', hr2: '11-26-11 HR 2.mp3' }
|
|
]},
|
|
{ num: '12', name: 'December', eps: [
|
|
{ d: '12-03-11', hr1: '12-03-11 HR 1.mp3', hr2: '12-03-11 HR 2.mp3' },
|
|
{ d: '12-10-11', hr1: '12-10-11 HR 1.mp3', hr2: '12-10-11 HR 2.mp3' },
|
|
{ d: '12-17-11', hr1: '12-17-11 HR 1.mp3', hr2: '12-17-11 HR 2.mp3' },
|
|
{ d: '12-24-11', hr1: '12-24-11 HR 1.mp3', hr2: '12-24-11 HR 2.mp3' },
|
|
{ d: '12-31-11', hr1: '12-31-11- HR 1.mp3', hr2: '12-31-11 HR 2.mp3' }
|
|
]}
|
|
];
|
|
|
|
var monthNames = ['','January','February','March','April','May','June','July','August','September','October','November','December'];
|
|
|
|
months2011.forEach(function(m) {
|
|
var dir = '2011/' + m.num + ' - ' + m.name + '/';
|
|
m.eps.forEach(function(ep) {
|
|
var parts = ep.d.split('-');
|
|
var mo = parseInt(parts[0]), dy = parseInt(parts[1]);
|
|
addEp(2011, m.name + ' ' + dy + ', 2011', 'Hour 1 + Hour 2', [
|
|
{ label: 'HR 1', path: enc(dir + ep.hr1) },
|
|
{ label: 'HR 2', path: enc(dir + ep.hr2) }
|
|
]);
|
|
});
|
|
});
|
|
|
|
// ===== 2012 =====
|
|
var months2012 = [
|
|
{ num: '1', name: 'January', eps: [
|
|
{ d: '1-07-12', hr1: '1-07-12 HR 1.mp3', hr2: '1-07-12 HR 2.mp3' },
|
|
{ d: '1-14-12', hr1: '1-14-12 HR 1.mp3', hr2: '1-14-12 HR 2.mp3' },
|
|
{ d: '1-21-12', hr1: '1-21-12 HR 1.mp3', hr2: '1-21-12 HR 2.mp3' },
|
|
{ d: '1-28-12', hr1: '1-28-12 HR 1.mp3', hr2: '1-28-12 HR 2.mp3' }
|
|
]},
|
|
{ num: '2', name: 'February', eps: [
|
|
{ d: '2-04-12', hr1: '2-04-12 HR 1.mp3', hr2: '2-04-12 HR 2.mp3' },
|
|
{ d: '2-11-12', hr1: '2-11-12 HR 1.mp3', hr2: '2-11-12 HR 2.mp3' },
|
|
{ d: '2-18-12', hr1: '2-18-12 HR 1.mp3', hr2: '2-18-12 HR 2.mp3' },
|
|
{ d: '2-25-12', hr1: '2-25-12 HR 1.mp3', hr2: '2-25-12 HR 2.mp3' }
|
|
]},
|
|
{ num: '3', name: 'March', eps: [
|
|
{ d: '3-03-12', hr1: '2012/30312HR1.mp3', hr2: '2012/30312HR2.mp3', abs: true },
|
|
{ d: '3-10-12', hr1: '3-10-12HR1.mp3', hr2: '3-10-12HR2.mp3' },
|
|
{ d: '3-17-12', hr1: '2012/3-17-12HR1.mp3', hr2: '2012/3-17-12HR2.mp3', abs: true },
|
|
{ d: '3-24-12', hr1: '3-24-12HR1.mp3', hr2: '3-24-12HR2.mp3' },
|
|
{ d: '3-31-12', hr1: '33112HR1.mp3', hr2: '33112HR2.mp3' }
|
|
]},
|
|
{ num: '4', name: 'April', eps: [
|
|
{ d: '4-07-12', hr1: '4-07-12HR1.mp3', hr2: '4-07-12HR2.mp3' },
|
|
{ d: '4-14-12', hr1: '4-14-12HR1.mp3', hr2: '4-14-12HR2.mp3' },
|
|
{ d: '4-21-12', hr1: '4-21-12HR1.mp3', hr2: '4-21-12HR2.mp3' },
|
|
{ d: '4-28-12', hr1: '4-28-12HR1.mp3', hr2: '4-28-12HR2.mp3' }
|
|
]},
|
|
{ num: '5', name: 'May', eps: [
|
|
{ d: '5-5-12', hr1: '5-5-12HR1.mp3', hr2: '5-5-12HR2.mp3' },
|
|
{ d: '5-12-12', hr1: '5-12-12HR1.mp3', hr2: '5-12-12HR2.mp3' },
|
|
{ d: '5-19-12', hr1: '5-19-12HR1.mp3', hr2: '5-19-12HR2.mp3' },
|
|
{ d: '5-26-12', hr1: '5-26-12HR1.mp3', hr2: '5-26-12HR2.mp3' }
|
|
]},
|
|
{ num: '6', name: 'June', eps: [
|
|
{ d: '6-2-12', hr1: '6-2-12HR1.mp3', hr2: '6-2-12HR2.mp3' },
|
|
{ d: '6-9-12', hr1: '6-9-12HR1.mp3', hr2: '6-9-12HR2.mp3' },
|
|
{ d: '6-16-12', hr1: '6-16-12HR1.mp3', hr2: '6-16-12HR2.mp3' },
|
|
{ d: '6-23-12', hr1: '6-23-12HR1.mp3', hr2: '6-23-12HR2.mp3' }
|
|
]},
|
|
{ num: '7', name: 'July', eps: [
|
|
{ d: '7-7-12', hr1: '7-7-12HR1.mp3', hr2: '7-7-12HR2.mp3' },
|
|
{ d: '7-14-12', hr1: '7-14-12HR1.mp3', hr2: '7-14-12HR2.mp3' },
|
|
{ d: '7-21-12', hr1: '7-21-12HR1.mp3', hr2: '7-21-12HR2.mp3' },
|
|
{ d: '7-28-12', hr1: '7-28-12HR1.mp3', hr2: '7-28-12HR2.mp3' }
|
|
]},
|
|
{ num: '8', name: 'August', eps: [
|
|
{ d: '8-11-12', hr1: '8-11-12HR1.mp3', hr2: '8-11-12HR2.mp3' },
|
|
{ d: '8-18-12', hr1: '8-18-12HR1.mp3', hr2: '8-18-12HR2.mp3' },
|
|
{ d: '8-25-12', hr1: '8-25-12HR1.mp3', hr2: '8-25-12HR2.mp3' }
|
|
]},
|
|
{ num: '9', name: 'September', eps: [
|
|
{ d: '9-1-12', hr1: '9-1-12HR1.mp3', hr2: '9-1-12HR2.mp3' },
|
|
{ d: '9-8-12', hr1: '9-8-12HR1.mp3', hr2: '9-8-12HR2.mp3' },
|
|
{ d: '9-15-12', hr1: '9-15-12HR1.mp3', hr2: '9-15-12HR2.mp3' },
|
|
{ d: '9-22-12', hr1: '9-22-12HR1.mp3', hr2: '9-22-12HR2.mp3' },
|
|
{ d: '9-29-12', hr1: '9-29-12HR1.mp3', hr2: '9-29-12HR2.mp3' }
|
|
]},
|
|
{ num: '10', name: 'October', eps: [
|
|
{ d: '10-6-12', hr1: '10-6-12HR1.mp3', hr2: '10-6-12HR2.mp3' }
|
|
]}
|
|
];
|
|
|
|
months2012.forEach(function(m) {
|
|
var dir = '2012/' + m.num + ' - ' + m.name + '/';
|
|
m.eps.forEach(function(ep) {
|
|
var parts = ep.d.split('-');
|
|
var mo = parseInt(parts[0]), dy = parseInt(parts[1]);
|
|
var h1 = ep.abs ? ep.hr1 : dir + ep.hr1;
|
|
var h2 = ep.abs ? ep.hr2 : dir + ep.hr2;
|
|
addEp(2012, monthNames[mo] + ' ' + dy + ', 2012', 'Hour 1 + Hour 2', [
|
|
{ label: 'HR 1', path: enc(h1) },
|
|
{ label: 'HR 2', path: enc(h2) }
|
|
]);
|
|
});
|
|
});
|
|
|
|
// ===== 2014 (season/episode) =====
|
|
var se2014 = [
|
|
{ mo: '02', eps: ['s6e01','s6e02','s6e03','s6e04'] },
|
|
{ mo: '03', eps: ['s6e05','s6e06','s6e07','s6e08'] },
|
|
{ mo: '04', eps: ['s6e09','s6e10','s6e11','s6e12'] },
|
|
{ mo: '05', eps: ['s6e13','s6e14','s6e15','s6e16','s6e17'] },
|
|
{ mo: '06', eps: ['s6e18','s6e19','s6e20','s6e21'] },
|
|
{ mo: '07', eps: ['s6e22','s6e23','s6e24','s6e25'] },
|
|
{ mo: '08', eps: ['s6e26','s6e27','s6e28','s7e01','s7e02'] },
|
|
{ mo: '09', eps: ['s7e03','s7e04','s7e05','s7e06'] },
|
|
{ mo: '10', eps: ['s7e07','s7e08','s7e09','s7e10'] },
|
|
{ mo: '11', eps: ['s7e11','s7e12','s7e13','s7e14'] },
|
|
{ mo: '12', eps: ['s7e15','s7e16','s7e17','s7e18'] }
|
|
];
|
|
|
|
function parseSE(code) {
|
|
var m = code.match(/s(\d+)e(\d+)/);
|
|
return { season: parseInt(m[1]), episode: parseInt(m[2]) };
|
|
}
|
|
|
|
se2014.forEach(function(g) {
|
|
g.eps.forEach(function(code) {
|
|
var se = parseSE(code);
|
|
addEp(2014, 'Season ' + se.season + ', Episode ' + se.episode,
|
|
monthNames[parseInt(g.mo)] + ' 2014',
|
|
[{ label: 'Play', path: '2014/' + g.mo + '/' + code + '.mp3' }]);
|
|
});
|
|
});
|
|
|
|
// Special
|
|
addEp(2014, 'Liberty Watch Special', 'May 11, 2014',
|
|
[{ label: 'Play', path: '2014/specials/libertywatch5-11-14.mp3' }]);
|
|
|
|
// ===== 2015 =====
|
|
var se2015 = [
|
|
{ mo: '01', eps: ['s7e19','s7e20','s7e21','s7e22','s7e23'] },
|
|
{ mo: '02', eps: ['s7e24','s7e25','s7e26','s7e27'] },
|
|
{ mo: '03', eps: ['s7e28','s7e29','s7e30','s7e31'] },
|
|
{ mo: '04', eps: ['s7e32','s7e33','s7e34','s7e35'] },
|
|
{ mo: '05', eps: ['s7e36','s7e37','s7e38','s7e39','s7e40'] },
|
|
{ mo: '06', eps: ['s7e41','s7e42','s7e43','s7e44'] },
|
|
{ mo: '07', eps: ['s7e45','s7e46','s7e47'] },
|
|
{ mo: '08', eps: ['s7e48','s7e49','s7e50','s7e51','s8e01','s8e02'] },
|
|
{ mo: '09', eps: ['s8e03','s8e04','s8e05'] },
|
|
{ mo: '10', eps: ['s8e06','s8e07','s8e08','s8e09'] },
|
|
{ mo: '11', eps: ['s8e10','s8e11','s8e12','s8e13'] },
|
|
{ mo: '12', eps: ['s8e14','s8e15','s8e16','s8e17'] }
|
|
];
|
|
|
|
se2015.forEach(function(g) {
|
|
g.eps.forEach(function(code) {
|
|
var se = parseSE(code);
|
|
addEp(2015, 'Season ' + se.season + ', Episode ' + se.episode,
|
|
monthNames[parseInt(g.mo)] + ' 2015',
|
|
[{ label: 'Play', path: '2015/' + g.mo + '/' + code + '.mp3' }]);
|
|
});
|
|
});
|
|
|
|
// ===== 2016 =====
|
|
var se2016 = [
|
|
{ mo: '01', eps: ['s8e18','s8e19','s8e20','s8e21','s8e22'] },
|
|
{ mo: '02', eps: ['s8e23','s8e24','s8e25','s8e26'] },
|
|
{ mo: '03', eps: ['s8e27','s8e28','s8e29','s8e30'] },
|
|
{ mo: '04', eps: ['s8e31','s8e32','s8e33','s8e34','s8e35'] },
|
|
{ mo: '05', eps: ['s8e36','s8e37','s8e38','s8e39','s8e40'] },
|
|
{ mo: '06', eps: ['s8e41','s8e42','s8e43'] },
|
|
{ mo: '07', eps: ['s8e44','s8e45','s8e46','s8e47','s8e48'] },
|
|
{ mo: '08', eps: ['s8e49','s8e50','s8e51','s8e52'] },
|
|
{ mo: '09', eps: ['s9e01','s9e02','s9e03','s9e04'] },
|
|
{ mo: '10', eps: ['s9e05','s9e06','s9e07','s9e08','s9e09'] },
|
|
{ mo: '11', eps: ['s9e10','s9e11','s9e12','s9e13'] },
|
|
{ mo: '12', eps: ['s9e14','s9e15','s9e16','s9e17','s9e18'] }
|
|
];
|
|
|
|
se2016.forEach(function(g) {
|
|
g.eps.forEach(function(code) {
|
|
var se = parseSE(code);
|
|
addEp(2016, 'Season ' + se.season + ', Episode ' + se.episode,
|
|
monthNames[parseInt(g.mo)] + ' 2016',
|
|
[{ label: 'Play', path: '2016/' + g.mo + '/' + code + '.mp3' }]);
|
|
});
|
|
});
|
|
|
|
// ===== 2017 =====
|
|
var se2017 = [
|
|
{ mo: '01', eps: ['s9e19','s9e20','s9e21','s9e22'] },
|
|
{ mo: '02', eps: ['s9e23','s9e24','s9e25'] },
|
|
{ mo: '03', eps: ['s9e26','s9e27','s9e28','s9e29'] },
|
|
{ mo: '04', eps: ['s9e30','s9e31','s9e32','s9e33','s9e34'] },
|
|
{ mo: '05', eps: ['s9e35','s9e36','s9e37','s9e38'] },
|
|
{ mo: '06', eps: ['s9e39','s9e40','s9e41'] },
|
|
{ mo: '07', eps: ['s9e42','s9e43','s9e44','s9e45'] },
|
|
{ mo: '08', eps: ['s9e46','s9e47'] },
|
|
{ mo: '09', eps: ['s10e01','s10e02','s10e03','s10e04','s10e05'] },
|
|
{ mo: '10', eps: ['s10e06','s10e07','s10e08'] },
|
|
{ mo: '11', eps: ['s10e09','s10e10','s10e11'] },
|
|
{ mo: '12', eps: ['s10e12'] }
|
|
];
|
|
|
|
se2017.forEach(function(g) {
|
|
g.eps.forEach(function(code) {
|
|
var se = parseSE(code);
|
|
addEp(2017, 'Season ' + se.season + ', Episode ' + se.episode,
|
|
monthNames[parseInt(g.mo)] + ' 2017',
|
|
[{ label: 'Play', path: '2017/' + g.mo + '/' + code + '.mp3' }]);
|
|
});
|
|
});
|
|
|
|
// ===== 2018 =====
|
|
var se2018 = [
|
|
{ mo: '01', eps: ['s10e17','s10e18'] },
|
|
{ mo: '02', eps: ['s10e19','s10e20'] },
|
|
{ mo: '03', eps: ['s10e21'] }
|
|
];
|
|
|
|
se2018.forEach(function(g) {
|
|
g.eps.forEach(function(code) {
|
|
var se = parseSE(code);
|
|
addEp(2018, 'Season ' + se.season + ', Episode ' + se.episode,
|
|
monthNames[parseInt(g.mo)] + ' 2018',
|
|
[{ label: 'Play', path: '2018/' + g.mo + '/' + code + '.mp3' }]);
|
|
});
|
|
});
|
|
|
|
// ===== RADIO ELEMENTS =====
|
|
var RADIO_ELEMENTS = {
|
|
'Bumpers': [
|
|
{ name: 'Cities in Dust', path: 'Radio/Elements/Bumpers/cities_in_dust.mp3' },
|
|
{ name: 'ET Edit Intro/Outro', path: 'Radio/Elements/Bumpers/ET_edit_intro_or_outro.mp3' },
|
|
{ name: 'Rancid Riot', path: 'Radio/Elements/Bumpers/rancid_riot.mp3' },
|
|
{ name: 'Stereo MC Sophisticated', path: 'Radio/Elements/Bumpers/stereo_mc_sofisticated_edit.mp3' },
|
|
{ name: 'Warning Intro', path: 'Radio/Elements/Bumpers/Warnng_INTRO.mp3' },
|
|
{ name: 'Warning Intro/Outro', path: 'Radio/Elements/Bumpers/Warnng_intro_or_outro.mp3' },
|
|
{ name: 'White & Nerdy Intro (MP3)', path: 'Radio/Elements/Bumpers/white_n_nerdy_intro.mp3' },
|
|
{ name: 'White & Nerdy Intro (WAV)', path: 'Radio/Elements/Bumpers/white_n_nerdy_intro.wav' }
|
|
],
|
|
'Show Elements': [
|
|
{ name: 'Intro - Beast', path: enc('Radio/Elements/Computer_Guru_Elements/Computer_Guru_intro_beast.wav') },
|
|
{ name: 'Intro - Kick Back', path: enc('Radio/Elements/Computer_Guru_Elements/Computer Guru_intro_kick_back.wav') },
|
|
{ name: 'Intro / Outro', path: enc('Radio/Elements/Computer_Guru_Elements/Computer Guru_intro__or_outro.wav') },
|
|
{ name: 'Outro', path: enc('Radio/Elements/Computer_Guru_Elements/Computer Guru_outro.wav') },
|
|
{ name: 'Show Intro', path: enc('Radio/Elements/Computer_Guru_Elements/Computer Guru SHOW INTRO.wav') }
|
|
],
|
|
'Show Elements (Corrected)': [
|
|
{ name: 'Intro - Beast (Corrected)', path: enc('Radio/Elements/Computer_Guru_Elements_CORRECTED/Computer_Guru_intro_beast_correct_number.wav') },
|
|
{ name: 'Intro - Kick Back (Corrected)', path: enc('Radio/Elements/Computer_Guru_Elements_CORRECTED/Computer Guru_intro_kick_back_corrected_number.wav') },
|
|
{ name: 'Intro / Outro', path: enc('Radio/Elements/Computer_Guru_Elements_CORRECTED/Computer Guru_intro__or_outro.wav') },
|
|
{ name: 'Outro (Corrected)', path: enc('Radio/Elements/Computer_Guru_Elements_CORRECTED/Computer Guru_outro_corrected_number.wav') },
|
|
{ name: 'Show Intro (Corrected)', path: enc('Radio/Elements/Computer_Guru_Elements_CORRECTED/Computer Guru SHOW INTRO_Corrected_number.wav') }
|
|
],
|
|
'Permanent Elements': [
|
|
{ name: 'AZ Comp Guru Spot - Frozen', path: enc('Radio/Elements/Elements - Permanent/az_comp_guru_spot_frozen.wav') },
|
|
{ name: 'Welcome In/Out Combo', path: enc('Radio/Elements/Elements - Permanent/Combo_comp_guru_in_or_out_welcome.wav') },
|
|
{ name: 'Intro - Streaming + Yeah Science', path: enc('Radio/Elements/Elements - Permanent/Combo_Comp_Guru_INTRO__Streaming_yeahscience.wav') },
|
|
{ name: 'Intro - Beast Combo', path: enc('Radio/Elements/Elements - Permanent/Combo_Intro_Beast_Computer Guru.wav') },
|
|
{ name: 'Intro - Streaming Combo', path: enc('Radio/Elements/Elements - Permanent/Combo_Intro_STREAMING_Computer Guru.wav') },
|
|
{ name: 'Show Intro (Newer)', path: enc('Radio/Elements/Elements - Permanent/Comp Guru_SHOW_INTRO_NEWER.wav') },
|
|
{ name: 'Promo Window (Newer)', path: enc('Radio/Elements/Elements - Permanent/Computer Guru_promo_window_NEWER.wav') }
|
|
]
|
|
};
|
|
// Flat list for backward compat
|
|
var BUMPERS = [];
|
|
Object.keys(RADIO_ELEMENTS).forEach(function(cat) {
|
|
RADIO_ELEMENTS[cat].forEach(function(item) {
|
|
item._category = cat;
|
|
BUMPERS.push(item);
|
|
});
|
|
});
|
|
|
|
// ===== COMPUTE YEAR INDEX =====
|
|
var YEARS = [2010, 2011, 2012, 2014, 2015, 2016, 2017, 2018];
|
|
var yearEpisodes = {};
|
|
YEARS.forEach(function(y) { yearEpisodes[y] = []; });
|
|
ARCHIVE.forEach(function(ep, i) {
|
|
ep._idx = i;
|
|
if (yearEpisodes[ep.year]) yearEpisodes[ep.year].push(ep);
|
|
});
|
|
|
|
// ===== STATE =====
|
|
var currentYear = null;
|
|
var currentEpIdx = null;
|
|
var currentFileIdx = 0;
|
|
var isPlaying = false;
|
|
|
|
// ===== DOM REFS =====
|
|
var audioEl = document.getElementById('audioEl');
|
|
var playerBar = document.getElementById('playerBar');
|
|
var playerTitle = document.getElementById('playerTitle');
|
|
var playerSub = document.getElementById('playerSub');
|
|
var playerTime = document.getElementById('playerTime');
|
|
var progressFill = document.getElementById('progressFill');
|
|
var progressWrap = document.getElementById('progressWrap');
|
|
var btnPlayPause = document.getElementById('btnPlayPause');
|
|
var iconPlay = document.getElementById('iconPlay');
|
|
var iconPause = document.getElementById('iconPause');
|
|
var btnPrev = document.getElementById('btnPrev');
|
|
var btnNext = document.getElementById('btnNext');
|
|
var volumeSlider = document.getElementById('volumeSlider');
|
|
var navTabs = document.getElementById('navTabs');
|
|
var mainContent = document.getElementById('mainContent');
|
|
|
|
// ===== BUILD NAV TABS =====
|
|
YEARS.forEach(function(y) {
|
|
var btn = document.createElement('button');
|
|
btn.className = 'nav-tab';
|
|
btn.dataset.year = y;
|
|
btn.innerHTML = '<span>' + y + '</span><span class="tab-count">' + yearEpisodes[y].length + ' eps</span>';
|
|
btn.addEventListener('click', function() { selectYear(y); });
|
|
navTabs.appendChild(btn);
|
|
});
|
|
|
|
// Add bumpers tab
|
|
var bumperTab = document.createElement('button');
|
|
bumperTab.className = 'nav-tab';
|
|
bumperTab.dataset.year = 'bumpers';
|
|
bumperTab.innerHTML = '<span>Elements</span><span class="tab-count">' + BUMPERS.length + '</span>';
|
|
bumperTab.addEventListener('click', function() { selectYear('bumpers'); });
|
|
navTabs.appendChild(bumperTab);
|
|
|
|
// ===== SELECT YEAR =====
|
|
function selectYear(year) {
|
|
currentYear = year;
|
|
|
|
// Update tab active state
|
|
var tabs = navTabs.querySelectorAll('.nav-tab');
|
|
tabs.forEach(function(t) {
|
|
t.classList.toggle('active', t.dataset.year === String(year));
|
|
});
|
|
|
|
renderEpisodes(year);
|
|
saveState();
|
|
}
|
|
|
|
// ===== RENDER EPISODES =====
|
|
function renderEpisodes(year) {
|
|
if (year === 'bumpers') {
|
|
renderBumpers();
|
|
return;
|
|
}
|
|
|
|
var eps = yearEpisodes[year];
|
|
if (!eps || eps.length === 0) {
|
|
mainContent.innerHTML = '<div class="empty-state"><p>No episodes found for ' + year + '</p></div>';
|
|
return;
|
|
}
|
|
|
|
var html = '<h2 class="section-title"><span>' + year + '</span> Episodes</h2>';
|
|
html += '<div class="episode-list">';
|
|
|
|
eps.forEach(function(ep) {
|
|
var isActive = currentEpIdx === ep._idx;
|
|
html += '<div class="episode-item' + (isActive ? ' playing' : '') + '" data-idx="' + ep._idx + '">';
|
|
html += '<button class="episode-play-btn" data-idx="' + ep._idx + '">' + SVG_PLAY + '</button>';
|
|
html += '<div class="episode-info">';
|
|
html += '<div class="episode-title">' + escHtml(ep.title) + '</div>';
|
|
html += '<div class="episode-meta">' + escHtml(ep.subtitle) + '</div>';
|
|
html += '</div>';
|
|
|
|
if (ep.files.length > 1) {
|
|
html += '<div class="episode-parts">';
|
|
ep.files.forEach(function(f, fi) {
|
|
var partActive = isActive && currentFileIdx === fi;
|
|
html += '<button class="part-btn' + (partActive ? ' active-part' : '') + '" data-idx="' + ep._idx + '" data-fi="' + fi + '">' + escHtml(f.label) + '</button>';
|
|
});
|
|
html += '</div>';
|
|
}
|
|
|
|
html += '<div class="episode-download">';
|
|
ep.files.forEach(function(f) {
|
|
var dlLabel = ep.files.length > 1 ? f.label : '';
|
|
html += '<a class="dl-btn" href="' + f.path + '" download title="Download ' + escHtml(ep.title + (dlLabel ? ' - ' + dlLabel : '')) + '">' + SVG_DOWNLOAD + (dlLabel ? ' ' + escHtml(dlLabel) : '') + '</a>';
|
|
});
|
|
html += '</div>';
|
|
|
|
html += '</div>';
|
|
});
|
|
|
|
html += '</div>';
|
|
mainContent.innerHTML = html;
|
|
|
|
// Attach click handlers
|
|
mainContent.querySelectorAll('.episode-play-btn').forEach(function(btn) {
|
|
btn.addEventListener('click', function(e) {
|
|
e.stopPropagation();
|
|
playEpisode(parseInt(this.dataset.idx), 0);
|
|
});
|
|
});
|
|
|
|
mainContent.querySelectorAll('.episode-item').forEach(function(item) {
|
|
item.addEventListener('click', function() {
|
|
playEpisode(parseInt(this.dataset.idx), 0);
|
|
});
|
|
});
|
|
|
|
mainContent.querySelectorAll('.part-btn').forEach(function(btn) {
|
|
btn.addEventListener('click', function(e) {
|
|
e.stopPropagation();
|
|
playEpisode(parseInt(this.dataset.idx), parseInt(this.dataset.fi));
|
|
});
|
|
});
|
|
|
|
mainContent.querySelectorAll('.dl-btn').forEach(function(btn) {
|
|
btn.addEventListener('click', function(e) {
|
|
e.stopPropagation();
|
|
});
|
|
});
|
|
}
|
|
|
|
function renderBumpers() {
|
|
var html = '<h2 class="section-title"><span>Radio</span> Elements</h2>';
|
|
var globalIdx = 0;
|
|
|
|
Object.keys(RADIO_ELEMENTS).forEach(function(cat) {
|
|
html += '<h3 style="font-size:1rem;font-weight:500;color:var(--text-muted);margin:20px 0 10px;padding-left:4px;">' + escHtml(cat) + '</h3>';
|
|
html += '<div class="bumper-grid">';
|
|
RADIO_ELEMENTS[cat].forEach(function(b) {
|
|
var idx = BUMPERS.indexOf(b);
|
|
html += '<div class="bumper-item" data-bumper="' + idx + '">';
|
|
html += '<button class="bumper-play" data-bumper="' + idx + '">' + SVG_PLAY + '</button>';
|
|
html += '<div class="bumper-name">' + escHtml(b.name) + '</div>';
|
|
html += '<a class="bumper-dl" href="' + b.path + '" download title="Download">' + SVG_DOWNLOAD + '</a>';
|
|
html += '</div>';
|
|
});
|
|
html += '</div>';
|
|
});
|
|
|
|
mainContent.innerHTML = html;
|
|
|
|
mainContent.querySelectorAll('.bumper-item').forEach(function(item) {
|
|
item.addEventListener('click', function() {
|
|
playBumper(parseInt(this.dataset.bumper));
|
|
});
|
|
});
|
|
|
|
mainContent.querySelectorAll('.bumper-dl').forEach(function(btn) {
|
|
btn.addEventListener('click', function(e) {
|
|
e.stopPropagation();
|
|
});
|
|
});
|
|
}
|
|
|
|
// ===== PLAY EPISODE =====
|
|
function playEpisode(idx, fileIdx) {
|
|
var ep = ARCHIVE[idx];
|
|
if (!ep) return;
|
|
|
|
currentEpIdx = idx;
|
|
currentFileIdx = fileIdx || 0;
|
|
|
|
var file = ep.files[currentFileIdx];
|
|
audioEl.src = file.path;
|
|
audioEl.play().catch(function() {});
|
|
|
|
// Restore position if saved
|
|
var savedPos = getSavedPosition(file.path);
|
|
if (savedPos > 0) {
|
|
audioEl.addEventListener('loadedmetadata', function onLoad() {
|
|
audioEl.currentTime = savedPos;
|
|
audioEl.removeEventListener('loadedmetadata', onLoad);
|
|
});
|
|
}
|
|
|
|
isPlaying = true;
|
|
updatePlayerUI();
|
|
updatePlayPauseIcon();
|
|
|
|
playerBar.classList.remove('hidden');
|
|
|
|
// Refresh episode list to show active state
|
|
if (currentYear && currentYear !== 'bumpers') {
|
|
renderEpisodes(currentYear);
|
|
}
|
|
|
|
saveState();
|
|
}
|
|
|
|
function playBumper(idx) {
|
|
var b = BUMPERS[idx];
|
|
if (!b) return;
|
|
|
|
currentEpIdx = null;
|
|
currentFileIdx = 0;
|
|
|
|
audioEl.src = b.path;
|
|
audioEl.play().catch(function() {});
|
|
|
|
isPlaying = true;
|
|
playerTitle.textContent = b.name;
|
|
playerSub.textContent = 'Radio Element - Bumper';
|
|
|
|
updatePlayPauseIcon();
|
|
playerBar.classList.remove('hidden');
|
|
}
|
|
|
|
// ===== PLAYER UI UPDATE =====
|
|
function updatePlayerUI() {
|
|
if (currentEpIdx === null) return;
|
|
var ep = ARCHIVE[currentEpIdx];
|
|
if (!ep) return;
|
|
|
|
var file = ep.files[currentFileIdx];
|
|
playerTitle.textContent = ep.title;
|
|
|
|
var sub = ep.subtitle;
|
|
if (ep.files.length > 1) {
|
|
sub += ' - ' + file.label;
|
|
}
|
|
playerSub.textContent = sub;
|
|
}
|
|
|
|
function updatePlayPauseIcon() {
|
|
if (isPlaying) {
|
|
iconPlay.style.display = 'none';
|
|
iconPause.style.display = 'block';
|
|
} else {
|
|
iconPlay.style.display = 'block';
|
|
iconPause.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
function formatTime(s) {
|
|
if (isNaN(s) || !isFinite(s)) return '0:00';
|
|
var h = Math.floor(s / 3600);
|
|
var m = Math.floor((s % 3600) / 60);
|
|
var sec = Math.floor(s % 60);
|
|
if (h > 0) {
|
|
return h + ':' + (m < 10 ? '0' : '') + m + ':' + (sec < 10 ? '0' : '') + sec;
|
|
}
|
|
return m + ':' + (sec < 10 ? '0' : '') + sec;
|
|
}
|
|
|
|
// ===== AUDIO EVENT HANDLERS =====
|
|
audioEl.addEventListener('timeupdate', function() {
|
|
var cur = audioEl.currentTime;
|
|
var dur = audioEl.duration;
|
|
playerTime.textContent = formatTime(cur) + ' / ' + formatTime(dur);
|
|
if (dur > 0) {
|
|
progressFill.style.width = (cur / dur * 100) + '%';
|
|
}
|
|
// Save position every 5 seconds
|
|
if (audioEl.src && Math.floor(cur) % 5 === 0) {
|
|
savePosition(audioEl.src, cur);
|
|
}
|
|
});
|
|
|
|
audioEl.addEventListener('play', function() {
|
|
isPlaying = true;
|
|
updatePlayPauseIcon();
|
|
});
|
|
|
|
audioEl.addEventListener('pause', function() {
|
|
isPlaying = false;
|
|
updatePlayPauseIcon();
|
|
if (audioEl.src) {
|
|
savePosition(audioEl.src, audioEl.currentTime);
|
|
}
|
|
});
|
|
|
|
audioEl.addEventListener('ended', function() {
|
|
// Clear saved position for completed file
|
|
if (audioEl.src) {
|
|
clearPosition(audioEl.src);
|
|
}
|
|
playNext();
|
|
});
|
|
|
|
// ===== PLAYER CONTROLS =====
|
|
btnPlayPause.addEventListener('click', function() {
|
|
if (!audioEl.src) return;
|
|
if (audioEl.paused) {
|
|
audioEl.play().catch(function() {});
|
|
} else {
|
|
audioEl.pause();
|
|
}
|
|
});
|
|
|
|
btnPrev.addEventListener('click', function() { playPrev(); });
|
|
btnNext.addEventListener('click', function() { playNext(); });
|
|
|
|
function playNext() {
|
|
if (currentEpIdx === null) return;
|
|
var ep = ARCHIVE[currentEpIdx];
|
|
|
|
// Try next file part
|
|
if (currentFileIdx < ep.files.length - 1) {
|
|
playEpisode(currentEpIdx, currentFileIdx + 1);
|
|
return;
|
|
}
|
|
|
|
// Try next episode in same year
|
|
var yearEps = yearEpisodes[ep.year];
|
|
var posInYear = yearEps.indexOf(ep);
|
|
if (posInYear < yearEps.length - 1) {
|
|
playEpisode(yearEps[posInYear + 1]._idx, 0);
|
|
}
|
|
}
|
|
|
|
function playPrev() {
|
|
if (currentEpIdx === null) return;
|
|
|
|
// If past 3s, restart current
|
|
if (audioEl.currentTime > 3) {
|
|
audioEl.currentTime = 0;
|
|
return;
|
|
}
|
|
|
|
var ep = ARCHIVE[currentEpIdx];
|
|
|
|
// Try previous file part
|
|
if (currentFileIdx > 0) {
|
|
playEpisode(currentEpIdx, currentFileIdx - 1);
|
|
return;
|
|
}
|
|
|
|
// Try previous episode in same year
|
|
var yearEps = yearEpisodes[ep.year];
|
|
var posInYear = yearEps.indexOf(ep);
|
|
if (posInYear > 0) {
|
|
var prevEp = yearEps[posInYear - 1];
|
|
playEpisode(prevEp._idx, prevEp.files.length - 1);
|
|
}
|
|
}
|
|
|
|
// ===== PROGRESS BAR SEEK =====
|
|
progressWrap.addEventListener('click', function(e) {
|
|
if (!audioEl.duration) return;
|
|
var rect = progressWrap.getBoundingClientRect();
|
|
var pct = (e.clientX - rect.left) / rect.width;
|
|
audioEl.currentTime = pct * audioEl.duration;
|
|
});
|
|
|
|
// ===== VOLUME =====
|
|
volumeSlider.addEventListener('input', function() {
|
|
audioEl.volume = this.value / 100;
|
|
try { localStorage.setItem('cgs_volume', this.value); } catch(e) {}
|
|
});
|
|
|
|
// Restore volume
|
|
try {
|
|
var savedVol = localStorage.getItem('cgs_volume');
|
|
if (savedVol !== null) {
|
|
volumeSlider.value = savedVol;
|
|
audioEl.volume = savedVol / 100;
|
|
} else {
|
|
audioEl.volume = 0.8;
|
|
}
|
|
} catch(e) { audioEl.volume = 0.8; }
|
|
|
|
// ===== LOCAL STORAGE HELPERS =====
|
|
function savePosition(src, time) {
|
|
try {
|
|
var key = 'cgs_pos_' + hashStr(src);
|
|
localStorage.setItem(key, String(time));
|
|
} catch(e) {}
|
|
}
|
|
|
|
function getSavedPosition(src) {
|
|
try {
|
|
var key = 'cgs_pos_' + hashStr(src);
|
|
var val = localStorage.getItem(key);
|
|
return val ? parseFloat(val) : 0;
|
|
} catch(e) { return 0; }
|
|
}
|
|
|
|
function clearPosition(src) {
|
|
try {
|
|
var key = 'cgs_pos_' + hashStr(src);
|
|
localStorage.removeItem(key);
|
|
} catch(e) {}
|
|
}
|
|
|
|
function saveState() {
|
|
try {
|
|
localStorage.setItem('cgs_state', JSON.stringify({
|
|
year: currentYear,
|
|
epIdx: currentEpIdx,
|
|
fileIdx: currentFileIdx
|
|
}));
|
|
} catch(e) {}
|
|
}
|
|
|
|
function restoreState() {
|
|
try {
|
|
var raw = localStorage.getItem('cgs_state');
|
|
if (!raw) return;
|
|
var state = JSON.parse(raw);
|
|
if (state.year) {
|
|
selectYear(state.year === 'bumpers' ? 'bumpers' : parseInt(state.year));
|
|
}
|
|
} catch(e) {}
|
|
}
|
|
|
|
function hashStr(s) {
|
|
var hash = 0;
|
|
for (var i = 0; i < s.length; i++) {
|
|
hash = ((hash << 5) - hash) + s.charCodeAt(i);
|
|
hash |= 0;
|
|
}
|
|
return Math.abs(hash).toString(36);
|
|
}
|
|
|
|
function escHtml(s) {
|
|
var div = document.createElement('div');
|
|
div.textContent = s;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
// ===== KEYBOARD SHORTCUTS =====
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
|
|
|
switch(e.code) {
|
|
case 'Space':
|
|
e.preventDefault();
|
|
if (audioEl.src) {
|
|
if (audioEl.paused) audioEl.play().catch(function() {});
|
|
else audioEl.pause();
|
|
}
|
|
break;
|
|
case 'ArrowLeft':
|
|
if (audioEl.src && audioEl.duration) {
|
|
audioEl.currentTime = Math.max(0, audioEl.currentTime - 10);
|
|
}
|
|
break;
|
|
case 'ArrowRight':
|
|
if (audioEl.src && audioEl.duration) {
|
|
audioEl.currentTime = Math.min(audioEl.duration, audioEl.currentTime + 10);
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
|
|
// ===== INIT =====
|
|
restoreState();
|
|
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|