mirror of
https://github.com/5rahim/seanime
synced 2026-05-02 22:42:11 +02:00
@@ -5,6 +5,8 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
neturl "net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
@@ -59,9 +61,18 @@ func (api *MpcHc) Execute(command int, data map[string]interface{}) (string, err
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func escapeInput(input string) string {
|
||||
if strings.HasPrefix(input, "http") {
|
||||
return neturl.QueryEscape(input)
|
||||
} else {
|
||||
input = filepath.FromSlash(input)
|
||||
return strings.ReplaceAll(neturl.QueryEscape(input), "+", "%20")
|
||||
}
|
||||
}
|
||||
|
||||
// OpenAndPlay opens a video file in MPC.
|
||||
func (api *MpcHc) OpenAndPlay(filePath string) (string, error) {
|
||||
url := fmt.Sprintf("%s/browser.html?path=%s", api.url(), neturl.QueryEscape(filePath))
|
||||
url := fmt.Sprintf("%s/browser.html?path=%s", api.url(), escapeInput(filePath))
|
||||
api.Logger.Trace().Str("url", url).Msg("mpc hc: Opening and playing")
|
||||
|
||||
response, err := http.Get(url)
|
||||
|
||||
@@ -196,6 +196,15 @@ func (vlc *VLC) ToggleFullscreen() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func escapeInput(input string) string {
|
||||
if strings.HasPrefix(input, "http") {
|
||||
return url.QueryEscape(input)
|
||||
} else {
|
||||
input = filepath.FromSlash(input)
|
||||
return strings.ReplaceAll(url.QueryEscape(input), "+", "%20")
|
||||
}
|
||||
}
|
||||
|
||||
// AddAndPlay adds a URI to the playlist and starts playback.
|
||||
// The option field is optional and can have the values: noaudio, novideo
|
||||
func (vlc *VLC) AddAndPlay(uri string, option ...string) error {
|
||||
@@ -203,10 +212,7 @@ func (vlc *VLC) AddAndPlay(uri string, option ...string) error {
|
||||
if len(option) > 1 {
|
||||
return errors.New("please provide only one option")
|
||||
}
|
||||
urlSegment := "/requests/status.json?command=in_play&input=" + url.QueryEscape(filepath.FromSlash(uri))
|
||||
if strings.HasPrefix(uri, "http") {
|
||||
urlSegment = "/requests/status.json?command=in_play&input=" + url.QueryEscape(uri)
|
||||
}
|
||||
urlSegment := "/requests/status.json?command=in_play&input=" + escapeInput(uri)
|
||||
if len(option) == 1 {
|
||||
if (option[0] != "noaudio") && (option[0] != "novideo") {
|
||||
return errors.New("invalid option")
|
||||
@@ -219,13 +225,13 @@ func (vlc *VLC) AddAndPlay(uri string, option ...string) error {
|
||||
|
||||
// Add adds a URI to the playlist
|
||||
func (vlc *VLC) Add(uri string) (err error) {
|
||||
_, err = vlc.RequestMaker("/requests/status.json?command=in_enqueue&input=" + url.QueryEscape(uri))
|
||||
_, err = vlc.RequestMaker("/requests/status.json?command=in_enqueue&input=" + escapeInput(uri))
|
||||
return
|
||||
}
|
||||
|
||||
// AddSubtitle adds a subtitle from URI to currently playing file
|
||||
func (vlc *VLC) AddSubtitle(uri string) (err error) {
|
||||
_, err = vlc.RequestMaker("/requests/status.json?command=addsubtitle&val=" + url.QueryEscape(uri))
|
||||
_, err = vlc.RequestMaker("/requests/status.json?command=addsubtitle&val=" + escapeInput(uri))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -302,7 +308,7 @@ func (vlc *VLC) Volume(val string) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// SeekTo seeks to <val>
|
||||
// Seek seeks to <val>
|
||||
//
|
||||
// Allowed values are of the form:
|
||||
// [+ or -][<int><H or h>:][<int><M or m or '>:][<int><nothing or S or s or ">]
|
||||
@@ -312,7 +318,7 @@ func (vlc *VLC) Volume(val string) (err error) {
|
||||
// 1000 -> seek to the 1000th second
|
||||
// +1H:2M -> seek 1 hour and 2 minutes forward
|
||||
// -10% -> seek 10% back
|
||||
func (vlc *VLC) SeekTo(val string) (err error) {
|
||||
func (vlc *VLC) Seek(val string) (err error) {
|
||||
_, err = vlc.RequestMaker("/requests/status.json?command=seek&val=" + val)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -201,6 +201,20 @@ func VideoProxy(c echo.Context) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
allAlternatives := masterPl.GetAllAlternatives()
|
||||
for _, alternative := range allAlternatives {
|
||||
if alternative != nil && alternative.URI != "" {
|
||||
if !isAlreadyProxied(alternative.URI) {
|
||||
alternativeURI := alternative.URI
|
||||
if !strings.HasPrefix(alternative.URI, "http") {
|
||||
alternativeURI = resolveURL(baseURL, alternative.URI)
|
||||
}
|
||||
alternative.URI = rewriteProxyURL(alternativeURI, headerMap)
|
||||
needsRewrite = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite session key URIs
|
||||
for i, sessionKey := range masterPl.SessionKeys {
|
||||
if sessionKey.URI != "" {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"productName": "Seanime Desktop",
|
||||
"version": "2.9.7",
|
||||
"version": "2.9.8",
|
||||
"identifier": "app.seanime.desktop",
|
||||
"build": {
|
||||
"frontendDist": "../../web-desktop",
|
||||
|
||||
@@ -278,7 +278,10 @@ export function AnimeEntryPage() {
|
||||
>
|
||||
<div className="h-10 lg:h-0" />
|
||||
<div className="space-y-4" data-anime-entry-page-online-streaming-view-content>
|
||||
<div className="absolute right-0 top-[-3rem]" data-anime-entry-page-online-streaming-view-content-title-container>
|
||||
<div
|
||||
className="absolute right-0 top-[-0.5rem] lg:top-[-3rem]"
|
||||
data-anime-entry-page-online-streaming-view-content-title-container
|
||||
>
|
||||
<h2 className="text-xl lg:text-3xl flex items-center gap-3">Online streaming</h2>
|
||||
</div>
|
||||
<OnlinestreamPage
|
||||
|
||||
@@ -121,9 +121,9 @@ export const TorrentPreviewList = React.memo((
|
||||
<TorrentSeedersBadge seeders={item.torrent.seeders} />
|
||||
{!!item.torrent.size && <p className="text-gray-300 text-sm flex items-center gap-1">
|
||||
{item.torrent.formattedSize}</p>}
|
||||
<p className="text-[--muted] text-sm flex items-center gap-1">
|
||||
{item.torrent.date && <p className="text-[--muted] text-sm flex items-center gap-1">
|
||||
<BiCalendarAlt /> {formatDistanceToNowSafe(item.torrent.date)}
|
||||
</p>
|
||||
</p>}
|
||||
</div>
|
||||
<TorrentParsedMetadata metadata={torrentMetadata?.[item.torrent.infoHash!]} />
|
||||
</TorrentPreviewItem>
|
||||
|
||||
@@ -163,9 +163,9 @@ export const TorrentTable = memo((
|
||||
<TorrentSeedersBadge seeders={torrent.seeders} />
|
||||
{!!torrent.size && <p className="text-gray-300 text-sm flex items-center gap-1">
|
||||
{torrent.formattedSize}</p>}
|
||||
<p className="text-[--muted] text-sm flex items-center gap-1">
|
||||
{torrent.date && <p className="text-[--muted] text-sm flex items-center gap-1">
|
||||
<BiCalendarAlt /> {formatDistanceToNowSafe(torrent.date)}
|
||||
</p>
|
||||
</p>}
|
||||
</div>
|
||||
<TorrentParsedMetadata metadata={torrentMetadata?.[torrent.infoHash!]} />
|
||||
</TorrentPreviewItem>
|
||||
|
||||
@@ -477,9 +477,9 @@ function TorrentSearchTorrentStreamBatchHistory({ entry, type, debridInstantAvai
|
||||
<TorrentSeedersBadge seeders={batchHistory?.torrent?.seeders} />
|
||||
{!!batchHistory?.torrent?.size && <p className="text-gray-300 text-sm flex items-center gap-1">
|
||||
{batchHistory?.torrent?.formattedSize}</p>}
|
||||
<p className="text-[--muted] text-sm flex items-center gap-1">
|
||||
{batchHistory?.torrent?.date && <p className="text-[--muted] text-sm flex items-center gap-1">
|
||||
<BiCalendarAlt /> {formatDistanceToNowSafe(batchHistory?.torrent?.date)}
|
||||
</p>
|
||||
</p>}
|
||||
</div>
|
||||
</TorrentPreviewItem>
|
||||
</AppLayoutStack>
|
||||
|
||||
@@ -80,10 +80,6 @@ export function useHandleMangaCollection() {
|
||||
}, 500)
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => {
|
||||
setUnreadOnly(params.unreadOnly)
|
||||
}, [params.unreadOnly])
|
||||
|
||||
// Reset params when data changes
|
||||
React.useEffect(() => {
|
||||
if (!!data) {
|
||||
@@ -92,6 +88,13 @@ export function useHandleMangaCollection() {
|
||||
}
|
||||
}, [data, unreadOnly])
|
||||
|
||||
// Sync unreadOnly to persistent storage when params change
|
||||
React.useEffect(() => {
|
||||
if (mountedRef.current && params.unreadOnly !== unreadOnly) {
|
||||
setUnreadOnly(params.unreadOnly)
|
||||
}
|
||||
}, [params.unreadOnly])
|
||||
|
||||
const genres = React.useMemo(() => {
|
||||
const genresSet = new Set<string>()
|
||||
data?.lists?.forEach(l => {
|
||||
|
||||
Reference in New Issue
Block a user