✨ 点击图片换一张(老婆/猫/狗/二次元) ✨
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>PCROCK BLOG</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" media="screen" href="/pcrock/pcrock.css" />
<link rel="shortcut icon" href="favicon.ico" />
<style>
/* 全局样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #404040;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
}
/* 全局容器 */
.page-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
/* ========== 顶部区域:Logo 左,时间+播放器右(时间在上) ========== */
.top-bar {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: 30px;
gap: 20px;
flex-wrap: nowrap;
}
.logo-area {
flex-shrink: 0;
}
.ascii-logo {
overflow-x: auto;
text-align: left;
}
.ascii-logo pre {
font-family: 'DejaVu Sans Mono', 'Courier New', monospace;
font-size: 18px;
color: #FFFFFF;
margin: 0;
display: inline-block;
line-height: 1.2;
}
/* 右侧:时间在上,播放器在下 */
.right-group {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 8px;
flex-shrink: 0;
}
.time-display {
color: #FFFFFF;
font-size: 18px;
white-space: nowrap;
}
.music-player {
display: flex;
align-items: center;
gap: 8px;
}
.music-player button {
background: #404040;
color: white;
border: none;
font-size: 18px;
width: 34px;
height: 34px;
border-radius: 50%;
cursor: pointer;
transition: all 0.2s;
display: inline-flex;
align-items: center;
justify-content: center;
}
.music-player button:hover {
background: #FFCC33;
color: #202020;
}
.music-player .song-name {
font-size: 15px;
color: #FFFFFF;
white-space: nowrap;
}
/* ========== 双栏布局:左侧导航 + 右侧内容 ========== */
.two-columns {
display: flex;
gap: 30px;
flex-wrap: wrap;
}
/* 左侧导航栏 - 固定宽度约5个字 */
.sidebar {
flex: 0 0 20px;
min-width: 20px;
max-width: 20px;
}
.nav-menu {
display: flex;
flex-direction: column;
gap: 10px;
}
.nav-menu a {
color: #DDDDDD;
text-decoration: none;
font-size: 15px;
padding: 4px 0;
transition: all 0.2s;
display: block;
white-space: nowrap;
}
.nav-menu a:hover {
color: #FFCC33;
padding-left: 4px;
}
/* 右侧内容区 - 自动占满剩余宽度 */
.content-area {
flex: 1;
min-width: 260px;
background: rgba(245, 245, 245, 0.98);
border-radius: 16px;
padding: 28px 32px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
/* 嵌入 bg 页面 */
.bg-embed {
margin-bottom: 28px;
width: 100%;
border-radius: 12px;
overflow: hidden;
background: #fff;
box-shadow: 0 1px 4px rgba(0,0,0,0.1);
}
.bg-embed iframe {
width: 100%;
height: 380px;
border: none;
display: block;
}
/* 博客列表 - 类似文件列表 */
.blog-header {
margin: 20px 0 16px 0;
padding-bottom: 8px;
border-bottom: 2px solid #e0e0e0;
}
.blog-header h2 {
font-size: 20px;
color: #202020;
font-weight: 600;
}
.blog-list {
list-style: none;
}
.blog-item {
display: flex;
justify-content: space-between;
align-items: baseline;
padding: 10px 0;
flex-wrap: wrap;
gap: 8px;
}
.blog-title {
font-size: 16px;
color: #2c3e50;
text-decoration: none;
font-weight: 500;
transition: color 0.2s;
}
.blog-title:hover {
color: #FFCC33;
text-decoration: underline;
}
.blog-date {
font-size: 13px;
color: #888;
white-space: nowrap;
}
.no-posts {
text-align: center;
padding: 40px;
color: #999;
}
/* 引用/名言区域 - 一言样式 */
.quote-section {
margin-top: 28px;
padding-top: 20px;
border-top: 1px solid #e0e0e0;
color: #555;
font-style: italic;
font-size: 14px;
text-align: center;
cursor: pointer;
transition: opacity 0.3s;
}
.quote-section:hover {
opacity: 0.8;
}
.quote-text {
display: block;
margin-bottom: 6px;
}
.quote-from {
font-size: 12px;
color: #888;
font-style: normal;
}
.quote-refresh {
font-size: 11px;
color: #aaa;
margin-top: 8px;
display: inline-block;
}
/* 计数器样式 */
.counter-container {
text-align: center;
margin-top: 30px;
padding: 15px 0 5px;
}
.counter-container img {
display: inline-block;
max-width: 100%;
height: auto;
}
/* ========== 响应式 ========== */
@media (max-width: 800px) {
.two-columns {
flex-direction: column;
}
.sidebar {
flex: 0 0 auto;
min-width: auto;
max-width: none;
}
.nav-menu {
flex-direction: row;
flex-wrap: wrap;
gap: 16px;
}
.nav-menu a {
white-space: nowrap;
}
.nav-menu a:hover {
padding-left: 0;
}
.content-area {
padding: 20px;
}
}
@media (max-width: 700px) {
.page-container {
padding: 12px;
}
.ascii-logo pre {
font-size: 12px;
}
.music-player .song-name {
white-space: normal;
max-width: 120px;
font-size: 12px;
}
.time-display {
font-size: 14px;
}
.music-player button {
width: 28px;
height: 28px;
font-size: 14px;
}
.blog-title {
font-size: 14px;
}
.blog-date {
font-size: 11px;
}
}
</style>
</head>
<body>
<div class="page-container">
<!-- 顶部:Logo 左,时间+播放器右(不变) -->
<div class="top-bar">
<div class="logo-area">
<a target="_blank" href="http://pcrock.hkfree.work/">
<div class="ascii-logo">
<pre>
_____ _____ _____ ____ _____ _ __
| _ \ / ____| | __ \ / __ \ / ____| | |/ /
| |_) | | | | |__) | | | | | | | | ' /
| _ / | | | _ / | | | | | | | <
| | | |____ | | \ \ | |__| | | |____ | . \
|_| \_____| |_| \_\ \____/ \_____| |_|\_\
</pre>
</div>
</a>
</div>
<div class="right-group">
<div id="time" class="time-display"></div>
<div class="music-player">
<button id="playBtn">▶</button>
<button id="nextBtn">⏭</button>
<span class="song-name" id="songName">未播放</span>
</div>
</div>
</div>
<!-- 双栏布局 -->
<div class="two-columns">
<!-- 左侧导航 -->
<div class="sidebar">
<div id="navBar" class="nav-menu">
<span style="color:#FFFFFF">加载中...</span>
</div>
</div>
<!-- 右侧内容 -->
<div class="content-area">
<!-- 嵌入 bg 页面 -->
<div class="bg-embed">
<iframe src="https://pcrock99.github.io/bg" title="Bg页面"></iframe>
</div>
<!-- 博客列表 -->
<div class="blog-header">
<h4>📄 文章列表</h4>
</div>
<div id="blogContainer" class="blog-list">
<div class="no-posts">加载文章中...</div>
</div>
<!-- 一言区域 -->
<div class="quote-section" id="quoteBox">
<span class="quote-text" id="quoteText">加载一言中...</span>
<span class="quote-from" id="quoteFrom"></span>
<div class="quote-refresh">⏎ 点击刷新 / 自动更新</div>
</div>
</div>
</div>
<!-- 计数器区域 - 页面最下方 -->
<div class="counter-container">
<img src="https://count.getloli.com/@pcrock-blog?theme=asoul" alt="访问计数器">
</div>
</div>
<!-- 粒子特效脚本 -->
<script type="text/javascript" color="255,255,255" opacity="0.5" zindex="-2" count="180" src="/pcrock/pcrock.js"></script>
<script>
// ========== 一言(Hitokoto)功能 ==========
const localQuotes = [
{ text: "「今天你要是不做,你明天还是得做。」", from: "MengCheng1015" },
{ text: "「代码就像黑暗森林,你永远不知道下一个 bug 在哪里。」", from: "程序员箴言" },
{ text: "「保持好奇,保持愚蠢。」", from: "Stay Hungry, Stay Foolish" },
{ text: "「简单是终极的复杂。」", from: "达·芬奇" },
{ text: "「Talk is cheap. Show me the code.」", from: "Linus Torvalds" },
{ text: "「求知若饥,虚心若愚。」", from: "Steve Jobs" },
{ text: "「不要等待机会,而要创造机会。」", from: "佚名" }
];
let quoteTimer = null;
async function fetchHitokoto() {
try {
const response = await fetch('https://v1.hitokoto.cn/?c=i&c=k&c=h&c=l&c=d&encode=json');
if (!response.ok) throw new Error('API 请求失败');
const data = await response.json();
return {
text: data.hitokoto || data.text,
from: data.from || data.source || "佚名"
};
} catch (error) {
console.warn('一言 API 获取失败,使用本地备选:', error);
const randomIndex = Math.floor(Math.random() * localQuotes.length);
return localQuotes[randomIndex];
}
}
async function updateQuote() {
const quoteTextEl = document.getElementById('quoteText');
const quoteFromEl = document.getElementById('quoteFrom');
quoteTextEl.textContent = '加载中...';
quoteFromEl.textContent = '';
const quote = await fetchHitokoto();
quoteTextEl.textContent = `「${quote.text}」`;
if (quote.from && quote.from !== '佚名') {
quoteFromEl.textContent = `—— ${quote.from}`;
} else if (quote.from === '佚名') {
quoteFromEl.textContent = '';
} else {
quoteFromEl.textContent = `—— ${quote.from}`;
}
}
function startQuoteTimer() {
if (quoteTimer) clearInterval(quoteTimer);
quoteTimer = setInterval(() => {
updateQuote();
}, 2 * 60 * 1000);
}
async function initQuote() {
await updateQuote();
startQuoteTimer();
const quoteBox = document.getElementById('quoteBox');
quoteBox.addEventListener('click', async (e) => {
e.stopPropagation();
await updateQuote();
startQuoteTimer();
});
}
// ========== 导航栏加载 ==========
async function loadNav() {
try {
const response = await fetch('/nav.json');
if (!response.ok) throw new Error('导航加载失败');
const navItems = await response.json();
const navBar = document.getElementById('navBar');
if (navItems.length === 0) {
navBar.innerHTML = '<span style="color:#AAAAAA">暂无导航</span>';
return;
}
navBar.innerHTML = navItems.map(item =>
`<a href="${item.url}" target="${item.target}">${item.name}</a>`
).join('');
} catch (error) {
console.error('导航加载失败', error);
document.getElementById('navBar').innerHTML = '<span style="color:#AAAAAA">导航加载失败</span>';
}
}
// ========== 博客文章加载 ==========
async function loadPosts() {
try {
const response = await fetch('/posts.json');
if (!response.ok) throw new Error('文章加载失败');
const posts = await response.json();
renderPosts(posts);
} catch (error) {
document.getElementById('blogContainer').innerHTML = '<div class="no-posts">暂无文章,请在 posts.json 中添加</div>';
console.error(error);
}
}
function renderPosts(posts) {
const container = document.getElementById('blogContainer');
if (!posts.length) {
container.innerHTML = '<div class="no-posts">暂无文章</div>';
return;
}
posts.sort((a, b) => new Date(b.date) - new Date(a.date));
container.innerHTML = posts.map(post => `
<div class="blog-item">
<a class="blog-title" href="/post.html?id=${post.id}">${post.title}</a>
<span class="blog-date">${post.date}</span>
</div>
`).join('');
}
// ========== 音乐播放器 ==========
// ========== 音乐播放器 - 支持标准 M3U8 格式 ==========
let playlist = [];
let currentIndex = 0;
let audio = new Audio();
let isPlaying = false;
let isSkipping = false;
// 新版的加载函数:直接读取 .m3u8 文件
async function loadPlaylist() {
try {
const response = await fetch('https://pcrock99.github.io/playlist.m3u8');
if (!response.ok) throw new Error('M3U8列表加载失败');
const text = await response.text();
// 解析 M3U8 内容
const lines = text.split(/\r?\n/);
playlist = [];
let currentSong = null;
for (let line of lines) {
line = line.trim();
if (line === '' || line.startsWith('#')) {
// 如果是 #EXTINF 行,提取歌名
if (line.startsWith('#EXTINF:')) {
// 格式: #EXTINF:时长,歌名
const match = line.match(/#EXTINF:.*?,(.+)$/);
if (match && match[1]) {
currentSong = { name: match[1].trim(), url: '' };
}
}
continue;
}
// 非注释行且不是空行,应该是歌曲 URL
if (currentSong && line) {
currentSong.url = line;
playlist.push(currentSong);
currentSong = null;
}
}
if (playlist.length === 0) {
document.getElementById('songName').innerText = '无歌曲';
return;
}
// 随机选一首开始
currentIndex = Math.floor(Math.random() * playlist.length);
tryLoadSong(currentIndex);
} catch (error) {
console.error('播放列表加载失败', error);
document.getElementById('songName').innerText = '列表加载失败';
}
}
function isLinkReachable(url, timeout = 4000) {
return new Promise((resolve) => {
const controller = new AbortController();
const timer = setTimeout(() => {
controller.abort();
resolve(false);
}, timeout);
fetch(url, {
method: 'HEAD',
signal: controller.signal,
mode: 'no-cors'
})
.then(() => {
clearTimeout(timer);
resolve(true);
})
.catch(() => {
clearTimeout(timer);
resolve(false);
});
});
}
async function tryLoadSong(index) {
if (isSkipping) return;
if (playlist.length === 0) return;
isSkipping = true;
if (index >= playlist.length) index = 0;
const song = playlist[index];
document.getElementById('songName').innerText = '检测中...';
const isReachable = await isLinkReachable(song.url, 4000);
if (!isReachable) {
console.log(`失效或超时,自动跳过: ${song.name}`);
let newIndex = Math.floor(Math.random() * playlist.length);
currentIndex = newIndex;
isSkipping = false;
tryLoadSong(currentIndex);
return;
}
document.getElementById('songName').innerText = song.name;
audio.src = song.url;
isSkipping = false;
if (isPlaying) {
const playPromise = audio.play();
if (playPromise !== undefined) {
playPromise.catch(() => nextSong(true));
}
}
}
function playPause() {
if (playlist.length === 0) return;
if (isPlaying) {
audio.pause();
document.getElementById('playBtn').innerHTML = '▶';
} else {
audio.play().catch(() => nextSong(true));
document.getElementById('playBtn').innerHTML = '⏸';
}
isPlaying = !isPlaying;
}
function nextSong(autoSkip = false) {
if (isSkipping && !autoSkip) return;
if (playlist.length === 0) return;
let newIndex = Math.floor(Math.random() * playlist.length);
currentIndex = newIndex;
tryLoadSong(currentIndex);
}
audio.addEventListener('ended', () => nextSong(true));
audio.addEventListener('error', () => nextSong(true));
document.getElementById('playBtn').onclick = playPause;
document.getElementById('nextBtn').onclick = () => nextSong(false);
// 初始化所有
loadNav();
loadPosts();
loadPlaylist();
initQuote();
</script>
<script>
setInterval("time.innerHTML = new Date().toLocaleString('chinese',{hour12:false});",1000);
</script>
<script>
document.oncontextmenu = new Function("event.returnValue=false;");
document.onselectstart = new Function("event.returnValue=false;");
</script>
</body>
</html>