Radio show website: Full Astro build with 194 episodes imported

Complete website for The Computer Guru Show (radio.azcomputerguru.com):
- Astro 6.0.4 static site with React islands
- 194 episodes imported from gurushow.com RSS feed
- Dark/light mode HSL design system
- Persistent audio player with session persistence
- Episode archive with search and season filtering
- Home page with animated hero, stats, latest episodes
- All pages: About, Subscribe, Community, Live, Contact, Blog, 404
- Podcast RSS feed with iTunes namespace
- Session log updated

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 20:44:42 -07:00
parent 1adc2ed3a4
commit ee89727662
236 changed files with 16513 additions and 0 deletions

24
projects/radio-show/website/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store
# jetbrains setting folder
.idea/

View File

@@ -0,0 +1,43 @@
# Astro Starter Kit: Minimal
```sh
npm create astro@latest -- --template minimal
```
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```text
/
├── public/
├── src/
│ └── pages/
│ └── index.astro
└── package.json
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
Any static assets, like images, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).

View File

@@ -0,0 +1,14 @@
// @ts-check
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import mdx from '@astrojs/mdx';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://radio.azcomputerguru.com',
output: 'static',
integrations: [react(), mdx(), sitemap()],
image: {
service: { entrypoint: 'astro/assets/services/sharp' },
},
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
{
"name": "website",
"type": "module",
"version": "0.0.1",
"engines": {
"node": ">=22.12.0"
},
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/check": "^0.9.7",
"@astrojs/mdx": "^5.0.0",
"@astrojs/react": "^5.0.0",
"@astrojs/rss": "^4.0.17",
"@astrojs/sitemap": "^3.7.1",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"astro": "^6.0.4",
"fuse.js": "^7.1.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"typescript": "^5.9.3",
"wavesurfer.js": "^7.12.2"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

After

Width:  |  Height:  |  Size: 749 B

View File

@@ -0,0 +1,570 @@
/**
* import-episodes.mjs
*
* Fetches all episodes from the Computer Guru Show podcast RSS feed
* and converts them into Astro content collection markdown files.
*
* Usage:
* node scripts/import-episodes.mjs
*
* Dependencies: fast-xml-parser, turndown
*/
import { writeFileSync, mkdirSync, existsSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { XMLParser } from "fast-xml-parser";
import TurndownService from "turndown";
// ---------------------------------------------------------------------------
// Configuration
// ---------------------------------------------------------------------------
const FEED_URL = "https://gurushow.com/feed/podcast";
const __dirname = dirname(fileURLToPath(import.meta.url));
const PROJECT_ROOT = join(__dirname, "..");
const EPISODES_DIR = join(PROJECT_ROOT, "src", "content", "episodes");
/** Common tech keywords mapped to tag slugs. */
const TAG_KEYWORDS = new Map([
["apple", "apple"],
["iphone", "iphone"],
["ipad", "ipad"],
["\\bmac\\b", "mac"],
["macbook", "macbook"],
["google", "google"],
["android", "android"],
["chrome", "chrome"],
["chromebook", "chromebook"],
["microsoft", "microsoft"],
["windows", "windows"],
["security", "security"],
["privacy", "privacy"],
["hacking", "security"],
["hack", "security"],
["malware", "malware"],
["ransomware", "ransomware"],
["virus", "malware"],
["\\bai\\b", "ai"],
["artificial.intelligence", "ai"],
["chatgpt", "ai"],
["openai", "ai"],
["machine.learning", "ai"],
["space", "space"],
["spacex", "space"],
["nasa", "space"],
["bitcoin", "cryptocurrency"],
["crypto", "cryptocurrency"],
["cryptocurrency", "cryptocurrency"],
["blockchain", "cryptocurrency"],
["net.neutrality", "net-neutrality"],
["gaming", "gaming"],
["playstation", "gaming"],
["xbox", "gaming"],
["nintendo", "gaming"],
["social.media", "social-media"],
["facebook", "facebook"],
["twitter", "twitter"],
["instagram", "instagram"],
["tiktok", "social-media"],
["amazon", "amazon"],
["alexa", "amazon"],
["tesla", "tesla"],
["samsung", "samsung"],
["cybersecurity", "security"],
["phishing", "security"],
["data.breach", "security"],
["linux", "linux"],
["ubuntu", "linux"],
["roku", "streaming"],
["netflix", "streaming"],
["streaming", "streaming"],
["5g", "5g"],
["\\bvr\\b", "vr"],
["virtual.reality", "vr"],
["augmented.reality", "ar"],
["drone", "drones"],
["robot", "robotics"],
]);
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/**
* Decode common HTML entities that appear in RSS feed titles/descriptions.
* Handles numeric (&#8211;), hex (&#x2019;), and named (&amp;) entities.
*/
function decodeHtmlEntities(text) {
if (!text) return "";
const namedEntities = {
"&amp;": "&",
"&lt;": "<",
"&gt;": ">",
"&quot;": '"',
"&apos;": "'",
"&nbsp;": " ",
"&ndash;": "\u2013",
"&mdash;": "\u2014",
"&lsquo;": "\u2018",
"&rsquo;": "\u2019",
"&ldquo;": "\u201C",
"&rdquo;": "\u201D",
"&hellip;": "\u2026",
};
let result = text;
// Named entities
for (const [entity, char] of Object.entries(namedEntities)) {
result = result.replaceAll(entity, char);
}
// Numeric decimal entities: &#8211;
result = result.replace(/&#(\d+);/g, (_match, dec) =>
String.fromCodePoint(Number(dec))
);
// Numeric hex entities: &#x2019;
result = result.replace(/&#x([0-9a-fA-F]+);/g, (_match, hex) =>
String.fromCodePoint(parseInt(hex, 16))
);
return result;
}
/**
* Parse season and episode numbers from a title string.
* Looks for patterns like "S10E21", "S1E3", "s02e05", case-insensitive.
* Returns { season, episode } or null if not found.
*/
function parseSeasonEpisode(title) {
// Pattern: S{n}E{n} anywhere in the title (with optional space between S and E parts)
const match = title.match(/S(\d+)\s*E(\d+)/i);
if (match) {
return {
season: parseInt(match[1], 10),
episode: parseInt(match[2], 10),
};
}
// Fallback: "Season X Episode Y" pattern
const longMatch = title.match(/Season\s+(\d+)\s+Episode\s+(\d+)/i);
if (longMatch) {
return {
season: parseInt(longMatch[1], 10),
episode: parseInt(longMatch[2], 10),
};
}
return null;
}
/**
* Strip "Podcast " prefix from episode titles.
*/
function cleanTitle(rawTitle) {
let title = decodeHtmlEntities(rawTitle).trim();
// Remove leading "Podcast " (case-insensitive)
title = title.replace(/^Podcast\s+/i, "");
return title;
}
/**
* Generate a URL-safe slug from a title string.
* - Lowercase
* - Replace non-alphanumeric chars with hyphens
* - Collapse multiple hyphens
* - Trim hyphens from ends
* - Max 60 characters
*/
function slugify(title) {
// Remove the S{n}E{n} prefix before slugifying to keep the slug about content
const withoutCode = title.replace(/^S\d+\s*E\d+\s*[-\u2013:]?\s*/i, "").trim();
const base = withoutCode || title;
let slug = base
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "");
if (slug.length > 60) {
slug = slug.substring(0, 60).replace(/-$/, "");
}
return slug || "untitled";
}
/**
* Generate a filename for an episode.
* Format: s{SS}e{EE}-{slug}.md
*/
function episodeFilename(season, episode, title) {
const s = String(season).padStart(2, "0");
const e = String(episode).padStart(2, "0");
const slug = slugify(title);
return `s${s}e${e}-${slug}.md`;
}
/**
* Extract tags from a title string based on keyword matching.
* Returns a deduplicated, sorted array of tag slugs.
*/
function extractTags(title, description) {
const text = `${title} ${description}`.toLowerCase();
const tags = new Set();
for (const [pattern, tag] of TAG_KEYWORDS) {
const regex = new RegExp(pattern, "i");
if (regex.test(text)) {
tags.add(tag);
}
}
return [...tags].sort();
}
/**
* Convert an ISO date string or RSS pubDate into YYYY-MM-DD format.
*/
function formatDate(dateStr) {
if (!dateStr) return "1970-01-01";
const d = new Date(dateStr);
if (isNaN(d.getTime())) return "1970-01-01";
return d.toISOString().split("T")[0];
}
/**
* Normalize audio URL to HTTPS.
*/
function normalizeAudioUrl(url) {
if (!url) return "";
return url.replace(/^http:\/\//i, "https://");
}
/**
* Escape YAML string values. Wraps in double quotes and escapes inner quotes.
*/
function yamlString(value) {
if (value === undefined || value === null) return '""';
const str = String(value);
// Escape backslashes first, then double quotes
const escaped = str.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
return `"${escaped}"`;
}
// ---------------------------------------------------------------------------
// Turndown (HTML -> Markdown) setup
// ---------------------------------------------------------------------------
function createTurndown() {
const td = new TurndownService({
headingStyle: "atx",
bulletListMarker: "-",
codeBlockStyle: "fenced",
emDelimiter: "*",
});
// Remove script/style elements
td.remove(["script", "style"]);
return td;
}
/**
* Convert HTML content to clean markdown.
* Handles null/empty input gracefully.
*/
function htmlToMarkdown(html, turndown) {
if (!html || typeof html !== "string" || html.trim().length === 0) {
return "";
}
let md = turndown.turndown(html);
// Clean up excessive blank lines
md = md.replace(/\n{3,}/g, "\n\n");
// Decode any leftover HTML entities
md = decodeHtmlEntities(md);
return md.trim();
}
// ---------------------------------------------------------------------------
// Feed fetching and parsing
// ---------------------------------------------------------------------------
async function fetchFeed(url) {
console.log(`Fetching RSS feed from ${url} ...`);
const response = await fetch(url, {
headers: {
"User-Agent": "GuruShowImporter/1.0",
Accept: "application/rss+xml, application/xml, text/xml",
},
});
if (!response.ok) {
throw new Error(
`Failed to fetch feed: ${response.status} ${response.statusText}`
);
}
const xml = await response.text();
console.log(`Received ${xml.length.toLocaleString()} bytes of XML.`);
return xml;
}
function parseFeed(xml) {
const parser = new XMLParser({
ignoreAttributes: false,
attributeNamePrefix: "@_",
// Parse CDATA sections
cdataPropName: "__cdata",
// Keep text nodes
textNodeName: "#text",
// Do not trim whitespace from values
trimValues: false,
// Ensure items are always arrays
isArray: (name) => name === "item",
});
const result = parser.parse(xml);
const channel = result?.rss?.channel;
if (!channel) {
throw new Error("Invalid RSS feed: no channel element found.");
}
const items = channel.item;
if (!items || !Array.isArray(items) || items.length === 0) {
throw new Error("No episodes found in feed.");
}
console.log(`Parsed ${items.length} episode items from feed.`);
return items;
}
// ---------------------------------------------------------------------------
// Episode extraction
// ---------------------------------------------------------------------------
/**
* Extract a text value from a parsed XML node, handling CDATA wrappers.
*/
function extractText(node) {
if (node === undefined || node === null) return "";
if (typeof node === "string") return node.trim();
if (typeof node === "number") return String(node);
if (typeof node === "object") {
// CDATA wrapped
if (node.__cdata !== undefined) {
return typeof node.__cdata === "string" ? node.__cdata.trim() : "";
}
if (node["#text"] !== undefined) {
return String(node["#text"]).trim();
}
}
return "";
}
/**
* Process a single RSS item into an episode data object.
*/
function processItem(item, index, warnings) {
const rawTitle = extractText(item.title);
const title = cleanTitle(rawTitle);
// Parse season/episode
const parsed = parseSeasonEpisode(rawTitle);
let season, episode;
if (parsed) {
season = parsed.season;
episode = parsed.episode;
} else {
season = 0;
episode = index + 1;
warnings.push(
`[WARNING] Could not parse S/E from title: "${title}" -- using season=0, episode=${episode}`
);
}
// Date
const pubDate = formatDate(extractText(item.pubDate));
// Description: prefer content:encoded, fall back to description
const contentEncoded = extractText(item["content:encoded"]);
const description = extractText(item.description);
const bodyHtml = contentEncoded || description || "";
if (!bodyHtml || bodyHtml.trim().length === 0) {
warnings.push(`[WARNING] Empty description for: "${title}"`);
}
// Audio enclosure
let audioUrl = "";
let audioSize = 0;
const enclosure = item.enclosure;
if (enclosure) {
audioUrl = normalizeAudioUrl(
enclosure["@_url"] || extractText(enclosure.url) || ""
);
const rawSize =
enclosure["@_length"] || extractText(enclosure.length) || "0";
audioSize = parseInt(rawSize, 10) || 0;
}
// Duration
const duration = extractText(item["itunes:duration"]) || "";
// Original URL
const link = extractText(item.link) || "";
// Tags
const tags = extractTags(title, description);
return {
title,
season,
episode,
pubDate,
bodyHtml,
audioUrl,
audioSize,
duration,
link,
tags,
};
}
// ---------------------------------------------------------------------------
// Markdown file generation
// ---------------------------------------------------------------------------
function generateMarkdown(ep, turndown) {
const body = htmlToMarkdown(ep.bodyHtml, turndown);
const tagList =
ep.tags.length > 0 ? `[${ep.tags.map((t) => `"${t}"`).join(", ")}]` : "[]";
const frontmatter = [
"---",
`title: ${yamlString(ep.title)}`,
`season: ${ep.season}`,
`episode: ${ep.episode}`,
`pubDate: ${ep.pubDate}`,
`duration: ${yamlString(ep.duration)}`,
`audioUrl: ${yamlString(ep.audioUrl)}`,
`audioSize: ${ep.audioSize}`,
`episodeType: "full"`,
`originalUrl: ${yamlString(ep.link)}`,
`featured: false`,
`classic: false`,
`tags: ${tagList}`,
"---",
].join("\n");
return `${frontmatter}\n\n${body}\n`;
}
// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
async function main() {
console.log("=== Computer Guru Show Episode Importer ===\n");
// Ensure output directory exists
if (!existsSync(EPISODES_DIR)) {
mkdirSync(EPISODES_DIR, { recursive: true });
console.log(`Created directory: ${EPISODES_DIR}`);
}
// Fetch and parse
const xml = await fetchFeed(FEED_URL);
const items = parseFeed(xml);
// Process episodes
const turndown = createTurndown();
const warnings = [];
const episodes = [];
const filenames = new Set();
for (let i = 0; i < items.length; i++) {
const ep = processItem(items[i], i, warnings);
episodes.push(ep);
}
// Sort by season then episode (oldest first)
episodes.sort((a, b) => {
if (a.season !== b.season) return a.season - b.season;
return a.episode - b.episode;
});
// Write files
let written = 0;
let skippedDuplicates = 0;
for (const ep of episodes) {
let filename = episodeFilename(ep.season, ep.episode, ep.title);
// Handle duplicate filenames
if (filenames.has(filename)) {
const suffix = `-${Date.now().toString(36).slice(-4)}`;
filename = filename.replace(/\.md$/, `${suffix}.md`);
warnings.push(
`[WARNING] Duplicate filename resolved: ${filename} for "${ep.title}"`
);
skippedDuplicates++;
}
filenames.add(filename);
const markdown = generateMarkdown(ep, turndown);
const filepath = join(EPISODES_DIR, filename);
writeFileSync(filepath, markdown, "utf-8");
written++;
}
// Report
console.log("\n=== Import Complete ===");
console.log(`Total episodes in feed: ${items.length}`);
console.log(`Files written: ${written}`);
if (skippedDuplicates > 0) {
console.log(`Duplicate filenames resolved: ${skippedDuplicates}`);
}
if (warnings.length > 0) {
console.log(`\n--- Warnings (${warnings.length}) ---`);
for (const w of warnings) {
console.log(w);
}
}
// Show a sample of generated files
console.log("\n--- Sample Output (first 3 files) ---");
const sampleEpisodes = episodes.slice(0, 3);
for (const ep of sampleEpisodes) {
const filename = episodeFilename(ep.season, ep.episode, ep.title);
console.log(`\nFile: ${filename}`);
const markdown = generateMarkdown(ep, turndown);
// Show first 20 lines
const lines = markdown.split("\n").slice(0, 20);
console.log(lines.join("\n"));
if (markdown.split("\n").length > 20) {
console.log(" ...(truncated)");
}
}
console.log("\n[SUCCESS] Episode import finished.");
}
main().catch((err) => {
console.error(`[ERROR] Import failed: ${err.message}`);
console.error(err.stack);
process.exit(1);
});

View File

@@ -0,0 +1,202 @@
---
import type { CollectionEntry } from 'astro:content';
interface Props {
episode: CollectionEntry<'episodes'>;
}
const { episode } = Astro.props;
const { title, season, episode: episodeNum, pubDate, duration, tags, audioUrl } = episode.data;
const formattedDate = new Date(pubDate).toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
});
const seasonLabel = season === 0 ? 'Special' : `S${season}`;
const episodeLabel = `${seasonLabel} E${String(episodeNum).padStart(2, '0')}`;
const description = episode.body
? episode.body.replace(/\\?\*\\?\*\\?\*/g, '').replace(/\\\[.*?\\\]/g, '').replace(/\[.*?\]/g, '').trim()
: '';
---
<article
class="episode-card card"
data-season={String(season)}
data-tags={tags.join(',')}
data-title={title.toLowerCase()}
data-description={description.toLowerCase()}
>
<div class="episode-card__header">
<span class="badge">{episodeLabel}</span>
<span class="episode-card__duration">{duration}</span>
</div>
<a href={`/episodes/${episode.id}`} class="episode-card__title-link">
<h3 class="episode-card__title">{title}</h3>
</a>
<time class="episode-card__date" datetime={pubDate.toISOString()}>
{formattedDate}
</time>
{description && (
<p class="episode-card__description">{description}</p>
)}
<div class="episode-card__footer">
{tags.length > 0 && (
<div class="episode-card__tags">
{tags.slice(0, 4).map((tag: string) => (
<span class="episode-card__tag">{tag}</span>
))}
{tags.length > 4 && (
<span class="episode-card__tag episode-card__tag--more">+{tags.length - 4}</span>
)}
</div>
)}
<button
class="btn btn--primary episode-card__play"
data-audio-url={audioUrl}
data-episode-title={title}
data-season={String(season)}
data-episode-num={String(episodeNum)}
aria-label={`Play ${title}`}
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<polygon points="5 3 19 12 5 21 5 3"></polygon>
</svg>
Play
</button>
</div>
</article>
<style>
.episode-card {
display: flex;
flex-direction: column;
gap: var(--space-3);
border-left: 3px solid transparent;
transition:
transform var(--transition-base),
box-shadow var(--transition-base),
border-color var(--transition-base);
}
.episode-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-lg);
border-left-color: var(--color-accent);
}
.episode-card__header {
display: flex;
align-items: center;
justify-content: space-between;
}
.episode-card__duration {
font-size: var(--text-xs);
color: var(--color-text-muted);
font-family: var(--font-mono);
}
.episode-card__title-link {
color: inherit;
text-decoration: none;
}
.episode-card__title-link:hover {
color: var(--color-accent);
}
.episode-card__title {
font-size: var(--text-lg);
font-weight: 600;
line-height: 1.3;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.episode-card__date {
font-size: var(--text-sm);
color: var(--color-text-muted);
}
.episode-card__description {
font-size: var(--text-sm);
color: var(--color-text-secondary);
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.episode-card__footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-3);
margin-top: auto;
padding-top: var(--space-3);
border-top: 1px solid var(--color-border);
}
.episode-card__tags {
display: flex;
flex-wrap: wrap;
gap: var(--space-1);
}
.episode-card__tag {
font-size: 0.65rem;
padding: 2px var(--space-2);
border-radius: 9999px;
background: var(--color-bg-tertiary);
color: var(--color-text-muted);
white-space: nowrap;
}
.episode-card__tag--more {
background: var(--color-accent-glow);
color: var(--color-accent);
}
.episode-card__play {
flex-shrink: 0;
padding: var(--space-2) var(--space-3);
font-size: var(--text-xs);
}
.episode-card__play svg {
width: 12px;
height: 12px;
}
</style>
<script>
document.addEventListener('astro:page-load', () => {
document.querySelectorAll<HTMLButtonElement>('.episode-card__play').forEach((btn) => {
btn.addEventListener('click', (e) => {
e.preventDefault();
const audioUrl = btn.dataset.audioUrl;
const title = btn.dataset.episodeTitle;
const season = parseInt(btn.dataset.season ?? '0', 10);
const episode = parseInt(btn.dataset.episodeNum ?? '0', 10);
if (audioUrl) {
document.dispatchEvent(
new CustomEvent('play-episode', {
detail: { audioUrl, title, season, episode },
})
);
}
});
});
});
</script>

View File

@@ -0,0 +1,110 @@
---
interface SeasonCount {
season: number;
count: number;
label: string;
}
interface Props {
seasonCounts: SeasonCount[];
totalCount: number;
}
const { seasonCounts, totalCount } = Astro.props;
---
<div class="season-filter" id="season-filter">
<div class="season-filter__scroll">
<button
class="season-filter__tab season-filter__tab--active"
data-season="all"
type="button"
>
All Episodes
<span class="season-filter__count">{totalCount}</span>
</button>
{seasonCounts.map(({ season, count, label }) => (
<button
class="season-filter__tab"
data-season={String(season)}
type="button"
>
{label}
<span class="season-filter__count">{count}</span>
</button>
))}
</div>
</div>
<style>
.season-filter {
margin-bottom: var(--space-6);
border-bottom: 1px solid var(--color-border);
}
.season-filter__scroll {
display: flex;
gap: var(--space-1);
overflow-x: auto;
scrollbar-width: thin;
scrollbar-color: var(--color-border) transparent;
padding-bottom: 1px;
-webkit-overflow-scrolling: touch;
}
.season-filter__scroll::-webkit-scrollbar {
height: 4px;
}
.season-filter__scroll::-webkit-scrollbar-track {
background: transparent;
}
.season-filter__scroll::-webkit-scrollbar-thumb {
background: var(--color-border);
border-radius: 2px;
}
.season-filter__tab {
display: inline-flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-3) var(--space-4);
background: none;
border: none;
border-bottom: 2px solid transparent;
color: var(--color-text-secondary);
font-size: var(--text-sm);
font-weight: 500;
cursor: pointer;
white-space: nowrap;
transition:
color var(--transition-fast),
border-color var(--transition-fast);
margin-bottom: -1px;
}
.season-filter__tab:hover {
color: var(--color-text-primary);
}
.season-filter__tab--active {
color: var(--color-accent);
border-bottom-color: var(--color-accent);
}
.season-filter__count {
font-size: var(--text-xs);
padding: 1px var(--space-2);
border-radius: 9999px;
background: var(--color-bg-tertiary);
color: var(--color-text-muted);
font-weight: 600;
}
.season-filter__tab--active .season-filter__count {
background: var(--color-accent-glow);
color: var(--color-accent);
}
</style>

View File

@@ -0,0 +1,919 @@
import { useState, useEffect, useRef, useCallback } from 'react';
// ─── Types ───────────────────────────────────────────────────────────────────
interface EpisodeInfo {
audioUrl: string;
title: string;
season: number;
episode: number;
}
interface PlayerState extends EpisodeInfo {
currentTime: number;
}
interface PlayEpisodeEvent extends CustomEvent {
detail: EpisodeInfo;
}
// ─── Constants ───────────────────────────────────────────────────────────────
const STORAGE_KEY = 'persistent-player-state';
const PLAYBACK_SPEEDS = [0.75, 1, 1.25, 1.5, 2] as const;
const SKIP_SECONDS = 15;
const SAVE_INTERVAL_MS = 5000;
// ─── Helpers ─────────────────────────────────────────────────────────────────
function formatTime(seconds: number): string {
if (!isFinite(seconds) || seconds < 0) return '0:00';
const hrs = Math.floor(seconds / 3600);
const mins = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
const paddedSecs = secs.toString().padStart(2, '0');
if (hrs > 0) {
return `${hrs}:${mins.toString().padStart(2, '0')}:${paddedSecs}`;
}
return `${mins}:${paddedSecs}`;
}
function loadState(): PlayerState | null {
try {
const raw = sessionStorage.getItem(STORAGE_KEY);
if (!raw) return null;
const parsed = JSON.parse(raw) as PlayerState;
if (parsed.audioUrl && parsed.title != null) {
return parsed;
}
return null;
} catch {
return null;
}
}
function saveState(state: PlayerState): void {
try {
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(state));
} catch {
// sessionStorage may be unavailable or full; silently ignore
}
}
function clearState(): void {
try {
sessionStorage.removeItem(STORAGE_KEY);
} catch {
// ignore
}
}
function buildEpisodeLabel(season: number, episode: number): string {
if (season === 0) return 'Special';
return `S${season}E${String(episode).padStart(2, '0')}`;
}
// ─── Component ───────────────────────────────────────────────────────────────
export default function PersistentPlayer() {
const audioRef = useRef<HTMLAudioElement | null>(null);
const progressBarRef = useRef<HTMLDivElement | null>(null);
const saveTimerRef = useRef<number | null>(null);
const [episodeInfo, setEpisodeInfo] = useState<EpisodeInfo | null>(null);
const [isPlaying, setIsPlaying] = useState(false);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const [buffered, setBuffered] = useState(0);
const [volume, setVolume] = useState(1);
const [speedIndex, setSpeedIndex] = useState(1); // default 1x
const [isVisible, setIsVisible] = useState(false);
const [isDraggingProgress, setIsDraggingProgress] = useState(false);
// ── Restore state on mount ──────────────────────────────────────────────
useEffect(() => {
const saved = loadState();
if (saved) {
setEpisodeInfo({
audioUrl: saved.audioUrl,
title: saved.title,
season: saved.season,
episode: saved.episode,
});
setCurrentTime(saved.currentTime);
setIsVisible(true);
}
}, []);
// ── Listen for play-episode custom events ───────────────────────────────
useEffect(() => {
function handlePlayEpisode(e: Event) {
const event = e as PlayEpisodeEvent;
const { audioUrl, title, season, episode } = event.detail;
if (!audioUrl) return;
setEpisodeInfo({ audioUrl, title, season, episode });
setCurrentTime(0);
setDuration(0);
setBuffered(0);
setIsVisible(true);
// Playback will start once the audio element loads the new source
// (handled in the audioUrl effect below)
}
document.addEventListener('play-episode', handlePlayEpisode);
return () => document.removeEventListener('play-episode', handlePlayEpisode);
}, []);
// ── Wire up audio element when episode changes ──────────────────────────
useEffect(() => {
const audio = audioRef.current;
if (!audio || !episodeInfo) return;
// If source changed, load new source
if (audio.src !== episodeInfo.audioUrl) {
audio.src = episodeInfo.audioUrl;
audio.load();
}
// Restore saved position for restored state (non-zero currentTime means restored)
if (currentTime > 0 && audio.readyState >= 1) {
audio.currentTime = currentTime;
}
// Auto-play if this is a fresh play-episode event (currentTime === 0)
// For restored sessions, don't auto-play
const isRestoredSession = currentTime > 0;
if (!isRestoredSession) {
const playOnReady = () => {
audio.play().catch(() => {
// Browser may block autoplay; user will click play manually
});
audio.removeEventListener('canplay', playOnReady);
};
if (audio.readyState >= 3) {
audio.play().catch(() => {});
} else {
audio.addEventListener('canplay', playOnReady);
return () => audio.removeEventListener('canplay', playOnReady);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [episodeInfo?.audioUrl]);
// ── Restore currentTime once audio metadata is loaded ───────────────────
useEffect(() => {
const audio = audioRef.current;
if (!audio || !episodeInfo) return;
const handleLoadedMetadata = () => {
const saved = loadState();
if (saved && saved.audioUrl === episodeInfo.audioUrl && saved.currentTime > 0) {
audio.currentTime = saved.currentTime;
setCurrentTime(saved.currentTime);
}
};
audio.addEventListener('loadedmetadata', handleLoadedMetadata);
return () => audio.removeEventListener('loadedmetadata', handleLoadedMetadata);
}, [episodeInfo]);
// ── Audio event handlers ────────────────────────────────────────────────
useEffect(() => {
const audio = audioRef.current;
if (!audio) return;
const onPlay = () => setIsPlaying(true);
const onPause = () => setIsPlaying(false);
const onEnded = () => {
setIsPlaying(false);
setCurrentTime(0);
};
const onTimeUpdate = () => {
if (!isDraggingProgress) {
setCurrentTime(audio.currentTime);
}
};
const onDurationChange = () => {
if (isFinite(audio.duration)) {
setDuration(audio.duration);
}
};
const onProgress = () => {
if (audio.buffered.length > 0) {
setBuffered(audio.buffered.end(audio.buffered.length - 1));
}
};
audio.addEventListener('play', onPlay);
audio.addEventListener('pause', onPause);
audio.addEventListener('ended', onEnded);
audio.addEventListener('timeupdate', onTimeUpdate);
audio.addEventListener('durationchange', onDurationChange);
audio.addEventListener('progress', onProgress);
return () => {
audio.removeEventListener('play', onPlay);
audio.removeEventListener('pause', onPause);
audio.removeEventListener('ended', onEnded);
audio.removeEventListener('timeupdate', onTimeUpdate);
audio.removeEventListener('durationchange', onDurationChange);
audio.removeEventListener('progress', onProgress);
};
}, [isDraggingProgress]);
// ── Periodic state persistence ──────────────────────────────────────────
useEffect(() => {
if (!episodeInfo || !isPlaying) {
if (saveTimerRef.current != null) {
window.clearInterval(saveTimerRef.current);
saveTimerRef.current = null;
}
return;
}
saveTimerRef.current = window.setInterval(() => {
const audio = audioRef.current;
if (audio && episodeInfo) {
saveState({
...episodeInfo,
currentTime: audio.currentTime,
});
}
}, SAVE_INTERVAL_MS);
return () => {
if (saveTimerRef.current != null) {
window.clearInterval(saveTimerRef.current);
saveTimerRef.current = null;
}
};
}, [episodeInfo, isPlaying]);
// ── Save state on pause / before unload ─────────────────────────────────
useEffect(() => {
function handleBeforeUnload() {
const audio = audioRef.current;
if (audio && episodeInfo) {
saveState({ ...episodeInfo, currentTime: audio.currentTime });
}
}
window.addEventListener('beforeunload', handleBeforeUnload);
return () => window.removeEventListener('beforeunload', handleBeforeUnload);
}, [episodeInfo]);
// ── Controls ────────────────────────────────────────────────────────────
const togglePlayPause = useCallback(() => {
const audio = audioRef.current;
if (!audio) return;
if (audio.paused) {
audio.play().catch(() => {});
} else {
audio.pause();
if (episodeInfo) {
saveState({ ...episodeInfo, currentTime: audio.currentTime });
}
}
}, [episodeInfo]);
const skipBack = useCallback(() => {
const audio = audioRef.current;
if (!audio) return;
audio.currentTime = Math.max(0, audio.currentTime - SKIP_SECONDS);
}, []);
const skipForward = useCallback(() => {
const audio = audioRef.current;
if (!audio) return;
audio.currentTime = Math.min(audio.duration || 0, audio.currentTime + SKIP_SECONDS);
}, []);
const cycleSpeed = useCallback(() => {
setSpeedIndex((prev) => {
const next = (prev + 1) % PLAYBACK_SPEEDS.length;
const audio = audioRef.current;
if (audio) {
audio.playbackRate = PLAYBACK_SPEEDS[next];
}
return next;
});
}, []);
const handleVolumeChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
const val = parseFloat(e.target.value);
setVolume(val);
const audio = audioRef.current;
if (audio) {
audio.volume = val;
}
}, []);
const handleProgressClick = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
const bar = progressBarRef.current;
const audio = audioRef.current;
if (!bar || !audio || !duration) return;
const rect = bar.getBoundingClientRect();
const fraction = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
const newTime = fraction * duration;
audio.currentTime = newTime;
setCurrentTime(newTime);
},
[duration],
);
const handleProgressMouseDown = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
setIsDraggingProgress(true);
handleProgressClick(e);
const handleMouseMove = (ev: MouseEvent) => {
const bar = progressBarRef.current;
const audio = audioRef.current;
if (!bar || !audio || !duration) return;
const rect = bar.getBoundingClientRect();
const fraction = Math.max(0, Math.min(1, (ev.clientX - rect.left) / rect.width));
const newTime = fraction * duration;
audio.currentTime = newTime;
setCurrentTime(newTime);
};
const handleMouseUp = () => {
setIsDraggingProgress(false);
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
},
[duration, handleProgressClick],
);
const closePlayer = useCallback(() => {
const audio = audioRef.current;
if (audio) {
audio.pause();
audio.removeAttribute('src');
audio.load();
}
setIsPlaying(false);
setEpisodeInfo(null);
setCurrentTime(0);
setDuration(0);
setBuffered(0);
setIsVisible(false);
clearState();
}, []);
// ── Keyboard shortcuts ──────────────────────────────────────────────────
useEffect(() => {
if (!episodeInfo) return;
function handleKeyDown(e: KeyboardEvent) {
// Only respond when not typing in an input
const target = e.target as HTMLElement;
if (
target.tagName === 'INPUT' ||
target.tagName === 'TEXTAREA' ||
target.tagName === 'SELECT' ||
target.isContentEditable
) {
return;
}
switch (e.key) {
case ' ':
e.preventDefault();
togglePlayPause();
break;
case 'ArrowLeft':
e.preventDefault();
skipBack();
break;
case 'ArrowRight':
e.preventDefault();
skipForward();
break;
}
}
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [episodeInfo, togglePlayPause, skipBack, skipForward]);
// ── Derived values ──────────────────────────────────────────────────────
const progressPercent = duration > 0 ? (currentTime / duration) * 100 : 0;
const bufferedPercent = duration > 0 ? (buffered / duration) * 100 : 0;
const currentSpeed = PLAYBACK_SPEEDS[speedIndex];
const episodeLabel = episodeInfo ? buildEpisodeLabel(episodeInfo.season, episodeInfo.episode) : '';
// ── Render ──────────────────────────────────────────────────────────────
return (
<>
{/* Hidden audio element - always mounted so it survives re-renders */}
<audio ref={audioRef} preload="metadata" />
<div
className="persistent-player"
style={{
transform: isVisible ? 'translateY(0)' : 'translateY(100%)',
}}
role="region"
aria-label="Audio player"
>
{/* Progress bar - full width across top of player */}
<div
className="persistent-player__progress-bar"
ref={progressBarRef}
onClick={handleProgressClick}
onMouseDown={handleProgressMouseDown}
role="slider"
aria-label="Playback progress"
aria-valuemin={0}
aria-valuemax={duration}
aria-valuenow={currentTime}
aria-valuetext={`${formatTime(currentTime)} of ${formatTime(duration)}`}
tabIndex={0}
>
<div
className="persistent-player__progress-buffered"
style={{ width: `${bufferedPercent}%` }}
/>
<div
className="persistent-player__progress-fill"
style={{ width: `${progressPercent}%` }}
/>
<div
className="persistent-player__progress-handle"
style={{ left: `${progressPercent}%` }}
/>
</div>
<div className="persistent-player__content">
{/* Left: Episode info */}
<div className="persistent-player__info">
{episodeLabel && (
<span className="persistent-player__badge">{episodeLabel}</span>
)}
<span className="persistent-player__title" title={episodeInfo?.title ?? ''}>
{episodeInfo?.title ?? ''}
</span>
</div>
{/* Center: Playback controls */}
<div className="persistent-player__controls">
<button
className="persistent-player__btn persistent-player__btn--skip"
onClick={skipBack}
aria-label="Skip back 15 seconds"
title="Skip back 15 seconds"
>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polyline points="1 4 1 10 7 10" />
<path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10" />
<text x="12" y="16" textAnchor="middle" fill="currentColor" stroke="none" fontSize="8" fontWeight="700">15</text>
</svg>
</button>
<button
className="persistent-player__btn persistent-player__btn--play"
onClick={togglePlayPause}
aria-label={isPlaying ? 'Pause' : 'Play'}
title={isPlaying ? 'Pause' : 'Play'}
>
{isPlaying ? (
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor">
<rect x="6" y="4" width="4" height="16" rx="1" />
<rect x="14" y="4" width="4" height="16" rx="1" />
</svg>
) : (
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor">
<polygon points="6 3 20 12 6 21 6 3" />
</svg>
)}
</button>
<button
className="persistent-player__btn persistent-player__btn--skip"
onClick={skipForward}
aria-label="Skip forward 15 seconds"
title="Skip forward 15 seconds"
>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polyline points="23 4 23 10 17 10" />
<path d="M20.49 15a9 9 0 1 1-2.13-9.36L23 10" />
<text x="12" y="16" textAnchor="middle" fill="currentColor" stroke="none" fontSize="8" fontWeight="700">15</text>
</svg>
</button>
</div>
{/* Time display */}
<div className="persistent-player__time">
<span>{formatTime(currentTime)}</span>
<span className="persistent-player__time-separator">/</span>
<span>{formatTime(duration)}</span>
</div>
{/* Right: Volume, speed, close */}
<div className="persistent-player__right">
<div className="persistent-player__volume">
<button
className="persistent-player__btn persistent-player__btn--icon"
onClick={() => {
const audio = audioRef.current;
if (!audio) return;
if (audio.volume > 0) {
audio.volume = 0;
setVolume(0);
} else {
audio.volume = 1;
setVolume(1);
}
}}
aria-label={volume === 0 ? 'Unmute' : 'Mute'}
title={volume === 0 ? 'Unmute' : 'Mute'}
>
{volume === 0 ? (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" />
<line x1="23" y1="9" x2="17" y2="15" />
<line x1="17" y1="9" x2="23" y2="15" />
</svg>
) : volume < 0.5 ? (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" />
<path d="M15.54 8.46a5 5 0 0 1 0 7.07" />
</svg>
) : (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" />
<path d="M15.54 8.46a5 5 0 0 1 0 7.07" />
<path d="M19.07 4.93a10 10 0 0 1 0 14.14" />
</svg>
)}
</button>
<input
type="range"
className="persistent-player__volume-slider"
min="0"
max="1"
step="0.05"
value={volume}
onChange={handleVolumeChange}
aria-label="Volume"
/>
</div>
<button
className="persistent-player__btn persistent-player__btn--speed"
onClick={cycleSpeed}
aria-label={`Playback speed: ${currentSpeed}x`}
title={`Playback speed: ${currentSpeed}x`}
>
{currentSpeed}x
</button>
<button
className="persistent-player__btn persistent-player__btn--close"
onClick={closePlayer}
aria-label="Close player"
title="Close player"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
</div>
</div>
</div>
<style>{`
.persistent-player {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1000;
height: 72px;
background: hsl(220 20% 6% / 0.95);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-top: 1px solid hsl(200 85% 55% / 0.15);
transition: transform 350ms cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
flex-direction: column;
}
[data-theme="light"] .persistent-player {
background: hsl(220 20% 97% / 0.92);
border-top-color: hsl(200 85% 42% / 0.2);
}
/* ── Progress bar ─────────────────────────────────────── */
.persistent-player__progress-bar {
position: relative;
width: 100%;
height: 3px;
background: hsl(220 12% 24% / 0.5);
cursor: pointer;
flex-shrink: 0;
}
.persistent-player__progress-bar:hover {
height: 5px;
}
.persistent-player__progress-bar:hover .persistent-player__progress-handle {
opacity: 1;
}
[data-theme="light"] .persistent-player__progress-bar {
background: hsl(220 12% 85% / 0.5);
}
.persistent-player__progress-buffered {
position: absolute;
top: 0;
left: 0;
height: 100%;
background: hsl(220 8% 48% / 0.3);
pointer-events: none;
}
.persistent-player__progress-fill {
position: absolute;
top: 0;
left: 0;
height: 100%;
background: var(--color-accent, hsl(200 85% 55%));
pointer-events: none;
}
.persistent-player__progress-handle {
position: absolute;
top: 50%;
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--color-accent, hsl(200 85% 55%));
transform: translate(-50%, -50%);
opacity: 0;
transition: opacity 150ms ease;
pointer-events: none;
}
/* ── Content layout ───────────────────────────────────── */
.persistent-player__content {
display: flex;
align-items: center;
flex: 1;
padding: 0 16px;
gap: 16px;
min-width: 0;
}
/* ── Episode info ─────────────────────────────────────── */
.persistent-player__info {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
flex: 1;
}
.persistent-player__badge {
display: inline-flex;
align-items: center;
padding: 2px 8px;
border-radius: 9999px;
font-size: 0.7rem;
font-weight: 600;
background: hsl(200 85% 55% / 0.2);
color: var(--color-accent, hsl(200 85% 55%));
border: 1px solid hsl(200 85% 55% / 0.3);
white-space: nowrap;
flex-shrink: 0;
}
.persistent-player__title {
font-size: 0.875rem;
font-weight: 500;
color: var(--color-text-primary, hsl(220 10% 92%));
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
min-width: 0;
}
/* ── Controls ─────────────────────────────────────────── */
.persistent-player__controls {
display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
}
.persistent-player__btn {
display: inline-flex;
align-items: center;
justify-content: center;
background: none;
border: none;
cursor: pointer;
color: var(--color-text-secondary, hsl(220 8% 68%));
padding: 6px;
border-radius: 6px;
transition: color 150ms ease, background 150ms ease;
}
.persistent-player__btn:hover {
color: var(--color-text-primary, hsl(220 10% 92%));
background: hsl(220 16% 16% / 0.6);
}
[data-theme="light"] .persistent-player__btn:hover {
background: hsl(220 16% 92% / 0.6);
}
.persistent-player__btn--play {
width: 40px;
height: 40px;
border-radius: 50%;
color: hsl(220 20% 8%);
background: var(--color-accent, hsl(200 85% 55%));
}
.persistent-player__btn--play:hover {
color: hsl(220 20% 8%);
background: var(--color-accent-hover, hsl(200 85% 65%));
box-shadow: 0 0 16px hsl(200 85% 55% / 0.3);
}
.persistent-player__btn--speed {
font-size: 0.7rem;
font-weight: 700;
min-width: 36px;
font-family: var(--font-mono, monospace);
}
.persistent-player__btn--close {
opacity: 0.5;
}
.persistent-player__btn--close:hover {
opacity: 1;
}
/* ── Time display ─────────────────────────────────────── */
.persistent-player__time {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.75rem;
font-family: var(--font-mono, monospace);
color: var(--color-text-muted, hsl(220 6% 48%));
white-space: nowrap;
flex-shrink: 0;
}
.persistent-player__time-separator {
opacity: 0.5;
}
/* ── Volume ───────────────────────────────────────────── */
.persistent-player__right {
display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
}
.persistent-player__volume {
display: flex;
align-items: center;
gap: 4px;
}
.persistent-player__volume-slider {
width: 72px;
height: 4px;
-webkit-appearance: none;
appearance: none;
background: hsl(220 12% 24%);
border-radius: 2px;
outline: none;
cursor: pointer;
}
[data-theme="light"] .persistent-player__volume-slider {
background: hsl(220 12% 80%);
}
.persistent-player__volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--color-accent, hsl(200 85% 55%));
border: none;
cursor: pointer;
}
.persistent-player__volume-slider::-moz-range-thumb {
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--color-accent, hsl(200 85% 55%));
border: none;
cursor: pointer;
}
/* ── Mobile responsive ────────────────────────────────── */
@media (max-width: 768px) {
.persistent-player {
height: 64px;
}
.persistent-player__content {
padding: 0 10px;
gap: 8px;
}
.persistent-player__info {
flex: 1;
min-width: 0;
}
.persistent-player__title {
font-size: 0.8rem;
max-width: 120px;
}
.persistent-player__volume {
display: none;
}
.persistent-player__btn--speed {
display: none;
}
.persistent-player__time {
font-size: 0.65rem;
}
.persistent-player__btn--play {
width: 34px;
height: 34px;
}
.persistent-player__btn--play svg {
width: 18px;
height: 18px;
}
.persistent-player__btn--skip svg {
width: 14px;
height: 14px;
}
}
@media (max-width: 480px) {
.persistent-player__btn--skip {
display: none;
}
.persistent-player__time {
display: none;
}
.persistent-player__title {
max-width: 140px;
}
}
`}</style>
</>
);
}

View File

@@ -0,0 +1,174 @@
---
---
<section class="section about-preview fade-in">
<div class="container">
<div class="about-grid">
<div class="about-text">
<span class="badge">About the Show</span>
<h2 class="about-title">Meet Your Host</h2>
<p class="about-lead">
Mike Swanson has been breaking down technology for everyday people since 2014.
As a Tucson-based tech professional and broadcaster, he brings decades of
hands-on experience to every episode.
</p>
<blockquote class="about-quote">
"Technology should empower you, not intimidate you. That is what this show is all about."
</blockquote>
<p class="about-body">
From cybersecurity and privacy to the latest gadgets and internet culture,
The Computer Guru Show covers it all -- with a healthy dose of humor, real-world
advice, and honest opinions. No corporate sponsors telling us what to say.
Just straight talk about tech.
</p>
<a href="/about" class="btn btn--primary">
Learn More
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="12" x2="19" y2="12"></line>
<polyline points="12 5 19 12 12 19"></polyline>
</svg>
</a>
</div>
<div class="about-visual">
<div class="about-image-placeholder">
<div class="about-image-inner">
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"></path>
<path d="M19 10v2a7 7 0 0 1-14 0v-2"></path>
<line x1="12" y1="19" x2="12" y2="23"></line>
<line x1="8" y1="23" x2="16" y2="23"></line>
</svg>
<span class="about-image-text">The Computer Guru</span>
</div>
</div>
<div class="about-visual-accent"></div>
</div>
</div>
</div>
</section>
<style>
.about-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-12);
align-items: center;
}
.about-text {
display: flex;
flex-direction: column;
gap: var(--space-4);
}
.about-title {
font-size: var(--text-3xl);
font-weight: 800;
}
.about-lead {
font-size: var(--text-lg);
color: var(--color-text-secondary);
line-height: 1.7;
}
.about-quote {
font-size: var(--text-lg);
font-style: italic;
color: var(--color-accent);
padding-left: var(--space-6);
border-left: 3px solid var(--color-accent);
margin-block: var(--space-2);
line-height: 1.6;
}
.about-body {
font-size: var(--text-base);
color: var(--color-text-secondary);
line-height: 1.7;
}
.about-text .btn {
align-self: flex-start;
margin-top: var(--space-4);
}
/* Visual / placeholder area */
.about-visual {
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.about-image-placeholder {
width: 100%;
aspect-ratio: 4 / 5;
max-width: 400px;
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
display: flex;
align-items: center;
justify-content: center;
position: relative;
z-index: 1;
overflow: hidden;
}
.about-image-placeholder::before {
content: '';
position: absolute;
inset: 0;
background:
linear-gradient(135deg, hsl(200 85% 55% / 0.05), transparent 50%),
linear-gradient(315deg, hsl(270 60% 50% / 0.03), transparent 50%);
}
.about-image-inner {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-4);
color: var(--color-text-muted);
position: relative;
z-index: 1;
}
.about-image-text {
font-size: var(--text-sm);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.about-visual-accent {
position: absolute;
width: 120%;
height: 120%;
top: -10%;
left: -10%;
border: 1px solid hsl(200 85% 55% / 0.06);
border-radius: var(--radius-lg);
transform: rotate(3deg);
z-index: 0;
}
@media (max-width: 768px) {
.about-grid {
grid-template-columns: 1fr;
gap: var(--space-8);
}
.about-visual {
order: -1;
}
.about-image-placeholder {
max-width: 280px;
margin-inline: auto;
aspect-ratio: 1;
}
}
</style>

View File

@@ -0,0 +1,185 @@
---
import { getCollection } from 'astro:content';
const allPosts = await getCollection('blog');
const publishedPosts = allPosts
.filter((post) => !post.data.draft)
.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime())
.slice(0, 3);
const isSingle = publishedPosts.length === 1;
---
{publishedPosts.length > 0 && (
<section class="section blog-highlights fade-in">
<div class="container">
<div class="section-header">
<h2 class="section__title">From the Blog</h2>
<a href="/blog" class="section-header__link">
All Posts
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</a>
</div>
<div class:list={['blog-grid', { 'blog-grid--single': isSingle }]}>
{publishedPosts.map((post) => {
const formattedDate = new Date(post.data.pubDate).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
});
return (
<article class="blog-card card">
<div class="blog-card__meta">
<time datetime={post.data.pubDate.toISOString()}>{formattedDate}</time>
{post.data.tags.length > 0 && (
<div class="blog-card__tags">
{post.data.tags.slice(0, 2).map((tag: string) => (
<span class="blog-card__tag">{tag}</span>
))}
</div>
)}
</div>
<a href={`/blog/${post.id}`} class="blog-card__title-link">
<h3 class="blog-card__title">{post.data.title}</h3>
</a>
<p class="blog-card__description">{post.data.description}</p>
<div class="blog-card__footer">
<span class="blog-card__author">By {post.data.author}</span>
<a href={`/blog/${post.id}`} class="blog-card__read-more">
Read More
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="12" x2="19" y2="12"></line>
<polyline points="12 5 19 12 12 19"></polyline>
</svg>
</a>
</div>
</article>
);
})}
</div>
</div>
</section>
)}
<style>
.section-header {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: var(--space-4);
flex-wrap: wrap;
}
.section-header__link {
display: inline-flex;
align-items: center;
gap: var(--space-1);
font-size: var(--text-sm);
font-weight: 600;
color: var(--color-accent);
transition: gap var(--transition-fast);
}
.section-header__link:hover {
gap: var(--space-2);
}
.blog-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: var(--space-6);
}
.blog-grid--single {
max-width: 600px;
margin-inline: auto;
}
.blog-card {
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.blog-card__meta {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-3);
flex-wrap: wrap;
}
.blog-card__meta time {
font-size: var(--text-sm);
color: var(--color-text-muted);
}
.blog-card__tags {
display: flex;
gap: var(--space-1);
}
.blog-card__tag {
font-size: 0.65rem;
padding: 2px var(--space-2);
border-radius: 9999px;
background: var(--color-accent-glow);
color: var(--color-accent);
}
.blog-card__title-link {
color: inherit;
text-decoration: none;
}
.blog-card__title-link:hover {
color: var(--color-accent);
}
.blog-card__title {
font-size: var(--text-xl);
font-weight: 700;
line-height: 1.3;
}
.blog-card__description {
font-size: var(--text-sm);
color: var(--color-text-secondary);
line-height: 1.6;
}
.blog-card__footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: auto;
padding-top: var(--space-3);
border-top: 1px solid var(--color-border);
}
.blog-card__author {
font-size: var(--text-xs);
color: var(--color-text-muted);
}
.blog-card__read-more {
display: inline-flex;
align-items: center;
gap: var(--space-1);
font-size: var(--text-sm);
font-weight: 600;
color: var(--color-accent);
transition: gap var(--transition-fast);
}
.blog-card__read-more:hover {
gap: var(--space-2);
}
</style>

View File

@@ -0,0 +1,219 @@
---
import { getCollection } from 'astro:content';
const allEpisodes = await getCollection('episodes');
const classicEpisodes = allEpisodes.filter((ep) => ep.data.classic);
// If no episodes marked as classic, pick a curated selection:
// the first episode of each season for variety
const fallbackEpisodes = (() => {
const seasonFirsts = new Map<number, typeof allEpisodes[0]>();
for (const ep of allEpisodes) {
const s = ep.data.season;
if (s === 0) continue;
if (!seasonFirsts.has(s) || ep.data.episode < seasonFirsts.get(s)!.data.episode) {
seasonFirsts.set(s, ep);
}
}
return Array.from(seasonFirsts.values())
.sort((a, b) => b.data.season - a.data.season)
.slice(0, 5);
})();
const displayEpisodes = classicEpisodes.length > 0 ? classicEpisodes : fallbackEpisodes;
const hasClassics = classicEpisodes.length > 0;
---
{displayEpisodes.length > 0 && (
<section class="section classics fade-in">
<div class="container">
<div class="section-header">
<h2 class="section__title">
{hasClassics ? 'Fan Favorites' : 'From the Archive'}
</h2>
<a href="/episodes" class="section-header__link">
Browse All Episodes
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</a>
</div>
<div class="classics-scroll">
<div class="classics-track">
{displayEpisodes.map((episode) => {
const { title, season, episode: episodeNum, pubDate, duration } = episode.data;
const seasonLabel = season === 0 ? 'Special' : `S${season}`;
const episodeLabel = `${seasonLabel} E${String(episodeNum).padStart(2, '0')}`;
const formattedDate = new Date(pubDate).toLocaleDateString('en-US', {
month: 'short',
year: 'numeric',
});
const description = episode.body
? episode.body.replace(/\\?\*\\?\*\\?\*/g, '').replace(/\\\[.*?\\\]/g, '').replace(/\[.*?\]/g, '').trim()
: '';
return (
<a href={`/episodes/${episode.id}`} class="classic-card">
{hasClassics && <span class="classic-card__badge">Classic</span>}
{!hasClassics && <span class="classic-card__badge classic-card__badge--season">Season {season}</span>}
<div class="classic-card__meta">
<span class="badge">{episodeLabel}</span>
<span class="classic-card__duration">{duration}</span>
</div>
<h3 class="classic-card__title">{title}</h3>
{description && (
<p class="classic-card__desc">{description.slice(0, 100)}{description.length > 100 ? '...' : ''}</p>
)}
<time class="classic-card__date" datetime={pubDate.toISOString()}>{formattedDate}</time>
</a>
);
})}
</div>
</div>
</div>
</section>
)}
<style>
.section-header {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: var(--space-4);
flex-wrap: wrap;
}
.section-header__link {
display: inline-flex;
align-items: center;
gap: var(--space-1);
font-size: var(--text-sm);
font-weight: 600;
color: var(--color-accent);
transition: gap var(--transition-fast);
}
.section-header__link:hover {
gap: var(--space-2);
}
.classics-scroll {
overflow-x: auto;
margin-inline: calc(-1 * var(--space-6));
padding-inline: var(--space-6);
scrollbar-width: thin;
scrollbar-color: var(--color-border) transparent;
-webkit-overflow-scrolling: touch;
}
.classics-scroll::-webkit-scrollbar {
height: 6px;
}
.classics-scroll::-webkit-scrollbar-track {
background: transparent;
}
.classics-scroll::-webkit-scrollbar-thumb {
background: var(--color-border);
border-radius: 3px;
}
.classics-track {
display: flex;
gap: var(--space-6);
padding-bottom: var(--space-4);
}
.classic-card {
flex: 0 0 280px;
display: flex;
flex-direction: column;
gap: var(--space-2);
padding: var(--space-6);
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
text-decoration: none;
color: inherit;
transition:
transform var(--transition-base),
box-shadow var(--transition-base),
border-color var(--transition-base);
}
.classic-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-lg);
border-color: var(--color-accent);
color: inherit;
}
.classic-card__badge {
align-self: flex-start;
font-size: 0.65rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
padding: 2px var(--space-2);
border-radius: var(--radius-sm);
background: hsl(40 90% 55% / 0.15);
color: var(--color-warning);
border: 1px solid hsl(40 90% 55% / 0.25);
}
.classic-card__badge--season {
background: var(--color-accent-glow);
color: var(--color-accent);
border-color: hsl(200 85% 55% / 0.25);
}
.classic-card__meta {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: var(--space-1);
}
.classic-card__duration {
font-size: var(--text-xs);
color: var(--color-text-muted);
font-family: var(--font-mono);
}
.classic-card__title {
font-size: var(--text-base);
font-weight: 600;
line-height: 1.3;
color: var(--color-text-primary);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.classic-card__desc {
font-size: var(--text-xs);
color: var(--color-text-secondary);
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.classic-card__date {
font-size: var(--text-xs);
color: var(--color-text-muted);
margin-top: auto;
}
@media (max-width: 480px) {
.classic-card {
flex: 0 0 240px;
}
}
</style>

View File

@@ -0,0 +1,295 @@
---
---
<section class="hero">
<div class="hero-bg-effects">
<div class="hero-glow hero-glow--primary"></div>
<div class="hero-glow hero-glow--secondary"></div>
<div class="hero-ring"></div>
</div>
<div class="hero-content container">
<span class="badge hero-badge">Returning 2026</span>
<h1 class="hero-title">
<span class="hero-title__line hero-title__line--1">The Computer</span>
<span class="hero-title__line hero-title__line--2">Guru Show</span>
</h1>
<p class="hero-tagline">Technology: Fun and Simple</p>
<p class="hero-description">
Your source for making sense of the tech world without the jargon.
Hosted by Mike Swanson from Tucson, Arizona -- cutting through the noise
so you can enjoy technology the way it was meant to be.
</p>
<div class="hero-actions">
<a href="/episodes" class="btn btn--primary btn--lg">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<polygon points="10 8 16 12 10 16 10 8" fill="currentColor" stroke="none"></polygon>
</svg>
Browse Episodes
</a>
<a href="/subscribe" class="btn btn--ghost btn--lg">Subscribe</a>
</div>
</div>
<div class="hero-scroll-hint" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="7 13 12 18 17 13"></polyline>
<polyline points="7 6 12 11 17 6"></polyline>
</svg>
</div>
</section>
<style>
.hero {
min-height: calc(100vh - 64px);
display: flex;
align-items: center;
position: relative;
overflow: hidden;
background:
linear-gradient(135deg, hsl(220 30% 6% / 0.95), hsl(200 40% 8% / 0.9)),
radial-gradient(ellipse at 20% 50%, hsl(200 85% 55% / 0.12), transparent 50%),
radial-gradient(ellipse at 80% 20%, hsl(270 60% 50% / 0.08), transparent 50%);
}
[data-theme="light"] .hero {
background:
linear-gradient(135deg, hsl(220 20% 95% / 0.95), hsl(200 30% 92% / 0.9)),
radial-gradient(ellipse at 20% 50%, hsl(200 85% 55% / 0.08), transparent 50%),
radial-gradient(ellipse at 80% 20%, hsl(270 60% 50% / 0.05), transparent 50%);
}
/* Background effects */
.hero-bg-effects {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 0;
}
.hero-glow {
position: absolute;
border-radius: 50%;
filter: blur(80px);
opacity: 0.4;
}
.hero-glow--primary {
width: 600px;
height: 600px;
top: -200px;
right: -100px;
background: radial-gradient(circle, hsl(200 85% 55% / 0.15), transparent 70%);
animation: glowDrift 8s ease-in-out infinite alternate;
}
.hero-glow--secondary {
width: 400px;
height: 400px;
bottom: -100px;
left: -50px;
background: radial-gradient(circle, hsl(270 60% 50% / 0.1), transparent 70%);
animation: glowDrift 10s ease-in-out infinite alternate-reverse;
}
.hero-ring {
position: absolute;
width: 500px;
height: 500px;
top: 50%;
right: 5%;
transform: translate(0, -50%);
border: 1px solid hsl(200 85% 55% / 0.08);
border-radius: 50%;
animation: ringPulse 4s ease-in-out infinite;
}
.hero-ring::before {
content: '';
position: absolute;
inset: 40px;
border: 1px solid hsl(200 85% 55% / 0.06);
border-radius: 50%;
animation: ringPulse 4s ease-in-out infinite 0.5s;
}
.hero-ring::after {
content: '';
position: absolute;
inset: 80px;
border: 1px solid hsl(200 85% 55% / 0.04);
border-radius: 50%;
animation: ringPulse 4s ease-in-out infinite 1s;
}
[data-theme="light"] .hero-glow--primary {
background: radial-gradient(circle, hsl(200 85% 55% / 0.08), transparent 70%);
}
[data-theme="light"] .hero-glow--secondary {
background: radial-gradient(circle, hsl(270 60% 50% / 0.06), transparent 70%);
}
[data-theme="light"] .hero-ring {
border-color: hsl(200 85% 55% / 0.12);
}
[data-theme="light"] .hero-ring::before {
border-color: hsl(200 85% 55% / 0.08);
}
[data-theme="light"] .hero-ring::after {
border-color: hsl(200 85% 55% / 0.05);
}
/* Content */
.hero-content {
position: relative;
z-index: 1;
max-width: 720px;
}
.hero-badge {
animation: fadeSlideIn 0.6s ease-out both;
}
.hero-title {
margin-top: var(--space-6);
display: flex;
flex-direction: column;
gap: 0;
}
.hero-title__line {
display: block;
font-size: clamp(2.5rem, 6vw, 5rem);
font-weight: 800;
line-height: 1.05;
background: linear-gradient(135deg, var(--color-text-primary) 30%, var(--color-accent) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-title__line--1 {
animation: fadeSlideIn 0.6s ease-out 0.15s both;
}
.hero-title__line--2 {
animation: fadeSlideIn 0.6s ease-out 0.3s both;
background: linear-gradient(135deg, var(--color-accent) 0%, hsl(220 70% 65%) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-tagline {
font-size: var(--text-xl);
color: var(--color-accent);
font-weight: 600;
margin-top: var(--space-4);
animation: fadeSlideIn 0.6s ease-out 0.45s both;
letter-spacing: 0.02em;
}
.hero-description {
font-size: var(--text-lg);
color: var(--color-text-secondary);
margin-top: var(--space-4);
line-height: 1.7;
max-width: 560px;
animation: fadeSlideIn 0.6s ease-out 0.6s both;
}
.hero-actions {
display: flex;
gap: var(--space-4);
margin-top: var(--space-8);
flex-wrap: wrap;
animation: fadeSlideIn 0.6s ease-out 0.75s both;
}
.btn--lg {
padding: var(--space-4) var(--space-8);
font-size: var(--text-base);
}
/* Scroll hint */
.hero-scroll-hint {
position: absolute;
bottom: var(--space-8);
left: 50%;
transform: translateX(-50%);
color: var(--color-text-muted);
animation: bounceDown 2s ease-in-out infinite;
z-index: 1;
}
/* Keyframes */
@keyframes fadeSlideIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes glowDrift {
from {
transform: translate(0, 0) scale(1);
}
to {
transform: translate(30px, -20px) scale(1.1);
}
}
@keyframes ringPulse {
0%, 100% {
opacity: 0.3;
transform: translate(0, -50%) scale(1);
}
50% {
opacity: 0.6;
transform: translate(0, -50%) scale(1.05);
}
}
@keyframes bounceDown {
0%, 100% {
transform: translateX(-50%) translateY(0);
opacity: 0.4;
}
50% {
transform: translateX(-50%) translateY(8px);
opacity: 0.8;
}
}
@media (max-width: 768px) {
.hero {
min-height: auto;
padding-block: var(--space-16) var(--space-24);
}
.hero-ring {
width: 300px;
height: 300px;
right: -80px;
opacity: 0.5;
}
.hero-scroll-hint {
display: none;
}
}
@media (max-width: 480px) {
.hero-title__line {
font-size: clamp(2rem, 10vw, 3rem);
}
}
</style>

View File

@@ -0,0 +1,69 @@
---
import { getCollection } from 'astro:content';
import EpisodeCard from '../episodes/EpisodeCard.astro';
const allEpisodes = await getCollection('episodes');
const latestEpisodes = allEpisodes
.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime())
.slice(0, 6);
---
<section class="section latest-episodes fade-in">
<div class="container">
<div class="section-header">
<h2 class="section__title">Latest Episodes</h2>
<a href="/episodes" class="section-header__link">
View All
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</a>
</div>
{latestEpisodes.length > 0 ? (
<div class="grid grid--3">
{latestEpisodes.map((episode) => (
<EpisodeCard episode={episode} />
))}
</div>
) : (
<div class="empty-state">
<p>New episodes coming soon. Stay tuned for the return of The Computer Guru Show.</p>
</div>
)}
</div>
</section>
<style>
.section-header {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: var(--space-4);
flex-wrap: wrap;
}
.section-header__link {
display: inline-flex;
align-items: center;
gap: var(--space-1);
font-size: var(--text-sm);
font-weight: 600;
color: var(--color-accent);
transition: gap var(--transition-fast);
}
.section-header__link:hover {
gap: var(--space-2);
}
.empty-state {
text-align: center;
padding: var(--space-16) var(--space-8);
color: var(--color-text-secondary);
font-size: var(--text-lg);
background: var(--color-bg-secondary);
border: 1px dashed var(--color-border);
border-radius: var(--radius-md);
}
</style>

View File

@@ -0,0 +1,129 @@
---
import { getCollection } from 'astro:content';
const allEpisodes = await getCollection('episodes');
const totalEpisodes = allEpisodes.length;
const seasons = new Set(allEpisodes.map((ep) => ep.data.season).filter((s) => s > 0));
const totalSeasons = seasons.size;
const stats = [
{ value: `${totalEpisodes}+`, label: 'Episodes', sublabel: 'and counting' },
{ value: String(totalSeasons), label: 'Seasons', sublabel: 'on the air' },
{ value: '2014', label: 'Since', sublabel: 'year one' },
{ value: 'Tucson', label: 'Arizona', sublabel: 'home base' },
];
---
<section class="section stats fade-in">
<div class="container">
<div class="stats-grid">
{stats.map((stat) => (
<div class="stat-card">
<span class="stat-card__value">{stat.value}</span>
<span class="stat-card__label">{stat.label}</span>
<span class="stat-card__sublabel">{stat.sublabel}</span>
</div>
))}
</div>
</div>
</section>
<style>
.stats {
position: relative;
}
.stats::before {
content: '';
position: absolute;
inset: 0;
background:
radial-gradient(ellipse at 50% 50%, hsl(200 85% 55% / 0.04), transparent 70%);
pointer-events: none;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--space-6);
}
.stat-card {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding: var(--space-8) var(--space-6);
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
position: relative;
overflow: hidden;
transition:
transform var(--transition-base),
box-shadow var(--transition-base),
border-color var(--transition-base);
}
.stat-card::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 2px;
background: var(--color-accent);
transition: width var(--transition-base);
}
.stat-card:hover {
transform: translateY(-4px);
box-shadow:
var(--shadow-md),
0 0 30px hsl(200 85% 55% / 0.08);
border-color: hsl(200 85% 55% / 0.3);
}
.stat-card:hover::after {
width: 60%;
}
.stat-card__value {
font-size: clamp(var(--text-2xl), 4vw, var(--text-4xl));
font-weight: 800;
line-height: 1;
background: linear-gradient(135deg, var(--color-text-primary), var(--color-accent));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.stat-card__label {
font-size: var(--text-lg);
font-weight: 600;
color: var(--color-text-primary);
margin-top: var(--space-2);
}
.stat-card__sublabel {
font-size: var(--text-xs);
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.06em;
margin-top: var(--space-1);
}
@media (max-width: 768px) {
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 400px) {
.stats-grid {
grid-template-columns: 1fr;
}
}
</style>

View File

@@ -0,0 +1,153 @@
---
import { platforms } from '../../data/platforms';
const platformIcons: Record<string, string> = {
apple: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>`,
spotify: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.66 0 12 0zm5.521 17.34c-.24.359-.66.48-1.021.24-2.82-1.74-6.36-2.101-10.561-1.141-.418.122-.779-.179-.899-.539-.12-.421.18-.78.54-.9 4.56-1.021 8.52-.6 11.64 1.32.42.18.479.659.301 1.02zm1.44-3.3c-.301.42-.841.6-1.262.3-3.239-1.98-8.159-2.58-11.939-1.38-.479.12-1.02-.12-1.14-.6-.12-.48.12-1.021.6-1.141C9.6 9.9 15 10.561 18.72 12.84c.361.181.54.78.241 1.2zm.12-3.36C15.24 8.4 8.82 8.16 5.16 9.301c-.6.179-1.2-.181-1.38-.721-.18-.601.18-1.2.72-1.381 4.26-1.26 11.28-1.02 15.721 1.621.539.3.719 1.02.419 1.56-.299.421-1.02.599-1.559.3z"/></svg>`,
google: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.372 0 0 5.373 0 12s5.372 12 12 12c6.627 0 12-5.373 12-12S18.627 0 12 0zm-.5 4.5h1v3.258l2.82-1.628.5.866-2.82 1.628 2.82 1.628-.5.866L12.5 9.49V12.5h-1V9.49l-2.82 1.628-.5-.866 2.82-1.628-2.82-1.628.5-.866L11.5 7.758V4.5z"/></svg>`,
overcast: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2"/><circle cx="12" cy="12" r="3"/><line x1="12" y1="5" x2="12" y2="7" stroke="currentColor" stroke-width="2"/><line x1="12" y1="17" x2="12" y2="19" stroke="currentColor" stroke-width="2"/><line x1="5" y1="12" x2="7" y2="12" stroke="currentColor" stroke-width="2"/><line x1="17" y1="12" x2="19" y2="12" stroke="currentColor" stroke-width="2"/></svg>`,
pocketcasts: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.372 0 0 5.372 0 12s5.372 12 12 12 12-5.372 12-12S18.628 0 12 0zm0 3.6c4.636 0 8.4 3.764 8.4 8.4h-2.4c0-3.312-2.688-6-6-6s-6 2.688-6 6 2.688 6 6 6v2.4c-4.636 0-8.4-3.764-8.4-8.4S7.364 3.6 12 3.6zm0 4.8a3.6 3.6 0 110 7.2 3.6 3.6 0 010-7.2z"/></svg>`,
rss: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 0 1 9 9"></path><path d="M4 4a16 16 0 0 1 16 16"></path><circle cx="5" cy="19" r="1" fill="currentColor"></circle></svg>`,
};
---
<section class="subscribe-cta fade-in">
<div class="subscribe-cta__bg"></div>
<div class="container subscribe-cta__content">
<h2 class="subscribe-cta__title">Never Miss an Episode</h2>
<p class="subscribe-cta__subtitle">
Subscribe on your favorite platform and get notified when new episodes drop.
</p>
<div class="subscribe-cta__platforms">
{platforms.map((platform) => (
<a
href={platform.url}
class="subscribe-cta__platform"
target={platform.url.startsWith('http') ? '_blank' : undefined}
rel={platform.url.startsWith('http') ? 'noopener noreferrer' : undefined}
aria-label={`Subscribe on ${platform.name}`}
>
<span class="subscribe-cta__icon" set:html={platformIcons[platform.icon] || platformIcons.rss} />
<span class="subscribe-cta__platform-name">{platform.name}</span>
</a>
))}
</div>
</div>
</section>
<style>
.subscribe-cta {
position: relative;
overflow: hidden;
padding-block: var(--space-16);
}
.subscribe-cta__bg {
position: absolute;
inset: 0;
background:
linear-gradient(135deg, hsl(200 85% 55% / 0.08), hsl(270 60% 50% / 0.05)),
linear-gradient(to bottom, var(--color-bg-secondary), var(--color-bg-primary));
z-index: 0;
}
.subscribe-cta__bg::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(to right, transparent, var(--color-accent), transparent);
opacity: 0.3;
}
.subscribe-cta__bg::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(to right, transparent, var(--color-accent), transparent);
opacity: 0.3;
}
.subscribe-cta__content {
position: relative;
z-index: 1;
text-align: center;
}
.subscribe-cta__title {
font-size: var(--text-3xl);
font-weight: 800;
background: linear-gradient(135deg, var(--color-text-primary), var(--color-accent));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.subscribe-cta__subtitle {
font-size: var(--text-lg);
color: var(--color-text-secondary);
margin-top: var(--space-3);
max-width: 500px;
margin-inline: auto;
}
.subscribe-cta__platforms {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: var(--space-4);
margin-top: var(--space-8);
}
.subscribe-cta__platform {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-3) var(--space-6);
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
color: var(--color-text-primary);
font-size: var(--text-sm);
font-weight: 500;
text-decoration: none;
transition:
transform var(--transition-fast),
border-color var(--transition-fast),
box-shadow var(--transition-fast),
color var(--transition-fast);
}
.subscribe-cta__platform:hover {
transform: translateY(-2px);
border-color: var(--color-accent);
box-shadow: var(--shadow-glow);
color: var(--color-accent);
}
.subscribe-cta__icon {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
}
@media (max-width: 640px) {
.subscribe-cta__platforms {
flex-direction: column;
align-items: center;
}
.subscribe-cta__platform {
width: 100%;
max-width: 280px;
justify-content: center;
}
}
</style>

