fixing mfc download
This commit is contained in:
parent
eeda6976ec
commit
125a3018ae
@ -1,2 +1,2 @@
|
|||||||
HTTPS_ENABLED=1
|
HTTPS_ENABLED=0
|
||||||
AUTH_RP_ORIGINS=https://l14pbbk95100006.tegdssd.de:9999,https://l14pbbk95100006.tegdssd.de:5173,https://localhost:9999,https://127.0.0.1:9999,https://10.0.1.25:9999,http://localhost:5173,http://127.0.0.1:5173,http://10.0.1.25:5173
|
AUTH_RP_ORIGINS=https://l14pbbk95100006.tegdssd.de:9999,https://l14pbbk95100006.tegdssd.de:5173,https://localhost:9999,https://127.0.0.1:9999,https://10.0.1.25:9999,http://localhost:5173,http://127.0.0.1:5173,http://10.0.1.25:5173
|
||||||
Binary file not shown.
@ -1,8 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
// backend\cookies_crypto.go
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
// backend\cors.go
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@ -65,6 +65,8 @@ type RecordJob struct {
|
|||||||
VideoHeight int `json:"videoHeight,omitempty"`
|
VideoHeight int `json:"videoHeight,omitempty"`
|
||||||
FPS float64 `json:"fps,omitempty"`
|
FPS float64 `json:"fps,omitempty"`
|
||||||
Meta *videoMeta `json:"meta,omitempty"`
|
Meta *videoMeta `json:"meta,omitempty"`
|
||||||
|
ModelImageURL string `json:"modelImageUrl,omitempty"`
|
||||||
|
Avatar string `json:"avatar,omitempty"`
|
||||||
|
|
||||||
Hidden bool `json:"-"`
|
Hidden bool `json:"-"`
|
||||||
|
|
||||||
@ -119,6 +121,8 @@ type jobEventSnapshot struct {
|
|||||||
PreviewState string
|
PreviewState string
|
||||||
PostWorkKey string
|
PostWorkKey string
|
||||||
PostWork *PostWorkKeyStatus
|
PostWork *PostWorkKeyStatus
|
||||||
|
ModelImageURL string
|
||||||
|
Avatar string
|
||||||
}
|
}
|
||||||
|
|
||||||
type dummyResponseWriter struct {
|
type dummyResponseWriter struct {
|
||||||
@ -178,6 +182,8 @@ func publishJobUpsertSnapshot(s jobEventSnapshot) {
|
|||||||
"previewState": s.PreviewState,
|
"previewState": s.PreviewState,
|
||||||
"postWorkKey": s.PostWorkKey,
|
"postWorkKey": s.PostWorkKey,
|
||||||
"ts": time.Now().UnixMilli(),
|
"ts": time.Now().UnixMilli(),
|
||||||
|
"modelImageUrl": s.ModelImageURL,
|
||||||
|
"avatar": s.Avatar,
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.PostWork != nil {
|
if s.PostWork != nil {
|
||||||
@ -232,6 +238,8 @@ func publishJobSnapshot(jobID string) bool {
|
|||||||
PreviewState: job.PreviewState,
|
PreviewState: job.PreviewState,
|
||||||
PostWorkKey: job.PostWorkKey,
|
PostWorkKey: job.PostWorkKey,
|
||||||
PostWork: pw,
|
PostWork: pw,
|
||||||
|
ModelImageURL: job.ModelImageURL,
|
||||||
|
Avatar: job.Avatar,
|
||||||
}
|
}
|
||||||
jobsMu.RUnlock()
|
jobsMu.RUnlock()
|
||||||
|
|
||||||
|
|||||||
@ -147,7 +147,7 @@ func getWantedResolutionPlaylistWithHeaders(
|
|||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, appErrorf("HTTP %d beim Abruf der m3u8", resp.StatusCode)
|
return nil, appErrorf("HTTP %d beim Abruf der m3u8: %s", resp.StatusCode, playlistURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
rawBody, err := io.ReadAll(resp.Body)
|
rawBody, err := io.ReadAll(resp.Body)
|
||||||
|
|||||||
@ -3,18 +3,16 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
|
||||||
"github.com/grafov/m3u8"
|
"github.com/grafov/m3u8"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -38,18 +36,31 @@ func RecordStreamMFC(
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
st, err := mfc.GetStatus()
|
st, err := mfc.GetStatus(ctx, hc.userAgent)
|
||||||
if err == nil && st == StatusPublic {
|
if err == nil && st == StatusPublic {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Now().After(deadline) {
|
if time.Now().After(deadline) {
|
||||||
|
if err != nil {
|
||||||
|
return appErrorf("mfc status: %w", err)
|
||||||
|
}
|
||||||
return context.DeadlineExceeded
|
return context.DeadlineExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if job != nil && strings.TrimSpace(mfc.AvatarURL) != "" {
|
||||||
|
jobsMu.Lock()
|
||||||
|
job.ModelImageURL = strings.TrimSpace(mfc.AvatarURL)
|
||||||
|
job.Avatar = strings.TrimSpace(mfc.AvatarURL)
|
||||||
|
job.PreviewState = "public"
|
||||||
|
jobsMu.Unlock()
|
||||||
|
|
||||||
|
_ = publishJobSnapshot(job.ID)
|
||||||
|
}
|
||||||
|
|
||||||
// ✅ erst jetzt die Video URL holen (weil public)
|
// ✅ erst jetzt die Video URL holen (weil public)
|
||||||
stream, err := mfc.GetSelectedStream(ctx, false, hc.userAgent)
|
stream, err := mfc.GetSelectedStream(ctx, false, hc.userAgent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -70,20 +81,78 @@ func RecordStreamMFC(
|
|||||||
job.PreviewM3U8 = previewURL
|
job.PreviewM3U8 = previewURL
|
||||||
job.PreviewCookie = ""
|
job.PreviewCookie = ""
|
||||||
job.PreviewUA = hc.userAgent
|
job.PreviewUA = hc.userAgent
|
||||||
|
|
||||||
|
if strings.TrimSpace(mfc.AvatarURL) != "" {
|
||||||
|
job.ModelImageURL = strings.TrimSpace(mfc.AvatarURL)
|
||||||
|
job.Avatar = strings.TrimSpace(mfc.AvatarURL)
|
||||||
|
job.PreviewState = "public"
|
||||||
|
}
|
||||||
|
|
||||||
jobsMu.Unlock()
|
jobsMu.Unlock()
|
||||||
|
|
||||||
|
_ = publishJobSnapshot(job.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if job != nil {
|
if job != nil {
|
||||||
_ = publishJob(job.ID)
|
_ = publishJob(job.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleM3U8Mode(ctx, stream, outputPath, job, "", hc.userAgent, mfc.GetWebsiteURL())
|
return recordHLSPlaylistSegments(ctx, stream, outputPath, job, "", hc.userAgent, mfc.GetWebsiteURL())
|
||||||
}
|
}
|
||||||
|
|
||||||
type MyFreeCams struct {
|
type MyFreeCams struct {
|
||||||
Username string
|
Username string
|
||||||
Attrs map[string]string
|
Attrs map[string]string
|
||||||
VideoURL string
|
VideoURL string
|
||||||
|
|
||||||
|
ModelID string
|
||||||
|
ServerID string
|
||||||
|
Phase string
|
||||||
|
AvatarURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
type mfcUsernameLookupResp struct {
|
||||||
|
Result struct {
|
||||||
|
User struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Avatar string `json:"avatar"`
|
||||||
|
|
||||||
|
Sessions []struct {
|
||||||
|
ServerName string `json:"server_name"`
|
||||||
|
Phase string `json:"phase"`
|
||||||
|
} `json:"sessions"`
|
||||||
|
} `json:"user"`
|
||||||
|
} `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func mfcDigitsOnly(s string) string {
|
||||||
|
var b strings.Builder
|
||||||
|
for _, r := range strings.TrimSpace(s) {
|
||||||
|
if r >= '0' && r <= '9' {
|
||||||
|
b.WriteRune(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeMFCPhase(phase string) string {
|
||||||
|
phase = strings.TrimSpace(phase)
|
||||||
|
|
||||||
|
// API liefert meistens schon "a_" oder "".
|
||||||
|
if phase == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if phase == "a" {
|
||||||
|
return "a_"
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(phase, "a") {
|
||||||
|
return "a_"
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(phase, "_") {
|
||||||
|
return phase + "_"
|
||||||
|
}
|
||||||
|
return phase
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMyFreeCams(username string) *MyFreeCams {
|
func NewMyFreeCams(username string) *MyFreeCams {
|
||||||
@ -97,145 +166,197 @@ func (m *MyFreeCams) GetWebsiteURL() string {
|
|||||||
return "https://www.myfreecams.com/#" + m.Username
|
return "https://www.myfreecams.com/#" + m.Username
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func uniqueStrings(in []string) []string {
|
||||||
|
seen := map[string]struct{}{}
|
||||||
|
out := make([]string, 0, len(in))
|
||||||
|
|
||||||
|
for _, s := range in {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
if s == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := seen[s]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[s] = struct{}{}
|
||||||
|
out = append(out, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyFreeCams) GetVideoURLCandidates() ([]string, error) {
|
||||||
|
serverID := strings.TrimSpace(m.ServerID)
|
||||||
|
modelID := strings.TrimSpace(m.ModelID)
|
||||||
|
phase := normalizeMFCPhase(m.Phase)
|
||||||
|
|
||||||
|
if serverID == "" || modelID == "" {
|
||||||
|
return nil, errors.New("mfc: lookup daten fehlen; GetStatus() wurde nicht erfolgreich ausgeführt")
|
||||||
|
}
|
||||||
|
|
||||||
|
streamName := phase + modelID
|
||||||
|
|
||||||
|
candidates := []string{
|
||||||
|
fmt.Sprintf(
|
||||||
|
"https://edgevideo.myfreecams.com/llhls/NxServer/%s/ngrp:mfc_%s.f4v_cmaf/playlist.m3u8?nc=%d&v=1.97.23",
|
||||||
|
serverID,
|
||||||
|
streamName,
|
||||||
|
time.Now().UnixNano(),
|
||||||
|
),
|
||||||
|
|
||||||
|
fmt.Sprintf(
|
||||||
|
"https://edgevideo.myfreecams.com/llhls/NxServer/%s/ngrp:mfc_%s.f4v_cmaf/playlist.m3u8",
|
||||||
|
serverID,
|
||||||
|
streamName,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
return uniqueStrings(candidates), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MyFreeCams) GetVideoURL(refresh bool) (string, error) {
|
func (m *MyFreeCams) GetVideoURL(refresh bool) (string, error) {
|
||||||
if !refresh && m.VideoURL != "" {
|
if !refresh && strings.TrimSpace(m.VideoURL) != "" {
|
||||||
return m.VideoURL, nil
|
return m.VideoURL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prüfen, ob alle benötigten Attribute vorhanden sind
|
candidates, err := m.GetVideoURLCandidates()
|
||||||
if _, ok := m.Attrs["data-cam-preview-model-id-value"]; !ok {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
sid := m.Attrs["data-cam-preview-server-id-value"]
|
|
||||||
midBase := m.Attrs["data-cam-preview-model-id-value"]
|
|
||||||
isWzobs := strings.ToLower(m.Attrs["data-cam-preview-is-wzobs-value"]) == "true"
|
|
||||||
|
|
||||||
midInt, err := strconv.Atoi(midBase)
|
|
||||||
if err != nil {
|
|
||||||
return "", appErrorf("model-id parse error: %w", err)
|
|
||||||
}
|
|
||||||
mid := 100000000 + midInt
|
|
||||||
a := ""
|
|
||||||
if isWzobs {
|
|
||||||
a = "a_"
|
|
||||||
}
|
|
||||||
|
|
||||||
playlistURL := fmt.Sprintf(
|
|
||||||
"https://previews.myfreecams.com/hls/NxServer/%s/ngrp:mfc_%s%d.f4v_mobile_mhp1080_previewurl/playlist.m3u8",
|
|
||||||
sid, a, mid,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Validieren (HTTP 200) & ggf. auf gewünschte Auflösung verlinken
|
|
||||||
u, err := getWantedResolutionPlaylist(playlistURL)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
m.VideoURL = u
|
if len(candidates) == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m.VideoURL = candidates[0]
|
||||||
return m.VideoURL, nil
|
return m.VideoURL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MyFreeCams) GetSelectedStream(ctx context.Context, refresh bool, userAgent string) (*selectedHLSStream, error) {
|
func (m *MyFreeCams) GetSelectedStream(ctx context.Context, refresh bool, userAgent string) (*selectedHLSStream, error) {
|
||||||
m3u8URL, err := m.GetVideoURL(refresh)
|
candidates, err := m.GetVideoURLCandidates()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(m3u8URL) == "" {
|
if len(candidates) == 0 {
|
||||||
return nil, errors.New("keine m3u8 URL gefunden")
|
return nil, errors.New("keine mfc m3u8 kandidaten gefunden")
|
||||||
}
|
}
|
||||||
|
|
||||||
return getWantedResolutionPlaylistWithHeaders(
|
var lastErr error
|
||||||
|
tried := make([]string, 0, len(candidates))
|
||||||
|
|
||||||
|
for _, candidate := range candidates {
|
||||||
|
tried = append(tried, candidate)
|
||||||
|
|
||||||
|
stream, err := getWantedResolutionPlaylistWithHeaders(
|
||||||
ctx,
|
ctx,
|
||||||
m3u8URL,
|
candidate,
|
||||||
"", // MFC normalerweise ohne Cookies
|
"",
|
||||||
userAgent,
|
userAgent,
|
||||||
m.GetWebsiteURL(),
|
m.GetWebsiteURL(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if err == nil && stream != nil && strings.TrimSpace(stream.VideoURL) != "" {
|
||||||
|
m.VideoURL = candidate
|
||||||
|
appLogln("✅ mfc playlist ok:", candidate)
|
||||||
|
appLogln("✅ mfc video playlist:", stream.VideoURL)
|
||||||
|
return stream, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
lastErr = err
|
||||||
|
appLogln("⚠️ mfc playlist candidate failed:", candidate, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastErr != nil {
|
||||||
|
return nil, appErrorf(
|
||||||
|
"keine mfc m3u8 abrufbar; letzter fehler: %w; getestete urls: %s",
|
||||||
|
lastErr,
|
||||||
|
strings.Join(tried, " | "),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, appErrorf("keine mfc m3u8 abrufbar; getestete urls: %s", strings.Join(tried, " | "))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MyFreeCams) GetStatus() (Status, error) {
|
func (m *MyFreeCams) GetStatus(ctx context.Context, userAgent string) (Status, error) {
|
||||||
// 1) share-Seite prüfen (existiert/nicht existiert)
|
username := strings.TrimSpace(m.Username)
|
||||||
shareURL := "https://share.myfreecams.com/" + m.Username
|
if username == "" {
|
||||||
resp, err := http.Get(shareURL)
|
return StatusUnknown, errors.New("mfc: username fehlt")
|
||||||
|
}
|
||||||
|
|
||||||
|
lookupURL := "https://api-edge.myfreecams.com/usernameLookup/" + url.PathEscape(username)
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, lookupURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return StatusUnknown, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ua := strings.TrimSpace(userAgent); ua != "" {
|
||||||
|
req.Header.Set("User-Agent", ua)
|
||||||
|
}
|
||||||
|
req.Header.Set("Accept", "application/json")
|
||||||
|
req.Header.Set("Referer", "https://www.myfreecams.com/#"+username)
|
||||||
|
req.Header.Set("Origin", "https://www.myfreecams.com")
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return StatusUnknown, err
|
return StatusUnknown, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode == 404 {
|
if resp.StatusCode == http.StatusNotFound {
|
||||||
return StatusNotExist, nil
|
return StatusNotExist, nil
|
||||||
}
|
}
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return StatusUnknown, appErrorf("HTTP %d", resp.StatusCode)
|
return StatusUnknown, appErrorf("mfc usernameLookup HTTP %d: %s", resp.StatusCode, lookupURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// wir brauchen sowohl Bytes (für Suche) als auch Reader (für HTML)
|
var data mfcUsernameLookupResp
|
||||||
bodyBytes, err := io.ReadAll(resp.Body)
|
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||||
if err != nil {
|
return StatusUnknown, appErrorf("mfc usernameLookup json: %w", err)
|
||||||
return StatusUnknown, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) „tracking.php?“ suchen und prüfen, ob model_id vorhanden ist
|
if data.Result.User.ID <= 0 {
|
||||||
start := bytes.Index(bodyBytes, []byte("https://www.myfreecams.com/php/tracking.php?"))
|
|
||||||
if start == -1 {
|
|
||||||
// ohne tracking Parameter -> behandeln wie nicht existent
|
|
||||||
return StatusNotExist, nil
|
|
||||||
}
|
|
||||||
end := bytes.IndexByte(bodyBytes[start:], '"')
|
|
||||||
if end == -1 {
|
|
||||||
return StatusUnknown, errors.New("tracking url parse failed")
|
|
||||||
}
|
|
||||||
raw := string(bodyBytes[start : start+end])
|
|
||||||
u, err := url.Parse(raw)
|
|
||||||
if err != nil {
|
|
||||||
return StatusUnknown, appErrorf("tracking url invalid: %w", err)
|
|
||||||
}
|
|
||||||
qs := u.Query()
|
|
||||||
if qs.Get("model_id") == "" {
|
|
||||||
return StatusNotExist, nil
|
return StatusNotExist, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) HTML parsen und <div class="campreview" ...> Attribute auslesen
|
m.AvatarURL = strings.TrimSpace(data.Result.User.Avatar)
|
||||||
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(bodyBytes))
|
|
||||||
if err != nil {
|
|
||||||
return StatusUnknown, err
|
|
||||||
}
|
|
||||||
|
|
||||||
params := doc.Find(".campreview").First()
|
if len(data.Result.User.Sessions) == 0 {
|
||||||
if params.Length() == 0 {
|
|
||||||
// keine campreview -> offline
|
|
||||||
return StatusOffline, nil
|
return StatusOffline, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
attrs := map[string]string{}
|
sess := data.Result.User.Sessions[0]
|
||||||
params.Each(func(_ int, s *goquery.Selection) {
|
|
||||||
for _, a := range []string{
|
|
||||||
"data-cam-preview-server-id-value",
|
|
||||||
"data-cam-preview-model-id-value",
|
|
||||||
"data-cam-preview-is-wzobs-value",
|
|
||||||
} {
|
|
||||||
if v, ok := s.Attr(a); ok {
|
|
||||||
attrs[a] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
m.Attrs = attrs
|
|
||||||
|
|
||||||
// 4) Versuchen, VideoURL (Preview-HLS) zu ermitteln
|
serverID := mfcDigitsOnly(sess.ServerName)
|
||||||
uStr, err := m.GetVideoURL(true)
|
if serverID == "" {
|
||||||
if err != nil {
|
return StatusUnknown, appErrorf("mfc: server_name ohne server-id: %q", sess.ServerName)
|
||||||
return StatusUnknown, err
|
|
||||||
}
|
}
|
||||||
if uStr != "" {
|
|
||||||
|
modelID := data.Result.User.ID
|
||||||
|
if modelID < 100000000 {
|
||||||
|
modelID += 100000000
|
||||||
|
}
|
||||||
|
|
||||||
|
m.ModelID = strconv.Itoa(modelID)
|
||||||
|
m.ServerID = serverID
|
||||||
|
m.Phase = normalizeMFCPhase(sess.Phase)
|
||||||
|
|
||||||
|
appLogln("✅ mfc lookup:",
|
||||||
|
"user=", username,
|
||||||
|
"modelID=", m.ModelID,
|
||||||
|
"serverID=", m.ServerID,
|
||||||
|
"phase=", m.Phase,
|
||||||
|
"serverName=", sess.ServerName,
|
||||||
|
)
|
||||||
|
|
||||||
return StatusPublic, nil
|
return StatusPublic, nil
|
||||||
}
|
|
||||||
// campreview vorhanden, aber keine playable url -> „PRIVATE“
|
|
||||||
return StatusPrivate, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runMFC(ctx context.Context, username string, outArg string) error {
|
func runMFC(ctx context.Context, username string, outArg string) error {
|
||||||
mfc := NewMyFreeCams(username)
|
mfc := NewMyFreeCams(username)
|
||||||
|
|
||||||
st, err := mfc.GetStatus()
|
st, err := mfc.GetStatus(ctx, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"databaseUrl": "postgres://postgres@127.0.0.1:5432/nsfwapp?sslmode=disable",
|
"databaseUrl": "postgres://postgres@127.0.0.1:5432/nsfwapp?sslmode=disable",
|
||||||
"encryptedDbPassword": "vkNvHM+/AgEkRPW0X+rDDys4bPb5Q7ZPGqPSADCluIJ6GCDGNGlDy3s=",
|
"encryptedDbPassword": "vkNvHM+/AgEkRPW0X+rDDys4bPb5Q7ZPGqPSADCluIJ6GCDGNGlDy3s=",
|
||||||
"recordDir": "C:\\Users\\Rother\\Desktop\\Privat\\records",
|
"recordDir": "C:\\Users\\Chris\\Desktop\\privat\\records",
|
||||||
"doneDir": "C:\\Users\\Rother\\Desktop\\Privat\\records\\done",
|
"doneDir": "C:\\Users\\Chris\\Desktop\\privat\\records\\done",
|
||||||
"ffmpegPath": "",
|
"ffmpegPath": "",
|
||||||
"autoAddToDownloadList": true,
|
"autoAddToDownloadList": true,
|
||||||
"enableConcurrentDownloadsLimit": true,
|
"enableConcurrentDownloadsLimit": true,
|
||||||
@ -13,7 +13,7 @@
|
|||||||
"autoDeleteSmallDownloadsBelowMB": 300,
|
"autoDeleteSmallDownloadsBelowMB": 300,
|
||||||
"autoDeleteSmallDownloadsKeepFavorites": false,
|
"autoDeleteSmallDownloadsKeepFavorites": false,
|
||||||
"lowDiskPauseBelowGB": 5,
|
"lowDiskPauseBelowGB": 5,
|
||||||
"blurPreviews": true,
|
"blurPreviews": false,
|
||||||
"teaserPlayback": "all",
|
"teaserPlayback": "all",
|
||||||
"teaserAudio": true,
|
"teaserAudio": true,
|
||||||
"enableNotifications": true,
|
"enableNotifications": true,
|
||||||
@ -25,5 +25,5 @@
|
|||||||
"enrichPostworkEnabled": false,
|
"enrichPostworkEnabled": false,
|
||||||
"trainingRecognitionEnabled": true,
|
"trainingRecognitionEnabled": true,
|
||||||
"trainingDetectorEpochs": 60,
|
"trainingDetectorEpochs": 60,
|
||||||
"encryptedCookies": "FEPj/igIPaDy8xr709QjHKOqXWVGesegmwzhTP1Whnusetrl1WenN7t0V1Rm/lw2nLkyis78kz+4yjsDtVgO3MXLU4Uq85AhAJQOE7SMVD+YMNenCS8Qr0JLkmI6cr/kR6sKrx7Jm6x6DY1BolFFMX1Ii3EO+8b4Re2GYaFi1iuh97oI8Zg4r5sMYmt7FIMRkr1uy0u0ToH8hsO0iDoExcxubQNHIZTQ07kkWP70Up78tYs59INxzosijATbiVhOT1H0YwS23iWC3bwLgeo2lOzMejJWpEG16CN55CoEAymFd2ktlQZ4Lmv0FlnBJ48H27Cj0TKR1yRbBxpGjgch9x0421C5qYGeXNB9jgczEKh28oYdt8ljXejoDsV7/oKYNaAFfIlICsC/Zz97ZpPRrKiRTSTkTLmIXjnWtVdwGRfCvJrYjJpoe3ZOaubulqCQs6Ug501b6JTzdZujKAl68tL5stZD7hH9XGQSpquZWGeSUGOrI5jU8dULbxCLpkGtiOEmxGb93OLYdHQ8JJg6zHeExVtPSqANHoKX3NpFc33fqOX1vOQVeg8Ei4ymxntIMOwQWs8xnbuKXmnNx6GTBSqHiD7X/a8oeEkMSw1+bCODvwuXlNBcdxHlf3/wkvHb9lhnh231BD1+Ufrnij65Apko8A=="
|
"encryptedCookies": "p0aRfigWn3+TfypNAV7vPky13RW+gwhTlhNOGKKAED0FSMf0kdGV6LgIizzTeDMD5Ck+9SGGf6EfVg+RPnEr3ZjkkkjOTVGOJ9XNzpuczHqzi0ifmt0l9/lnNTaqh8oTzrk09uQC9YYe0CtsY6gjpYFAdVEPGimCPdULqn+8Jln1CSDiBGRGF0uZGxfmAZ1T/YQym/wjiCCdIRfcjTF4f+sz6PMBQOOnTA74if1OFNA6PxpGoXfHS7xvMeV6RX3J/qcTyMmFWr+uD2h1UKs7N9w4Y1sfw4heB10jzy/VL+9gWg2o4AKE5tGqOmlVYpv8KculMWfJHNxNpRonPNizPR4jO2jZYdr3AikuXTTeILyN7hb4MtO4fXblJ+9of1ABR6LvDe1dAXmbkNdqEqRo0k1WS6jaCdNkrleP8vGuH56H7jZMnEeJAcQxS99kVjFo0qMAEJ2ai8HPkdYURC3dWSu9uIbGmiqi6hgva2WM6jPG7TqZDyqUS7hjJwAmPa3aLPnsEWHqkcE="
|
||||||
}
|
}
|
||||||
|
|||||||
@ -115,7 +115,6 @@ const previewInitialSrcOfJob = (
|
|||||||
job: RecordJob,
|
job: RecordJob,
|
||||||
modelsByKey?: Record<string, { imageUrl?: string }>
|
modelsByKey?: Record<string, { imageUrl?: string }>
|
||||||
): string => {
|
): string => {
|
||||||
if (!isChaturbateJob(job, modelsByKey)) return ''
|
|
||||||
return onlineImageUrlOfJob(job, modelsByKey)
|
return onlineImageUrlOfJob(job, modelsByKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,6 +191,9 @@ const onlineImageUrlOfJob = (
|
|||||||
const direct =
|
const direct =
|
||||||
String(
|
String(
|
||||||
anyJ?.modelImageUrl ??
|
anyJ?.modelImageUrl ??
|
||||||
|
anyJ?.avatar ??
|
||||||
|
anyJ?.previewImageUrl ??
|
||||||
|
anyJ?.imageUrl ??
|
||||||
anyJ?.model?.imageUrl ??
|
anyJ?.model?.imageUrl ??
|
||||||
''
|
''
|
||||||
).trim()
|
).trim()
|
||||||
|
|||||||
@ -151,7 +151,6 @@ const previewInitialSrcOfJob = (
|
|||||||
job: RecordJob,
|
job: RecordJob,
|
||||||
modelsByKey?: Record<string, { imageUrl?: string }>
|
modelsByKey?: Record<string, { imageUrl?: string }>
|
||||||
): string => {
|
): string => {
|
||||||
if (!isChaturbateJob(job, modelsByKey)) return ''
|
|
||||||
return onlineImageUrlOfJob(job, modelsByKey)
|
return onlineImageUrlOfJob(job, modelsByKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,6 +163,9 @@ const onlineImageUrlOfJob = (
|
|||||||
const direct =
|
const direct =
|
||||||
String(
|
String(
|
||||||
anyJ?.modelImageUrl ??
|
anyJ?.modelImageUrl ??
|
||||||
|
anyJ?.avatar ??
|
||||||
|
anyJ?.previewImageUrl ??
|
||||||
|
anyJ?.imageUrl ??
|
||||||
anyJ?.model?.imageUrl ??
|
anyJ?.model?.imageUrl ??
|
||||||
''
|
''
|
||||||
).trim()
|
).trim()
|
||||||
|
|||||||
@ -197,6 +197,11 @@ export type RecordJob = {
|
|||||||
exitCode?: number
|
exitCode?: number
|
||||||
error?: string
|
error?: string
|
||||||
logTail?: string
|
logTail?: string
|
||||||
|
|
||||||
|
modelImageUrl?: string
|
||||||
|
avatar?: string
|
||||||
|
previewImageUrl?: string
|
||||||
|
imageUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ParsedModel = {
|
export type ParsedModel = {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user