Compare commits
No commits in common. "5a468140395fff4e8b09337e26cc18daa3f363ca" and "eeda6976ec1d4b297015e64d4ceff94af1e743a1" have entirely different histories.
5a46814039
...
eeda6976ec
Binary file not shown.
@ -1,8 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
// backend\cookies_crypto.go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
// backend\cors.go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@ -65,8 +65,6 @@ type RecordJob struct {
|
||||
VideoHeight int `json:"videoHeight,omitempty"`
|
||||
FPS float64 `json:"fps,omitempty"`
|
||||
Meta *videoMeta `json:"meta,omitempty"`
|
||||
ModelImageURL string `json:"modelImageUrl,omitempty"`
|
||||
Avatar string `json:"avatar,omitempty"`
|
||||
|
||||
Hidden bool `json:"-"`
|
||||
|
||||
@ -121,8 +119,6 @@ type jobEventSnapshot struct {
|
||||
PreviewState string
|
||||
PostWorkKey string
|
||||
PostWork *PostWorkKeyStatus
|
||||
ModelImageURL string
|
||||
Avatar string
|
||||
}
|
||||
|
||||
type dummyResponseWriter struct {
|
||||
@ -182,8 +178,6 @@ func publishJobUpsertSnapshot(s jobEventSnapshot) {
|
||||
"previewState": s.PreviewState,
|
||||
"postWorkKey": s.PostWorkKey,
|
||||
"ts": time.Now().UnixMilli(),
|
||||
"modelImageUrl": s.ModelImageURL,
|
||||
"avatar": s.Avatar,
|
||||
}
|
||||
|
||||
if s.PostWork != nil {
|
||||
@ -238,8 +232,6 @@ func publishJobSnapshot(jobID string) bool {
|
||||
PreviewState: job.PreviewState,
|
||||
PostWorkKey: job.PostWorkKey,
|
||||
PostWork: pw,
|
||||
ModelImageURL: job.ModelImageURL,
|
||||
Avatar: job.Avatar,
|
||||
}
|
||||
jobsMu.RUnlock()
|
||||
|
||||
|
||||
@ -147,7 +147,7 @@ func getWantedResolutionPlaylistWithHeaders(
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, appErrorf("HTTP %d beim Abruf der m3u8: %s", resp.StatusCode, playlistURL)
|
||||
return nil, appErrorf("HTTP %d beim Abruf der m3u8", resp.StatusCode)
|
||||
}
|
||||
|
||||
rawBody, err := io.ReadAll(resp.Body)
|
||||
|
||||
@ -3,16 +3,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/grafov/m3u8"
|
||||
)
|
||||
|
||||
@ -36,34 +38,18 @@ func RecordStreamMFC(
|
||||
return err
|
||||
}
|
||||
|
||||
st, err := mfc.GetStatus(ctx, hc.userAgent)
|
||||
st, err := mfc.GetStatus()
|
||||
if err == nil && st == StatusPublic {
|
||||
break
|
||||
}
|
||||
|
||||
if time.Now().After(deadline) {
|
||||
if err != nil {
|
||||
return appErrorf("mfc status: %w", err)
|
||||
}
|
||||
return context.DeadlineExceeded
|
||||
}
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
if job != nil {
|
||||
img := strings.TrimSpace(mfc.PreviewImageURL())
|
||||
if img != "" {
|
||||
jobsMu.Lock()
|
||||
job.ModelImageURL = img
|
||||
job.Avatar = img
|
||||
job.PreviewState = "public"
|
||||
jobsMu.Unlock()
|
||||
|
||||
_ = publishJobSnapshot(job.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ erst jetzt die Video URL holen (weil public)
|
||||
stream, err := mfc.GetSelectedStream(ctx, false, hc.userAgent)
|
||||
if err != nil {
|
||||
@ -84,128 +70,20 @@ func RecordStreamMFC(
|
||||
job.PreviewM3U8 = previewURL
|
||||
job.PreviewCookie = ""
|
||||
job.PreviewUA = hc.userAgent
|
||||
|
||||
if img := strings.TrimSpace(mfc.PreviewImageURL()); img != "" {
|
||||
job.ModelImageURL = img
|
||||
job.Avatar = img
|
||||
job.PreviewState = "public"
|
||||
}
|
||||
|
||||
jobsMu.Unlock()
|
||||
|
||||
_ = publishJobSnapshot(job.ID)
|
||||
}
|
||||
|
||||
if job != nil {
|
||||
_ = publishJob(job.ID)
|
||||
}
|
||||
|
||||
return recordHLSPlaylistSegments(ctx, stream, outputPath, job, "", hc.userAgent, mfc.GetWebsiteURL())
|
||||
return handleM3U8Mode(ctx, stream, outputPath, job, "", hc.userAgent, mfc.GetWebsiteURL())
|
||||
}
|
||||
|
||||
type MyFreeCams struct {
|
||||
Username string
|
||||
Attrs map[string]string
|
||||
VideoURL string
|
||||
|
||||
ModelID string
|
||||
ServerID string
|
||||
Phase string
|
||||
AvatarURL string
|
||||
|
||||
RawUserID int
|
||||
SnapURL 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"`
|
||||
SnapURL string `json:"snap_url"`
|
||||
} `json:"sessions"`
|
||||
} `json:"user"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
func normalizeMFCSnapURL(raw string) string {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
if strings.HasPrefix(raw, "//") {
|
||||
raw = "https:" + raw
|
||||
} else if strings.HasPrefix(raw, "/") {
|
||||
raw = "https://snap.mfcimg.com" + raw
|
||||
} else if !strings.HasPrefix(raw, "http://") && !strings.HasPrefix(raw, "https://") {
|
||||
raw = "https://snap.mfcimg.com/" + strings.TrimLeft(raw, "/")
|
||||
}
|
||||
|
||||
u, err := url.Parse(raw)
|
||||
if err != nil || u.Scheme == "" || u.Host == "" {
|
||||
return raw
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
q.Set("no-cache", strconv.FormatInt(time.Now().UnixNano(), 10))
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func buildMFCSnapimgURL(serverID string, rawUserID int) string {
|
||||
serverID = strings.TrimSpace(serverID)
|
||||
if serverID == "" || rawUserID <= 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return normalizeMFCSnapURL(fmt.Sprintf(
|
||||
"https://snap.mfcimg.com/snapimg/%s/853x480/mfc_%d",
|
||||
serverID,
|
||||
rawUserID,
|
||||
))
|
||||
}
|
||||
|
||||
func (m *MyFreeCams) PreviewImageURL() string {
|
||||
if s := strings.TrimSpace(m.SnapURL); s != "" {
|
||||
return s
|
||||
}
|
||||
return strings.TrimSpace(m.AvatarURL)
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -219,204 +97,145 @@ func (m *MyFreeCams) GetWebsiteURL() string {
|
||||
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) {
|
||||
if !refresh && strings.TrimSpace(m.VideoURL) != "" {
|
||||
if !refresh && m.VideoURL != "" {
|
||||
return m.VideoURL, nil
|
||||
}
|
||||
|
||||
candidates, err := m.GetVideoURLCandidates()
|
||||
// Prüfen, ob alle benötigten Attribute vorhanden sind
|
||||
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 {
|
||||
return "", err
|
||||
}
|
||||
if len(candidates) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
m.VideoURL = candidates[0]
|
||||
m.VideoURL = u
|
||||
return m.VideoURL, nil
|
||||
}
|
||||
|
||||
func (m *MyFreeCams) GetSelectedStream(ctx context.Context, refresh bool, userAgent string) (*selectedHLSStream, error) {
|
||||
candidates, err := m.GetVideoURLCandidates()
|
||||
m3u8URL, err := m.GetVideoURL(refresh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(candidates) == 0 {
|
||||
return nil, errors.New("keine mfc m3u8 kandidaten gefunden")
|
||||
if strings.TrimSpace(m3u8URL) == "" {
|
||||
return nil, errors.New("keine m3u8 URL gefunden")
|
||||
}
|
||||
|
||||
var lastErr error
|
||||
tried := make([]string, 0, len(candidates))
|
||||
|
||||
for _, candidate := range candidates {
|
||||
tried = append(tried, candidate)
|
||||
|
||||
stream, err := getWantedResolutionPlaylistWithHeaders(
|
||||
ctx,
|
||||
candidate,
|
||||
"",
|
||||
userAgent,
|
||||
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, " | "))
|
||||
return getWantedResolutionPlaylistWithHeaders(
|
||||
ctx,
|
||||
m3u8URL,
|
||||
"", // MFC normalerweise ohne Cookies
|
||||
userAgent,
|
||||
m.GetWebsiteURL(),
|
||||
)
|
||||
}
|
||||
|
||||
func (m *MyFreeCams) GetStatus(ctx context.Context, userAgent string) (Status, error) {
|
||||
username := strings.TrimSpace(m.Username)
|
||||
if username == "" {
|
||||
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)
|
||||
func (m *MyFreeCams) GetStatus() (Status, error) {
|
||||
// 1) share-Seite prüfen (existiert/nicht existiert)
|
||||
shareURL := "https://share.myfreecams.com/" + m.Username
|
||||
resp, err := http.Get(shareURL)
|
||||
if err != nil {
|
||||
return StatusUnknown, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
if resp.StatusCode == 404 {
|
||||
return StatusNotExist, nil
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return StatusUnknown, appErrorf("mfc usernameLookup HTTP %d: %s", resp.StatusCode, lookupURL)
|
||||
if resp.StatusCode != 200 {
|
||||
return StatusUnknown, appErrorf("HTTP %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var data mfcUsernameLookupResp
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return StatusUnknown, appErrorf("mfc usernameLookup json: %w", err)
|
||||
// wir brauchen sowohl Bytes (für Suche) als auch Reader (für HTML)
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return StatusUnknown, err
|
||||
}
|
||||
|
||||
if data.Result.User.ID <= 0 {
|
||||
// 2) „tracking.php?“ suchen und prüfen, ob model_id vorhanden ist
|
||||
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
|
||||
}
|
||||
|
||||
m.RawUserID = data.Result.User.ID
|
||||
m.AvatarURL = strings.TrimSpace(data.Result.User.Avatar)
|
||||
// 3) HTML parsen und <div class="campreview" ...> Attribute auslesen
|
||||
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(bodyBytes))
|
||||
if err != nil {
|
||||
return StatusUnknown, err
|
||||
}
|
||||
|
||||
if len(data.Result.User.Sessions) == 0 {
|
||||
params := doc.Find(".campreview").First()
|
||||
if params.Length() == 0 {
|
||||
// keine campreview -> offline
|
||||
return StatusOffline, nil
|
||||
}
|
||||
|
||||
sess := data.Result.User.Sessions[0]
|
||||
attrs := map[string]string{}
|
||||
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
|
||||
|
||||
serverID := mfcDigitsOnly(sess.ServerName)
|
||||
if serverID == "" {
|
||||
return StatusUnknown, appErrorf("mfc: server_name ohne server-id: %q", sess.ServerName)
|
||||
// 4) Versuchen, VideoURL (Preview-HLS) zu ermitteln
|
||||
uStr, err := m.GetVideoURL(true)
|
||||
if err != nil {
|
||||
return StatusUnknown, err
|
||||
}
|
||||
|
||||
m.SnapURL = buildMFCSnapimgURL(serverID, data.Result.User.ID)
|
||||
|
||||
if strings.TrimSpace(m.SnapURL) == "" && strings.TrimSpace(sess.SnapURL) != "" {
|
||||
m.SnapURL = normalizeMFCSnapURL(sess.SnapURL)
|
||||
if uStr != "" {
|
||||
return StatusPublic, nil
|
||||
}
|
||||
|
||||
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
|
||||
// campreview vorhanden, aber keine playable url -> „PRIVATE“
|
||||
return StatusPrivate, nil
|
||||
}
|
||||
|
||||
func runMFC(ctx context.Context, username string, outArg string) error {
|
||||
mfc := NewMyFreeCams(username)
|
||||
|
||||
st, err := mfc.GetStatus(ctx, "")
|
||||
st, err := mfc.GetStatus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
{
|
||||
"databaseUrl": "postgres://postgres@127.0.0.1:5432/nsfwapp?sslmode=disable",
|
||||
"encryptedDbPassword": "vkNvHM+/AgEkRPW0X+rDDys4bPb5Q7ZPGqPSADCluIJ6GCDGNGlDy3s=",
|
||||
"recordDir": "C:\\Users\\Chris\\Desktop\\privat\\records",
|
||||
"doneDir": "C:\\Users\\Chris\\Desktop\\privat\\records\\done",
|
||||
"recordDir": "C:\\Users\\Rother\\Desktop\\Privat\\records",
|
||||
"doneDir": "C:\\Users\\Rother\\Desktop\\Privat\\records\\done",
|
||||
"ffmpegPath": "",
|
||||
"autoAddToDownloadList": true,
|
||||
"enableConcurrentDownloadsLimit": true,
|
||||
@ -13,7 +13,7 @@
|
||||
"autoDeleteSmallDownloadsBelowMB": 300,
|
||||
"autoDeleteSmallDownloadsKeepFavorites": false,
|
||||
"lowDiskPauseBelowGB": 5,
|
||||
"blurPreviews": false,
|
||||
"blurPreviews": true,
|
||||
"teaserPlayback": "all",
|
||||
"teaserAudio": true,
|
||||
"enableNotifications": true,
|
||||
@ -25,5 +25,5 @@
|
||||
"enrichPostworkEnabled": false,
|
||||
"trainingRecognitionEnabled": true,
|
||||
"trainingDetectorEpochs": 60,
|
||||
"encryptedCookies": "p0aRfigWn3+TfypNAV7vPky13RW+gwhTlhNOGKKAED0FSMf0kdGV6LgIizzTeDMD5Ck+9SGGf6EfVg+RPnEr3ZjkkkjOTVGOJ9XNzpuczHqzi0ifmt0l9/lnNTaqh8oTzrk09uQC9YYe0CtsY6gjpYFAdVEPGimCPdULqn+8Jln1CSDiBGRGF0uZGxfmAZ1T/YQym/wjiCCdIRfcjTF4f+sz6PMBQOOnTA74if1OFNA6PxpGoXfHS7xvMeV6RX3J/qcTyMmFWr+uD2h1UKs7N9w4Y1sfw4heB10jzy/VL+9gWg2o4AKE5tGqOmlVYpv8KculMWfJHNxNpRonPNizPR4jO2jZYdr3AikuXTTeILyN7hb4MtO4fXblJ+9of1ABR6LvDe1dAXmbkNdqEqRo0k1WS6jaCdNkrleP8vGuH56H7jZMnEeJAcQxS99kVjFo0qMAEJ2ai8HPkdYURC3dWSu9uIbGmiqi6hgva2WM6jPG7TqZDyqUS7hjJwAmPa3aLPnsEWHqkcE="
|
||||
"encryptedCookies": "FEPj/igIPaDy8xr709QjHKOqXWVGesegmwzhTP1Whnusetrl1WenN7t0V1Rm/lw2nLkyis78kz+4yjsDtVgO3MXLU4Uq85AhAJQOE7SMVD+YMNenCS8Qr0JLkmI6cr/kR6sKrx7Jm6x6DY1BolFFMX1Ii3EO+8b4Re2GYaFi1iuh97oI8Zg4r5sMYmt7FIMRkr1uy0u0ToH8hsO0iDoExcxubQNHIZTQ07kkWP70Up78tYs59INxzosijATbiVhOT1H0YwS23iWC3bwLgeo2lOzMejJWpEG16CN55CoEAymFd2ktlQZ4Lmv0FlnBJ48H27Cj0TKR1yRbBxpGjgch9x0421C5qYGeXNB9jgczEKh28oYdt8ljXejoDsV7/oKYNaAFfIlICsC/Zz97ZpPRrKiRTSTkTLmIXjnWtVdwGRfCvJrYjJpoe3ZOaubulqCQs6Ug501b6JTzdZujKAl68tL5stZD7hH9XGQSpquZWGeSUGOrI5jU8dULbxCLpkGtiOEmxGb93OLYdHQ8JJg6zHeExVtPSqANHoKX3NpFc33fqOX1vOQVeg8Ei4ymxntIMOwQWs8xnbuKXmnNx6GTBSqHiD7X/a8oeEkMSw1+bCODvwuXlNBcdxHlf3/wkvHb9lhnh231BD1+Ufrnij65Apko8A=="
|
||||
}
|
||||
|
||||
@ -94,10 +94,28 @@ const countryOfPending = (
|
||||
).trim()
|
||||
}
|
||||
|
||||
const isChaturbateJob = (
|
||||
job: RecordJob,
|
||||
modelsByKey?: Record<string, { imageUrl?: string }>
|
||||
): boolean => {
|
||||
const anyJ = job as any
|
||||
const src = String(anyJ?.sourceUrl ?? anyJ?.SourceURL ?? '').toLowerCase()
|
||||
const chat = String(anyJ?.modelChatRoomUrl ?? '').toLowerCase()
|
||||
const img = onlineImageUrlOfJob(job, modelsByKey).toLowerCase()
|
||||
|
||||
return (
|
||||
src.includes('chaturbate.com') ||
|
||||
chat.includes('chaturbate.com') ||
|
||||
img.includes('highwebmedia') ||
|
||||
img.includes('chaturbate')
|
||||
)
|
||||
}
|
||||
|
||||
const previewInitialSrcOfJob = (
|
||||
job: RecordJob,
|
||||
modelsByKey?: Record<string, { imageUrl?: string }>
|
||||
): string => {
|
||||
if (!isChaturbateJob(job, modelsByKey)) return ''
|
||||
return onlineImageUrlOfJob(job, modelsByKey)
|
||||
}
|
||||
|
||||
@ -174,9 +192,6 @@ const onlineImageUrlOfJob = (
|
||||
const direct =
|
||||
String(
|
||||
anyJ?.modelImageUrl ??
|
||||
anyJ?.avatar ??
|
||||
anyJ?.previewImageUrl ??
|
||||
anyJ?.imageUrl ??
|
||||
anyJ?.model?.imageUrl ??
|
||||
''
|
||||
).trim()
|
||||
|
||||
@ -130,10 +130,28 @@ const modelKeyFromJob = (job: RecordJob): string => {
|
||||
return String(modelNameFromOutput(j.output || '')).trim().toLowerCase()
|
||||
}
|
||||
|
||||
const isChaturbateJob = (
|
||||
job: RecordJob,
|
||||
modelsByKey?: Record<string, { imageUrl?: string }>
|
||||
): boolean => {
|
||||
const anyJ = job as any
|
||||
const src = String(anyJ?.sourceUrl ?? anyJ?.SourceURL ?? '').toLowerCase()
|
||||
const chat = String(anyJ?.modelChatRoomUrl ?? '').toLowerCase()
|
||||
const img = onlineImageUrlOfJob(job, modelsByKey).toLowerCase()
|
||||
|
||||
return (
|
||||
src.includes('chaturbate.com') ||
|
||||
chat.includes('chaturbate.com') ||
|
||||
img.includes('highwebmedia') ||
|
||||
img.includes('chaturbate')
|
||||
)
|
||||
}
|
||||
|
||||
const previewInitialSrcOfJob = (
|
||||
job: RecordJob,
|
||||
modelsByKey?: Record<string, { imageUrl?: string }>
|
||||
): string => {
|
||||
if (!isChaturbateJob(job, modelsByKey)) return ''
|
||||
return onlineImageUrlOfJob(job, modelsByKey)
|
||||
}
|
||||
|
||||
@ -146,9 +164,6 @@ const onlineImageUrlOfJob = (
|
||||
const direct =
|
||||
String(
|
||||
anyJ?.modelImageUrl ??
|
||||
anyJ?.avatar ??
|
||||
anyJ?.previewImageUrl ??
|
||||
anyJ?.imageUrl ??
|
||||
anyJ?.model?.imageUrl ??
|
||||
''
|
||||
).trim()
|
||||
|
||||
@ -197,11 +197,6 @@ export type RecordJob = {
|
||||
exitCode?: number
|
||||
error?: string
|
||||
logTail?: string
|
||||
|
||||
modelImageUrl?: string
|
||||
avatar?: string
|
||||
previewImageUrl?: string
|
||||
imageUrl?: string
|
||||
}
|
||||
|
||||
export type ParsedModel = {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user