View File

@@ -0,0 +1,39 @@
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
const episodes = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/episodes' }),
schema: z.object({
title: z.string(),
season: z.number(),
episode: z.number(),
pubDate: z.coerce.date(),
duration: z.string(),
audioUrl: z.string().url(),
audioSize: z.number(),
episodeType: z.enum(['full', 'trailer', 'bonus']).default('full'),
originalUrl: z.string().url().optional(),
featured: z.boolean().default(false),
classic: z.boolean().default(false),
tags: z.array(z.string()).default([]),
chapters: z.array(z.object({
time: z.string(),
title: z.string(),
})).optional(),
}),
});
const blog = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
schema: z.object({
title: z.string(),
pubDate: z.coerce.date(),
description: z.string(),
author: z.string().default('Mike Swanson'),
tags: z.array(z.string()).default([]),
image: z.string().optional(),
draft: z.boolean().default(false),
}),
});
export const collections = { episodes, blog };

View File

@@ -0,0 +1,13 @@
---
title: "The Computer Guru Show Returns"
pubDate: 2026-03-14
description: "After five years off the air, The Computer Guru Show is back with a fresh format and a brand new website."
author: "Mike Swanson"
tags: ["announcement", "show-news"]
---
After a five-year hiatus, The Computer Guru Show is officially returning to the airwaves. We have been off the air since Season 10, Episode 21 back in March 2018, but we have not been idle.
The tech landscape has changed dramatically since then. AI has gone from a buzzword to a daily tool. The way we interact with technology has fundamentally shifted. And we are here to make sense of it all -- the same way we always have: by making technology fun and simple.
Stay tuned for new episodes, and in the meantime, browse our archive of 194 classic episodes spanning Seasons 6 through 10.

