Created
January 23, 2025 06:36
-
-
Save sonygod/341dfc990dae8aa45ecf4156dc119a1e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name HeiMuer TV Video Player | |
// @namespace http://tampermonkey.net/ | |
// @version 0.3 | |
// @description Add video player for HeiMuer TV | |
// @author You | |
// @match https://heimuer.tv/index.php/vod/detail/id/* | |
// @grant GM_log | |
// @require https://cdnjs.cloudflare.com/ajax/libs/hls.js/1.4.10/hls.min.js | |
// ==/UserScript== | |
(function () { | |
'use strict'; | |
const processedUrls = new Set(); | |
let currentHls = null; | |
function createVideoPlayer() { | |
// Create main panel | |
const panelDiv = document.createElement('div'); | |
panelDiv.className = 'stui-pannel clearfix'; | |
// Create header | |
const headDiv = document.createElement('div'); | |
headDiv.className = 'stui-pannel__head clearfix'; | |
// Create title | |
const title = document.createElement('h3'); | |
title.className = 'title'; | |
title.textContent = '在线播放'; | |
headDiv.appendChild(title); | |
// Create episode list container | |
const episodeListDiv = document.createElement('div'); | |
episodeListDiv.className = 'episode-list'; | |
episodeListDiv.style.cssText = 'margin: 10px 0; display: flex; flex-wrap: wrap; gap: 5px;'; | |
headDiv.appendChild(episodeListDiv); | |
// Create video wrapper | |
const wrapper = document.createElement('div'); | |
wrapper.className = 'video-wrapper'; | |
wrapper.style.cssText = 'width: 100%; position: relative; aspect-ratio: 16/9;'; | |
// Create video container | |
const container = document.createElement('div'); | |
container.className = 'custom-video-player'; | |
container.style.cssText = 'position: absolute; top: 0; left: 0; width: 100%; height: 100%;'; | |
// Create video element | |
const video = document.createElement('video'); | |
video.controls = true; | |
video.style.cssText = 'width: 100%; height: 100%; object-fit: contain;'; | |
// Assemble DOM | |
container.appendChild(video); | |
wrapper.appendChild(container); | |
panelDiv.appendChild(headDiv); | |
panelDiv.appendChild(wrapper); | |
// Add loading indicator | |
const loadingDiv = document.createElement('div'); | |
loadingDiv.style.cssText = ` | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
background: rgba(0,0,0,0.7); | |
color: white; | |
padding: 10px 20px; | |
border-radius: 4px; | |
display: none; | |
`; | |
loadingDiv.textContent = '加载中...'; | |
container.appendChild(loadingDiv); | |
return { | |
container: panelDiv, | |
episodeList: episodeListDiv, | |
video: video, | |
playUrl: function (url) { | |
loadingDiv.style.display = 'block'; | |
if (currentHls) { | |
currentHls.destroy(); | |
} | |
const tryPlay = () => { | |
video.play().catch(error => { | |
console.log("Retrying autoplay..."); | |
setTimeout(tryPlay, 1000); | |
}); | |
}; | |
if (Hls.isSupported()) { | |
currentHls = new Hls(); | |
currentHls.loadSource(url); | |
currentHls.attachMedia(video); | |
currentHls.on(Hls.Events.MANIFEST_LOADED, () => { | |
console.log("Manifest loaded"); | |
}); | |
currentHls.on(Hls.Events.MANIFEST_PARSED, () => { | |
console.log("Manifest parsed"); | |
loadingDiv.style.display = 'none'; | |
tryPlay(); | |
}); | |
currentHls.on(Hls.Events.ERROR, (event, data) => { | |
console.log("HLS error:", data); | |
loadingDiv.style.display = 'none'; | |
}); | |
} else if (video.canPlayType('application/vnd.apple.mpegurl')) { | |
video.src = url; | |
video.addEventListener('loadedmetadata', () => { | |
loadingDiv.style.display = 'none'; | |
tryPlay(); | |
}); | |
} | |
} | |
}; | |
} | |
function init() { | |
const descDiv = document.getElementById('desc'); | |
if (!descDiv) return; | |
// Parse all episodes | |
const episodes = []; | |
document.querySelectorAll('.hidden-xs').forEach(el => { | |
const text = el.textContent || ''; | |
if (text.includes('m3u8.heimuertv.com')) { | |
const [number, fullUrl] = text.split('$'); | |
const url = fullUrl.match(/https:\/\/m3u8\.heimuertv\.com\/play\/[\w\.]+\.m3u8/)?.[0]; | |
if (url) { | |
console.log('Episode number:', number); // Debug log | |
episodes.push({ number, url }); | |
} | |
} | |
}); | |
if (episodes.length > 0) { | |
// Sort episodes | |
episodes.sort((a, b) => Number(a.number) - Number(b.number)); | |
// Create player | |
const player = createVideoPlayer(); | |
descDiv.parentNode.insertBefore(player.container, descDiv); | |
// Create episode buttons | |
episodes.forEach((episode, idx) => { | |
const btn = document.createElement('button'); | |
const episodeNum = (idx + 1).toString().padStart(2, '0'); // Convert index to episode number | |
btn.textContent = `第${episodeNum}集`; | |
btn.style.cssText = ` | |
padding: 5px 15px; | |
border: 1px solid ${idx === 0 ? '#007bff' : '#ddd'}; | |
background: ${idx === 0 ? '#007bff' : '#fff'}; | |
color: ${idx === 0 ? '#fff' : '#333'}; | |
border-radius: 3px; | |
cursor: pointer; | |
`; | |
btn.onclick = () => { | |
// Update button styles | |
player.episodeList.querySelectorAll('button').forEach(b => { | |
b.style.background = '#fff'; | |
b.style.color = '#333'; | |
b.style.borderColor = '#ddd'; | |
}); | |
btn.style.background = '#007bff'; | |
btn.style.color = '#fff'; | |
btn.style.borderColor = '#007bff'; | |
// Play episode | |
player.playUrl(episode.url); | |
}; | |
player.episodeList.appendChild(btn); | |
}); | |
// Play first episode | |
// Trigger initial play with slight delay | |
setTimeout(() => { | |
player.playUrl(episodes[0].url); | |
}, 500); | |
} | |
} | |
if (document.readyState === 'loading') { | |
document.addEventListener('DOMContentLoaded', init); | |
} else { | |
init(); | |
} | |
})(); |
Author
sonygod
commented
Jan 23, 2025
•
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment