feat: torrent streaming with hls transcoding (#1)
* feat: add ffmpeg for hls streaming * feat: torrent streaming with hls transcoding - add nyaa.si torrent search client - add streaming service using anacrolix/torrent - add hls transcoding via ffmpeg for browser playback - add watch page with episode selection - add socks5 proxy support via TORRENT_PROXY env - switch to modernc.org/sqlite (pure go, no cgo conflicts) - update dockerfile with ffmpeg
This commit is contained in:
72
internal/templates/episodes.templ
Normal file
72
internal/templates/episodes.templ
Normal file
@@ -0,0 +1,72 @@
|
||||
package templates
|
||||
|
||||
import "mal/internal/jikan"
|
||||
import "fmt"
|
||||
|
||||
// EpisodesList renders a list of episodes for an anime
|
||||
templ EpisodesList(animeID int, animeTitle string, episodes []jikan.Episode, totalEpisodes int) {
|
||||
<section class="episodes-section">
|
||||
<div class="episodes-header">
|
||||
<h3>episodes</h3>
|
||||
if totalEpisodes > 0 {
|
||||
<span class="episode-count">{ fmt.Sprintf("%d episodes", totalEpisodes) }</span>
|
||||
}
|
||||
</div>
|
||||
if len(episodes) == 0 {
|
||||
<p class="no-episodes">no episode data available</p>
|
||||
} else {
|
||||
<div class="episodes-grid">
|
||||
for _, ep := range episodes {
|
||||
@EpisodeCard(animeID, animeTitle, ep)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</section>
|
||||
}
|
||||
|
||||
// EpisodeCard renders a single episode card with play button
|
||||
templ EpisodeCard(animeID int, animeTitle string, ep jikan.Episode) {
|
||||
<div class="episode-card" data-episode={ fmt.Sprintf("%d", ep.MalID) }>
|
||||
<div class="episode-number">{ fmt.Sprintf("%d", ep.MalID) }</div>
|
||||
<div class="episode-info">
|
||||
<div class="episode-title">
|
||||
if ep.Title != "" {
|
||||
{ ep.Title }
|
||||
} else {
|
||||
{ fmt.Sprintf("Episode %d", ep.MalID) }
|
||||
}
|
||||
</div>
|
||||
if ep.Filler {
|
||||
<span class="episode-tag filler">filler</span>
|
||||
}
|
||||
if ep.Recap {
|
||||
<span class="episode-tag recap">recap</span>
|
||||
}
|
||||
</div>
|
||||
<button
|
||||
class="episode-play"
|
||||
hx-get={ string(templ.URL(fmt.Sprintf("/watch/%d/%d", animeID, ep.MalID))) }
|
||||
hx-target="body"
|
||||
hx-push-url="true"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" class="play-icon">
|
||||
<path d="M8 5v14l11-7z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
// EpisodesLoading renders a loading state for episodes
|
||||
templ EpisodesLoading() {
|
||||
<section class="episodes-section">
|
||||
<div class="episodes-header">
|
||||
<h3>episodes</h3>
|
||||
</div>
|
||||
<div class="loading-indicator">
|
||||
<div class="loading-dot"></div>
|
||||
<div class="loading-dot"></div>
|
||||
<div class="loading-dot"></div>
|
||||
<span>loading episodes</span>
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
Reference in New Issue
Block a user