View File

@@ -0,0 +1,16 @@
---
title: " Guest appearance on Liberty Watch"
season: 0
episode: 179
pubDate: 2014-05-23
duration: "42:34"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/specials/libertywatch5-11-14.mp3"
audioSize: 17882275
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-guest-appearance-on-liberty-watch/"
featured: false
classic: false
tags: []
---
Special Edition Podcast Liberty Watch from 5/11/2014 I sat down with Charles Heller on Liberty Watch to discuss #Netneutrality This was the result.

View File

@@ -0,0 +1,16 @@
---
title: "S6E01 Computer Guru Show New Digs"
season: 6
episode: 1
pubDate: 2014-02-10
duration: "46:50"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/02/s6e01.mp3"
audioSize: 44963279
episodeType: "full"
originalUrl: "https://www.gurushow.com/s6e01-computer-guru-show-new-digs/"
featured: false
classic: false
tags: ["net-neutrality", "streaming"]
---
\*\*\* Im aware that we cannot hear Harry in the podcasts.  Ill see if we can get that fixed next week. \*\*\* The first episode on KVOI.  Thanks for your support, everyone. Topics : Welcome to KVOI Net Neutrality Netflix vs. Verizon Legislating a Business Model

View File

@@ -0,0 +1,16 @@
---
title: "S6E02 Computer Guru Show Ripples"
season: 6
episode: 2
pubDate: 2014-02-17
duration: "48:53"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/02/s6e02.mp3"
audioSize: 20536828
episodeType: "full"
originalUrl: "https://www.gurushow.com/s6e02-computer-guru-show-ripples/"
featured: false
classic: false
tags: ["google", "windows"]
---
In this episode we talk about the ripple effects of Windows 8 and how certain vendors are responding to the new operating system.  We also touch on how UFC Pay Per View is bullying sites to give up userlists.  As always, your phone calls, and various other topics. Sl!cklogin : http://www.engadget.com/2014/02/16/google-acquires-slicklogin-sound-passwords/ Nike Laces : http://solecollector.com/news/tinker-hatfield-confirms-nike-mag-will-release-with-power-laces-20151/ Google Fiber \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S6E03 Computer Guru Show TechHole!"
season: 6
episode: 3
pubDate: 2014-02-24
duration: "48:24"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/02/s6e03.mp3"
audioSize: 46474202
episodeType: "full"
originalUrl: "https://www.gurushow.com/s6e03-computer-guru-show-techhole/"
featured: false
classic: false
tags: ["apple"]
---
Mike and Tara go over how not to be a jerk with Technology, Apples SSL problems, Your calls, and more! Visit the Aftershow Post for links and more details, including things we promised to update you on after the show!

