(cherry picked from commit a0f86e4ecd)
This commit is contained in:
5rahim
2025-08-29 13:57:07 +00:00
parent 5c2dca941f
commit 345247b61b
9 changed files with 58 additions and 21 deletions

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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 != "" {

View File

@@ -1,6 +1,6 @@
{
"productName": "Seanime Desktop",
"version": "2.9.7",
"version": "2.9.8",
"identifier": "app.seanime.desktop",
"build": {
"frontendDist": "../../web-desktop",

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 => {