View File

@@ -0,0 +1,16 @@
---
title: "S06E04 Computer Guru Show No Topic"
season: 6
episode: 4
pubDate: 2014-03-03
duration: "48:57"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/02/s6e04.mp3"
audioSize: 46987456
episodeType: "full"
originalUrl: "https://www.gurushow.com/s06e04-computer-guru-show-no-topic/"
featured: false
classic: false
tags: []
---
Various Rants about Stuff 🙂

View File

@@ -0,0 +1,16 @@
---
title: "S06E05 Computer Guru Show ID10T Governance"
season: 6
episode: 5
pubDate: 2014-03-10
duration: "47:28"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/03/s6e05.mp3"
audioSize: 45568902
episodeType: "full"
originalUrl: "https://www.gurushow.com/s06e05-computer-guru-show-id10t-governance/"
featured: false
classic: false
tags: []
---
Do you know what an ISP is?  I hope so.  Our government doesnt.

View File

@@ -0,0 +1,16 @@
---
title: "S06E06 Computer Guru Show Double Interviews! Kihon Games and Malwarebytes"
season: 6
episode: 6
pubDate: 2014-03-22
duration: "45:15"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/03/s6e06.mp3"
audioSize: 19005582
episodeType: "full"
originalUrl: "https://www.gurushow.com/s06e06-computer-guru-show-double-interviews-kihon-games-and-malwarebytes/"
featured: false
classic: false
tags: ["malware"]
---
For the first part of this podcast, we speak with Mark from Kihon Games about their new endeavor, “Conquest of Champions”. We also speak with Adam from Malwarebytes about whats next for the anti-malware company, and how you should be protecting your computer.

View File

@@ -0,0 +1,16 @@
---
title: "S06E07 Martian Koalas switch to Linux"
season: 6
episode: 7
pubDate: 2014-03-23
duration: "48:27"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/03/s6e07.mp3"
audioSize: 46507638
episodeType: "full"
originalUrl: "https://www.gurushow.com/s06e07-martian-koalas-switch-to-linux/"
featured: false
classic: false
tags: ["linux", "space", "windows"]
---
We had a lot of fun discussing the Game Developers Conference that finished up last week, the prospect of a Russian Space Hotel, and the end of support for Windows XP. PLUS, what you type can now be deciphered by sound…YIKES! Didnt get to listen in live? Catch up by playing the podcast. Regarding the \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06E08 Facebook Rage, ICAN Tango II, and a brand new time!"
season: 6
episode: 8
pubDate: 2014-03-31
duration: "48:55"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/03/s6e08.mp3"
audioSize: 46969065
episodeType: "full"
originalUrl: "https://www.gurushow.com/s06e08-facebook-rage-ican-tango-ii-and-a-brand-new-time/"
featured: false
classic: false
tags: ["facebook"]
---
Listen in as we discuss the pros and cons of Facebook spending billions of dollars on acquiring new projects, including the Oculus Rift. We also talk with Chuck Clayton about the ICAN Tango II Charity Event (details below), and help out a few callers with their technology issues. AND WE ARE MOVING! Thats right, \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06E09 Tracking Data"
season: 6
episode: 9
pubDate: 2014-04-07
duration: "46:36"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/04/s6e09.mp3"
audioSize: 19573397
episodeType: "full"
originalUrl: "https://www.gurushow.com/s06e09-tracking-data/"
featured: false
classic: false
tags: ["google"]
---
New time, new board-op, new experiences. This week we bring you several stories on how tracking data is and isnt used, and how Google is shaking things up with everything they do.

View File

@@ -0,0 +1,16 @@
---
title: "S06E10 Commercial Break!"
season: 6
episode: 10
pubDate: 2014-04-13
duration: "48:59"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/04/s6e10.mp3"
audioSize: 47026744
episodeType: "full"
originalUrl: "https://www.gurushow.com/s06e10-commercial-break/"
featured: false
classic: false
tags: ["google"]
---
Today we talked about the Heartbleed Bug (and a list of passwords you should change \[from Mashable, by the way\]), the sale of Google Glass for one day, the IRS not sticking to a deadline, and 3D Printing for the masses brought to you by … Staples? We also have a little debate about those darned commercials. \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06E11 Technology Optimism and Fear"
season: 6
episode: 11
pubDate: 2014-04-21
duration: "48:02"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/04/s6e11.mp3"
audioSize: 46119665
episodeType: "full"
originalUrl: "https://www.gurushow.com/s06e11-technology-optimism-and-fear/"
featured: false
classic: false
tags: []
---
On this episode, we talked about whether we were more optimistic or fearful of todays technology. This was all spurred by a recent study by the Pew Research Center, which broke down what people think about technology such as wearable tech, teleportation, and driverless vehicles. While some numbers are optimistic, there is still a healthy \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06E12 The beginning of the end of “Net Neutrality”"
season: 6
episode: 12
pubDate: 2014-04-28
duration: "45:06"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/04/s6e12.mp3"
audioSize: 43292589
episodeType: "full"
originalUrl: "https://www.gurushow.com/s06e12-the-beginning-of-the-end-of-net-neutrality/"
featured: false
classic: false
tags: ["net-neutrality"]
---
Just as we are beginning to see the power that free resources produce, changes in the architecture of the Internetboth legal and technicalare sapping the Internet of this power. Fueled by bias in favor of control, pushed by those whose financial interest favor control, our social and political institutions are ratifying changes in the Internet \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06E13 Open Internet and Star Wars Day"
season: 6
episode: 13
pubDate: 2014-05-07
duration: "50:39"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/05/s6e13.mp3"
audioSize: 48629617
episodeType: "full"
originalUrl: "https://www.gurushow.com/s06e13-open-internet-and-star-wars-day/"
featured: false
classic: false
tags: ["net-neutrality"]
---
We get just a bit more in-depth into the issue with Net Neutrality, specifically peering, to clarify a few things that were said last week. If youd like to let your thoughts be known to the FCC (which, you should) then you need to go to this website for information. It doesnt matter if youre \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06E14 Printing, and Trojans, and Net Neutrality… oh my!"
season: 6
episode: 14
pubDate: 2014-05-10
duration: "49:00"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/05/s6e14.mp3"
audioSize: 47043773
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s06e14-printing-and-trojans-and-net-neutrality-oh-my/"
featured: false
classic: false
tags: ["facebook", "malware", "net-neutrality"]
---
On todays show, we discuss a plethora of topics, including a heated discussion on Net Neutrality, turning your car into a hybrid, printing out makeup, and how we can help you with your computer REMOTELY. Links are below: Malwarebytes AntiExploit Hybrid Conversions Facebook Pokes (RIP) MINK Makeup Printer and Net Neutrality

View File

@@ -0,0 +1,16 @@
---
title: "S06E15 Get ready for summer!"
season: 6
episode: 15
pubDate: 2014-05-23
duration: "45:39"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/05/s6e15.mp3"
audioSize: 43827225
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s06e15-get-ready-for-summer/"
featured: false
classic: false
tags: []
---
We all know that Spring Cleaning usually entails cleaning out your closet and garage, but you also need to focus on your computer. If you dont want to lose all your information to a killer monsoon lightning strike, then you need to take some precautions. We talk about this, Russias threat against GPS systems, random \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06 E16 E3 Pre Party!"
season: 6
episode: 16
pubDate: 2014-05-25
duration: "47:58"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/05/s6e16.mp3"
audioSize: 46058332
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s06-e16-e3-pre-party/"
featured: false
classic: false
tags: ["security"]
---
Such energy. Much random. Wow. Today we talked with Clay from Nerd Junkies about our trip to E3 (Electronic Entertainment Expo) 2014, coming up in just a couple of weeks. (Links to all E3 items discussed, as well as the Nerd Junkies info, will be posted below.) We also spoke about Ebay getting hacked, the importance \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06 E17 Snowden and Privacy"
season: 6
episode: 17
pubDate: 2014-05-31
duration: "50:39"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/05/s6e17.mp3"
audioSize: 48629617
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s06-e17-snowden-and-privacy/"
featured: false
classic: false
tags: ["privacy"]
---
We discussed the case of Edward Snowden today, and whether or not hes a “whistleblower” or a “traitor”. We also mentioned a documentary, “Frontline”, which asks the question whether or not were “safer” after the events of 9/11. Another thing that has hit the news is TrueCrypt asking its users to go elsewhere because, “WARNING: \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06E18 Hack your car?"
season: 6
episode: 18
pubDate: 2014-06-08
duration: "49:48"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/06/s6e18.mp3"
audioSize: 47814908
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s06e18-hack-your-car/"
featured: false
classic: false
tags: ["android", "security"]
---
A multitude of subjects covered this week: Car Hacking Safari plays DuckDuckGo Backup your Android with Lookout Need a link to something we covered? Send us a message!

View File

@@ -0,0 +1,16 @@
---
title: "S06 E19 Back from E3!"
season: 6
episode: 19
pubDate: 2014-06-21
duration: "48:34"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/06/s6e19.mp3"
audioSize: 46634591
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s06-e19-back-from-e3/"
featured: false
classic: false
tags: ["tesla"]
---
So, were back from E3! We talk about some of the things we saw there, and what we may or may not be excited about. ALSO, Tesla has opened up their technologies to the masses. Is this wise? What does this mean with patent law? We discuss this and more! Listen below.

View File

@@ -0,0 +1,16 @@
---
title: "S06 E20 Our 20th episode on KVOI!"
season: 6
episode: 20
pubDate: 2014-06-24
duration: "49:06"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/06/s6e20.mp3"
audioSize: 47144083
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s06-e20-our-20th-episode-on-kvoi/"
featured: false
classic: false
tags: ["security"]
---
We would like to remind you that this show is all about YOU. We dont just talk about technology, we talk about YOUR technology. So, for our 20th episode, we took your calls and talked about what YOU wanted to talk about, which included: Solar Roadways Security Cameras Email Archiving in Outlook and other random topics! \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06 E21 Ownphones, eSports, and YOUR calls!"
season: 6
episode: 21
pubDate: 2014-06-28
duration: ""
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/06/s6e21.mp3"
audioSize: 5242880
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s06-e21-ownphones-esports-and-your-calls/"
featured: false
classic: false
tags: []
---
Today we had a great conversation with Itamar Jobani of Ownphones, 3D printed earbuds that are personalized to fit into YOUR ears. Check out their information below, and help them out with their Kickstarter campaign. As a special “Thank You”, the first 1000 people that help fund Ownphones will be able to purchase their own \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06E22 Carnivorous Coffee Tables"
season: 6
episode: 22
pubDate: 2014-07-10
duration: "48:56"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/07/s6e22.mp3"
audioSize: 20551639
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s06e22-carnivorous-coffee-tables/"
featured: false
classic: false
tags: []
---
We went through a lot of topics today, discussing everything from Solid State Hard Drives to Lindsay Lohan. In fact, we had so many different topics that we touched on yesterday, we didnt even really talk about all of them in detail. So, below youll find all of the links to everything you want to \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06E23 Unscrewed Theater"
season: 6
episode: 23
pubDate: 2014-07-12
duration: "47:55"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/07/s6e23.mp3"
audioSize: 46009431
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s06e23-unscrewed-theater/"
featured: false
classic: false
tags: ["malware"]
---
Today we had Mike from Unscrewed Theater on the show to promote their awesome antics, and the fact that The Guru will be joining them on July 26th as a special guest. We had a full house with four people on the show at once! We talked about the resurgence of the Zeus virus, how to \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06E24 Guru UNLEASHED!"
season: 6
episode: 24
pubDate: 2014-07-21
duration: "48:50"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/07/s6e24.mp3"
audioSize: 46883384
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s06e24-guru-unleashed/"
featured: false
classic: false
tags: []
---
This episode we went on remote to present Guru Unleashed at the Arizona Computer Guru Shop. We had Chris from Unscrewed Theater on to talk about their group, “Not Burn Out, Just Unscrewed”, and Mikes upcoming appearance with them on the 26th of July. It was a great experience, and fun was had by all. \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06E25 Verizon, youre on notice"
season: 6
episode: 25
pubDate: 2014-07-29
duration: "47:34"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/07/s6e25.mp3"
audioSize: 11420529
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s06e25-verizon-youre-on-notice/"
featured: false
classic: false
tags: []
---
We opened up the show with congratulations for “Weird” Al Yankovic for FINALLY hitting #1 on Billboard Top 200 for his album “Mandatory Fun”. We also talked about eSports and the number of sponsors that are coming out to support them. Additionally, Russia has put a bounty up on Tor, Verizon has started throttling the \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06E26 911 emergency! Facebook is down!"
season: 6
episode: 26
pubDate: 2014-08-03
duration: "47:39"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/08/s6e26.mp3"
audioSize: 45744756
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s06e26-911-emergency-facebook-is-down/"
featured: false
classic: false
tags: ["facebook"]
---
Thats it. The world is falling apart. People are calling 911 when Facebook goes down for a half an hour, you can take a picture of a key and get a physical copy printed at a kiosk, and the CIA and Senate are having a lovers spat. On the plus side, “Idiocracy” is alive and \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S06E27 Ding Dong, the Windows 8 is dead!"
season: 6
episode: 27
pubDate: 2014-08-10
duration: "47:49"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/08/s6e27.mp3"
audioSize: 45900655
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s06e27-ding-dong-the-windows-8-is-dead/"
featured: false
classic: false
tags: ["security", "windows"]
---
It was a busy show, as we discussed topics that varied from the upcoming Windows 9, to the insecurity of USB, to the arrest of a 20 year old who created a proxy. We didnt get to some topics, as we just didnt have enough time to touch on them all, but we can link \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S6E28 Encryption is Key"
season: 6
episode: 28
pubDate: 2014-08-16
duration: "48:40"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/08/s6e28.mp3"
audioSize: 46723616
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s6e28-encryption-is-key/"
featured: false
classic: false
tags: ["security"]
---
We had so much to talk about today,  that we just couldnt fit it all in! Most of what we had to talk about revolved around encryption, and the fact that everyone should be using it. There are too many ways to hack into an unencrypted system, so if you want your information to be \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E01 Season Seven Unleashed!"
season: 7
episode: 1
pubDate: 2014-08-23
duration: "48:48"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/08/s7e01.mp3"
audioSize: 46847439
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e1-season-seven-unleashed/"
featured: false
classic: false
tags: []
---
We are now into Season 7 of the Computer Guru Radio Show! We started things off by hosting a Guru Unleashed at Arizona Computer Guru! Though we had a little technical trouble to begin with, we got past it quickly and got to the things that matter. Whether or not you think theyre useful, heres \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E02 Microsoft says NO."
season: 7
episode: 2
pubDate: 2014-08-30
duration: "48:23"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/08/s7e02.mp3"
audioSize: 46452779
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e02-microsoft-says/"
featured: false
classic: false
tags: ["microsoft"]
---
So today we started the show off talking about tablets, in a way picking up off of the conversation we had at the end of last week. We also discussed Microsoft and their decision to go against court orders, mandatory kill-switch in California, and social networks such as Pinterest! PC Sales Up / Tablets Down Microsoft \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E03 Security, metadata, and … the #Fappening"
season: 7
episode: 3
pubDate: 2014-09-06
duration: "49:13"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/09/s7e03.mp3"
audioSize: 47253589
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e03-security-metadata-fappening/"
featured: false
classic: false
tags: ["security"]
---
Thats right. We discussed the #Fappening and the nude photos of all the celebrities that came out this past week. You cant have a security breach that HUGE and expect us not to talk about it. So what side of the debate do you fall on? Does blame lie more on the celebrities or the \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E04 No, its not a phone…"
season: 7
episode: 4
pubDate: 2014-09-15
duration: "49:43"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/09/s7e04.mp3"
audioSize: 11935141
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s07e04-phone/"
featured: false
classic: false
tags: ["chrome", "microsoft", "privacy", "security", "streaming"]
---
Mike flew solo this week as he made his way through topics of privacy, entertainment on the go, and firing back at phishing schemes. He also mentioned the NoPhone, which is (indubitably) not a phone. Seriously. Its not. HBO Go considering standalone payment Roku or Chromecast Turning the tables on “Microsoft” phishing schemes System Update \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E05 Netflix and Canada, eh?"
season: 7
episode: 5
pubDate: 2014-09-21
duration: "49:11"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/09/s7e05.mp3"
audioSize: 47220570
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s07e05-netflix-canada-eh/"
featured: false
classic: false
tags: ["streaming"]
---
Canada is usually known for being a pretty polite nation, so what has Netflix done to ruffle their feathers so much? Can it really all boil down to greed? Canada is asking Netflix for private data on its consumers. Most people seem to agree with Netflix in saying, “no”. Mike also answers the question: Whats \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E06 Stay the Hell away from my phone!"
season: 7
episode: 6
pubDate: 2014-09-29
duration: "49:03"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/09/s7e06.mp3"
audioSize: 47091838
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s07e06-stay-hell-away-phone/"
featured: false
classic: false
tags: []
---
They say no news is good news, so of course were seeing tons of news coming out right now (most of which is depressing). We tried to start out with a little good news about the appointment of the new CTO, but the harder we try to get positive, the more the headlines bring us \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E07 Lets all sue Google"
season: 7
episode: 7
pubDate: 2014-10-04
duration: "49:26"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/10/s7e07.mp3"
audioSize: 11865656
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s07e07-lets-sue-google/"
featured: false
classic: false
tags: ["google", "security", "windows"]
---
We spoke about a plethora of topics today, including Tiny Death Star and Disney, Celebrities and Google, home security issues with Comcast, and even a terrible Windows 7 joke. Click the links below to find out more about the stories we brought up, comment below if you have two cents to throw in on \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E08 Cmon, get happy!"
season: 7
episode: 8
pubDate: 2014-10-12
duration: "48:59"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/10/s7e08.mp3"
audioSize: 11758616
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s07e08-cmon-get-happy/"
featured: false
classic: false
tags: ["privacy"]
---
Were tired of all the doom and gloom in the news, so we tried to bring some happiness and humor into the studio today. Links to all the stories are below, following a TED talk by Glenn Greewald on, “Why Privacy Matters”. Want to help keep our show going? As you may know, the show \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E09 Learning to Fly…"
season: 7
episode: 9
pubDate: 2014-10-18
duration: "49:05"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/10/s7e09.mp3"
audioSize: 11783527
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s07e09/"
featured: false
classic: false
tags: []
---
Today we talked about Clean Energy from Lockheed Martin, Tor Routers being pulled from Kickstarter (even after being funded), a Tetris movie, and a bit about flying. Were also still learning that we need to Encrypt EVERYTHING after Aarons pulls a big no-no with spyware. Additionally, if you like the show and would like to \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E10 The cultural revolution that has spun out of control, #Gamergate"
season: 7
episode: 10
pubDate: 2014-10-25
duration: "49:26"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/10/s7e10.mp3"
audioSize: 11867327
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s07e09-cultural-revolution-spun-control-gamergate/"
featured: false
classic: false
tags: ["gaming"]
---
There are a lot of views and perspectives on #Gamergate. Some support it due to issues in ethics within the gaming industry. Others oppose it, saying its a way for supporters to harass women. Either way you look at it, theres really no clear understanding of the basis and endgame of #Gamergate, except that it has \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E11 Wearable Tech, Faster DSL, and Manuel Noriega!"
season: 7
episode: 11
pubDate: 2014-11-01
duration: "49:16"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/11/s7e11.mp3"
audioSize: 11826576
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s07e11-wearable-tech-faster-dsl-manuel-noriega/"
featured: false
classic: false
tags: ["apple", "google", "microsoft"]
---
In this episode, we talked a bit about Nike and Apple deepening their relationship, Microsofts awesome wearable tech, and DSL bumping up their service. We also discovered that Spain knows nothing about how Google works, and Manuel Noriegas lawsuit was dismissed by the judge. If youre interested in our GPS (Guru Protection Services) available through \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E12 Questionable Legality and new IP"
season: 7
episode: 12
pubDate: 2014-11-10
duration: "49:17"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/11/s7e12.mp3"
audioSize: 11831696
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s07e12-questionable-legality-new-ip/"
featured: false
classic: false
tags: ["google"]
---
We had a lot to discuss today, including going back to two topics we discussed last week. What has Google done about the laws brought against it? What exactly is FreedomPop? Also, the re-emergence of Popcorn Time, and the first new IP from Blizzard in 17 years! Listen to the podcast below, and check out \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E13 What is FreedomPop?"
season: 7
episode: 13
pubDate: 2014-11-15
duration: "49:07"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/11/s7e13.mp3"
audioSize: 11789378
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s07e13-freedompop/"
featured: false
classic: false
tags: []
---
Were getting into gadgets! First and foremost, we talk about FreedomPop, which is something that weve mentioned over the past two weeks. We were able to get in touch with them, and had an awesome Q&A session with Samantha. Not only did she answer our questions, and explain what FreedomPop isall about, she also got \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E14 DOJ and Apple butt heads"
season: 7
episode: 14
pubDate: 2014-11-25
duration: "49:04"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/11/s7e14.mp3"
audioSize: 11779660
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s07e14-doj-apple-butt-heads/"
featured: false
classic: false
tags: ["apple", "iphone"]
---
Apparently, the DoJ is still trying to get Apple to let them decrypt iPhones, because “a child will die” due to the police being unable to gain access. We also answered your phone calls, looked up “Axpergle”, and talked about some of the top video games for this holiday season. Click the links below to \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E15 Making your computer faster"
season: 7
episode: 15
pubDate: 2014-12-08
duration: "46:54"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/12/s7e15.mp3"
audioSize: 11257838
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s07e15-making-computer-faster/"
featured: false
classic: false
tags: []
---
Maybe you didnt want to buy a new computer over the big shopping weekend. If not, Mike discusses a few tips on how to make your current computer faster. If you did buy a new computer, we can provide you with options for recycling your old computer, as well as help protect your new computer \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E16 Post Cyber Monday"
season: 7
episode: 16
pubDate: 2014-12-15
duration: "47:59"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/12/s7e16.mp3"
audioSize: 11519481
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s07e16-post-cyber-monday/"
featured: false
classic: false
tags: []
---
Did you go shopping online for Cyber Monday? We chatted about the biggest online retail day of the year, and what kind of deals we found. We also discussed an awesome gift list for geeks, the Hour of Code, and took some of your calls, including one rave review from one of our customers! Heres \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E17 Whos responsible for the Sony hack?"
season: 7
episode: 17
pubDate: 2014-12-23
duration: "49:45"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/12/s7e17.mp3"
audioSize: 11943979
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s07e17-whos-responsible-sony-hack/"
featured: false
classic: false
tags: ["security"]
---
Sony has been hacked. The Interview has been pulled. So has Team America. But the game Glorious Leader lives on… Whos responsible? North Korea? Or is there someone else… As always, thank you for listening to the show. The Computer Guru Radio Show is for you, the listener, and we love providing that service to \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E18 Hacking, monopolies, and #WakeUpSony"
season: 7
episode: 18
pubDate: 2014-12-27
duration: "49:34"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2014/12/s7e18.mp3"
audioSize: 11900345
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s07e18-hacking-monopolies-wakeupsony/"
featured: false
classic: false
tags: ["security"]
---
Lots to talk about today as the year winds down to a close. The majority of our last show of 2014 was spent on discussing hacking, lulz, and Sony. There have been the issues with The Interview and all of the leaked information, but now Sony also has the added headache of PSN being down. \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E19 Nerd Junkies help bring in the New Year!"
season: 7
episode: 19
pubDate: 2015-01-07
duration: "47:13"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/01/s7e19.mp3"
audioSize: 11334116
episodeType: "full"
originalUrl: "https://www.gurushow.com/s7e19-nerd-junkies-help-bring-new-year/"
featured: false
classic: false
tags: ["net-neutrality", "privacy"]
---
Happy New Year! Mike brings on Clay from the Nerd Junkies to help out on the show, as Tara was out for the week (NOT snowboarding). Mike and Clay review 2014 and the great strides that technology has taken this year, not just in physical form, but also with secrecy, privacy, and net neutrality. \[…\]

View File

@@ -0,0 +1,16 @@
---
title: " S7E20 All a matter of perspective"
season: 7
episode: 20
pubDate: 2015-01-11
duration: "47:38"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/01/s7e20.mp3"
audioSize: 11433695
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e20-matter-perspective/"
featured: false
classic: false
tags: ["microsoft"]
---
Today on the Computer Guru Show, not only is Tara back, but so are thousands of classic games! Mike gives more love to Elon Musk, Anonymous steps up for Charlie Hebdo, people all over the place are getting separation anxiety from their phones, and Microsoft is doing away with Internet Explorer. If youd like \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E21 Radioshack might be done, Google is our new brain, and XBox One won an Emmy… WHAT IS GOING ON?!"
season: 7
episode: 21
pubDate: 2015-01-17
duration: "46:46"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/01/s7e21.mp3"
audioSize: 11228268
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e21-radioshack-might-be-done-google-is-our-new-brain-and-xbox-one-won-an-emmy-what-is-going-on/"
featured: false
classic: false
tags: ["gaming", "google", "microsoft", "security"]
---
On this episode, we discuss learning to code for free, what the President has to say about encryption, and the fact that Microsofts XBox One actually one an Emmy. Check out the links below to learn more, or to listen to the podcast. If youd like to take a minute and just sit right there, \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S07E22 Dish made how many violations?!"
season: 7
episode: 22
pubDate: 2015-01-24
duration: "49:00"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/01/s7e22.mp3"
audioSize: 47048371
episodeType: "full"
originalUrl: "https://www.gurushow.com/s7e22-dish-made-how-many-violations/"
featured: false
classic: false
tags: ["google", "microsoft"]
---
This episode, Mike and Tara discuss various projects from Google and Microsoft, as well as some upcoming technologies to include 3D printing your food! (Article link and video below.) Mike also took some of your calls, and answered your questions! To help sponsor the show, head on over to Patreon like Desert Pro Commercial Cleaning did. \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E23 Mirror, mirror…"
season: 7
episode: 23
pubDate: 2015-01-31
duration: "47:04"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/01/s7e23.mp3"
audioSize: 11299530
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e23-mirror-mirror/"
featured: false
classic: false
tags: ["net-neutrality"]
---
A mirror that can tell you whats wrong with you? Thats right. Its from Panasonic. We discussed this, the Belfie Stick, Net Neutrality, and more on todays show. Check out the links and videos below, and find the podcast down at the bottom! Want to help out with the show and become a sponsor? Head over \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E24 DO NOT go to the Pirate Bay… Regardless of what Anonymous says."
season: 7
episode: 24
pubDate: 2015-02-07
duration: "46:46"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/02/s7e24.mp3"
audioSize: 11228059
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e24-do-not-go-to-the-pirate-bay-regardless-of-what-anonymous-says/"
featured: false
classic: false
tags: []
---
On this episode, there was a bit about Anonymous warnings on the resurrection of The Pirate Bay, and a lot of talk on the possibility of the internet being sourced as a public utility. We had a couple listeners call in to give their take on the situation, as well as plenty of chatter in the Live \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E25 Technology hates humanity and wants us to know it"
season: 7
episode: 25
pubDate: 2015-02-14
duration: "45:43"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/02/s7e25.mp3"
audioSize: 10972849
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e25-technology-hates-humanity-and-wants-us-to-know-it/"
featured: false
classic: false
tags: []
---
Mike is incensed over a mistake that Verizon has made. We also talk about how technology hates you, and has no problem letting you know. Tara goes over a list of her top 10 classic co-op games for Valentines Day. Find the links to what we talked about below, and find the podcast at the \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E26 Victory over Verizon"
season: 7
episode: 26
pubDate: 2015-02-24
duration: "45:58"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/02/s7e26.mp3"
audioSize: 11034126
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e26-victory-verizon/"
featured: false
classic: false
tags: []
---
So what ever happened with Mikes $1500 Verizon bill? We discuss this as well as Exploding Kittens, failing Kickstarter projects, and Lenovos Superfish. If youd like to be a patron of the show, you can support us for as little as $1 a month! Head on over to Patreon just like Desert Pro Commercial Cleaners, \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E27 “Equality is Poison”"
season: 7
episode: 27
pubDate: 2015-03-02
duration: "47:30"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/02/s7e27.mp3"
audioSize: 11403453
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e27-equality-is-poison/"
featured: false
classic: false
tags: ["net-neutrality"]
---
This week, there was a dress. Before that, Net Neutrality measures were approved by the FCC. This makes some people nervous, because theyre not exactly sure how Net Neutrality works, thanks to the disinformation being spread. Mike breaks down why this is a bipartisan issue, and explains why Net Neutrality is a good thing for \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E28 Laid Back and Fancy Free"
season: 7
episode: 28
pubDate: 2015-03-07
duration: "46:08"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/03/s7e28.mp3"
audioSize: 44282800
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e28-laid-back-and-fancy-free/"
featured: false
classic: false
tags: []
---
After the seriousness of the content we talked about last week, we decided to have a laid back show this week. We were all over the map with content ranging from cell phones to Java, and of course we took a few calls from listeners. If youd like to keep the show going, head on \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E29 Outsmarting your ISP"
season: 7
episode: 29
pubDate: 2015-03-14
duration: "57:52"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/03/s7e29.mp3"
audioSize: 55553947
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e29-outsmarting-the-internet/"
featured: false
classic: false
tags: []
---
Are you paying too much for internet? We think its possible. This week we discussed some of the problems that you can encounter in the confusing world of wi-fi, and how internet service providers might be selling you more broadband than you might need. If youre experiencing slow speeds on the web, this is one \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E30- The Antivirus Episode"
season: 7
episode: 30
pubDate: 2015-03-24
duration: "45:21"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/03/s7e30.mp3"
audioSize: 43544201
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e30-the-antivirus-episode/"
featured: false
classic: false
tags: ["malware"]
---
Its that time of year again! Time for our annual antivirus episode where we cover every thing from the software we love/hate, to figuring out which websites put you at risk. Hint: if you have teenagers, they are probably visiting these sites! Listen in to find out how to protect your machine. If youd like \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E31 Security Measures in Flying"
season: 7
episode: 31
pubDate: 2015-03-28
duration: "58:42"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/03/s7e31.mp3"
audioSize: 56356011
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e31-security-measures-in-flying/"
featured: false
classic: false
tags: ["security"]
---
Today we discussed what we might be able to learn from the terrible Germanwings crash. Mike has some ideas for added security, and our audience called in with their opinions as well. Some options are an override by Air Traffic Control to gain access of the plane, and secondary authentication codes for access to the \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E32 Badabing, Badaboom!"
season: 7
episode: 32
pubDate: 2015-04-04
duration: "46:09"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/04/s7e32.mp3"
audioSize: 44306560
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e32-badabing-badaboom/"
featured: false
classic: false
tags: ["windows"]
---
Happy Easter weekend! Today we discussed Windows XP vs Windows 8, Revenge Porn, Acorns, and more. PLUS Cat finally speaks! Click the links below to find out more information on what we talked about, and play the podcast below! If youd like to sponsor our show like Desert Commercial Cleaning, LLC and Perfection Auto Works, \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E33 Apps, apps, and more apps!"
season: 7
episode: 33
pubDate: 2015-04-12
duration: "46:09"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/04/s7e33.mp3"
audioSize: 44306558
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e33-apps-apps-and-more-apps/"
featured: false
classic: false
tags: ["malware"]
---
On todays show we discussed batteries, apps, viruses, and took YOUR calls to answer some questions! Hopefully, you dont become bored and distracted during the computery parts, and turn to some cat videos… oh wait… If YOU would like to become a sponsor of the show, head on over to Patreon to become a sponsor! \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E34 The death of Reddit?"
season: 7
episode: 34
pubDate: 2015-04-19
duration: "45:35"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/04/s7e34.mp3"
audioSize: 43763629
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e34-the-death-of-reddit/"
featured: false
classic: false
tags: []
---
We had some technical difficulties with the site today, so you may not have been able to livestream the show earlier today while it was being broadcast. But, fear not! We have resolved those issues, and you can now listen to the podcast. Today we talked about the government wasting money, all-female dev conferences, and \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E35 While the Gurus away…"
season: 7
episode: 35
pubDate: 2015-04-25
duration: "45:50"
audioUrl: "https://media.blubrry.com/gurushow/gurushow.com/podcasts/2015/04/s7e35.mp3"
audioSize: 44006046
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e35-while-the-gurus-away/"
featured: false
classic: false
tags: []
---
Today we were Mike-less, so we brought in Tara, Howard, the crew, and Cat to talk about some awesome news items and take your calls! Click on the links below to check out what we were talking about on the show, and comment below with your thoughts! Also, weve provided a list of the 5 \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E36 Rock, Paper, Scissors, Lizard, Spock"
season: 7
episode: 36
pubDate: 2015-05-02
duration: "45:59"
audioUrl: "https://media.blubrry.com/gurushow/gurushow.com/podcasts/2015/05/s7e36.mp3"
audioSize: 44143136
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e36-rock-paper-scissors-lizard-spock/"
featured: false
classic: false
tags: []
---
(Picture courtesy xSheldon on iFunny.co (http://ifunny.co/fun/LJ7tXAUw) Today things were pretty much back to normal as Mike regained control of the helm after missing last weeks show. Click the links below for more information on what we talked about during the show, and listen to the podcast at the end of the post. If youd like \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E37 Wearable Tech, Suicide Robots, Warp Drive Nonsense, and much more!"
season: 7
episode: 37
pubDate: 2015-05-09
duration: "45:53"
audioUrl: "https://media.blubrry.com/gurushow/gurushow.com/podcasts/2015/05/s7e37.mp3"
audioSize: 44052857
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e37-wearable-tech-suicide-robots-warp-drive-nonsense-and-much-more/"
featured: false
classic: false
tags: ["android", "apple", "robotics"]
---
This week we were missing Tara, who took the week off to spend some time with her husband. Mike and Cat hold down the fort, discussing everything from smart watches to suicide robots and the nonsense of the EM drive (at least according to the crew). Segment 1 Smart watches: Apple VS Android Call: How can \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E38 Smart Jewelry, Comcassed, GTA V Viruses"
season: 7
episode: 38
pubDate: 2015-05-16
duration: "45:49"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/05/s7e38.mp3"
audioSize: 43983894
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e38-smart-jewelry-comcassed-gta-v-viruses/"
featured: false
classic: false
tags: ["malware"]
---
Tara is back, but Mike is out of town! Howard and Cat help Tara hold down the fort. They discuss Smart Jewelry, a new word to describe poor customer service, viruses in GTA V for PC, and much more. Check it out! Segment 1 Comcassed: the crews adventures with terrible customer service led him to \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E39 The Tech of Big Hero 6, Discussion about intelligence, and more!"
season: 7
episode: 39
pubDate: 2015-05-23
duration: "45:51"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/05/s7e39.mp3"
audioSize: 44017749
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e39-the-tech-of-big-hero-6-discussion-about-intelligence-and-more/"
featured: false
classic: false
tags: []
---
Mikes back in action, joined by Cat and Tara. Together, they discuss the technology of Big Hero 6, how much we all dislike Comcast, and much more. Segment 1 -The technology of Big Hero 6 and how most of it is already real -Call: Popups on websites, AdBlock+ Segment 2 -Call: Toms desktop wont connect \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E40 New dictionary words, Silk Road, Youtubes 10th anniversary, and much more!"
season: 7
episode: 40
pubDate: 2015-05-30
duration: "46:05"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/05/s7e40.mp3"
audioSize: 44241357
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e40/"
featured: false
classic: false
tags: ["android", "google"]
---
Many discussions of much news were afoot at the Computer Guru show today. Mike, Tara, Cat, and Ron weigh in on lots of interesting tech-related news. Segment 1 1700 new words added to the dictionary, mainly focused on internet and technology. Silk Road founder sentenced to life in prison Segment 2 Google announces Android M \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E41 DARPA robots, HOUND demo, Solar Bike Paths, Tweets for Potholes, and much more!"
season: 7
episode: 41
pubDate: 2015-06-06
duration: "45:54"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/06/s7e41.mp3"
audioSize: 44063306
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e41-darpa-robots-hound-demo-solar-bike-paths-tweets-for-potholes-and-much-more/"
featured: false
classic: false
tags: ["robotics", "windows"]
---
Mike, Tara, and Cat talk about robots, virtual assistants, Windows 10, solar bike baths, self driving cars, and much more! Segment 1 DARPA Robotics Challenge 2015 This year, the robots are competing to act as rescue bots. Mike wants the robots to move faster. Well Mike, competitions like this are how robots will start to \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E42 Federal Employees hacked, DARPA robot challenge winners, Toolbar malware, and much more!"
season: 7
episode: 42
pubDate: 2015-06-15
duration: ""
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/06/s7e42.mp3"
audioSize: 5242880
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e42-federal-employees-hacked-darpa-robot-challenge-winners-toolbar-malware-and-much-more/"
featured: false
classic: false
tags: ["malware", "robotics", "security", "windows"]
---
Segment 1 OPM Hacking Investigation continues  All federal employees, past and present, are at risk of their information being stolen. Call Daves sisters Windows 7 Dell Laptop wont connect to the interwebs. Wat do? Segment 2 Call Tom is having printer trouble 🙁 DARPA robotics challenge winners! Also, lots of robot fails: Segment 3 \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E43 LastPass Hacked, AT&T Fined, Twitter Pivots, and E3 news"
season: 7
episode: 43
pubDate: 2015-06-20
duration: "45:53"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/06/s7e43.mp3"
audioSize: 44054529
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e43-lastpass-hacked-att-fined-twitter-pivots-and-e3-news/"
featured: false
classic: false
tags: ["gaming", "security", "twitter"]
---
This week Tara is calling in from LA, where shes attending the E3 gaming conference. Shes got the skinny on all the newest games and gaming news, and Mike and Cat have some awesome tech stories to share. Segment 1 LastPass was hacked this is a big deal if you use it. Change your \[…\]

View File

@@ -0,0 +1,16 @@
---
title: "S7E44 Baseball team gets hacked, Sharing photos online,"
season: 7
episode: 44
pubDate: 2015-06-27
duration: ""
audioUrl: "https://media.blubrry.com/gurushow/gurushow.com/podcasts/2015/06/s7e44.mp3"
audioSize: 5242880
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e44-baseball-team-gets-hacked-sharing-photos-online/"
featured: false
classic: false
tags: ["security", "windows"]
---
Mike, Cat and Tara discuss the latest in Tech news, including a hack in the baseball world, a new Lenovo Windows 8 computer stick, the state of sharing photos online, and much more!

View File

@@ -0,0 +1,16 @@
---
title: "S7E45 Ellen Pao Resigns from Reddit, Lizard Squad Antics, Facebook Icon Makeover, and more!"
season: 7
episode: 45
pubDate: 2015-07-14
duration: "37:51"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/07/s7e45.mp3"
audioSize: 54502989
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e45-ellen-pao-resigns-from-reddit-lizard-squad-antics-facebook-icon-makeover-and-more/"
featured: false
classic: false
tags: ["facebook"]
---
Sorry it took so long to get the podcast up this time guys and gals, the crew was moving over the weekend and didnt have access to the interwebs to make it happen. Hes back in action now though, and heres the latest episode of the Computer Guru show!

View File

@@ -0,0 +1,16 @@
---
title: "S7E46 Attack of the Callers, Windows 10 News, Drone Deliveries, and much more!"
season: 7
episode: 46
pubDate: 2015-07-18
duration: "55:00"
audioUrl: "https://media.blubrry.com/gurushow/gurushow.com/podcasts/2015/07/s7e46.mp3"
audioSize: 52803669
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e46-attack-of-the-callers-windows-10-news-drone-deliveries-and-much-more/"
featured: false
classic: false
tags: ["drones", "windows"]
---
Attack of the callers! Mike and Tara are happy to answer the tech questions of just about everyone in Tucson on this episode of the Computer Guru show. They also talk about Windows 10, drone deliveries, and much more.

View File

@@ -0,0 +1,16 @@
---
title: "S7E47"
season: 7
episode: 47
pubDate: 2015-07-25
duration: "37:34"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/07/s7e47.mp3"
audioSize: 36066495
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e47/"
featured: false
classic: false
tags: []
---

View File

@@ -0,0 +1,16 @@
---
title: "S7E48 Windows 10 Launch Date Extravaganza! Plus Dont Starve, GPS, and more"
season: 7
episode: 48
pubDate: 2015-08-02
duration: "54:49"
audioUrl: "https://media.blubrry.com/gurushow/gurushow.com/podcasts/2015/08/s7e48.mp3"
audioSize: 52623528
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e48-windows-10-launch-date-extravaganza-plus-dont-starve-gps-and-more/"
featured: false
classic: false
tags: ["windows"]
---
Thats right, folks! Windows 10 is officially a thing. You can totally get it for free, too, if youve already purchased Windows 7. But should you? Listen to this episode of the Computer Guru Radio Show to find out.

View File

@@ -0,0 +1,16 @@
---
title: "S7E49 Verizon ditches contracts, the poor hitchhiking robot is dead, Mike answers your questions + more!"
season: 7
episode: 49
pubDate: 2015-08-08
duration: "44:19"
audioUrl: "https://media.blubrry.com/gurushow/www.gurushow.com/podcasts/2015/08/s7e49.mp3"
audioSize: 42539009
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e49-verizon-ditches-contracts-the-poor-hitchhiking-robot-is-dead-mike-answers-your-questions-more/"
featured: false
classic: false
tags: ["robotics"]
---
This week Mike and Tara discuss the latest tech news, which includes Verizon deciding to ditch contracts, the death of the hitchhiking robot, and more.

View File

@@ -0,0 +1,16 @@
---
title: "S7E50 Samsung 16TB SSD, CIA leaks “apology” letter, Windows 10 Parental Controls, and much more!"
season: 7
episode: 50
pubDate: 2015-08-15
duration: "43:53"
audioUrl: "https://media.blubrry.com/gurushow/gurushow.com/podcasts/2015/08/s7e50.mp3"
audioSize: 42134425
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e50-samsung-16tb-ssd-cia-leaks-apology-letter-windows-10-parental-controls-and-much-more/"
featured: false
classic: false
tags: ["samsung", "windows"]
---
This week, Mike, Cat and Tara talk about everything from the largest drive ever made (Samsungs 16TB SSD), news about a CIA leak, Windows 10 Parental Control features and limitations, and more. They also take your calls and answer your questions. GuruShow post on Parental Controls Guide to setting up Parental Controls in Windows 10

View File

@@ -0,0 +1,16 @@
---
title: "S7E51 Season 7 Finale at the new Computer Guru location, + tech news and your questions"
season: 7
episode: 51
pubDate: 2015-08-22
duration: "54:30"
audioUrl: "https://media.blubrry.com/gurushow/gurushow.com/podcasts/2015/08/s7e51.mp3"
audioSize: 52328556
episodeType: "full"
originalUrl: "https://www.gurushow.com/podcast-s7e51-season-7-finale-at-the-new-computer-guru-location-tech-news-and-your-questions/"
featured: false
classic: false
tags: []
---
This week we celebrate not only the end of season 7 of the Computer Guru Show, but also the official opening of the new Computer Guru location in Tucson. Mike, Tara and Cat do the show on-location at our new East Side location, while discussing tech news and taking questions live from those of you \[…\]

Some files were not shown because too many files have changed in this diff Show More