- *
- * Copyright (c) 2014-2017, Jon Schlinkert.
- * Released under the MIT License.
- *)
-*/
diff --git a/dist-release/content/index.js b/dist-release/content/index.js
deleted file mode 100644
index 4202fce..0000000
--- a/dist-release/content/index.js
+++ /dev/null
@@ -1,5935 +0,0 @@
-"use strict";
-(() => {
- // src/shared/rate-normalizer.ts
- function normalizeRateDisplay(value) {
- const trimmedValue = value.trim();
- const rangeMatch = trimmedValue.match(
- /^([0-9]+(?:\.[0-9]+)?)\s*%?\s*-\s*([0-9]+(?:\.[0-9]+)?)\s*%$/
- );
- if (rangeMatch) {
- const [, lowerBound, upperBound] = rangeMatch;
- return `${lowerBound}% - ${upperBound}%`;
- }
- return trimmedValue.replace(/\s+/g, "");
- }
- function normalizeFractionRateDisplay(value) {
- const numericValue = Number(value);
- if (!Number.isFinite(numericValue)) {
- return null;
- }
- const percentageValue = numericValue * 100;
- return `${trimTrailingZeros(percentageValue.toFixed(6))}%`;
- }
- function parseRateLowerBound(value) {
- const comparableRate = toComparableRate(value);
- return comparableRate?.numeric ?? null;
- }
- function compareRateValues(leftValue, rightValue) {
- const leftComparable = toComparableRate(leftValue);
- const rightComparable = toComparableRate(rightValue);
- if (!leftComparable && !rightComparable) {
- return 0;
- }
- if (!leftComparable) {
- return 1;
- }
- if (!rightComparable) {
- return -1;
- }
- if (leftComparable.numeric !== rightComparable.numeric) {
- return leftComparable.numeric - rightComparable.numeric;
- }
- if (leftComparable.isLessThan === rightComparable.isLessThan) {
- return 0;
- }
- return leftComparable.isLessThan ? -1 : 1;
- }
- function toComparableRate(value) {
- if (!value) {
- return null;
- }
- const normalizedValue = normalizeRateDisplay(value);
- const lessThanMatch = normalizedValue.match(/^<\s*([0-9]+(?:\.[0-9]+)?)%$/);
- if (lessThanMatch) {
- return {
- isLessThan: true,
- numeric: Number(lessThanMatch[1])
- };
- }
- const rangeMatch = normalizedValue.match(
- /^([0-9]+(?:\.[0-9]+)?)%\s*-\s*([0-9]+(?:\.[0-9]+)?)%$/
- );
- if (rangeMatch) {
- return {
- isLessThan: false,
- numeric: Number(rangeMatch[1])
- };
- }
- const exactMatch = normalizedValue.match(/^([0-9]+(?:\.[0-9]+)?)%$/);
- if (exactMatch) {
- return {
- isLessThan: false,
- numeric: Number(exactMatch[1])
- };
- }
- return null;
- }
- function trimTrailingZeros(value) {
- return value.replace(/\.?0+$/, "");
- }
-
- // src/shared/csv.ts
- function escapeCsvCell(value) {
- if (/[",\n]/.test(value)) {
- return `"${value.replace(/"/g, '""')}"`;
- }
- return value;
- }
-
- // src/content/market/csv-exporter.ts
- var FALLBACK_BASE_COLUMNS = [
- {
- header: "\u8FBE\u4EBAID",
- readValue: (record) => record.authorId
- },
- {
- header: "\u8FBE\u4EBA\u540D\u79F0",
- readValue: (record) => record.authorName
- },
- {
- header: "\u5730\u533A",
- readValue: (record) => record.location ?? ""
- },
- {
- header: "21-60s\u62A5\u4EF7",
- readValue: (record) => record.price21To60s ?? ""
- }
- ];
- var RATE_COLUMNS = [
- {
- header: "\u5546\u5355\u89C6\u9891\u770B\u540E\u641C\u7387",
- readValue: (record) => record.rates?.singleVideoAfterSearchRate ? normalizeRateDisplay(record.rates.singleVideoAfterSearchRate) : ""
- },
- {
- header: "\u4E2A\u4EBA\u89C6\u9891\u770B\u540E\u641C\u7387",
- readValue: (record) => record.rates?.personalVideoAfterSearchRate ? normalizeRateDisplay(record.rates.personalVideoAfterSearchRate) : ""
- }
- ];
- var BACKEND_METRIC_COLUMNS = [
- {
- header: "\u79D2\u601Dapi-\u770B\u540E\u641C\u7387",
- readValue: (record) => record.backendMetrics?.afterViewSearchRate ?? ""
- },
- {
- header: "\u79D2\u601Dapi-\u770B\u540E\u641C\u6570",
- readValue: (record) => record.backendMetrics?.afterViewSearchCount ?? ""
- },
- {
- header: "\u79D2\u601Dapi-\u65B0\u589EA3\u6570",
- readValue: (record) => record.backendMetrics?.a3IncreaseCount ?? ""
- },
- {
- header: "\u79D2\u601Dapi-\u65B0\u589EA3\u7387",
- readValue: (record) => record.backendMetrics?.newA3Rate ?? ""
- },
- {
- header: "\u79D2\u601Dapi-CPA3",
- readValue: (record) => record.backendMetrics?.cpa3 ?? ""
- },
- {
- header: "\u79D2\u601Dapi-cp_search",
- readValue: (record) => record.backendMetrics?.cpSearch ?? ""
- }
- ];
- function listRateCsvHeaders() {
- return RATE_COLUMNS.map((column) => column.header);
- }
- function listBackendMetricCsvHeaders() {
- return BACKEND_METRIC_COLUMNS.map((column) => column.header);
- }
- function buildMarketCsv(records) {
- const csvColumns = buildMarketCsvColumns(records);
- const headerLine = csvColumns.map((column) => column.header).join(",");
- const rowLines = records.map(
- (record) => csvColumns.map((column) => escapeCsvCell(column.readValue(record))).join(",")
- );
- return [headerLine, ...rowLines].join("\n");
- }
- function buildMarketCsvColumns(records) {
- const baseColumns = buildBaseColumns(records);
- return [...baseColumns, ...RATE_COLUMNS, ...BACKEND_METRIC_COLUMNS];
- }
- function buildBaseColumns(records) {
- const orderedHeaders = [];
- const seenHeaders = /* @__PURE__ */ new Set();
- const excludedHeaders = /* @__PURE__ */ new Set(["\u4EE3\u8868\u89C6\u9891"]);
- records.forEach((record) => {
- Object.keys(record.exportFields ?? {}).forEach((header) => {
- if (seenHeaders.has(header) || excludedHeaders.has(header)) {
- return;
- }
- seenHeaders.add(header);
- orderedHeaders.push(header);
- });
- });
- if (orderedHeaders.length === 0) {
- return FALLBACK_BASE_COLUMNS;
- }
- return orderedHeaders.map((header) => ({
- header,
- readValue: (record) => record.exportFields?.[header] ?? ""
- }));
- }
-
- // src/content/market/audience-profile-csv.ts
- var PROFILE_LAYOUTS = [
- { includeGender: true, kind: "audience", label: "\u89C2\u4F17\u753B\u50CF" },
- { includeGender: true, kind: "fans", label: "\u7C89\u4E1D\u753B\u50CF" },
- { includeGender: false, kind: "longtimeFans", label: "\u94C1\u7C89\u753B\u50CF" }
- ];
- var GENDER_LABELS = ["\u7537\u6027", "\u5973\u6027"];
- var AGE_LABELS = ["18-23", "24-30", "31-40", "41-50", "50+"];
- var CITY_TIER_LABELS = [
- "\u4E00\u7EBF\u57CE\u5E02",
- "\u4E8C\u7EBF\u57CE\u5E02",
- "\u4E09\u7EBF\u57CE\u5E02",
- "\u56DB\u7EBF\u57CE\u5E02",
- "\u4E94\u7EBF\u57CE\u5E02"
- ];
- var CROWD_LABELS = [
- "\u7CBE\u81F4\u5988\u5988",
- "\u90FD\u5E02\u94F6\u53D1",
- "\u65B0\u9510\u767D\u9886",
- "\u8D44\u6DF1\u4E2D\u4EA7",
- "\u90FD\u5E02\u84DD\u9886",
- "Z\u4E16\u4EE3",
- "\u5C0F\u9547\u4E2D\u8001\u5E74",
- "\u5C0F\u9547\u9752\u5E74"
- ];
- var BUSINESS_VIDEO_LAYOUTS = [
- { key: "personalVideo", label: "\u4E2A\u4EBA\u89C6\u9891" },
- { key: "xingtuVideo", label: "\u661F\u56FE\u89C6\u9891" }
- ];
- var BUSINESS_VIDEO_METRIC_LAYOUTS = [
- { key: "medianPlay", label: "\u64AD\u653E\u91CF\u4E2D\u4F4D\u6570" },
- { key: "finishRate", label: "\u5B8C\u64AD\u7387" },
- { key: "interactionRate", label: "\u4E92\u52A8\u7387" },
- { key: "publishedItems", label: "\u53D1\u5E03\u4F5C\u54C1" },
- { key: "averageDuration", label: "\u5E73\u5747\u65F6\u957F" },
- { key: "averageLike", label: "\u5E73\u5747\u70B9\u8D5E" },
- { key: "averageComment", label: "\u5E73\u5747\u8BC4\u8BBA" },
- { key: "averageShare", label: "\u5E73\u5747\u8F6C\u53D1" }
- ];
- var BUSINESS_VIDEO_SECTION_LABEL = "\u5185\u5BB9\u6570\u636E";
- var BUSINESS_ESTIMATE_SECTION_LABEL = "\u6548\u679C\u9884\u4F30";
- var BUSINESS_ESTIMATE_LAYOUTS = [
- { key: "oneToTwenty", label: "1-20s\u89C6\u9891" },
- { key: "twentyToSixty", label: "20-60s\u89C6\u9891" },
- { key: "overSixty", label: "60s\u4EE5\u4E0A\u89C6\u9891" }
- ];
- var BUSINESS_ESTIMATE_METRIC_LAYOUTS = [
- { key: "expectedCpm", label: "\u9884\u671FCPM" },
- { key: "expectedCpe", label: "\u9884\u671FCPE" },
- { key: "expectedPlay", label: "\u9884\u671F\u64AD\u653E\u91CF" },
- { key: "hotRate", label: "\u7206\u6587\u7387" }
- ];
- function buildAudienceProfileCsv(rows, options = {}) {
- const marketColumns = buildMarketCsvColumns(rows.map((row) => row.record));
- const csvColumns = filterAudienceProfileCsvColumns([
- ...marketColumns.map(toMarketColumn),
- ...buildBusinessAbilityColumns(),
- ...PROFILE_LAYOUTS.flatMap((layout) => buildProfileColumns(layout))
- ], options.selectedHeaders);
- const headerLine = csvColumns.map((column) => column.header).join(",");
- const rowLines = rows.map(
- (row) => csvColumns.map((column) => escapeCsvCell(column.readValue(row))).join(",")
- );
- return [headerLine, ...rowLines].join("\n");
- }
- function listAudienceProfileSelectableFieldGroups() {
- return [
- {
- headers: listRateCsvHeaders(),
- label: "\u770B\u540E\u641C\u7387"
- },
- {
- headers: listBackendMetricCsvHeaders(),
- label: "\u79D2\u601Dapi\u6570\u636E"
- },
- {
- headers: buildBusinessVideoColumns().map((column) => column.header),
- label: "\u5185\u5BB9\u6570\u636E"
- },
- {
- headers: buildBusinessEstimateColumns().map((column) => column.header),
- label: "\u6548\u679C\u9884\u4F30"
- },
- ...PROFILE_LAYOUTS.map((layout) => ({
- headers: buildProfileColumns(layout).map((column) => column.header),
- label: layout.label
- }))
- ];
- }
- function filterAudienceProfileCsvColumns(columns, selectedHeaders) {
- if (!selectedHeaders) {
- return columns;
- }
- const selectableHeaderSet = new Set(listAudienceProfileSelectableHeaders());
- const selectedHeaderSet = new Set(selectedHeaders);
- return columns.filter(
- (column) => !selectableHeaderSet.has(column.header) || selectedHeaderSet.has(column.header)
- );
- }
- function listAudienceProfileSelectableHeaders() {
- return listAudienceProfileSelectableFieldGroups().flatMap(
- (group) => group.headers
- );
- }
- function buildBusinessAbilityColumns() {
- return [...buildBusinessVideoColumns(), ...buildBusinessEstimateColumns()];
- }
- function buildBusinessVideoColumns() {
- return [
- ...BUSINESS_VIDEO_LAYOUTS.flatMap(
- (videoLayout) => BUSINESS_VIDEO_METRIC_LAYOUTS.map((metricLayout) => ({
- header: `${BUSINESS_VIDEO_SECTION_LABEL}-${videoLayout.label}-${metricLayout.label}`,
- readValue: (row) => readBusinessVideoValue(row, videoLayout.key, metricLayout.key)
- }))
- )
- ];
- }
- function buildBusinessEstimateColumns() {
- return [
- ...BUSINESS_ESTIMATE_LAYOUTS.flatMap(
- (durationLayout) => BUSINESS_ESTIMATE_METRIC_LAYOUTS.map((metricLayout) => ({
- header: `${BUSINESS_ESTIMATE_SECTION_LABEL}-${durationLayout.label}-${metricLayout.label}`,
- readValue: (row) => readBusinessEstimateValue(row, durationLayout.key, metricLayout.key)
- }))
- )
- ];
- }
- function readBusinessVideoValue(row, videoKey, metricKey) {
- const businessAbility = row.businessAbility;
- if (!businessAbility || businessAbility.status !== "success") {
- return "";
- }
- return businessAbility.videos[videoKey]?.[metricKey] ?? "";
- }
- function readBusinessEstimateValue(row, durationKey, metricKey) {
- const businessAbility = row.businessAbility;
- if (!businessAbility || businessAbility.status !== "success") {
- return "";
- }
- return businessAbility.estimates[durationKey]?.[metricKey] ?? "";
- }
- function toMarketColumn(column) {
- return {
- header: column.header,
- readValue: (row) => column.readValue(row.record)
- };
- }
- function buildProfileColumns(layout) {
- const columns = [];
- if (layout.includeGender) {
- columns.push(
- ...buildFixedDistributionColumns(
- layout.label,
- layout.kind,
- "gender",
- GENDER_LABELS
- )
- );
- }
- columns.push(
- ...buildFixedDistributionColumns(layout.label, layout.kind, "age", AGE_LABELS),
- ...buildFixedDistributionColumns(
- layout.label,
- layout.kind,
- "cityTier",
- CITY_TIER_LABELS
- ),
- ...buildFixedDistributionColumns(layout.label, layout.kind, "crowd", CROWD_LABELS)
- );
- return columns;
- }
- function buildFixedDistributionColumns(prefix, kind, key, labels) {
- return labels.map((label) => ({
- header: `${prefix}-${label}\u5360\u6BD4`,
- readValue: (row) => readDistributionValue(row.profiles[kind], key, label)
- }));
- }
- function readDistributionValue(profile, key, label) {
- if (profile.status !== "success") {
- return "";
- }
- return readProfileDistributionItems(profile, key).find(
- (candidate) => candidate.label === label
- )?.value ?? "0%";
- }
- function readProfileDistributionItems(profile, key) {
- return profile.status === "success" ? profile[key] ?? [] : [];
- }
-
- // src/content/market/audience-profile-client.ts
- var SECTION_BY_DISPLAY = [
- [/性别/, "gender"],
- [/年龄/, "age"],
- [/省份|全国省份/, "province"],
- [/城市分布|地域/, "cityTop"],
- [/城市等级/, "cityTier"],
- [/兴趣/, "interest"],
- [/八大人群/, "crowd"]
- ];
- var GENDER_LABELS2 = {
- female: "\u5973\u6027",
- male: "\u7537\u6027"
- };
- var AGE_ORDER = ["18-23", "24-30", "31-40", "41-50", "50+"];
- var CITY_TIER_ORDER = ["\u4E00\u7EBF", "\u65B0\u4E00\u7EBF", "\u4E8C\u7EBF", "\u4E09\u7EBF", "\u56DB\u7EBF", "\u4E94\u7EBF"];
- var AUDIENCE_PROFILE_TARGETS = {
- audience: { linkType: 5, source: "audienceDistribution" },
- fans: { authorType: 1, source: "fansDistribution" },
- longtimeFans: { authorType: 5, source: "fansDistribution" }
- };
- function createAudienceProfileClient(options = {}) {
- const baseUrl = options.baseUrl ?? resolveBaseUrl();
- const fetchImpl = options.fetchImpl ?? defaultFetch;
- const timeoutMs = options.timeoutMs ?? 8e3;
- return {
- async loadAudienceProfile(record, target) {
- const controller = new AbortController();
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
- try {
- const response = await fetchImpl(
- buildAudienceProfileUrl(record.authorId, baseUrl, target),
- {
- credentials: "include",
- method: "GET",
- signal: controller.signal
- }
- );
- if (!response.ok) {
- return {
- failureReason: "request-failed",
- status: "failed"
- };
- }
- return mapAudienceProfileResponse(await response.json());
- } catch (error) {
- return {
- failureReason: error instanceof Error && error.name === "AbortError" ? "timeout" : "request-failed",
- status: "failed"
- };
- } finally {
- clearTimeout(timeoutId);
- }
- }
- };
- }
- function buildAudienceProfileUrl(authorId, baseUrl, target) {
- const url = new URL(
- target.source === "audienceDistribution" ? "/gw/api/data_sp/author_audience_distribution" : "/gw/api/data_sp/get_author_fans_distribution",
- baseUrl
- );
- url.searchParams.set("o_author_id", authorId);
- url.searchParams.set("platform_source", "1");
- if (target.source === "audienceDistribution") {
- url.searchParams.set("platform_channel", "1");
- url.searchParams.set("link_type", String(target.linkType));
- } else {
- url.searchParams.set("author_type", String(target.authorType));
- }
- return url.toString();
- }
- function mapAudienceProfileResponse(payload) {
- if (!isRecord(payload) || !Array.isArray(payload.distributions)) {
- return {
- failureReason: "bad-response",
- status: "failed"
- };
- }
- const profile = {
- status: "success"
- };
- payload.distributions.forEach((section) => {
- if (!isRecord(section)) {
- return;
- }
- const display = readString(section.type_display);
- const sectionName = resolveSection(display);
- if (!sectionName || !Array.isArray(section.distribution_list)) {
- return;
- }
- profile[sectionName] = normalizeDistributionItems(
- section.distribution_list,
- sectionName
- );
- });
- if (Object.keys(profile).length === 1) {
- return {
- failureReason: "missing-profile",
- status: "failed"
- };
- }
- return profile;
- }
- function normalizeDistributionItems(rawItems, sectionName) {
- const parsedItems = rawItems.map((item) => {
- if (!isRecord(item)) {
- return null;
- }
- const key = readString(item.distribution_key);
- const value = readNumber(item.distribution_value);
- if (!key || value === null) {
- return null;
- }
- return {
- label: normalizeLabel(key, sectionName),
- rawLabel: key,
- value
- };
- }).filter(
- (item) => Boolean(item)
- );
- const total = parsedItems.reduce((sum, item) => sum + item.value, 0);
- if (total <= 0) {
- return [];
- }
- return parsedItems.sort((left, right) => compareDistributionItems(left, right, sectionName)).map((item) => ({
- label: item.label,
- value: formatPercent(item.value / total)
- }));
- }
- function compareDistributionItems(left, right, sectionName) {
- if (sectionName === "age") {
- return orderIndex(AGE_ORDER, left.rawLabel) - orderIndex(AGE_ORDER, right.rawLabel);
- }
- if (sectionName === "cityTier") {
- return orderIndex(CITY_TIER_ORDER, left.rawLabel) - orderIndex(CITY_TIER_ORDER, right.rawLabel);
- }
- return right.value - left.value;
- }
- function orderIndex(order, value) {
- const index = order.indexOf(value);
- return index === -1 ? order.length : index;
- }
- function normalizeLabel(label, sectionName) {
- if (sectionName === "gender") {
- return GENDER_LABELS2[label] ?? label;
- }
- if (sectionName === "cityTier" && !label.endsWith("\u57CE\u5E02")) {
- return `${label}\u57CE\u5E02`;
- }
- return label;
- }
- function resolveSection(display) {
- if (!display) {
- return null;
- }
- return SECTION_BY_DISPLAY.find(([pattern]) => pattern.test(display))?.[1] ?? null;
- }
- function formatPercent(value) {
- const percent = Math.round(value * 1e3) / 10;
- return `${Number.isInteger(percent) ? percent.toFixed(0) : percent.toFixed(1)}%`;
- }
- function readString(value) {
- return typeof value === "string" && value.trim() ? value.trim() : null;
- }
- function readNumber(value) {
- if (typeof value === "number" && Number.isFinite(value)) {
- return value;
- }
- if (typeof value === "string" && value.trim()) {
- const numericValue = Number(value);
- return Number.isFinite(numericValue) ? numericValue : null;
- }
- return null;
- }
- function resolveBaseUrl() {
- if (typeof location !== "undefined" && location.origin) {
- return location.origin;
- }
- return "https://xingtu.cn";
- }
- async function defaultFetch(input, init) {
- return fetch(input, init);
- }
- function isRecord(value) {
- return typeof value === "object" && value !== null;
- }
-
- // src/content/market/author-base-client.ts
- function createAuthorBaseClient(options = {}) {
- const baseUrl = options.baseUrl ?? resolveBaseUrl2();
- const fetchImpl = options.fetchImpl ?? defaultFetch2;
- const timeoutMs = options.timeoutMs ?? 8e3;
- return {
- async loadAuthorBaseInfo(authorId) {
- const controller = new AbortController();
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
- try {
- const response = await fetchImpl(
- buildAuthorBaseInfoUrl(authorId, baseUrl),
- {
- credentials: "include",
- method: "GET",
- signal: controller.signal
- }
- );
- if (!response.ok) {
- return buildFailedRecord(authorId, "request-failed");
- }
- return mapAuthorBaseInfoResponse(authorId, await response.json());
- } catch (error) {
- return buildFailedRecord(
- authorId,
- error instanceof Error && error.name === "AbortError" ? "timeout" : "request-failed"
- );
- } finally {
- clearTimeout(timeoutId);
- }
- }
- };
- }
- function buildAuthorBaseInfoUrl(authorId, baseUrl) {
- const url = new URL("/gw/api/author/get_author_base_info", baseUrl);
- url.searchParams.set("o_author_id", authorId);
- url.searchParams.set("platform_source", "1");
- url.searchParams.set("platform_channel", "1");
- url.searchParams.set("recommend", "true");
- url.searchParams.set("need_sec_uid", "true");
- url.searchParams.set("need_linkage_info", "true");
- return url.toString();
- }
- function mapAuthorBaseInfoResponse(authorId, payload) {
- if (!isRecord2(payload)) {
- return buildFailedRecord(authorId, "bad-response");
- }
- const authorName = readString2(payload.nick_name);
- if (!authorName) {
- return buildFailedRecord(authorId, "missing-rate");
- }
- return {
- authorId,
- authorName,
- status: "success"
- };
- }
- function buildFailedRecord(authorId, failureReason) {
- return {
- authorId,
- authorName: "",
- failureReason,
- status: "failed"
- };
- }
- function readString2(value) {
- return typeof value === "string" && value.trim() ? value.trim() : null;
- }
- function resolveBaseUrl2() {
- if (typeof location !== "undefined" && location.origin) {
- return location.origin;
- }
- return "https://xingtu.cn";
- }
- async function defaultFetch2(input, init) {
- return fetch(input, init);
- }
- function isRecord2(value) {
- return typeof value === "object" && value !== null;
- }
-
- // src/content/market/author-id-input.ts
- var AUTHOR_ID_PATTERN = /^\d{16,20}$/;
- function parseAuthorIds(input) {
- const ids = [];
- const duplicates = [];
- const invalidTokens = [];
- const seen = /* @__PURE__ */ new Set();
- input.split(/[\s,,;;]+/).map((token) => token.trim()).filter(Boolean).forEach((token) => {
- if (!/^\d+$/.test(token) || !AUTHOR_ID_PATTERN.test(token)) {
- invalidTokens.push(token);
- return;
- }
- if (seen.has(token)) {
- duplicates.push(token);
- return;
- }
- seen.add(token);
- ids.push(token);
- });
- return {
- duplicates,
- ids,
- invalidTokens
- };
- }
-
- // src/content/market/business-ability-client.ts
- var VIDEO_TYPES = {
- personalVideo: 1,
- xingtuVideo: 2
- };
- function createBusinessAbilityClient(options = {}) {
- const baseUrl = options.baseUrl ?? resolveBaseUrl3();
- const fetchImpl = options.fetchImpl ?? defaultFetch3;
- const timeoutMs = options.timeoutMs ?? 8e3;
- return {
- async loadBusinessAbility(record) {
- const personalVideo = await loadJson(
- buildBusinessAbilityVideoUrl(record.authorId, baseUrl, VIDEO_TYPES.personalVideo)
- );
- const xingtuVideo = await loadJson(
- buildBusinessAbilityVideoUrl(record.authorId, baseUrl, VIDEO_TYPES.xingtuVideo)
- );
- const estimates = await loadJson(
- buildBusinessAbilityEstimateUrl(record.authorId, baseUrl)
- );
- if (!personalVideo.ok || !xingtuVideo.ok || !estimates.ok) {
- return {
- failureReason: personalVideo.failureReason ?? xingtuVideo.failureReason ?? estimates.failureReason,
- status: "failed"
- };
- }
- return {
- estimates: mapBusinessAbilityEstimateResponse(estimates.payload),
- status: "success",
- videos: {
- personalVideo: mapBusinessAbilityVideoResponse(personalVideo.payload),
- xingtuVideo: mapBusinessAbilityVideoResponse(xingtuVideo.payload)
- }
- };
- }
- };
- async function loadJson(url) {
- const controller = new AbortController();
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
- try {
- const response = await fetchImpl(url, {
- credentials: "include",
- method: "GET",
- signal: controller.signal
- });
- if (!response.ok) {
- return { failureReason: "request-failed", ok: false };
- }
- return { ok: true, payload: await response.json() };
- } catch (error) {
- return {
- failureReason: error instanceof Error && error.name === "AbortError" ? "timeout" : "request-failed",
- ok: false
- };
- } finally {
- clearTimeout(timeoutId);
- }
- }
- }
- function buildBusinessAbilityVideoUrl(authorId, baseUrl, videoType) {
- const url = new URL("/gw/api/data_sp/get_author_spread_info", baseUrl);
- url.searchParams.set("o_author_id", authorId);
- url.searchParams.set("platform_source", "1");
- url.searchParams.set("platform_channel", "1");
- url.searchParams.set("type", String(videoType));
- url.searchParams.set("flow_type", "0");
- url.searchParams.set("only_assign", "true");
- url.searchParams.set("range", "2");
- return url.toString();
- }
- function buildBusinessAbilityEstimateUrl(authorId, baseUrl) {
- const url = new URL(
- "/gw/api/aggregator/get_author_commerce_spread_info",
- baseUrl
- );
- url.searchParams.set("o_author_id", authorId);
- return url.toString();
- }
- function mapBusinessAbilityVideoResponse(payload) {
- const data = getPayloadData(payload);
- return {
- averageComment: formatWan(readNumber2(data?.comment_avg)),
- averageDuration: formatDuration(readNumber2(data?.avg_duration)),
- averageLike: formatWan(readNumber2(data?.like_avg)),
- averageShare: formatWan(readNumber2(data?.share_avg)),
- finishRate: formatBasisPointRate(readNestedNumber(data, "play_over_rate", "value")),
- interactionRate: formatBasisPointRate(
- readNestedNumber(data, "interact_rate", "value")
- ),
- medianPlay: formatWan(readNumber2(data?.play_mid)),
- publishedItems: formatPublishedItems(readNumber2(data?.item_num))
- };
- }
- function mapBusinessAbilityEstimateResponse(payload) {
- const data = getPayloadData(payload);
- const expectedPlay = formatWan(readNumber2(data?.vv));
- const hotRate = formatDecimalRate(readNumber2(data?.platform_hot_rate));
- return {
- oneToTwenty: {
- expectedCpe: formatDecimal(readNumber2(data?.cpe_1_20), 1),
- expectedCpm: formatFixedDecimal(readNumber2(data?.cpm_1_20), 1),
- expectedPlay,
- hotRate
- },
- overSixty: {
- expectedCpe: formatDecimal(readNumber2(data?.cpe_60), 1),
- expectedCpm: formatFixedDecimal(readNumber2(data?.cpm_60), 1),
- expectedPlay,
- hotRate
- },
- twentyToSixty: {
- expectedCpe: formatDecimal(readNumber2(data?.cpe_20_60), 1),
- expectedCpm: formatFixedDecimal(readNumber2(data?.cpm_20_60), 1),
- expectedPlay,
- hotRate
- }
- };
- }
- function formatPublishedItems(value) {
- if (value === null) {
- return "";
- }
- return value > 0 && value < 5 ? "<5" : formatDecimal(value, 0);
- }
- function formatDuration(value) {
- if (value === null) {
- return "";
- }
- return `${formatDecimal(value / 100, 0)}s`;
- }
- function formatBasisPointRate(value) {
- if (value === null) {
- return "";
- }
- return `${formatDecimal(value / 100, 1)}%`;
- }
- function formatDecimalRate(value) {
- if (value === null) {
- return "\u7F3A\u5931";
- }
- return `${formatDecimal(value * 100, 0)}%`;
- }
- function formatWan(value) {
- if (value === null) {
- return "";
- }
- if (Math.abs(value) >= 1e4) {
- return `${formatDecimal(value / 1e4, 1)}w`;
- }
- return formatDecimal(value, 0);
- }
- function formatDecimal(value, digits) {
- if (value === null || !Number.isFinite(value)) {
- return "";
- }
- const fixed = value.toFixed(digits);
- return fixed.replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
- }
- function formatFixedDecimal(value, digits) {
- if (value === null || !Number.isFinite(value)) {
- return "";
- }
- return value.toFixed(digits);
- }
- function readNestedNumber(data, objectKey, valueKey) {
- const objectValue = data?.[objectKey];
- if (!isRecord3(objectValue)) {
- return null;
- }
- return readNumber2(objectValue[valueKey]);
- }
- function readNumber2(value) {
- if (typeof value === "number" && Number.isFinite(value)) {
- return value;
- }
- if (typeof value === "string" && value.trim()) {
- const numericValue = Number(value);
- return Number.isFinite(numericValue) ? numericValue : null;
- }
- return null;
- }
- function getPayloadData(payload) {
- if (!isRecord3(payload)) {
- return null;
- }
- return isRecord3(payload.data) ? payload.data : payload;
- }
- function resolveBaseUrl3() {
- if (typeof location !== "undefined" && location.origin) {
- return location.origin;
- }
- return "https://xingtu.cn";
- }
- async function defaultFetch3(input, init) {
- return fetch(input, init);
- }
- function isRecord3(value) {
- return typeof value === "object" && value !== null;
- }
-
- // src/content/market/audience-profile-field-dialog.ts
- function promptForAudienceProfileFields(document2, groups, selectedHeaders) {
- return new Promise((resolve) => {
- const selectableHeaders = groups.flatMap((group) => group.headers);
- const selectedHeaderSet = new Set(
- selectedHeaders.filter((header) => selectableHeaders.includes(header))
- );
- if (selectedHeaderSet.size === 0) {
- selectableHeaders.forEach((header) => selectedHeaderSet.add(header));
- }
- const overlay = document2.createElement("div");
- overlay.dataset.audienceProfileFieldDialog = "overlay";
- applyOverlayStyles(overlay);
- const dialog = document2.createElement("section");
- applyDialogStyles(dialog);
- const title = document2.createElement("h2");
- applyTitleStyles(title);
- const hint = document2.createElement("p");
- hint.textContent = "\u57FA\u7840\u5B57\u6BB5\u4F1A\u56FA\u5B9A\u4FDD\u7559\u3002\u53D6\u6D88\u52FE\u9009\u540E\uFF0C\u672C\u6B21\u53CA\u540E\u7EEDCSV\u5C06\u4E0D\u5305\u542B\u5BF9\u5E94\u5217\u3002";
- applyHintStyles(hint);
- const toolbar = document2.createElement("div");
- applyToolbarStyles(toolbar);
- const selectAllButton = document2.createElement("button");
- selectAllButton.type = "button";
- selectAllButton.textContent = "\u5168\u9009";
- applySecondaryButtonStyles(selectAllButton);
- const resetButton = document2.createElement("button");
- resetButton.type = "button";
- resetButton.textContent = "\u6062\u590D\u9ED8\u8BA4";
- applySecondaryButtonStyles(resetButton);
- toolbar.append(selectAllButton, resetButton);
- const groupContainer = document2.createElement("div");
- applyGroupContainerStyles(groupContainer);
- const fieldInputs = [];
- groups.forEach((group) => {
- const groupSection = document2.createElement("section");
- groupSection.dataset.audienceProfileFieldDialogGroup = "section";
- applyGroupSectionStyles(groupSection);
- const groupHeader = document2.createElement("label");
- applyGroupHeaderStyles(groupHeader);
- const groupInput = document2.createElement("input");
- groupInput.type = "checkbox";
- const groupTitle = document2.createElement("span");
- groupTitle.textContent = group.label;
- groupHeader.append(groupInput, groupTitle);
- const fieldList = document2.createElement("div");
- applyFieldListStyles(fieldList);
- const groupFieldInputs = group.headers.map((header) => {
- const fieldLabel = document2.createElement("label");
- applyFieldLabelStyles(fieldLabel);
- const input = document2.createElement("input");
- input.type = "checkbox";
- input.value = header;
- input.dataset.audienceProfileFieldDialogField = "checkbox";
- input.checked = selectedHeaderSet.has(header);
- const text = document2.createElement("span");
- text.textContent = header;
- fieldLabel.append(input, text);
- fieldList.append(fieldLabel);
- fieldInputs.push(input);
- return input;
- });
- const syncGroupInput = () => {
- const checkedCount = groupFieldInputs.filter((input) => input.checked).length;
- groupInput.checked = checkedCount === groupFieldInputs.length;
- groupInput.indeterminate = checkedCount > 0 && checkedCount < groupFieldInputs.length;
- };
- groupInput.addEventListener("change", () => {
- groupFieldInputs.forEach((input) => {
- input.checked = groupInput.checked;
- });
- syncTitle();
- });
- groupFieldInputs.forEach((input) => {
- input.addEventListener("change", () => {
- syncGroupInput();
- syncTitle();
- });
- });
- syncGroupInput();
- groupSection.append(groupHeader, fieldList);
- groupContainer.append(groupSection);
- });
- const actions = document2.createElement("div");
- applyActionsStyles(actions);
- const cancelButton = document2.createElement("button");
- cancelButton.type = "button";
- cancelButton.textContent = "\u53D6\u6D88";
- applySecondaryButtonStyles(cancelButton);
- const confirmButton = document2.createElement("button");
- confirmButton.type = "button";
- confirmButton.dataset.audienceProfileFieldDialogSave = "button";
- confirmButton.textContent = "\u4FDD\u5B58";
- applyPrimaryButtonStyles(confirmButton);
- actions.append(cancelButton, confirmButton);
- dialog.append(title, hint, toolbar, groupContainer, actions);
- overlay.append(dialog);
- document2.body.appendChild(overlay);
- function syncTitle() {
- const checkedCount = fieldInputs.filter((input) => input.checked).length;
- title.textContent = `\u53EF\u9009\u5B57\u6BB5\uFF08\u5DF2\u9009 ${checkedCount}/${fieldInputs.length} \u4E2A\u5B57\u6BB5\uFF09`;
- }
- function close(value) {
- overlay.remove();
- resolve(value);
- }
- selectAllButton.addEventListener("click", () => {
- fieldInputs.forEach((input) => {
- input.checked = true;
- });
- syncTitle();
- syncAllGroupInputs(dialog);
- });
- resetButton.addEventListener("click", () => {
- fieldInputs.forEach((input) => {
- input.checked = true;
- });
- syncTitle();
- syncAllGroupInputs(dialog);
- });
- cancelButton.addEventListener("click", () => close(null));
- confirmButton.addEventListener("click", () => {
- const nextHeaders = fieldInputs.filter((input) => input.checked).map((input) => input.value);
- close(nextHeaders);
- });
- overlay.addEventListener("click", (event) => {
- if (event.target === overlay) {
- close(null);
- }
- });
- syncTitle();
- });
- }
- function syncAllGroupInputs(dialog) {
- dialog.querySelectorAll('[data-audience-profile-field-dialog-group="section"]').forEach((section) => {
- const groupInput = section.querySelector(":scope > label > input");
- const fieldInputs = Array.from(
- section.querySelectorAll(":scope > div input")
- );
- if (!(groupInput instanceof HTMLInputElement) || fieldInputs.length === 0) {
- return;
- }
- const checkedCount = fieldInputs.filter((input) => input.checked).length;
- groupInput.checked = checkedCount === fieldInputs.length;
- groupInput.indeterminate = checkedCount > 0 && checkedCount < fieldInputs.length;
- });
- }
- function applyOverlayStyles(overlay) {
- overlay.style.position = "fixed";
- overlay.style.inset = "0";
- overlay.style.zIndex = "2147483647";
- overlay.style.display = "flex";
- overlay.style.alignItems = "center";
- overlay.style.justifyContent = "center";
- overlay.style.background = "rgba(15, 23, 42, 0.38)";
- }
- function applyDialogStyles(dialog) {
- dialog.style.width = "680px";
- dialog.style.maxWidth = "calc(100vw - 32px)";
- dialog.style.maxHeight = "calc(100vh - 48px)";
- dialog.style.display = "flex";
- dialog.style.flexDirection = "column";
- dialog.style.background = "#ffffff";
- dialog.style.borderRadius = "8px";
- dialog.style.boxShadow = "0 18px 45px rgba(15, 23, 42, 0.22)";
- dialog.style.padding = "20px";
- dialog.style.boxSizing = "border-box";
- }
- function applyTitleStyles(title) {
- title.style.margin = "0 0 8px";
- title.style.fontSize = "18px";
- title.style.fontWeight = "700";
- title.style.color = "#1f2329";
- }
- function applyHintStyles(hint) {
- hint.style.margin = "0 0 12px";
- hint.style.fontSize = "13px";
- hint.style.lineHeight = "20px";
- hint.style.color = "#64748b";
- }
- function applyToolbarStyles(toolbar) {
- toolbar.style.display = "flex";
- toolbar.style.gap = "8px";
- toolbar.style.marginBottom = "12px";
- }
- function applyGroupContainerStyles(container) {
- container.style.display = "flex";
- container.style.flexDirection = "column";
- container.style.gap = "10px";
- container.style.overflow = "auto";
- container.style.paddingRight = "4px";
- }
- function applyGroupSectionStyles(section) {
- section.style.border = "1px solid #e5e7eb";
- section.style.borderRadius = "8px";
- section.style.padding = "10px";
- }
- function applyGroupHeaderStyles(label) {
- label.style.display = "flex";
- label.style.alignItems = "center";
- label.style.gap = "8px";
- label.style.fontWeight = "700";
- label.style.color = "#1f2329";
- label.style.marginBottom = "8px";
- }
- function applyFieldListStyles(list) {
- list.style.display = "grid";
- list.style.gridTemplateColumns = "repeat(auto-fit, minmax(220px, 1fr))";
- list.style.gap = "8px";
- }
- function applyFieldLabelStyles(label) {
- label.style.display = "flex";
- label.style.alignItems = "center";
- label.style.gap = "6px";
- label.style.fontSize = "13px";
- label.style.lineHeight = "18px";
- label.style.color = "#374151";
- }
- function applyActionsStyles(actions) {
- actions.style.display = "flex";
- actions.style.justifyContent = "flex-end";
- actions.style.columnGap = "8px";
- actions.style.marginTop = "14px";
- }
- function applyPrimaryButtonStyles(button) {
- button.style.height = "32px";
- button.style.padding = "0 15px";
- button.style.border = "1px solid #7f1d2d";
- button.style.borderRadius = "8px";
- button.style.background = "#7f1d2d";
- button.style.color = "#ffffff";
- button.style.fontWeight = "600";
- }
- function applySecondaryButtonStyles(button) {
- button.style.height = "32px";
- button.style.padding = "0 15px";
- button.style.border = "1px solid #d0d7de";
- button.style.borderRadius = "8px";
- button.style.background = "#ffffff";
- button.style.color = "#1f2329";
- button.style.fontWeight = "600";
- }
-
- // src/content/market/author-id-dialog.ts
- function promptForAuthorIds(document2) {
- return new Promise((resolve) => {
- const overlay = document2.createElement("div");
- overlay.dataset.authorIdDialog = "overlay";
- applyOverlayStyles2(overlay);
- const dialog = document2.createElement("section");
- applyDialogStyles2(dialog);
- const title = document2.createElement("h2");
- title.textContent = "\u6309\u661F\u56FEID\u5BFC\u51FA";
- applyTitleStyles2(title);
- const textarea = document2.createElement("textarea");
- textarea.dataset.authorIdDialogInput = "textarea";
- textarea.placeholder = "\u6BCF\u884C\u4E00\u4E2A\u661F\u56FEID\uFF0C\u4E5F\u652F\u6301\u9017\u53F7\u3001\u7A7A\u683C\u5206\u9694";
- applyTextareaStyles(textarea);
- const hint = document2.createElement("p");
- hint.textContent = "\u7C98\u8D34\u5BA2\u6237\u63D0\u4F9B\u7684\u8FBE\u4EBA\u661F\u56FEID\uFF0C\u786E\u8BA4\u540E\u5C06\u6279\u91CF\u5BFC\u51FA\u8FBE\u4EBA\u6570\u636E\u3002";
- applyHintStyles2(hint);
- const actions = document2.createElement("div");
- applyActionsStyles2(actions);
- const cancelButton = document2.createElement("button");
- cancelButton.type = "button";
- cancelButton.textContent = "\u53D6\u6D88";
- applySecondaryButtonStyles2(cancelButton);
- const confirmButton = document2.createElement("button");
- confirmButton.type = "button";
- confirmButton.textContent = "\u5F00\u59CB\u5BFC\u51FA";
- applyPrimaryButtonStyles2(confirmButton);
- actions.append(cancelButton, confirmButton);
- dialog.append(title, hint, textarea, actions);
- overlay.append(dialog);
- document2.body.appendChild(overlay);
- const close = (value) => {
- overlay.remove();
- resolve(value);
- };
- cancelButton.addEventListener("click", () => close(null));
- confirmButton.addEventListener("click", () => close(textarea.value));
- overlay.addEventListener("click", (event) => {
- if (event.target === overlay) {
- close(null);
- }
- });
- textarea.focus();
- });
- }
- function applyOverlayStyles2(overlay) {
- overlay.style.position = "fixed";
- overlay.style.inset = "0";
- overlay.style.zIndex = "2147483647";
- overlay.style.display = "flex";
- overlay.style.alignItems = "center";
- overlay.style.justifyContent = "center";
- overlay.style.background = "rgba(15, 23, 42, 0.38)";
- }
- function applyDialogStyles2(dialog) {
- dialog.style.width = "520px";
- dialog.style.maxWidth = "calc(100vw - 32px)";
- dialog.style.background = "#ffffff";
- dialog.style.borderRadius = "8px";
- dialog.style.boxShadow = "0 18px 45px rgba(15, 23, 42, 0.22)";
- dialog.style.padding = "20px";
- dialog.style.boxSizing = "border-box";
- }
- function applyTitleStyles2(title) {
- title.style.margin = "0 0 8px";
- title.style.fontSize = "18px";
- title.style.fontWeight = "700";
- title.style.color = "#1f2329";
- }
- function applyHintStyles2(hint) {
- hint.style.margin = "0 0 12px";
- hint.style.fontSize = "13px";
- hint.style.lineHeight = "20px";
- hint.style.color = "#64748b";
- }
- function applyTextareaStyles(textarea) {
- textarea.style.width = "100%";
- textarea.style.height = "220px";
- textarea.style.resize = "vertical";
- textarea.style.border = "1px solid #d0d7de";
- textarea.style.borderRadius = "6px";
- textarea.style.padding = "10px";
- textarea.style.boxSizing = "border-box";
- textarea.style.fontSize = "13px";
- textarea.style.lineHeight = "20px";
- textarea.style.fontFamily = "ui-monospace, SFMono-Regular, Menlo, monospace";
- textarea.style.color = "#1f2329";
- }
- function applyActionsStyles2(actions) {
- actions.style.display = "flex";
- actions.style.justifyContent = "flex-end";
- actions.style.columnGap = "8px";
- actions.style.marginTop = "14px";
- }
- function applyPrimaryButtonStyles2(button) {
- button.style.height = "32px";
- button.style.padding = "0 15px";
- button.style.border = "1px solid #7f1d2d";
- button.style.borderRadius = "8px";
- button.style.background = "#7f1d2d";
- button.style.color = "#ffffff";
- button.style.fontWeight = "600";
- }
- function applySecondaryButtonStyles2(button) {
- button.style.height = "32px";
- button.style.padding = "0 15px";
- button.style.border = "1px solid #d0d7de";
- button.style.borderRadius = "8px";
- button.style.background = "#ffffff";
- button.style.color = "#1f2329";
- button.style.fontWeight = "600";
- }
-
- // src/content/market/batch-name-dialog.ts
- var DIALOG_STYLE_ID = "sces-batch-name-dialog-style";
- var activeDialogs = /* @__PURE__ */ new WeakMap();
- function promptForBatchName(document2) {
- const existingDialog = activeDialogs.get(document2);
- if (existingDialog) {
- existingDialog.input.focus();
- existingDialog.input.select();
- return existingDialog.promise;
- }
- ensureDialogStyles(document2);
- const dialogRoot = document2.createElement("div");
- dialogRoot.dataset.pluginBatchNameDialog = "root";
- dialogRoot.setAttribute("role", "dialog");
- dialogRoot.setAttribute("aria-modal", "true");
- dialogRoot.setAttribute("aria-labelledby", "sces-batch-name-title");
- applyOverlayStyles3(dialogRoot);
- const dialogPanel = document2.createElement("div");
- applyPanelStyles(dialogPanel);
- const title = document2.createElement("h2");
- title.id = "sces-batch-name-title";
- title.textContent = "\u63D0\u4EA4\u6279\u6B21";
- applyTitleStyles3(title);
- const description = document2.createElement("p");
- description.textContent = "\u8BF7\u8F93\u5165\u6279\u6B21\u540D\u79F0\uFF0C\u4FBF\u4E8E\u540E\u7EED\u5728\u7CFB\u7EDF\u4E2D\u8BC6\u522B\u548C\u8FFD\u8E2A\u3002";
- applyDescriptionStyles(description);
- const input = document2.createElement("input");
- input.type = "text";
- input.dataset.pluginBatchNameInput = "input";
- input.placeholder = "\u4F8B\u5982\uFF1A618\u8FBE\u4EBA\u7B5B\u9009\u7B2C\u4E00\u6279";
- input.maxLength = 60;
- applyInputStyles(input);
- const errorText = document2.createElement("p");
- errorText.dataset.pluginBatchNameError = "text";
- applyErrorStyles(errorText);
- const buttonRow = document2.createElement("div");
- applyButtonRowStyles(buttonRow);
- const cancelButton = document2.createElement("button");
- cancelButton.type = "button";
- cancelButton.dataset.pluginBatchNameCancel = "button";
- cancelButton.textContent = "\u53D6\u6D88";
- applySecondaryButtonStyles3(cancelButton);
- const confirmButton = document2.createElement("button");
- confirmButton.type = "button";
- confirmButton.dataset.pluginBatchNameConfirm = "button";
- confirmButton.textContent = "\u786E\u8BA4\u63D0\u4EA4";
- applyPrimaryButtonStyles3(confirmButton);
- buttonRow.append(cancelButton, confirmButton);
- dialogPanel.append(title, description, input, errorText, buttonRow);
- dialogRoot.appendChild(dialogPanel);
- document2.body.appendChild(dialogRoot);
- const dialogPromise = new Promise((resolve) => {
- const closeDialog = (value) => {
- activeDialogs.delete(document2);
- dialogRoot.remove();
- document2.removeEventListener("keydown", handleDocumentKeydown, true);
- resolve(value);
- };
- const submitValue = () => {
- const value = input.value.trim();
- if (!value) {
- errorText.textContent = "\u8BF7\u8F93\u5165\u6279\u6B21\u540D\u79F0";
- input.setAttribute("aria-invalid", "true");
- input.focus();
- return;
- }
- closeDialog(value);
- };
- const handleDocumentKeydown = (event) => {
- if (event.key === "Escape") {
- event.preventDefault();
- closeDialog(null);
- return;
- }
- if (event.key === "Enter") {
- event.preventDefault();
- submitValue();
- }
- };
- input.addEventListener("input", () => {
- if (!input.value.trim()) {
- return;
- }
- errorText.textContent = "";
- input.removeAttribute("aria-invalid");
- });
- cancelButton.addEventListener("click", () => {
- closeDialog(null);
- });
- confirmButton.addEventListener("click", () => {
- submitValue();
- });
- dialogRoot.addEventListener("click", (event) => {
- if (event.target === dialogRoot) {
- closeDialog(null);
- }
- });
- document2.addEventListener("keydown", handleDocumentKeydown, true);
- });
- activeDialogs.set(document2, {
- input,
- promise: dialogPromise
- });
- input.focus();
- return dialogPromise;
- }
- function ensureDialogStyles(document2) {
- if (document2.getElementById(DIALOG_STYLE_ID)) {
- return;
- }
- const style = document2.createElement("style");
- style.id = DIALOG_STYLE_ID;
- style.textContent = `
- [data-plugin-batch-name-dialog="root"] {
- animation: sces-batch-name-fade-in 0.16s ease;
- }
-
- @keyframes sces-batch-name-fade-in {
- from {
- opacity: 0;
- }
- to {
- opacity: 1;
- }
- }
- `;
- document2.head.appendChild(style);
- }
- function applyOverlayStyles3(root) {
- root.style.position = "fixed";
- root.style.inset = "0";
- root.style.background = "rgba(15, 23, 42, 0.38)";
- root.style.display = "flex";
- root.style.alignItems = "center";
- root.style.justifyContent = "center";
- root.style.padding = "24px";
- root.style.zIndex = "2147483647";
- }
- function applyPanelStyles(panel) {
- panel.style.width = "min(420px, calc(100vw - 32px))";
- panel.style.background = "#fffaf9";
- panel.style.border = "1px solid rgba(127, 29, 45, 0.14)";
- panel.style.borderRadius = "18px";
- panel.style.boxShadow = "0 28px 70px rgba(15, 23, 42, 0.22)";
- panel.style.padding = "24px";
- panel.style.boxSizing = "border-box";
- }
- function applyTitleStyles3(title) {
- title.style.margin = "0";
- title.style.color = "#4c0519";
- title.style.fontSize = "20px";
- title.style.fontWeight = "700";
- title.style.lineHeight = "28px";
- }
- function applyDescriptionStyles(description) {
- description.style.margin = "10px 0 0";
- description.style.color = "#64748b";
- description.style.fontSize = "13px";
- description.style.lineHeight = "20px";
- }
- function applyInputStyles(input) {
- input.style.width = "100%";
- input.style.height = "42px";
- input.style.marginTop = "18px";
- input.style.padding = "0 14px";
- input.style.boxSizing = "border-box";
- input.style.border = "1px solid #d8c1c6";
- input.style.borderRadius = "12px";
- input.style.background = "#ffffff";
- input.style.color = "#1f2937";
- input.style.fontSize = "14px";
- input.style.outline = "none";
- }
- function applyErrorStyles(errorText) {
- errorText.style.minHeight = "20px";
- errorText.style.margin = "8px 0 0";
- errorText.style.color = "#b91c1c";
- errorText.style.fontSize = "12px";
- errorText.style.lineHeight = "18px";
- }
- function applyButtonRowStyles(buttonRow) {
- buttonRow.style.display = "flex";
- buttonRow.style.justifyContent = "flex-end";
- buttonRow.style.gap = "10px";
- buttonRow.style.marginTop = "18px";
- }
- function applySecondaryButtonStyles3(button) {
- button.style.height = "36px";
- button.style.padding = "0 16px";
- button.style.border = "1px solid #d7dde6";
- button.style.borderRadius = "10px";
- button.style.background = "#ffffff";
- button.style.color = "#334155";
- button.style.fontWeight = "600";
- button.style.cursor = "pointer";
- }
- function applyPrimaryButtonStyles3(button) {
- button.style.height = "36px";
- button.style.padding = "0 16px";
- button.style.border = "1px solid #7f1d2d";
- button.style.borderRadius = "10px";
- button.style.background = "#7f1d2d";
- button.style.color = "#ffffff";
- button.style.fontWeight = "600";
- button.style.cursor = "pointer";
- }
-
- // src/content/market/batch-payload.ts
- function createBatchPayload(options) {
- const logtoUserId = options.authState.userInfo?.sub?.trim();
- if (!logtoUserId) {
- throw new Error("batch submit user id unavailable");
- }
- const resource = options.authState.resource?.trim();
- if (!resource) {
- throw new Error("batch submit resource unavailable");
- }
- const batchName = options.batchName.trim();
- if (!batchName) {
- throw new Error("batch submit batch name is required");
- }
- return {
- authors: options.records.map((record) => ({
- authorId: record.authorId,
- authorName: record.authorName,
- ...record.coreUserId ? { authorUid: record.coreUserId } : {}
- })),
- batchName,
- createdAt: options.createdAt,
- creatorName: options.authState.userInfo?.name ?? options.authState.userInfo?.username ?? logtoUserId,
- logtoUserId,
- resource
- };
- }
-
- // src/content/market/market-list-row.ts
- var PAGE_NUMBER_KEYS = [
- "currentPage",
- "page",
- "pageNo",
- "pageNum",
- "page_no",
- "page_num"
- ];
- var PAGE_SIZE_KEYS = [
- "limit",
- "pageSize",
- "page_size",
- "size"
- ];
- var TOTAL_COUNT_KEYS = [
- "total",
- "totalCount",
- "total_count"
- ];
- var TOTAL_PAGE_KEYS = [
- "pageCount",
- "page_count",
- "totalPage",
- "totalPages",
- "total_page",
- "total_pages"
- ];
- function mapMarketListRow(row) {
- const attributeDatas = readMarketAttributeDatas(row);
- const singleVideoAfterSearchRate = normalizeMarketListRate(
- readMarketFieldValue(row, attributeDatas, "avg_search_after_view_rate_30d")
- );
- return {
- authorId: readString3(readMarketFieldValue(row, attributeDatas, "star_id")) ?? readString3(readMarketFieldValue(row, attributeDatas, "id")) ?? "",
- authorName: readString3(readMarketFieldValue(row, attributeDatas, "nickname")) ?? readString3(readMarketFieldValue(row, attributeDatas, "nick_name")) ?? "",
- coreUserId: readString3(readMarketFieldValue(row, attributeDatas, "core_user_id")) ?? void 0,
- exportFields: buildMarketExportFieldFallbacks(row, attributeDatas),
- hasDirectRatesSource: true,
- location: readMarketLocation(row, attributeDatas),
- price21To60s: readMarketPrice21To60s(row, attributeDatas),
- rates: singleVideoAfterSearchRate ? {
- singleVideoAfterSearchRate
- } : void 0
- };
- }
- function parseMarketListResponse(payload) {
- const container = findMarketListContainer(payload);
- if (!container) {
- return null;
- }
- const marketList = readMarketListArray(container);
- if (!marketList) {
- return null;
- }
- return {
- currentPage: readKnownNumberDeep(container, PAGE_NUMBER_KEYS) ?? void 0,
- pageSize: readKnownNumberDeep(container, PAGE_SIZE_KEYS) ?? void 0,
- records: marketList.map((row) => isRecord4(row) ? mapMarketListRow(row) : null).filter(
- (row) => row !== null && Boolean(row.authorId || row.authorName)
- ),
- totalCount: readKnownNumberDeep(container, TOTAL_COUNT_KEYS) ?? void 0,
- totalPages: readKnownNumberDeep(container, TOTAL_PAGE_KEYS) ?? void 0
- };
- }
- function readKnownPaginationNumber(value, kind) {
- if (!isRecord4(value)) {
- return null;
- }
- return readKnownNumberDeep(value, kind === "page" ? PAGE_NUMBER_KEYS : PAGE_SIZE_KEYS);
- }
- function findMarketListContainer(value) {
- const queue = [value];
- while (queue.length > 0) {
- const current = queue.shift();
- if (!isRecord4(current)) {
- continue;
- }
- if (readMarketListArray(current)) {
- return current;
- }
- Object.values(current).forEach((entry) => {
- queue.push(unwrapVueRef(entry));
- });
- }
- return null;
- }
- function readMarketListArray(record) {
- const marketList = unwrapVueRef(record.marketList);
- if (Array.isArray(marketList)) {
- return marketList;
- }
- const authors = unwrapVueRef(record.authors);
- if (Array.isArray(authors)) {
- return authors;
- }
- return null;
- }
- function unwrapVueRef(value) {
- if (isRecord4(value) && "value" in value) {
- return value.value;
- }
- return value;
- }
- function isRecord4(value) {
- return typeof value === "object" && value !== null;
- }
- function readMarketAttributeDatas(record) {
- return isRecord4(record.attribute_datas) ? record.attribute_datas : {};
- }
- function readMarketFieldValue(record, attributeDatas, field) {
- return record[field] ?? attributeDatas[field];
- }
- function readString3(value) {
- return typeof value === "string" ? value : null;
- }
- function normalizeMarketListRate(value) {
- if (typeof value === "number") {
- return normalizeFractionRateDisplay(String(value));
- }
- return typeof value === "string" ? normalizeFractionRateDisplay(value) : null;
- }
- function normalizeExportCellText(value) {
- return value?.replace(/\s+/g, " ").trim() ?? "";
- }
- function buildMarketExportFieldFallbacks(record, attributeDatas) {
- const exportFields = {};
- const authorInfo = buildMarketAuthorInfo(record, attributeDatas);
- const authorType = buildMarketAuthorType(record, attributeDatas);
- const contentTheme = buildMarketContentTheme(record, attributeDatas);
- const connectedUsers = formatWanValue(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "link_link_cnt_by_industry"))
- );
- const followerCount = formatWanValue(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "follower"))
- );
- const expectedCpm = formatDecimalDisplay(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "prospective_20_60_cpm"))
- );
- const expectedPlayCount = formatWanValue(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "expected_play_num"))
- );
- const interactionRate = formatFractionPercent(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "interact_rate_within_30d"))
- );
- const finishRate = formatFractionPercent(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "play_over_rate_within_30d"))
- );
- const burstRate = readBurstRateDisplay(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "burst_text_rate"))
- );
- const price21To60s = readMarketPrice21To60s(record, attributeDatas);
- const representativeVideo = readMarketRepresentativeVideo(record, attributeDatas);
- assignExportField(exportFields, "\u8FBE\u4EBA\u4FE1\u606F", authorInfo);
- assignExportField(exportFields, "\u4EE3\u8868\u89C6\u9891", representativeVideo);
- assignExportField(exportFields, "\u8FBE\u4EBA\u7C7B\u578B", authorType);
- assignExportField(exportFields, "\u5185\u5BB9\u4E3B\u9898", contentTheme);
- assignExportField(exportFields, "\u8FDE\u63A5\u7528\u6237\u6570", connectedUsers);
- assignExportField(exportFields, "\u7C89\u4E1D\u6570", followerCount);
- assignExportField(exportFields, "\u9884\u671FCPM", expectedCpm);
- assignExportField(exportFields, "\u9884\u671F\u64AD\u653E\u91CF", expectedPlayCount);
- assignExportField(exportFields, "\u4E92\u52A8\u7387", interactionRate);
- assignExportField(exportFields, "\u5B8C\u64AD\u7387", finishRate);
- assignExportField(exportFields, "\u7206\u6587\u7387", burstRate);
- assignExportField(exportFields, "21-60s\u62A5\u4EF7", price21To60s);
- return Object.keys(exportFields).length > 0 ? exportFields : void 0;
- }
- function assignExportField(exportFields, key, value) {
- if (hasTextValue(value)) {
- exportFields[key] = value;
- }
- }
- function hasTextValue(value) {
- return Boolean(value && value.trim().length > 0);
- }
- function buildMarketAuthorInfo(record, attributeDatas) {
- const nickname = readString3(readMarketFieldValue(record, attributeDatas, "nickname")) ?? readString3(readMarketFieldValue(record, attributeDatas, "nick_name")) ?? "";
- const parts = [
- nickname,
- readMarketGenderLabel(readMarketFieldValue(record, attributeDatas, "gender")),
- readString3(readMarketFieldValue(record, attributeDatas, "city")) ?? ""
- ].filter((value) => Boolean(value));
- return parts.length > 0 ? parts.join(" ") : void 0;
- }
- function buildMarketAuthorType(record, attributeDatas) {
- const tagsRelation = readRecordLike(
- readMarketFieldValue(record, attributeDatas, "tags_relation")
- );
- if (tagsRelation) {
- const primaryTag = Object.keys(tagsRelation)[0];
- if (hasTextValue(primaryTag)) {
- return primaryTag;
- }
- }
- return void 0;
- }
- function buildMarketContentTheme(record, attributeDatas) {
- const themes = readStringArray(
- readMarketFieldValue(record, attributeDatas, "content_theme_labels_180d")
- );
- if (themes.length === 0) {
- return void 0;
- }
- if (themes.length <= 2) {
- return themes.join(" ");
- }
- return `${themes.slice(0, 2).join(" ")} ${themes.length - 2}+`;
- }
- function readMarketLocation(record, attributeDatas) {
- return readString3(readMarketFieldValue(record, attributeDatas, "city")) ?? void 0;
- }
- function readMarketPrice21To60s(record, attributeDatas) {
- return formatCurrencyValue(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "price_20_60"))
- );
- }
- function readMarketRepresentativeVideo(record, attributeDatas) {
- const items = readArrayLike(readMarketFieldValue(record, attributeDatas, "items"));
- for (const item of items) {
- if (!isRecord4(item)) {
- continue;
- }
- const title = readString3(item.title);
- if (hasTextValue(title)) {
- return normalizeExportCellText(title);
- }
- }
- return void 0;
- }
- function readMarketGenderLabel(value) {
- const rawValue = typeof value === "number" ? String(value) : readString3(value);
- if (rawValue === "1") {
- return "\u7537";
- }
- if (rawValue === "2") {
- return "\u5973";
- }
- return void 0;
- }
- function readBurstRateDisplay(value) {
- if (value === null) {
- return void 0;
- }
- if (value <= 0) {
- return "-";
- }
- return formatFractionPercent(value);
- }
- function formatCurrencyValue(value) {
- if (value === null) {
- return void 0;
- }
- return `\xA5${value.toLocaleString("en-US", {
- maximumFractionDigits: 0
- })}`;
- }
- function formatWanValue(value) {
- if (value === null) {
- return void 0;
- }
- return `${formatDecimalWithGrouping(value / 1e4)}w`;
- }
- function formatFractionPercent(value) {
- if (value === null) {
- return void 0;
- }
- return `${formatDecimalDisplay(value * 100)}%`;
- }
- function formatDecimalDisplay(value) {
- if (value === null) {
- return void 0;
- }
- return value.toLocaleString("en-US", {
- maximumFractionDigits: 1,
- minimumFractionDigits: 0,
- useGrouping: false
- });
- }
- function formatDecimalWithGrouping(value) {
- return value.toLocaleString("en-US", {
- maximumFractionDigits: 1,
- minimumFractionDigits: 0
- });
- }
- function readNumericValue(value) {
- if (typeof value === "number" && Number.isFinite(value)) {
- return value;
- }
- if (typeof value === "string") {
- const trimmedValue = value.trim();
- if (!trimmedValue) {
- return null;
- }
- const parsedValue = Number(trimmedValue);
- return Number.isFinite(parsedValue) ? parsedValue : null;
- }
- return null;
- }
- function readStringArray(value) {
- if (Array.isArray(value)) {
- return value.filter((item) => typeof item === "string");
- }
- if (typeof value === "string") {
- try {
- const parsedValue = JSON.parse(value);
- return Array.isArray(parsedValue) ? parsedValue.filter((item) => typeof item === "string") : [];
- } catch {
- return [];
- }
- }
- return [];
- }
- function readArrayLike(value) {
- if (Array.isArray(value)) {
- return value;
- }
- if (typeof value === "string") {
- try {
- const parsedValue = JSON.parse(value);
- return Array.isArray(parsedValue) ? parsedValue : [];
- } catch {
- return [];
- }
- }
- return [];
- }
- function readRecordLike(value) {
- if (isRecord4(value)) {
- return value;
- }
- if (typeof value === "string") {
- try {
- const parsedValue = JSON.parse(value);
- return isRecord4(parsedValue) ? parsedValue : null;
- } catch {
- return null;
- }
- }
- return null;
- }
- function readKnownNumber(record, keys) {
- for (const key of keys) {
- const value = readNumericValue(record[key]);
- if (value !== null) {
- return value;
- }
- }
- return void 0;
- }
- function readKnownNumberDeep(value, keys) {
- if (!isRecord4(value)) {
- return null;
- }
- const directValue = readKnownNumber(value, keys);
- if (typeof directValue === "number") {
- return directValue;
- }
- for (const nestedValue of Object.values(value)) {
- const candidate = readKnownNumberDeep(unwrapVueRef(nestedValue), keys);
- if (typeof candidate === "number") {
- return candidate;
- }
- }
- return null;
- }
-
- // src/content/market/dom-sync.ts
- var BACKEND_COLUMN_KEY = "backendMetrics";
- var SELECTION_COLUMN_KEY = "selection";
- var SINGLE_COLUMN_KEY = "singleVideoAfterSearchRate";
- var PERSONAL_COLUMN_KEY = "personalVideoAfterSearchRate";
- var ACTION_HEADER_TEXT = "\u64CD\u4F5C";
- var AUTHOR_HEADER_TEXT = "\u8FBE\u4EBA\u4FE1\u606F";
- var BACKEND_HEADER_TEXT = "\u79D2\u63A2\u6307\u6807";
- var MARKET_SCROLL_HINT_TEXT = "\u6A2A\u5411\u6EDA\u52A8\u53EF\u67E5\u770B\u770B\u540E\u641C\u7387\u3001\u79D2\u63A2\u6307\u6807";
- var MARKET_SCROLLBAR_STYLE_ID = "sces-market-scrollbar-style";
- var UNAVAILABLE_RATE_TEXT = "\u6682\u65E0\u6765\u6E90";
- var UNAVAILABLE_BACKEND_METRICS_TEXT = "\u6682\u65E0\u6570\u636E";
- var SERIALIZED_MARKET_ROWS_ATTRIBUTE = "data-sces-market-rows";
- var SORTABLE_RATE_FIELDS = [SINGLE_COLUMN_KEY, PERSONAL_COLUMN_KEY];
- var BACKEND_METRIC_COLUMNS2 = [
- {
- field: "afterViewSearchRate",
- label: "\u770B\u540E\u641C\u7387"
- },
- {
- field: "afterViewSearchCount",
- label: "\u770B\u540E\u641C\u6570"
- },
- {
- field: "a3IncreaseCount",
- label: "\u65B0\u589EA3\u6570"
- },
- {
- field: "newA3Rate",
- label: "\u65B0\u589EA3\u7387"
- },
- {
- field: "cpa3",
- label: "CPA3"
- },
- {
- field: "cpSearch",
- label: "cp_search"
- }
- ];
- var SORTABLE_MARKET_FIELDS = [
- ...SORTABLE_RATE_FIELDS,
- ...BACKEND_METRIC_COLUMNS2.map((column) => column.field)
- ];
- function syncMarketTable(root) {
- return syncSyntheticMarketTable(root) ?? syncDivGridMarketTable(root);
- }
- function readMarketPageSignature(root) {
- const document2 = getOwnerDocument(root);
- const explicitPageIndex = document2?.documentElement.getAttribute("data-test-page-index") ?? "";
- const activePageIndex = document2?.querySelector(".el-pagination .number.active, .xt-pagination .number.active")?.textContent?.trim() ?? "";
- const authorIds = readRawAuthorIds(root).join("|");
- return `${explicitPageIndex || activePageIndex}::${authorIds}`;
- }
- function findNextPageControl(root) {
- const document2 = getOwnerDocument(root);
- if (!document2) {
- return null;
- }
- const explicitControl = document2.querySelector('[data-testid="next-page"]');
- if (explicitControl instanceof document2.defaultView.HTMLElement) {
- return explicitControl;
- }
- const paginationNextControl = document2.querySelector(
- ".el-pagination .btn-next, .xt-pagination .btn-next"
- );
- if (paginationNextControl instanceof document2.defaultView.HTMLElement) {
- return paginationNextControl;
- }
- const candidates = Array.from(
- document2.querySelectorAll("button, a, [role='button']")
- ).filter(
- (element) => element instanceof document2.defaultView.HTMLElement
- );
- return candidates.find(
- (element) => /下一页|next/i.test(normalizeExportCellText2(element.textContent))
- ) ?? null;
- }
- function isPageControlDisabled(control) {
- if (!control) {
- return true;
- }
- if (control instanceof HTMLButtonElement) {
- return control.disabled;
- }
- return control.getAttribute("aria-disabled") === "true";
- }
- function renderMarketRowState(rowDom, record) {
- renderBackendMetricsCells(rowDom.backendMetricsCells, record);
- if (record.status === "success" && record.rates) {
- rowDom.singleCell.textContent = readRateCellText(
- record.rates.singleVideoAfterSearchRate
- );
- rowDom.personalCell.textContent = readRateCellText(
- record.rates.personalVideoAfterSearchRate
- );
- return;
- }
- if (record.status === "loading") {
- rowDom.singleCell.textContent = "\u52A0\u8F7D\u4E2D...";
- rowDom.personalCell.textContent = "\u52A0\u8F7D\u4E2D...";
- return;
- }
- if (record.status === "failed") {
- rowDom.singleCell.textContent = "\u52A0\u8F7D\u5931\u8D25";
- rowDom.personalCell.textContent = "\u52A0\u8F7D\u5931\u8D25";
- return;
- }
- rowDom.singleCell.textContent = "";
- rowDom.personalCell.textContent = "";
- }
- function applyRowVisibility(table, visibleAuthorIds) {
- table.rows.forEach((rowDom) => {
- const isVisible = visibleAuthorIds.has(rowDom.authorId);
- rowDom.visibilityTargets.forEach((target) => {
- target.hidden = !isVisible;
- });
- });
- }
- function applyRowOrder(table, orderedAuthorIds) {
- const rowById = new Map(table.rows.map((rowDom) => [rowDom.authorId, rowDom]));
- const orderByAuthorId = new Map(
- orderedAuthorIds.map((authorId, index) => [authorId, index])
- );
- orderedAuthorIds.forEach((authorId) => {
- const rowDom = rowById.get(authorId);
- if (!rowDom) {
- return;
- }
- rowDom.orderTargets.forEach(({ container, mode, node }) => {
- const visualOrder = orderByAuthorId.get(authorId) ?? orderedAuthorIds.length;
- if (mode === "css") {
- container.dataset.marketOrderMode = "css";
- container.style.display = "flex";
- container.style.flexDirection = "column";
- node.style.order = String(visualOrder);
- return;
- }
- container.dataset.marketOrderMode = "dom";
- container.appendChild(node);
- });
- });
- }
- function syncPluginSortHeaders(root, options) {
- SORTABLE_MARKET_FIELDS.forEach((field) => {
- const cell = root.querySelector(
- `[data-market-header-cell="${field}"]`
- );
- if (!cell) {
- return;
- }
- syncSortableHeaderCell(cell, {
- direction: options.activeSort?.field === field ? options.activeSort.direction : "none",
- field,
- onToggleSort: options.onToggleSort
- });
- });
- }
- function syncMarketSelectionState(table, selectedAuthorIds) {
- table.rows.forEach((rowDom) => {
- rowDom.selectionCheckbox.dataset.marketSelectionAuthorId = rowDom.authorId;
- rowDom.selectionCheckbox.checked = selectedAuthorIds.has(rowDom.authorId);
- });
- if (!table.headerSelectionCheckbox) {
- return;
- }
- const visibleRows = table.rows.filter(
- (rowDom) => rowDom.visibilityTargets.some((target) => !target.hidden)
- );
- const scopedRows = visibleRows.length > 0 ? visibleRows : table.rows;
- const selectedCount = scopedRows.filter(
- (rowDom) => selectedAuthorIds.has(rowDom.authorId)
- ).length;
- table.headerSelectionCheckbox.indeterminate = selectedCount > 0 && selectedCount < scopedRows.length;
- table.headerSelectionCheckbox.checked = scopedRows.length > 0 && selectedCount === scopedRows.length;
- table.headerSelectionCheckbox.disabled = scopedRows.length === 0;
- }
- function syncSyntheticMarketTable(root) {
- const header = root.querySelector("[data-market-header]");
- const body = root.querySelector("[data-market-body]");
- if (!header || !body) {
- return null;
- }
- const selectionHeader = ensureSyntheticHeaderCell(header, SELECTION_COLUMN_KEY, "");
- const headerSelectionCheckbox = ensureSelectionHeaderControl(selectionHeader);
- ensureSyntheticHeaderCell(header, SINGLE_COLUMN_KEY, "\u5546\u5355\u89C6\u9891\u770B\u540E\u641C\u7387");
- ensureSyntheticHeaderCell(header, PERSONAL_COLUMN_KEY, "\u4E2A\u4EBA\u89C6\u9891\u770B\u540E\u641C\u7387");
- BACKEND_METRIC_COLUMNS2.forEach(({ field, label }) => {
- ensureSyntheticHeaderCell(header, field, label);
- });
- const headerLabelsByField = readSyntheticHeaderLabels(header);
- const rows = Array.from(body.querySelectorAll("[data-market-row]")).map(
- (rowElement) => {
- const row = rowElement;
- const selectionCell = ensureSyntheticRowCell(row, SELECTION_COLUMN_KEY);
- const selectionCheckbox = ensureSelectionRowControl(selectionCell);
- const singleCell = ensureSyntheticRowCell(row, SINGLE_COLUMN_KEY);
- const personalCell = ensureSyntheticRowCell(row, PERSONAL_COLUMN_KEY);
- const backendMetricsCells = Object.fromEntries(
- BACKEND_METRIC_COLUMNS2.map(({ field }) => [field, ensureSyntheticRowCell(row, field)])
- );
- const authorId = row.dataset.authorId ?? "";
- selectionCheckbox.dataset.marketSelectionAuthorId = authorId;
- return {
- authorId,
- authorName: row.querySelector('[data-market-field="authorName"]')?.textContent?.trim() ?? "",
- backendMetricsCells,
- exportFields: readSyntheticExportFields(row, headerLabelsByField),
- hasDirectRatesSource: false,
- location: row.querySelector('[data-market-field="location"]')?.textContent?.trim() ?? "",
- orderTargets: [
- {
- container: body,
- mode: "dom",
- node: row
- }
- ],
- personalCell,
- price21To60s: row.querySelector('[data-market-field="price21To60s"]')?.textContent?.trim() ?? "",
- rates: void 0,
- row,
- selectionCheckbox,
- singleCell,
- visibilityTargets: [row]
- };
- }
- );
- return {
- headerSelectionCheckbox,
- rows
- };
- }
- function syncDivGridMarketTable(root) {
- const document2 = getOwnerDocument(root);
- if (!document2) {
- return null;
- }
- for (const marketRoot of document2.querySelectorAll(".base-author-list")) {
- if (!(marketRoot instanceof document2.defaultView.HTMLElement)) {
- continue;
- }
- const syncedTable = syncDivGridRoot(marketRoot);
- if (syncedTable) {
- return syncedTable;
- }
- }
- return null;
- }
- function readRawAuthorIds(root) {
- const document2 = getOwnerDocument(root);
- const syntheticAuthorIds = readSyntheticAuthorIds(root);
- if (syntheticAuthorIds && syntheticAuthorIds.length > 0) {
- return syntheticAuthorIds;
- }
- const divGridAuthorIds = readDivGridAuthorIds(root);
- if (divGridAuthorIds && divGridAuthorIds.length > 0) {
- return divGridAuthorIds;
- }
- if (!document2) {
- return [];
- }
- return readSerializedMarketRows(document2).map((row) => row.authorId).filter((authorId) => Boolean(authorId));
- }
- function readSyntheticAuthorIds(root) {
- const body = root.querySelector("[data-market-body]");
- if (!body) {
- return null;
- }
- return Array.from(body.querySelectorAll("[data-market-row]")).map(
- (row) => row instanceof HTMLElement ? row.dataset.authorId ?? "" : ""
- ).filter((authorId) => Boolean(authorId));
- }
- function readDivGridAuthorIds(root) {
- const document2 = getOwnerDocument(root);
- if (!document2) {
- return null;
- }
- const marketRoot = document2.querySelector(".base-author-list");
- if (!(marketRoot instanceof document2.defaultView.HTMLElement)) {
- return null;
- }
- const bodySection = Array.from(marketRoot.querySelectorAll(".section-wrapper")).find(
- (section) => section instanceof document2.defaultView.HTMLElement && !section.classList.contains("sticky-header")
- );
- const authorSection = bodySection ? Array.from(bodySection.children).find(
- (child) => child instanceof document2.defaultView.HTMLElement && child.querySelector(".content-column .content-cell")
- ) ?? null : null;
- const authorColumn = authorSection ? getNativeAuthorColumn(authorSection) : null;
- if (!authorColumn) {
- return null;
- }
- return getDirectContentCells(authorColumn).map((cell) => extractAuthorId(cell)).filter((authorId) => Boolean(authorId));
- }
- function syncDivGridRoot(root) {
- const headerSection = root.querySelector(
- ".section-wrapper.sticky-header"
- );
- const bodySection = Array.from(root.querySelectorAll(".section-wrapper")).find(
- (section) => section instanceof root.ownerDocument.defaultView.HTMLElement && !section.classList.contains("sticky-header")
- );
- if (!headerSection || !bodySection) {
- return null;
- }
- const authorHeader = findCellByText(getDirectHeaderCells(headerSection), AUTHOR_HEADER_TEXT);
- const actionHeader = findCellByText(getDirectHeaderCells(headerSection), ACTION_HEADER_TEXT);
- if (!authorHeader || !actionHeader) {
- return null;
- }
- const rightHeaderSection = actionHeader.parentElement;
- if (!(rightHeaderSection instanceof root.ownerDocument.defaultView.HTMLElement)) {
- return null;
- }
- const middleHeaderSection = findPreviousNativeSection(rightHeaderSection) ?? rightHeaderSection;
- const authorSection = getIndexedChild(
- bodySection,
- getDirectChildIndex(headerSection, authorHeader)
- );
- const authorHeaderSection = getIndexedChild(
- headerSection,
- getDirectChildIndex(headerSection, authorHeader)
- );
- const rightSection = getIndexedChild(
- bodySection,
- getDirectChildIndex(headerSection, actionHeader)
- );
- if (!authorSection || !authorHeaderSection || !rightSection) {
- return null;
- }
- const middleBodySection = findPreviousNativeSection(rightSection) ?? rightSection;
- const pluginHeaderSection = ensurePluginSection(headerSection, rightHeaderSection, {
- testId: "plugin-header",
- type: "header"
- });
- const pluginBodySection = ensurePluginSection(bodySection, rightSection, {
- testId: "plugin-section",
- type: "body"
- });
- const authorColumn = getNativeAuthorColumn(authorSection);
- const actionColumn = getActionColumn(rightSection);
- if (!authorColumn || !actionColumn) {
- return null;
- }
- const rowCount = getDirectContentCells(authorColumn).length;
- const selectionHeaderCell = ensureDivHeaderCell(
- authorHeaderSection,
- authorHeader,
- SELECTION_COLUMN_KEY,
- ""
- );
- const headerSelectionCheckbox = ensureSelectionHeaderControl(selectionHeaderCell);
- const selectionColumn = ensureDivBodyColumn(
- authorSection,
- authorColumn,
- SELECTION_COLUMN_KEY,
- rowCount
- );
- const headerTemplateCell = getDirectHeaderCells(middleHeaderSection).at(-1) ?? findPreviousHeaderCell(actionHeader) ?? actionHeader;
- const bodyTemplateColumn = getDirectContentColumns(middleBodySection).at(-1) ?? findPreviousColumn(actionColumn) ?? actionColumn;
- ensureDivHeaderCell(
- pluginHeaderSection,
- headerTemplateCell,
- SINGLE_COLUMN_KEY,
- "\u5546\u5355\u89C6\u9891\u770B\u540E\u641C\u7387"
- );
- ensureDivHeaderCell(
- pluginHeaderSection,
- headerTemplateCell,
- PERSONAL_COLUMN_KEY,
- "\u4E2A\u4EBA\u89C6\u9891\u770B\u540E\u641C\u7387"
- );
- const singleColumn = ensureDivBodyColumn(
- pluginBodySection,
- bodyTemplateColumn,
- SINGLE_COLUMN_KEY,
- rowCount
- );
- const personalColumn = ensureDivBodyColumn(
- pluginBodySection,
- bodyTemplateColumn,
- PERSONAL_COLUMN_KEY,
- rowCount
- );
- const backendMetricColumns = Object.fromEntries(
- BACKEND_METRIC_COLUMNS2.map(({ field, label }) => {
- ensureDivHeaderCell(pluginHeaderSection, headerTemplateCell, field, label);
- return [
- field,
- ensureDivBodyColumn(
- pluginBodySection,
- bodyTemplateColumn,
- field,
- rowCount
- )
- ];
- })
- );
- syncContainerWidth(pluginHeaderSection);
- syncContainerWidth(pluginBodySection);
- syncContainerWidth(authorHeaderSection);
- syncContainerWidth(authorSection);
- ensureVisibleHorizontalScroll(headerSection);
- ensureVisibleHorizontalScroll(bodySection);
- ensureScrollHint(root, headerSection);
- const allBodyColumns = Array.from(bodySection.children).flatMap(
- (section) => section instanceof root.ownerDocument.defaultView.HTMLElement ? getDirectContentColumns(section) : []
- );
- const allHeaderCells = Array.from(headerSection.children).flatMap(
- (section) => section instanceof root.ownerDocument.defaultView.HTMLElement ? getDirectHeaderCells(section) : []
- );
- const authorCells = getDirectContentCells(authorColumn);
- const selectionCells = getDirectContentCells(selectionColumn);
- const singleCells = getDirectContentCells(singleColumn);
- const personalCells = getDirectContentCells(personalColumn);
- const backendMetricCellsByField = Object.fromEntries(
- BACKEND_METRIC_COLUMNS2.map(({ field }) => [
- field,
- getDirectContentCells(backendMetricColumns[field])
- ])
- );
- const priceColumn = findPreviousColumn(actionColumn);
- const priceCells = priceColumn ? getDirectContentCells(priceColumn) : [];
- const remainingVueMarketRows = [...readVueMarketRows(root)];
- const remainingSerializedMarketRows = [...readSerializedMarketRows(root.ownerDocument)];
- const rows = authorCells.flatMap((authorCell, index) => {
- const selectionCell = selectionCells[index] ?? null;
- const singleCell = singleCells[index] ?? null;
- const personalCell = personalCells[index] ?? null;
- const backendMetricsCells = Object.fromEntries(
- BACKEND_METRIC_COLUMNS2.map(({ field }) => [
- field,
- backendMetricCellsByField[field][index] ?? null
- ])
- );
- if (!selectionCell || !singleCell || !personalCell || Object.values(backendMetricsCells).some((cell) => cell === null)) {
- return [];
- }
- const selectionCheckbox = ensureSelectionRowControl(selectionCell);
- const alignedRowCells = allBodyColumns.map(
- (column) => getDirectContentCells(column)[index] ?? null
- );
- const rowCells = alignedRowCells.filter(
- (cell) => cell !== null
- );
- const directAuthorId = extractAuthorId(authorCell) || "";
- const directAuthorName = extractAuthorName(authorCell) || "";
- const vueMarketRow = takeMatchedMarketDataRow(
- remainingVueMarketRows,
- directAuthorId,
- directAuthorName
- );
- const serializedMarketRow = takeMatchedMarketDataRow(
- remainingSerializedMarketRows,
- directAuthorId,
- directAuthorName
- );
- const fallbackMarketRow = mergeMarketDataRows(serializedMarketRow, vueMarketRow);
- const exportFields = mergeExportFieldMaps(
- readExportFieldsForDivGridRow(allHeaderCells, alignedRowCells),
- fallbackMarketRow?.exportFields
- );
- const authorId = directAuthorId || fallbackMarketRow?.authorId || "";
- const authorName = directAuthorName || fallbackMarketRow?.authorName || "";
- const price21To60s = mergeNonEmptyString(
- readDivGridPriceDisplay(priceCells[index]?.textContent),
- fallbackMarketRow?.price21To60s
- );
- selectionCheckbox.dataset.marketSelectionAuthorId = authorId;
- return [
- {
- authorId,
- authorName,
- backendMetricsCells,
- exportFields,
- hasDirectRatesSource: fallbackMarketRow?.hasDirectRatesSource ?? false,
- location: fallbackMarketRow?.location,
- orderTargets: rowCells.map((cell) => {
- const container = cell.parentElement;
- if (!(container instanceof root.ownerDocument.defaultView.HTMLElement)) {
- return null;
- }
- return {
- container,
- mode: "css",
- node: cell
- };
- }).filter((target) => target !== null),
- personalCell,
- price21To60s,
- rates: fallbackMarketRow?.rates,
- row: authorCell,
- selectionCheckbox,
- singleCell,
- visibilityTargets: rowCells
- }
- ];
- });
- return {
- headerSelectionCheckbox,
- rows
- };
- }
- function ensureSyntheticHeaderCell(header, field, label) {
- const existingCell = header.querySelector(
- `[data-market-header-cell="${field}"]`
- );
- if (existingCell) {
- existingCell.textContent = label;
- return existingCell;
- }
- const nextCell = header.ownerDocument.createElement("div");
- nextCell.dataset.marketHeaderCell = field;
- nextCell.textContent = label;
- if (field === SELECTION_COLUMN_KEY) {
- header.insertBefore(nextCell, header.firstChild);
- } else {
- header.appendChild(nextCell);
- }
- return nextCell;
- }
- function ensureSyntheticRowCell(row, field) {
- const existingCell = row.querySelector(
- `[data-market-row-cell="${field}"]`
- );
- if (existingCell) {
- return existingCell;
- }
- const nextCell = row.ownerDocument.createElement(field === BACKEND_COLUMN_KEY ? "div" : "span");
- nextCell.dataset.marketRowCell = field;
- if (field === SELECTION_COLUMN_KEY) {
- row.insertBefore(nextCell, row.firstChild);
- } else {
- row.appendChild(nextCell);
- }
- return nextCell;
- }
- function ensureDivHeaderCell(container, templateCell, field, label) {
- const existingCell = container.querySelector(
- `[data-market-header-cell="${field}"]`
- );
- if (existingCell) {
- existingCell.textContent = label;
- applyPluginHeaderCellStyles(existingCell);
- return existingCell;
- }
- const nextCell = cloneElementShallow(templateCell);
- nextCell.dataset.marketHeaderCell = field;
- nextCell.textContent = label;
- applyColumnWidth(nextCell, field);
- applyPluginHeaderCellStyles(nextCell);
- if (field === SELECTION_COLUMN_KEY) {
- container.insertBefore(nextCell, templateCell);
- } else {
- container.appendChild(nextCell);
- }
- return nextCell;
- }
- function ensureDivBodyColumn(container, templateColumn, field, rowCount) {
- const existingColumn = container.querySelector(
- `[data-market-column-group="${field}"]`
- );
- if (existingColumn) {
- syncDivColumnCells(existingColumn, templateColumn, field, rowCount);
- return existingColumn;
- }
- const nextColumn = cloneElementShallow(templateColumn);
- nextColumn.dataset.marketColumnGroup = field;
- applyColumnWidth(nextColumn, field);
- syncDivColumnCells(nextColumn, templateColumn, field, rowCount);
- if (field === SELECTION_COLUMN_KEY) {
- container.insertBefore(nextColumn, templateColumn);
- } else {
- container.appendChild(nextColumn);
- }
- return nextColumn;
- }
- function syncDivColumnCells(column, templateColumn, field, rowCount) {
- const currentCells = getDirectContentCells(column);
- while (currentCells.length > rowCount) {
- currentCells.pop()?.remove();
- }
- const templateCells = getDirectContentCells(templateColumn);
- for (let index = 0; index < rowCount; index += 1) {
- const existingCell = getDirectContentCells(column)[index] ?? null;
- const templateCell = templateCells[index] ?? templateCells[templateCells.length - 1] ?? null;
- if (existingCell) {
- existingCell.dataset.marketRowCell = field;
- applyPluginContentCellStyles(existingCell);
- syncContentCellHeight(existingCell, templateCell);
- continue;
- }
- const nextCell = field === SELECTION_COLUMN_KEY ? templateCell ? createSelectionContentCell(templateCell) : createBareContentCell(column.ownerDocument) : templateCell ? cloneElementShallow(templateCell) : createBareContentCell(column.ownerDocument);
- nextCell.dataset.marketRowCell = field;
- applyColumnWidth(nextCell, field);
- applyPluginContentCellStyles(nextCell);
- syncContentCellHeight(nextCell, templateCell);
- nextCell.textContent = "";
- column.appendChild(nextCell);
- }
- }
- function syncContentCellHeight(cell, templateCell) {
- if (!templateCell) {
- return;
- }
- const measuredHeight = Math.round(templateCell.getBoundingClientRect().height);
- const nextHeight = measuredHeight > 0 ? `${measuredHeight}px` : templateCell.style.height;
- if (nextHeight) {
- cell.style.height = nextHeight;
- } else {
- cell.style.removeProperty("height");
- }
- }
- function applyPluginHeaderCellStyles(cell) {
- cell.style.display = "flex";
- cell.style.alignItems = "center";
- cell.style.justifyContent = "normal";
- cell.style.cursor = "pointer";
- cell.style.whiteSpace = "nowrap";
- }
- function applyPluginContentCellStyles(cell) {
- cell.style.display = "flex";
- cell.style.alignItems = "center";
- cell.style.justifyContent = "normal";
- cell.style.paddingTop = "12px";
- cell.style.paddingBottom = "12px";
- cell.style.boxSizing = "border-box";
- cell.style.whiteSpace = "nowrap";
- }
- function ensureSelectionHeaderControl(cell) {
- cell.textContent = "";
- cell.style.gap = "6px";
- cell.style.justifyContent = "center";
- const checkbox = ensureSelectionCheckbox(cell, "header");
- const label = cell.querySelector(
- '[data-market-selection-label="header"]'
- );
- if (label) {
- label.textContent = "\u5168\u9009";
- return checkbox;
- }
- const nextLabel = cell.ownerDocument.createElement("span");
- nextLabel.dataset.marketSelectionLabel = "header";
- nextLabel.textContent = "\u5168\u9009";
- nextLabel.style.fontSize = "12px";
- cell.appendChild(nextLabel);
- return checkbox;
- }
- function ensureSelectionRowControl(cell) {
- cell.textContent = "";
- cell.style.justifyContent = "center";
- return ensureSelectionCheckbox(cell, "row");
- }
- function ensureSelectionCheckbox(container, kind) {
- const existingCheckbox = container.querySelector(
- `[data-market-selection-checkbox="${kind}"]`
- );
- if (existingCheckbox) {
- existingCheckbox.type = "checkbox";
- return existingCheckbox;
- }
- const checkbox = container.ownerDocument.createElement("input");
- checkbox.type = "checkbox";
- checkbox.dataset.marketSelectionCheckbox = kind;
- checkbox.style.cursor = "pointer";
- container.appendChild(checkbox);
- return checkbox;
- }
- function getOwnerDocument(root) {
- if ("ownerDocument" in root && root.ownerDocument) {
- return root.ownerDocument;
- }
- return "nodeType" in root && root.nodeType === 9 ? root : null;
- }
- function readSyntheticHeaderLabels(header) {
- return Array.from(header.querySelectorAll("[data-market-header-cell]")).reduce((labels, cell) => {
- if (!(cell instanceof header.ownerDocument.defaultView.HTMLElement)) {
- return labels;
- }
- const field = cell.dataset.marketHeaderCell;
- if (!field) {
- return labels;
- }
- labels[field] = normalizeExportCellText2(cell.textContent);
- return labels;
- }, {});
- }
- function readSyntheticExportFields(row, headerLabelsByField) {
- const exportFields = {};
- for (const cell of row.querySelectorAll("[data-market-field]")) {
- if (!(cell instanceof row.ownerDocument.defaultView.HTMLElement)) {
- continue;
- }
- const field = cell.dataset.marketField;
- const headerLabel = field ? headerLabelsByField[field] : "";
- if (!shouldExportColumn(headerLabel)) {
- continue;
- }
- exportFields[headerLabel] = normalizeExportCellText2(cell.textContent);
- }
- return exportFields;
- }
- function readExportFieldsForDivGridRow(headerCells, rowCells) {
- const exportFields = {};
- rowCells.forEach((cell, index) => {
- const headerLabel = normalizeExportCellText2(headerCells[index]?.textContent);
- if (!shouldExportColumn(headerLabel)) {
- return;
- }
- exportFields[headerLabel] = headerLabel === "21-60s\u62A5\u4EF7" ? readDivGridPriceDisplay(cell?.textContent) ?? "" : normalizeExportCellText2(cell?.textContent);
- });
- return exportFields;
- }
- function findPreviousHeaderCell(cell) {
- let current = cell.previousElementSibling;
- while (current) {
- if (current instanceof cell.ownerDocument.defaultView.HTMLElement && current.classList.contains("header-cell")) {
- return current;
- }
- current = current.previousElementSibling;
- }
- return null;
- }
- function findPreviousColumn(column) {
- let current = column.previousElementSibling;
- while (current) {
- if (current instanceof column.ownerDocument.defaultView.HTMLElement && current.classList.contains("content-column")) {
- return current;
- }
- current = current.previousElementSibling;
- }
- return null;
- }
- function ensurePluginSection(rootSection, referenceSection, options) {
- const existingSection = rootSection.querySelector(
- `[data-market-plugin-section="${options.type}"]`
- );
- if (existingSection) {
- existingSection.dataset.testid = options.testId;
- existingSection.setAttribute("data-testid", options.testId);
- return existingSection;
- }
- const templateSection = findPreviousSection(referenceSection) ?? referenceSection;
- const nextSection = cloneElementShallow(templateSection);
- nextSection.dataset.marketPluginSection = options.type;
- nextSection.dataset.testid = options.testId;
- nextSection.setAttribute("data-testid", options.testId);
- resetStickySectionStyles(nextSection);
- rootSection.insertBefore(nextSection, referenceSection);
- return nextSection;
- }
- function ensureVisibleHorizontalScroll(section) {
- ensureVisibleScrollbarStyles(section.ownerDocument);
- section.classList.remove("hide-scrollbar");
- section.dataset.marketScrollbar = "visible";
- section.style.overflowX = "auto";
- section.style.scrollbarWidth = "thin";
- section.style.scrollbarColor = "rgba(148, 163, 184, 0.95) rgba(226, 232, 240, 0.9)";
- }
- function ensureVisibleScrollbarStyles(document2) {
- if (document2.getElementById(MARKET_SCROLLBAR_STYLE_ID)) {
- return;
- }
- const style = document2.createElement("style");
- style.id = MARKET_SCROLLBAR_STYLE_ID;
- style.textContent = `
- [data-market-scrollbar="visible"]::-webkit-scrollbar {
- display: block !important;
- height: 10px !important;
- }
-
- [data-market-scrollbar="visible"]::-webkit-scrollbar-track {
- background: rgba(226, 232, 240, 0.9) !important;
- border-radius: 999px;
- }
-
- [data-market-scrollbar="visible"]::-webkit-scrollbar-thumb {
- background: rgba(148, 163, 184, 0.95) !important;
- border: 2px solid rgba(226, 232, 240, 0.9);
- border-radius: 999px;
- }
- `;
- document2.head.appendChild(style);
- }
- function ensureScrollHint(root, headerSection) {
- const existingHint = root.querySelector(
- '[data-testid="market-scroll-hint"]'
- );
- if (existingHint) {
- existingHint.textContent = MARKET_SCROLL_HINT_TEXT;
- return;
- }
- const hint = root.ownerDocument.createElement("div");
- hint.dataset.testid = "market-scroll-hint";
- hint.setAttribute("data-testid", "market-scroll-hint");
- hint.textContent = MARKET_SCROLL_HINT_TEXT;
- hint.style.color = "#64748b";
- hint.style.display = "flex";
- hint.style.fontSize = "12px";
- hint.style.justifyContent = "flex-end";
- hint.style.lineHeight = "18px";
- hint.style.padding = "0 12px 8px";
- root.insertBefore(hint, headerSection);
- }
- function findPreviousSection(section) {
- let current = section.previousElementSibling;
- while (current) {
- if (current instanceof section.ownerDocument.defaultView.HTMLElement) {
- return current;
- }
- current = current.previousElementSibling;
- }
- return null;
- }
- function findPreviousNativeSection(section) {
- let current = section.previousElementSibling;
- while (current) {
- if (current instanceof section.ownerDocument.defaultView.HTMLElement && !current.hasAttribute("data-market-plugin-section")) {
- return current;
- }
- current = current.previousElementSibling;
- }
- return null;
- }
- function resetStickySectionStyles(section) {
- section.style.position = "";
- section.style.left = "";
- section.style.right = "";
- section.style.zIndex = "";
- section.style.width = "";
- section.style.minWidth = "";
- }
- function getActionColumn(bodySection) {
- const columns = getDirectContentColumns(bodySection);
- return columns[columns.length - 1] ?? null;
- }
- function getNativeAuthorColumn(authorSection) {
- return getDirectContentColumns(authorSection).find(
- (column) => !column.dataset.marketColumnGroup && getDirectContentCells(column).some(
- (cell) => cell.querySelector("a") || cell.querySelector(".author-nickname") || Boolean(cell.dataset.authorId)
- )
- ) ?? null;
- }
- function getDirectHeaderCells(section) {
- return Array.from(section.querySelectorAll(".header-cell")).filter(
- (cell) => cell instanceof section.ownerDocument.defaultView.HTMLElement
- );
- }
- function getDirectContentColumns(section) {
- return Array.from(section.children).filter(
- (child) => child instanceof section.ownerDocument.defaultView.HTMLElement && child.classList.contains("content-column")
- );
- }
- function getDirectContentCells(column) {
- return Array.from(column.children).filter(
- (child) => child instanceof column.ownerDocument.defaultView.HTMLElement && child.classList.contains("content-cell")
- );
- }
- function getDirectChildIndex(root, descendant) {
- const directChild = Array.from(root.children).find((child) => child.contains(descendant));
- return directChild ? Array.from(root.children).indexOf(directChild) : -1;
- }
- function getIndexedChild(root, index) {
- if (index < 0) {
- return null;
- }
- const child = root.children[index] ?? null;
- return child instanceof root.ownerDocument.defaultView.HTMLElement ? child : null;
- }
- function findCellByText(cells, text) {
- return cells.find((cell) => cell.textContent?.trim() === text) ?? null;
- }
- function cloneElementShallow(reference) {
- const clone = reference.ownerDocument.createElement(reference.tagName);
- Array.from(reference.attributes).forEach((attribute) => {
- clone.setAttribute(attribute.name, attribute.value);
- });
- return clone;
- }
- function createBareContentCell(document2) {
- const cell = document2.createElement("div");
- cell.className = "content-cell";
- return cell;
- }
- function createSelectionContentCell(templateCell) {
- const cell = cloneElementShallow(templateCell);
- cell.removeAttribute("data-testid");
- cell.removeAttribute("data-author-id");
- return cell;
- }
- function extractAuthorId(authorCell) {
- const explicitAuthorId = authorCell.dataset.authorId;
- if (explicitAuthorId) {
- return explicitAuthorId;
- }
- const linkedAuthorId = Array.from(authorCell.querySelectorAll("a")).map((link) => extractAuthorIdFromHref(link.href)).find((value) => Boolean(value));
- if (linkedAuthorId) {
- return linkedAuthorId;
- }
- const fallbackAuthorId = authorCell.querySelector("[data-author-id]")?.getAttribute("data-author-id");
- return fallbackAuthorId ?? "";
- }
- function extractAuthorName(authorCell) {
- return authorCell.querySelector(".author-nickname")?.textContent?.trim() ?? authorCell.textContent?.trim() ?? "";
- }
- function extractAuthorIdFromHref(href) {
- const match = href.match(/\/author-homepage\/[^/]+\/(\d+)/);
- return match?.[1] ?? null;
- }
- function readVueMarketRows(marketRoot) {
- const vueRoot = marketRoot.__vue__;
- const setupStates = collectVueSetupStates(vueRoot);
- for (const setupState of setupStates) {
- for (const value of Object.values(setupState)) {
- const candidate = unwrapVueRef2(value);
- if (!candidate || typeof candidate !== "object") {
- continue;
- }
- const marketList = unwrapVueRef2(
- candidate.marketList
- );
- if (!Array.isArray(marketList)) {
- continue;
- }
- return marketList.map((row) => isRecord5(row) ? mapMarketListRow(row) : null).filter((row) => row !== null);
- }
- }
- return [];
- }
- function collectVueSetupStates(vueRoot) {
- if (!vueRoot) {
- return [];
- }
- const queue = [vueRoot];
- const setupStates = [];
- while (queue.length > 0) {
- const current = queue.shift();
- if (!isRecord5(current)) {
- continue;
- }
- if (isRecord5(current._setupState)) {
- setupStates.push(current._setupState);
- }
- const children = Array.isArray(current.$children) ? current.$children : [];
- queue.push(...children);
- }
- return setupStates;
- }
- function readSerializedMarketRows(document2) {
- const serializedRows = document2.documentElement.getAttribute(
- SERIALIZED_MARKET_ROWS_ATTRIBUTE
- );
- if (!serializedRows) {
- return [];
- }
- try {
- const parsedRows = JSON.parse(serializedRows);
- if (!Array.isArray(parsedRows)) {
- return [];
- }
- return parsedRows.map((row) => {
- const record = isRecord5(row) ? row : {};
- const singleVideoAfterSearchRate = readString4(
- record.singleVideoAfterSearchRate
- );
- return {
- authorId: readString4(record.authorId) ?? "",
- authorName: readString4(record.authorName) ?? "",
- coreUserId: readString4(record.coreUserId) ?? void 0,
- exportFields: readSerializedExportFields(record),
- hasDirectRatesSource: Boolean(singleVideoAfterSearchRate),
- location: readString4(record.location) ?? void 0,
- price21To60s: readString4(record.price21To60s) ?? void 0,
- rates: singleVideoAfterSearchRate ? {
- singleVideoAfterSearchRate
- } : void 0
- };
- }).filter((row) => Boolean(row.authorId || row.authorName));
- } catch {
- return [];
- }
- }
- function unwrapVueRef2(value) {
- if (isRecord5(value) && "value" in value) {
- return value.value;
- }
- return value;
- }
- function isRecord5(value) {
- return typeof value === "object" && value !== null;
- }
- function readString4(value) {
- return typeof value === "string" ? value : null;
- }
- function normalizeExportCellText2(value) {
- return value?.replace(/\s+/g, " ").trim() ?? "";
- }
- function readDivGridPriceDisplay(value) {
- const normalizedValue = normalizeExportCellText2(value);
- if (!normalizedValue) {
- return void 0;
- }
- const match = normalizedValue.match(/^¥?\s*([\d,]+(?:\.\d+)?)$/);
- if (!match) {
- return void 0;
- }
- const numericValue = Number(match[1].replace(/,/g, ""));
- if (!Number.isFinite(numericValue)) {
- return void 0;
- }
- return formatCurrencyValue2(numericValue);
- }
- function shouldExportColumn(label) {
- const excludedBackendLabels = new Set(BACKEND_METRIC_COLUMNS2.map((column) => column.label));
- return Boolean(
- label && label !== ACTION_HEADER_TEXT && label !== BACKEND_HEADER_TEXT && !excludedBackendLabels.has(label) && label !== "\u5546\u5355\u89C6\u9891\u770B\u540E\u641C\u7387" && label !== "\u4E2A\u4EBA\u89C6\u9891\u770B\u540E\u641C\u7387"
- );
- }
- function syncSortableHeaderCell(cell, options) {
- const label = readSortableHeaderLabel(cell);
- const sorterRoot = ensureHeaderSorterRoot(cell);
- const text = ensureHeaderSorterText(sorterRoot);
- const icon = ensureHeaderSorterIcon(sorterRoot);
- const upTriangle = ensureHeaderTriangle(icon, "up");
- const downTriangle = ensureHeaderTriangle(icon, "down");
- text.textContent = label;
- cell.dataset.marketSortField = options.field;
- cell.dataset.marketSortDirection = options.direction;
- cell.setAttribute("role", "button");
- cell.tabIndex = 0;
- cell.onclick = () => {
- options.onToggleSort(options.field);
- };
- cell.onkeydown = (event) => {
- if (event.key !== "Enter" && event.key !== " ") {
- return;
- }
- event.preventDefault();
- options.onToggleSort(options.field);
- };
- syncTriangleStyles(upTriangle, {
- active: options.direction === "asc",
- direction: "up"
- });
- syncTriangleStyles(downTriangle, {
- active: options.direction === "desc",
- direction: "down"
- });
- }
- function readSortableHeaderLabel(cell) {
- return cell.dataset.marketHeaderLabel ?? normalizeExportCellText2(cell.textContent) ?? "";
- }
- function ensureHeaderSorterRoot(cell) {
- const existingRoot = cell.querySelector(
- '[data-market-sorter="root"]'
- );
- if (existingRoot) {
- return existingRoot;
- }
- cell.dataset.marketHeaderLabel = normalizeExportCellText2(cell.textContent);
- cell.replaceChildren();
- const root = cell.ownerDocument.createElement("span");
- root.dataset.marketSorter = "root";
- root.style.alignItems = "center";
- root.style.display = "inline-flex";
- root.style.gap = "4px";
- root.style.maxWidth = "100%";
- cell.appendChild(root);
- return root;
- }
- function ensureHeaderSorterText(sorterRoot) {
- const existingText = sorterRoot.querySelector(
- '[data-market-sorter="text"]'
- );
- if (existingText) {
- return existingText;
- }
- const text = sorterRoot.ownerDocument.createElement("span");
- text.dataset.marketSorter = "text";
- text.style.display = "inline-block";
- text.style.lineHeight = "20px";
- text.style.whiteSpace = "nowrap";
- sorterRoot.appendChild(text);
- return text;
- }
- function ensureHeaderSorterIcon(sorterRoot) {
- const existingIcon = sorterRoot.querySelector(
- '[data-market-sorter="icon"]'
- );
- if (existingIcon) {
- return existingIcon;
- }
- const icon = sorterRoot.ownerDocument.createElement("span");
- icon.dataset.marketSorter = "icon";
- icon.style.display = "inline-flex";
- icon.style.flexDirection = "column";
- icon.style.gap = "2px";
- icon.style.justifyContent = "center";
- icon.style.minWidth = "8px";
- sorterRoot.appendChild(icon);
- return icon;
- }
- function ensureHeaderTriangle(iconRoot, direction) {
- const existingTriangle = iconRoot.querySelector(
- `[data-market-sorter-triangle="${direction}"]`
- );
- if (existingTriangle) {
- return existingTriangle;
- }
- const triangle = iconRoot.ownerDocument.createElement("span");
- triangle.dataset.marketSorterTriangle = direction;
- triangle.style.display = "block";
- triangle.style.height = "0";
- triangle.style.width = "0";
- triangle.style.borderLeft = "4px solid transparent";
- triangle.style.borderRight = "4px solid transparent";
- iconRoot.appendChild(triangle);
- return triangle;
- }
- function syncTriangleStyles(triangle, options) {
- const activeColor = "#1f2329";
- const inactiveColor = "#c9cdd4";
- if (options.direction === "up") {
- triangle.style.borderBottom = `5px solid ${options.active ? activeColor : inactiveColor}`;
- triangle.style.borderTop = "0 solid transparent";
- } else {
- triangle.style.borderTop = `5px solid ${options.active ? activeColor : inactiveColor}`;
- triangle.style.borderBottom = "0 solid transparent";
- }
- }
- function readRateCellText(value) {
- return value ? normalizeRateDisplay(value) : UNAVAILABLE_RATE_TEXT;
- }
- function applyColumnWidth(element, field) {
- if (field === SELECTION_COLUMN_KEY) {
- element.style.minWidth = "56px";
- element.style.width = "56px";
- }
- if (field === BACKEND_COLUMN_KEY) {
- element.style.minWidth = "240px";
- element.style.width = "240px";
- }
- if (field === SINGLE_COLUMN_KEY || field === PERSONAL_COLUMN_KEY) {
- element.style.minWidth = "160px";
- element.style.width = "160px";
- }
- if (BACKEND_METRIC_COLUMNS2.some((column) => column.field === field)) {
- element.style.minWidth = "120px";
- element.style.width = "120px";
- }
- }
- function syncContainerWidth(container) {
- if (!(container instanceof HTMLElement)) {
- return;
- }
- const directChildren = Array.from(container.children).filter(
- (child) => child instanceof HTMLElement
- );
- const totalWidth = directChildren.reduce((sum, child) => {
- return sum + readElementWidth(child);
- }, 0);
- if (totalWidth <= 0) {
- return;
- }
- container.style.width = `${totalWidth}px`;
- container.style.minWidth = `${totalWidth}px`;
- }
- function readElementWidth(element) {
- const styleWidth = Number.parseFloat(element.style.width || "");
- if (Number.isFinite(styleWidth) && styleWidth > 0) {
- return styleWidth;
- }
- const minWidth = Number.parseFloat(element.style.minWidth || "");
- if (Number.isFinite(minWidth) && minWidth > 0) {
- return minWidth;
- }
- return 0;
- }
- function mergeMarketDataRows(baseRow, preferredRow) {
- if (!baseRow && !preferredRow) {
- return null;
- }
- if (!baseRow) {
- return preferredRow;
- }
- if (!preferredRow) {
- return baseRow;
- }
- return {
- authorId: preferredRow.authorId || baseRow.authorId,
- authorName: preferredRow.authorName || baseRow.authorName,
- coreUserId: mergeNonEmptyString(baseRow.coreUserId, preferredRow.coreUserId),
- exportFields: mergeExportFieldMaps(baseRow.exportFields, preferredRow.exportFields),
- hasDirectRatesSource: preferredRow.hasDirectRatesSource || baseRow.hasDirectRatesSource,
- location: mergeNonEmptyString(baseRow.location, preferredRow.location),
- price21To60s: mergeNonEmptyString(
- baseRow.price21To60s,
- preferredRow.price21To60s
- ),
- rates: mergeRates(baseRow.rates, preferredRow.rates)
- };
- }
- function takeMatchedMarketDataRow(remainingRows, authorId, authorName) {
- if (remainingRows.length === 0) {
- return null;
- }
- const matchedIndex = remainingRows.findIndex((row) => {
- if (authorId && row.authorId === authorId) {
- return true;
- }
- if (authorName && row.authorName === authorName) {
- return true;
- }
- return false;
- });
- if (matchedIndex >= 0) {
- return remainingRows.splice(matchedIndex, 1)[0] ?? null;
- }
- if (!authorId && !authorName) {
- return remainingRows.shift() ?? null;
- }
- return null;
- }
- function mergeExportFieldMaps(current, fallback) {
- if (!current && !fallback) {
- return void 0;
- }
- const nextFields = {
- ...current ?? {}
- };
- Object.entries(fallback ?? {}).forEach(([key, value]) => {
- if (!hasTextValue2(nextFields[key]) && hasTextValue2(value)) {
- nextFields[key] = value;
- }
- });
- return nextFields;
- }
- function mergeRates(current, fallback) {
- if (!current && !fallback) {
- return void 0;
- }
- return {
- singleVideoAfterSearchRate: current?.singleVideoAfterSearchRate ?? fallback?.singleVideoAfterSearchRate,
- personalVideoAfterSearchRate: current?.personalVideoAfterSearchRate ?? fallback?.personalVideoAfterSearchRate
- };
- }
- function mergeNonEmptyString(current, fallback) {
- return hasTextValue2(current) ? current : fallback;
- }
- function formatCurrencyValue2(value) {
- if (value === null) {
- return void 0;
- }
- return `\xA5${value.toLocaleString("en-US", {
- maximumFractionDigits: 0
- })}`;
- }
- function readSerializedExportFields(record) {
- if (!isRecord5(record.exportFields)) {
- return void 0;
- }
- const entries = Object.entries(record.exportFields).flatMap(
- ([key, value]) => typeof value === "string" ? [[key, value]] : []
- );
- return entries.length > 0 ? Object.fromEntries(entries) : void 0;
- }
- function hasTextValue2(value) {
- return typeof value === "string" && value.trim().length > 0;
- }
- function renderBackendMetricsCells(cells, record) {
- if (record.backendMetricsStatus === "loading" || record.status === "loading" && !record.backendMetricsStatus) {
- fillBackendMetricCells(cells, "\u52A0\u8F7D\u4E2D...");
- return;
- }
- if (record.backendMetricsStatus === "failed") {
- fillBackendMetricCells(cells, "\u52A0\u8F7D\u5931\u8D25");
- return;
- }
- if (record.backendMetricsStatus === "missing") {
- fillBackendMetricCells(cells, UNAVAILABLE_BACKEND_METRICS_TEXT);
- return;
- }
- if (record.backendMetricsStatus !== "success" || !record.backendMetrics) {
- fillBackendMetricCells(cells, "");
- return;
- }
- BACKEND_METRIC_COLUMNS2.forEach(({ field }) => {
- cells[field].textContent = record.backendMetrics?.[field] ?? "";
- });
- }
- function fillBackendMetricCells(cells, value) {
- BACKEND_METRIC_COLUMNS2.forEach(({ field }) => {
- cells[field].textContent = value;
- });
- }
-
- // src/content/market/filter-sort-controller.ts
- function applyFilterAndSort(records, options = {}) {
- const filteredRecords = records.filter(
- (record) => matchesFilters(record, options.filters)
- );
- if (!options.sort) {
- return filteredRecords;
- }
- return [...filteredRecords].sort(
- (leftRecord, rightRecord) => compareRecords(leftRecord, rightRecord, options.sort)
- );
- }
- function matchesFilters(record, filters) {
- if (!filters) {
- return true;
- }
- return meetsThreshold(
- record.rates?.singleVideoAfterSearchRate,
- filters.singleVideoAfterSearchRateMin
- ) && meetsThreshold(
- record.rates?.personalVideoAfterSearchRate,
- filters.personalVideoAfterSearchRateMin
- );
- }
- function meetsThreshold(rateValue, minValue) {
- if (minValue == null) {
- return true;
- }
- const lowerBound = parseRateLowerBound(rateValue ?? null);
- return lowerBound != null && lowerBound >= minValue;
- }
- function compareRecords(leftRecord, rightRecord, sort) {
- if (isRateSortField(sort.field)) {
- return compareRateSortRecords(leftRecord, rightRecord, sort);
- }
- return compareBackendMetricRecords(leftRecord, rightRecord, sort);
- }
- function compareRateSortRecords(leftRecord, rightRecord, sort) {
- const field = sort.field;
- const leftValue = leftRecord.rates?.[field];
- const rightValue = rightRecord.rates?.[field];
- const leftLowerBound = parseRateLowerBound(leftValue ?? null);
- const rightLowerBound = parseRateLowerBound(rightValue ?? null);
- if (leftLowerBound == null && rightLowerBound == null) {
- return compareRecordIdentity(leftRecord, rightRecord);
- }
- if (leftLowerBound == null) {
- return 1;
- }
- if (rightLowerBound == null) {
- return -1;
- }
- if (leftLowerBound !== rightLowerBound) {
- return sort.direction === "asc" ? leftLowerBound - rightLowerBound : rightLowerBound - leftLowerBound;
- }
- const tieBreak = compareRateValues(leftValue, rightValue);
- if (tieBreak !== 0) {
- return sort.direction === "asc" ? tieBreak : -tieBreak;
- }
- return compareRecordIdentity(leftRecord, rightRecord);
- }
- function compareBackendMetricRecords(leftRecord, rightRecord, sort) {
- const field = sort.field;
- const leftValue = parseBackendMetricValue(leftRecord.backendMetrics?.[field]);
- const rightValue = parseBackendMetricValue(rightRecord.backendMetrics?.[field]);
- if (leftValue == null && rightValue == null) {
- return compareRecordIdentity(leftRecord, rightRecord);
- }
- if (leftValue == null) {
- return 1;
- }
- if (rightValue == null) {
- return -1;
- }
- if (leftValue !== rightValue) {
- return sort.direction === "asc" ? leftValue - rightValue : rightValue - leftValue;
- }
- return compareRecordIdentity(leftRecord, rightRecord);
- }
- function parseBackendMetricValue(value) {
- if (!value) {
- return null;
- }
- const normalizedValue = value.replace(/,/g, "").replace(/%/g, "").trim();
- if (!normalizedValue) {
- return null;
- }
- const numericValue = Number(normalizedValue);
- return Number.isFinite(numericValue) ? numericValue : null;
- }
- function isRateSortField(field) {
- return field === "singleVideoAfterSearchRate" || field === "personalVideoAfterSearchRate";
- }
- function compareRecordIdentity(leftRecord, rightRecord) {
- const authorIdCompare = leftRecord.authorId.localeCompare(rightRecord.authorId);
- if (authorIdCompare !== 0) {
- return authorIdCompare;
- }
- return leftRecord.authorName.localeCompare(rightRecord.authorName);
- }
-
- // src/content/market/api-client.ts
- function createMarketApiClient(options = {}) {
- const baseUrl = options.baseUrl ?? resolveBaseUrl4();
- const fetchImpl = options.fetchImpl ?? defaultFetch4;
- const timeoutMs = options.timeoutMs ?? 8e3;
- return {
- async loadAuthorAseInfo(authorId) {
- const primaryResult = await loadAuthorMetricsFromUrl(
- buildAuthorCommerceSeedBaseInfoUrl(authorId, baseUrl)
- );
- if (primaryResult.success || primaryResult.reason === "timeout") {
- return primaryResult;
- }
- return loadAuthorMetricsFromUrl(buildAuthorAseInfoUrl(authorId, baseUrl));
- }
- };
- async function loadAuthorMetricsFromUrl(url) {
- const controller = new AbortController();
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
- try {
- const response = await fetchImpl(url, {
- credentials: "include",
- method: "GET",
- signal: controller.signal
- });
- if (!response.ok) {
- return {
- success: false,
- reason: "request-failed"
- };
- }
- return mapAuthorAseInfoResponse(await response.json());
- } catch (error) {
- if (isAbortError(error) || controller.signal.aborted) {
- return {
- success: false,
- reason: "timeout"
- };
- }
- return {
- success: false,
- reason: "request-failed"
- };
- } finally {
- clearTimeout(timeoutId);
- }
- }
- }
- function buildAuthorAseInfoUrl(authorId, baseUrl) {
- const url = new URL("/gw/api/aggregator/get_author_ase_info", baseUrl);
- url.searchParams.set("author_id", authorId);
- url.searchParams.set("range", "30");
- return url.toString();
- }
- function buildAuthorCommerceSeedBaseInfoUrl(authorId, baseUrl) {
- const url = new URL(
- "/gw/api/aggregator/get_author_commerce_seed_base_info",
- baseUrl
- );
- url.searchParams.set("o_author_id", authorId);
- url.searchParams.set("range", "90");
- return url.toString();
- }
- function mapAuthorAseInfoResponse(payload) {
- const data = getPayloadData2(payload);
- if (!data) {
- return {
- success: false,
- reason: "bad-response"
- };
- }
- const singleVideoAfterSearchRate = readNormalizedRate(
- data.avg_search_after_view_rate
- );
- const personalVideoAfterSearchRate = readNormalizedRate(
- data.personal_avg_search_after_view_rate
- );
- if (!singleVideoAfterSearchRate && !personalVideoAfterSearchRate) {
- return {
- success: false,
- reason: "missing-rate"
- };
- }
- return {
- success: true,
- rates: {
- ...singleVideoAfterSearchRate ? { singleVideoAfterSearchRate } : {},
- ...personalVideoAfterSearchRate ? { personalVideoAfterSearchRate } : {}
- }
- };
- }
- function getPayloadData2(payload) {
- if (!isRecord6(payload)) {
- return null;
- }
- return isRecord6(payload.data) ? payload.data : payload;
- }
- function readNormalizedRate(value) {
- return typeof value === "string" ? normalizeRateDisplay(value) : null;
- }
- function resolveBaseUrl4() {
- if (typeof location !== "undefined" && location.origin) {
- return location.origin;
- }
- return "https://xingtu.cn";
- }
- async function defaultFetch4(input, init) {
- return fetch(input, init);
- }
- function isAbortError(error) {
- return error instanceof Error && error.name === "AbortError";
- }
- function isRecord6(value) {
- return typeof value === "object" && value !== null;
- }
-
- // src/content/market/export-range-controller.ts
- function createExportRangeController(options) {
- return {
- async exportRecords(target) {
- const mergedRecords = /* @__PURE__ */ new Map();
- let currentPage = 0;
- let expectedMinimumRowCount;
- while (true) {
- currentPage += 1;
- options.onProgress?.({
- currentPage,
- totalPages: target.mode === "count" ? target.pageCount : void 0
- });
- const currentPageRecords = await preparePageRecords(expectedMinimumRowCount);
- if (!currentPageRecords) {
- throw new Error(`\u7B2C ${currentPage} \u9875\u52A0\u8F7D\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5`);
- }
- currentPageRecords.forEach((record) => {
- const existingRecord = mergedRecords.get(record.authorId);
- mergedRecords.set(record.authorId, mergeMarketRecord(existingRecord, record));
- });
- expectedMinimumRowCount = Math.max(
- expectedMinimumRowCount ?? 0,
- currentPageRecords.length
- );
- if (target.mode === "count" && currentPage >= target.pageCount) {
- break;
- }
- const previousSignature = readMarketPageSignature(options.document);
- const nextPageControl = findNextPageControl(options.document);
- if (!nextPageControl || isPageControlDisabled(nextPageControl)) {
- break;
- }
- nextPageControl.click();
- const pageChanged = await waitForPageChange(previousSignature);
- if (!pageChanged) {
- throw new Error(`\u7B2C ${currentPage + 1} \u9875\u5BFC\u51FA\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5`);
- }
- }
- return Array.from(mergedRecords.values());
- }
- };
- async function preparePageRecords(expectedMinimumRowCount) {
- for (let attempt = 0; attempt < 4; attempt += 1) {
- const currentPageReady = await waitForCurrentPageReady();
- if (!currentPageReady) {
- return null;
- }
- await options.prepareCurrentPageForExport();
- const currentPageRecords = options.readCurrentPageRecords();
- if (currentPageRecords.length > 0 && (typeof expectedMinimumRowCount !== "number" || expectedMinimumRowCount <= 0 || isCurrentPageTerminal() || currentPageRecords.length >= expectedMinimumRowCount)) {
- return currentPageRecords;
- }
- }
- return null;
- }
- async function waitForPageChange(previousSignature) {
- const previousPageState = parsePageSignature(previousSignature);
- for (let attempt = 0; attempt < 60; attempt += 1) {
- await new Promise((resolve) => {
- options.window.setTimeout(resolve, 50);
- });
- await Promise.resolve();
- const nextSignature = readMarketPageSignature(options.document);
- const nextPageState = parsePageSignature(nextSignature);
- if (hasLoadedNextPage(previousPageState, nextPageState)) {
- return true;
- }
- }
- return false;
- }
- async function waitForCurrentPageReady() {
- let stableAttemptCount = 0;
- let lastReadyFingerprint = "";
- for (let attempt = 0; attempt < 80; attempt += 1) {
- await new Promise((resolve) => {
- options.window.setTimeout(resolve, 150);
- });
- await Promise.resolve();
- const pageState = readCurrentPageState();
- if (!pageState.authorIds || pageState.rowCount <= 0) {
- stableAttemptCount = 0;
- lastReadyFingerprint = "";
- continue;
- }
- const readyFingerprint = [
- pageState.pageToken,
- pageState.authorIds,
- String(pageState.rowCount),
- pageState.isTerminalPage ? "terminal" : "paged"
- ].join("::");
- if (readyFingerprint === lastReadyFingerprint) {
- stableAttemptCount += 1;
- } else {
- lastReadyFingerprint = readyFingerprint;
- stableAttemptCount = 1;
- }
- if (stableAttemptCount >= 6) {
- return true;
- }
- }
- return false;
- }
- function readCurrentPageState() {
- const pageSignature = parsePageSignature(readMarketPageSignature(options.document));
- const nextPageControl = findNextPageControl(options.document);
- return {
- authorIds: pageSignature.authorIds,
- isTerminalPage: isPageControlDisabled(nextPageControl),
- pageToken: pageSignature.pageToken,
- rowCount: options.readCurrentPageRowCount()
- };
- }
- function isCurrentPageTerminal() {
- return isPageControlDisabled(findNextPageControl(options.document));
- }
- }
- function parsePageSignature(signature) {
- const separatorIndex = signature.indexOf("::");
- if (separatorIndex < 0) {
- return {
- authorIds: "",
- pageToken: signature.trim()
- };
- }
- return {
- authorIds: signature.slice(separatorIndex + 2).trim(),
- pageToken: signature.slice(0, separatorIndex).trim()
- };
- }
- function hasLoadedNextPage(previousPageState, nextPageState) {
- if (!nextPageState.authorIds) {
- return false;
- }
- if (nextPageState.pageToken || previousPageState.pageToken) {
- return nextPageState.pageToken !== previousPageState.pageToken;
- }
- return nextPageState.authorIds !== previousPageState.authorIds;
- }
- function mergeMarketRecord(existingRecord, incomingRecord) {
- if (!existingRecord) {
- return {
- ...incomingRecord,
- exportFields: mergeFieldMap(void 0, incomingRecord.exportFields),
- rates: mergeFieldMap(void 0, incomingRecord.rates)
- };
- }
- return {
- ...existingRecord,
- ...incomingRecord,
- authorName: mergeStringValue(existingRecord.authorName, incomingRecord.authorName) ?? "",
- coreUserId: mergeStringValue(existingRecord.coreUserId, incomingRecord.coreUserId),
- exportFields: mergeFieldMap(
- existingRecord.exportFields,
- incomingRecord.exportFields
- ),
- failureReason: incomingRecord.failureReason ?? existingRecord.failureReason,
- hasDirectRatesSource: existingRecord.hasDirectRatesSource || incomingRecord.hasDirectRatesSource,
- location: mergeStringValue(existingRecord.location, incomingRecord.location),
- price21To60s: mergeStringValue(
- existingRecord.price21To60s,
- incomingRecord.price21To60s
- ),
- rates: mergeFieldMap(existingRecord.rates, incomingRecord.rates),
- status: mergeStatus(existingRecord.status, incomingRecord.status)
- };
- }
- function mergeFieldMap(current, incoming) {
- if (!current && !incoming) {
- return void 0;
- }
- const merged = {
- ...current ?? {}
- };
- Object.entries(incoming ?? {}).forEach(([key, value]) => {
- const currentValue = merged[key];
- if (hasTextValue3(value) || !hasTextValue3(currentValue)) {
- merged[key] = value;
- }
- });
- return merged;
- }
- function mergeStatus(current, incoming) {
- const priority = {
- failed: 1,
- idle: 0,
- loading: 2,
- missing: -1,
- success: 3
- };
- return priority[incoming] >= priority[current] ? incoming : current;
- }
- function mergeStringValue(current, incoming) {
- if (hasTextValue3(incoming) || !hasTextValue3(current)) {
- return incoming ?? current;
- }
- return current;
- }
- function hasTextValue3(value) {
- return typeof value === "string" && value.trim().length > 0;
- }
-
- // src/content/market/plugin-toolbar.ts
- var PLUGIN_ACTION_BUTTON_STYLE_ID = "sces-plugin-action-button-style";
- function isPluginToolbarMounted(root, document2) {
- const actionRow = findNativeActionRow(document2);
- return Boolean(actionRow && root.parentElement === actionRow && !root.hidden);
- }
- function ensurePluginToolbar(document2, handlers) {
- ensurePluginActionButtonTheme(document2);
- const existingRoot = document2.querySelector(
- "[data-plugin-toolbar='root']"
- );
- if (existingRoot) {
- if (existingRoot.querySelector(
- '[data-plugin-export-audience-profile-by-id="button"]'
- )) {
- ensureToolbarMounted(existingRoot, document2);
- return readToolbarDom(existingRoot);
- }
- existingRoot.remove();
- }
- const root = document2.createElement("section");
- root.dataset.pluginToolbar = "root";
- applyToolbarRootStyles(root);
- const exportRangeSelect = document2.createElement("select");
- exportRangeSelect.dataset.pluginExportRange = "select";
- exportRangeSelect.hidden = true;
- appendOption(exportRangeSelect, "current", "\u5F53\u524D\u9875");
- appendOption(exportRangeSelect, "first-5", "\u524D5\u9875");
- appendOption(exportRangeSelect, "first-10", "\u524D10\u9875");
- appendOption(exportRangeSelect, "all", "\u5168\u90E8");
- appendOption(exportRangeSelect, "custom", "\u81EA\u5B9A\u4E49");
- exportRangeSelect.value = "first-5";
- const exportCustomPagesInput = document2.createElement("input");
- exportCustomPagesInput.type = "number";
- exportCustomPagesInput.min = "1";
- exportCustomPagesInput.step = "1";
- exportCustomPagesInput.hidden = true;
- exportCustomPagesInput.placeholder = "\u9875\u6570";
- exportCustomPagesInput.dataset.pluginExportCustomPages = "input";
- const exportButton = document2.createElement("button");
- exportButton.type = "button";
- exportButton.dataset.pluginExport = "button";
- exportButton.hidden = true;
- exportButton.tabIndex = -1;
- const audienceProfileExportButton = document2.createElement("button");
- audienceProfileExportButton.type = "button";
- audienceProfileExportButton.dataset.pluginExportAudienceProfile = "button";
- audienceProfileExportButton.textContent = "\u5BFC\u51FA\u9009\u4E2D\u8FBE\u4EBA\u6570\u636E";
- audienceProfileExportButton.title = "\u4EC5\u5BFC\u51FA\u5DF2\u52FE\u9009\u8FBE\u4EBA\uFF0C\u5305\u542B\u5185\u5BB9\u6570\u636E\u3001\u6548\u679C\u9884\u4F30\u3001\u753B\u50CF\u7B49\u7EF4\u5EA6";
- const audienceProfileByIdExportButton = document2.createElement("button");
- audienceProfileByIdExportButton.type = "button";
- audienceProfileByIdExportButton.dataset.pluginExportAudienceProfileById = "button";
- audienceProfileByIdExportButton.textContent = "\u6309\u661F\u56FEID\u5BFC\u51FA";
- audienceProfileByIdExportButton.title = "\u7C98\u8D34\u8FBE\u4EBA\u661F\u56FEID\u540E\u6279\u91CF\u5BFC\u51FA\u8FBE\u4EBA\u6570\u636E\uFF0C\u4E0D\u4F9D\u8D56\u5F53\u524D\u5217\u8868\u52FE\u9009";
- const audienceProfileFieldButton = document2.createElement("button");
- audienceProfileFieldButton.type = "button";
- audienceProfileFieldButton.dataset.pluginAudienceProfileFields = "button";
- audienceProfileFieldButton.textContent = "\u9009\u62E9\u5B57\u6BB5";
- audienceProfileFieldButton.title = "\u52FE\u9009\u672C\u6B21CSV\u9700\u8981\u5BFC\u51FA\u7684\u5B57\u6BB5\uFF0C\u8BBE\u7F6E\u4F1A\u81EA\u52A8\u4FDD\u5B58";
- const batchSubmitButton = document2.createElement("button");
- batchSubmitButton.type = "button";
- batchSubmitButton.dataset.pluginBatchSubmit = "button";
- batchSubmitButton.textContent = "\u63D0\u4EA4\u6279\u6B21";
- batchSubmitButton.title = "\u5C06\u5F53\u524D\u9009\u4E2D\u7684\u8FBE\u4EBA\u63D0\u4EA4\u5230\u540E\u7EED\u4E1A\u52A1\u6279\u6B21";
- const exportStatusText = document2.createElement("span");
- exportStatusText.dataset.pluginExportStatus = "text";
- applyStatusStyles(exportStatusText);
- root.append(
- exportRangeSelect,
- exportCustomPagesInput,
- exportButton,
- audienceProfileExportButton,
- audienceProfileByIdExportButton,
- audienceProfileFieldButton,
- batchSubmitButton,
- exportStatusText
- );
- document2.body.appendChild(root);
- applyNativeControlStyles(document2, {
- audienceProfileExportButton,
- audienceProfileByIdExportButton,
- audienceProfileFieldButton,
- batchSubmitButton,
- exportButton,
- exportCustomPagesInput,
- exportRangeSelect
- });
- ensureToolbarMounted(root, document2);
- exportButton.addEventListener("click", () => {
- void handlers.onExport();
- });
- audienceProfileExportButton.addEventListener("click", () => {
- void handlers.onExportAudienceProfile();
- });
- audienceProfileByIdExportButton.addEventListener("click", () => {
- void handlers.onExportAudienceProfileByIds();
- });
- audienceProfileFieldButton.addEventListener("click", () => {
- void handlers.onConfigureAudienceProfileFields();
- });
- batchSubmitButton.addEventListener("click", () => {
- void handlers.onSubmitBatch();
- });
- exportRangeSelect.addEventListener("change", () => {
- syncCustomPagesInputVisibility({
- batchSubmitButton,
- audienceProfileFieldButton,
- audienceProfileByIdExportButton,
- audienceProfileExportButton,
- exportButton,
- exportCustomPagesInput,
- exportRangeSelect,
- exportStatusText,
- root
- });
- });
- const toolbarDom = {
- audienceProfileExportButton,
- audienceProfileByIdExportButton,
- audienceProfileFieldButton,
- batchSubmitButton,
- exportButton,
- exportCustomPagesInput,
- exportRangeSelect,
- exportStatusText,
- root
- };
- syncCustomPagesInputVisibility(toolbarDom);
- return toolbarDom;
- }
- function appendOption(select, value, label) {
- const option = select.ownerDocument.createElement("option");
- option.value = value;
- option.textContent = label;
- select.appendChild(option);
- }
- function readToolbarDom(root) {
- const toolbarDom = {
- audienceProfileByIdExportButton: root.querySelector(
- '[data-plugin-export-audience-profile-by-id="button"]'
- ),
- audienceProfileExportButton: root.querySelector(
- '[data-plugin-export-audience-profile="button"]'
- ),
- audienceProfileFieldButton: root.querySelector(
- '[data-plugin-audience-profile-fields="button"]'
- ),
- batchSubmitButton: root.querySelector(
- '[data-plugin-batch-submit="button"]'
- ),
- exportButton: root.querySelector(
- '[data-plugin-export="button"]'
- ),
- exportCustomPagesInput: root.querySelector(
- '[data-plugin-export-custom-pages="input"]'
- ),
- exportRangeSelect: root.querySelector(
- '[data-plugin-export-range="select"]'
- ),
- exportStatusText: root.querySelector(
- '[data-plugin-export-status="text"]'
- ),
- root
- };
- syncCustomPagesInputVisibility(toolbarDom);
- return toolbarDom;
- }
- function readToolbarExportTarget(toolbar) {
- const scope = toolbar.exportRangeSelect.value;
- if (scope === "all") {
- return {
- target: {
- mode: "all"
- }
- };
- }
- if (scope === "current") {
- return {
- target: {
- mode: "count",
- pageCount: 1
- }
- };
- }
- if (scope === "first-5") {
- return {
- target: {
- mode: "count",
- pageCount: 5
- }
- };
- }
- if (scope === "first-10") {
- return {
- target: {
- mode: "count",
- pageCount: 10
- }
- };
- }
- const pageCount = Number(toolbar.exportCustomPagesInput.value);
- if (!Number.isInteger(pageCount) || pageCount < 1) {
- return {
- error: "\u8BF7\u8F93\u5165\u6709\u6548\u9875\u6570"
- };
- }
- return {
- target: {
- mode: "count",
- pageCount
- }
- };
- }
- function setToolbarBusyState(toolbar, isBusy) {
- [
- toolbar.batchSubmitButton,
- toolbar.audienceProfileFieldButton,
- toolbar.audienceProfileByIdExportButton,
- toolbar.audienceProfileExportButton,
- toolbar.exportButton,
- toolbar.exportRangeSelect,
- toolbar.exportCustomPagesInput
- ].forEach((element) => {
- element.disabled = isBusy;
- });
- }
- function setToolbarExportStatus(toolbar, text) {
- toolbar.exportStatusText.textContent = text;
- }
- function syncCustomPagesInputVisibility(toolbar) {
- toolbar.exportRangeSelect.hidden = true;
- toolbar.exportCustomPagesInput.hidden = true;
- }
- function ensureToolbarMounted(root, document2) {
- const actionRow = findNativeActionRow(document2);
- if (!actionRow) {
- root.hidden = true;
- return;
- }
- const customizeButton = findNativeActionButton(actionRow, "\u81EA\u5B9A\u4E49\u6307\u6807");
- const insertionAnchor = customizeButton ? findDirectChildAnchor(actionRow, customizeButton) : null;
- if (insertionAnchor) {
- actionRow.insertBefore(root, insertionAnchor);
- } else if (root.parentElement !== actionRow) {
- actionRow.prepend(root);
- }
- root.hidden = false;
- }
- function findNativeActionRow(document2) {
- const customizeButton = findNativeActionButton(document2, "\u81EA\u5B9A\u4E49\u6307\u6807");
- const exportButton = findNativeActionButton(document2, "\u5BFC\u51FA");
- const header = findHeaderContainer(customizeButton, exportButton);
- const sharedActionRow = customizeButton && exportButton ? findSmallestSharedActionRow(customizeButton, exportButton, header) : null;
- if (sharedActionRow) {
- return sharedActionRow;
- }
- const scope = header ?? document2;
- const candidates = Array.from(
- scope.querySelectorAll(".xt-space.xt-space--medium, .search-content--header")
- ).filter(
- (element) => element instanceof document2.defaultView.HTMLElement
- );
- const rankedCandidates = candidates.filter(
- (candidate) => isNativeActionRowCandidate(candidate, customizeButton, exportButton)
- ).sort((left, right) => {
- const depthDelta = getDepthWithinAncestor(right, header) - getDepthWithinAncestor(left, header);
- if (depthDelta !== 0) {
- return depthDelta;
- }
- return normalizeText(left.textContent).length - normalizeText(right.textContent).length;
- });
- return rankedCandidates[0] ?? null;
- }
- function findHeaderContainer(customizeButton, exportButton) {
- return customizeButton?.closest(".search-content--header") ?? exportButton?.closest(".search-content--header");
- }
- function findSmallestSharedActionRow(customizeButton, exportButton, boundary) {
- const exportAncestors = new Set(collectAncestorChain(exportButton, boundary));
- for (const candidate of collectAncestorChain(customizeButton, boundary)) {
- if (exportAncestors.has(candidate) && isNativeActionRowCandidate(candidate, customizeButton, exportButton)) {
- return candidate;
- }
- }
- return null;
- }
- function collectAncestorChain(element, boundary) {
- const ancestors = [];
- let current = element.parentElement;
- while (current) {
- ancestors.push(current);
- if (current === boundary) {
- break;
- }
- current = current.parentElement;
- }
- return ancestors;
- }
- function isNativeActionRowCandidate(candidate, customizeButton, exportButton) {
- if (customizeButton && !candidate.contains(customizeButton)) {
- return false;
- }
- if (exportButton && !candidate.contains(exportButton)) {
- return false;
- }
- const directChildLabels = Array.from(candidate.children).flatMap((child) => {
- const buttons = [];
- if (child instanceof candidate.ownerDocument.defaultView.HTMLButtonElement) {
- buttons.push(child);
- }
- buttons.push(...Array.from(child.querySelectorAll("button")));
- return buttons;
- }).map((button) => normalizeText(button.textContent));
- return directChildLabels.includes("\u5BFC\u51FA") && (directChildLabels.includes("\u81EA\u5B9A\u4E49\u6307\u6807") || Boolean(customizeButton));
- }
- function getDepthWithinAncestor(element, boundary) {
- let depth = 0;
- let current = element.parentElement;
- while (current && current !== boundary) {
- depth += 1;
- current = current.parentElement;
- }
- return depth;
- }
- function findNativeActionButton(root, text) {
- const document2 = root instanceof Document ? root : root.ownerDocument;
- if (!document2) {
- return null;
- }
- const candidates = Array.from(root.querySelectorAll("button")).filter(
- (element) => element instanceof document2.defaultView.HTMLElement
- );
- return candidates.find((element) => normalizeText(element.textContent) === text) ?? null;
- }
- function applyToolbarRootStyles(root) {
- root.style.display = "inline-flex";
- root.style.alignItems = "center";
- root.style.columnGap = "8px";
- root.style.flexWrap = "wrap";
- }
- function applyNativeControlStyles(document2, controls) {
- const primaryButton = findButtonContainingText(document2, "\u53D1\u5E03\u4EFB\u52A1") ?? findButtonContainingText(document2, "+\u53D1\u5E03\u4EFB\u52A1");
- const nativeButton = primaryButton ?? findNativeActionButton(document2, "\u81EA\u5B9A\u4E49\u6307\u6807") ?? findNativeActionButton(document2, "\u5BFC\u51FA");
- if (nativeButton) {
- controls.audienceProfileExportButton.className = nativeButton.className;
- controls.audienceProfileByIdExportButton.className = nativeButton.className;
- controls.audienceProfileFieldButton.className = nativeButton.className;
- controls.batchSubmitButton.className = nativeButton.className;
- }
- [
- controls.audienceProfileExportButton,
- controls.audienceProfileByIdExportButton,
- controls.audienceProfileFieldButton,
- controls.batchSubmitButton
- ].forEach((button) => {
- applyPrimaryButtonStyles4(button);
- button.style.whiteSpace = "nowrap";
- });
- [controls.exportRangeSelect, controls.exportCustomPagesInput].forEach((element) => {
- element.style.height = "32px";
- element.style.border = "1px solid #d0d7de";
- element.style.borderRadius = "6px";
- element.style.padding = "0 10px";
- element.style.background = "#fff";
- element.style.color = "#1f2329";
- element.style.boxSizing = "border-box";
- });
- controls.exportRangeSelect.style.minWidth = "104px";
- controls.exportCustomPagesInput.style.width = "72px";
- }
- function applyPrimaryButtonStyles4(button) {
- button.style.backgroundColor = "#7f1d2d";
- button.style.border = "1px solid #7f1d2d";
- button.style.borderRadius = "8px";
- button.style.color = "#ffffff";
- button.style.height = "32px";
- button.style.padding = "0 15px";
- button.style.boxSizing = "border-box";
- button.style.fontWeight = "600";
- button.style.transition = "background-color 0.16s ease, border-color 0.16s ease, box-shadow 0.16s ease, transform 0.16s ease";
- }
- function applyStatusStyles(statusText) {
- statusText.style.color = "#64748b";
- statusText.style.fontSize = "12px";
- statusText.style.lineHeight = "20px";
- statusText.style.marginLeft = "4px";
- statusText.style.whiteSpace = "nowrap";
- }
- function ensurePluginActionButtonTheme(document2) {
- if (document2.getElementById(PLUGIN_ACTION_BUTTON_STYLE_ID)) {
- return;
- }
- const style = document2.createElement("style");
- style.id = PLUGIN_ACTION_BUTTON_STYLE_ID;
- style.textContent = `
- [data-plugin-export-audience-profile="button"]:hover:not(:disabled),
- [data-plugin-export-audience-profile-by-id="button"]:hover:not(:disabled),
- [data-plugin-audience-profile-fields="button"]:hover:not(:disabled),
- [data-plugin-batch-submit="button"]:hover:not(:disabled) {
- background-color: #6d1627 !important;
- border-color: #6d1627 !important;
- }
-
- [data-plugin-export-audience-profile="button"]:active:not(:disabled),
- [data-plugin-export-audience-profile-by-id="button"]:active:not(:disabled),
- [data-plugin-audience-profile-fields="button"]:active:not(:disabled),
- [data-plugin-batch-submit="button"]:active:not(:disabled) {
- background-color: #58111f !important;
- border-color: #58111f !important;
- transform: translateY(1px);
- }
-
- [data-plugin-export-audience-profile="button"]:focus-visible,
- [data-plugin-export-audience-profile-by-id="button"]:focus-visible,
- [data-plugin-audience-profile-fields="button"]:focus-visible,
- [data-plugin-batch-submit="button"]:focus-visible {
- outline: none !important;
- box-shadow: 0 0 0 3px rgba(127, 29, 45, 0.2) !important;
- }
-
- [data-plugin-export-audience-profile="button"]:disabled,
- [data-plugin-export-audience-profile-by-id="button"]:disabled,
- [data-plugin-audience-profile-fields="button"]:disabled,
- [data-plugin-batch-submit="button"]:disabled {
- background-color: #c89ca4 !important;
- border-color: #c89ca4 !important;
- color: rgba(255, 255, 255, 0.95) !important;
- cursor: not-allowed !important;
- opacity: 1 !important;
- transform: none !important;
- box-shadow: none !important;
- }
- `;
- document2.head.appendChild(style);
- }
- function normalizeText(value) {
- return value?.replace(/\s+/g, " ").trim() ?? "";
- }
- function findButtonContainingText(root, text) {
- const document2 = root instanceof Document ? root : root.ownerDocument;
- if (!document2) {
- return null;
- }
- const candidates = Array.from(root.querySelectorAll("button")).filter(
- (element) => element instanceof document2.defaultView.HTMLElement
- );
- return candidates.find((element) => normalizeText(element.textContent).includes(text)) ?? null;
- }
- function findDirectChildAnchor(ancestor, descendant) {
- let current = descendant;
- let previous = null;
- while (current && current !== ancestor) {
- previous = current;
- current = current.parentElement;
- }
- return current === ancestor ? previous : null;
- }
-
- // src/content/market/market-list-request-snapshot.ts
- var MARKET_REQUEST_SNAPSHOT_ATTRIBUTE = "data-sces-market-request-snapshot";
- var MARKET_SEARCH_ENDPOINT_PATH = "/gw/api/gsearch/search_for_author_square";
- function readMarketListRequestSnapshot(document2) {
- const serializedSnapshot = document2.documentElement.getAttribute(
- MARKET_REQUEST_SNAPSHOT_ATTRIBUTE
- );
- if (!serializedSnapshot) {
- return readMarketListRequestSnapshotFromPageState(document2);
- }
- try {
- const parsedSnapshot = normalizeMarketListRequestSnapshot(
- JSON.parse(serializedSnapshot)
- );
- if (!parsedSnapshot) {
- return readMarketListRequestSnapshotFromPageState(document2);
- }
- return parsedSnapshot;
- } catch {
- return readMarketListRequestSnapshotFromPageState(document2);
- }
- }
- function isMarketListRequestSnapshot(value) {
- if (!value || typeof value !== "object") {
- return false;
- }
- const candidate = value;
- return typeof candidate.method === "string" && typeof candidate.url === "string" && (!("body" in candidate) || typeof candidate.body === "string") && (!("headers" in candidate) || isStringRecord(candidate.headers));
- }
- function normalizeMarketListRequestSnapshot(value) {
- if (!value || typeof value !== "object") {
- return null;
- }
- const candidate = value;
- const normalizedSnapshot = {
- body: typeof candidate.body === "string" ? candidate.body : void 0,
- method: typeof candidate.method === "string" ? candidate.method : void 0,
- url: typeof candidate.url === "string" ? candidate.url : void 0
- };
- if (candidate.headers && typeof candidate.headers === "object") {
- normalizedSnapshot.headers = Object.fromEntries(
- Object.entries(candidate.headers).filter(
- ([, entry]) => ["string", "number", "boolean"].includes(typeof entry)
- ).map(([key, entry]) => [key, String(entry)])
- );
- }
- return isMarketListRequestSnapshot(normalizedSnapshot) ? normalizedSnapshot : null;
- }
- function isStringRecord(value) {
- if (!value || typeof value !== "object") {
- return false;
- }
- return Object.values(value).every((entry) => typeof entry === "string");
- }
- function readMarketListRequestSnapshotFromPageState(document2) {
- const reqParams = findMarketReqParams(document2);
- if (!reqParams) {
- return null;
- }
- return {
- body: JSON.stringify(reqParams),
- method: "POST",
- url: buildMarketSearchUrl(document2)
- };
- }
- function findMarketReqParams(document2) {
- const marketRoot = document2.querySelector(".base-author-list");
- const setupState = marketRoot?.__vue__?._setupState;
- if (!setupState) {
- return null;
- }
- const queue = Object.values(setupState);
- while (queue.length > 0) {
- const current = unwrapVueRef3(queue.shift());
- if (!isRecord7(current)) {
- continue;
- }
- const reqParams = unwrapVueRef3(current.reqParams);
- if (isRecord7(reqParams)) {
- return reqParams;
- }
- Object.values(current).forEach((value) => {
- queue.push(value);
- });
- }
- return null;
- }
- function buildMarketSearchUrl(document2) {
- if (document2.location?.origin && document2.location.origin !== "null" && document2.location.origin !== "about:blank") {
- return document2.location.origin.includes("xingtu.cn") ? MARKET_SEARCH_ENDPOINT_PATH : new URL(MARKET_SEARCH_ENDPOINT_PATH, document2.location.origin).toString();
- }
- return MARKET_SEARCH_ENDPOINT_PATH;
- }
- function unwrapVueRef3(value) {
- if (isRecord7(value) && "value" in value) {
- return value.value;
- }
- return value;
- }
- function isRecord7(value) {
- return typeof value === "object" && value !== null;
- }
-
- // src/content/market/silent-export-controller.ts
- var PAGE_NUMBER_KEYS2 = [
- "currentPage",
- "page",
- "pageNo",
- "pageNum",
- "page_no",
- "page_num"
- ];
- function createSilentExportController(options) {
- const fetchImpl = options.fetchImpl ?? defaultFetch5;
- return {
- async exportRecords(target) {
- const snapshot = readMarketListRequestSnapshot(options.document);
- if (!snapshot) {
- return null;
- }
- const baseRequest = createPagedRequest(snapshot);
- if (!baseRequest) {
- return null;
- }
- const mergedRecords = /* @__PURE__ */ new Map();
- const maxPageCount = target.mode === "count" ? target.pageCount : 200;
- let totalPagesHint;
- for (let offset = 0; offset < maxPageCount; offset += 1) {
- const pageNumber = baseRequest.initialPage + offset;
- options.onProgress?.({
- currentPage: offset + 1,
- totalPages: target.mode === "count" ? target.pageCount : totalPagesHint
- });
- const payload = await fetchPagePayload(fetchImpl, baseRequest, pageNumber);
- const parsedResponse = parseMarketListResponse(payload);
- if (!parsedResponse) {
- return null;
- }
- totalPagesHint = parsedResponse.totalPages ?? totalPagesHint;
- if (parsedResponse.records.length === 0) {
- break;
- }
- parsedResponse.records.forEach((record) => {
- const existingRecord = mergedRecords.get(record.authorId);
- mergedRecords.set(record.authorId, mergeMarketRecord2(existingRecord, record));
- });
- if (target.mode === "count" && offset + 1 >= target.pageCount) {
- break;
- }
- if (target.mode === "all") {
- if (typeof parsedResponse.totalPages === "number" && pageNumber >= parsedResponse.totalPages) {
- break;
- }
- if (typeof parsedResponse.pageSize === "number" && parsedResponse.records.length < parsedResponse.pageSize) {
- break;
- }
- }
- }
- return Array.from(mergedRecords.values());
- }
- };
- }
- function createPagedRequest(snapshot) {
- const bodyPage = readPageFromBody(snapshot.body);
- if (bodyPage !== null) {
- return {
- initialPage: bodyPage,
- pageSource: "body",
- snapshot
- };
- }
- const urlPage = readPageFromUrl(snapshot.url);
- if (urlPage !== null) {
- return {
- initialPage: urlPage,
- pageSource: "url",
- snapshot
- };
- }
- return {
- initialPage: 1,
- pageSource: "none",
- snapshot
- };
- }
- async function fetchPagePayload(fetchImpl, request, pageNumber) {
- const nextUrl = request.pageSource === "url" ? mutateUrlPage(request.snapshot.url, pageNumber) : request.snapshot.url;
- const nextBody = mutateBodyPage(request.snapshot.body, pageNumber);
- const response = await fetchImpl(nextUrl, {
- body: nextBody,
- credentials: "include",
- headers: filterReplayHeaders(request.snapshot.headers, nextBody),
- method: request.snapshot.method
- });
- if (!response.ok) {
- throw new Error("\u9759\u9ED8\u5BFC\u51FA\u8BF7\u6C42\u5931\u8D25");
- }
- return response.json();
- }
- function readPageFromUrl(url) {
- try {
- const parsedUrl = new URL(url);
- for (const key of PAGE_NUMBER_KEYS2) {
- const value = readNumericString(parsedUrl.searchParams.get(key));
- if (value !== null) {
- return value;
- }
- }
- } catch {
- return null;
- }
- return null;
- }
- function mutateUrlPage(url, pageNumber) {
- try {
- const parsedUrl = new URL(url);
- for (const key of PAGE_NUMBER_KEYS2) {
- if (!parsedUrl.searchParams.has(key)) {
- continue;
- }
- parsedUrl.searchParams.set(key, String(pageNumber));
- return parsedUrl.toString();
- }
- parsedUrl.searchParams.set("page", String(pageNumber));
- return parsedUrl.toString();
- } catch {
- return url;
- }
- }
- function readPageFromBody(body) {
- const parsedBody = parseBody(body);
- if (!parsedBody) {
- return null;
- }
- return readKnownPaginationNumber(parsedBody, "page");
- }
- function mutateBodyPage(body, pageNumber) {
- if (!body) {
- return body;
- }
- const trimmedBody = body.trim();
- if (!trimmedBody) {
- return body;
- }
- try {
- const parsedJson = JSON.parse(trimmedBody);
- if (!replacePageNumberInValue(parsedJson, pageNumber) && isRecord8(parsedJson)) {
- parsedJson.page = pageNumber;
- }
- return JSON.stringify(parsedJson);
- } catch {
- const searchParams = new URLSearchParams(trimmedBody);
- for (const key of PAGE_NUMBER_KEYS2) {
- if (!searchParams.has(key)) {
- continue;
- }
- searchParams.set(key, String(pageNumber));
- return searchParams.toString();
- }
- searchParams.set("page", String(pageNumber));
- return searchParams.toString();
- }
- }
- function parseBody(body) {
- if (!body) {
- return null;
- }
- const trimmedBody = body.trim();
- if (!trimmedBody) {
- return null;
- }
- try {
- const parsedBody = JSON.parse(trimmedBody);
- return isRecord8(parsedBody) ? parsedBody : null;
- } catch {
- const searchParams = new URLSearchParams(trimmedBody);
- const payload = {};
- searchParams.forEach((value, key) => {
- payload[key] = value;
- });
- return payload;
- }
- }
- function replacePageNumberInValue(value, pageNumber) {
- if (!isRecord8(value)) {
- return false;
- }
- let replaced = false;
- PAGE_NUMBER_KEYS2.forEach((key) => {
- if (!(key in value)) {
- return;
- }
- value[key] = pageNumber;
- replaced = true;
- });
- if (replaced) {
- return true;
- }
- return Object.values(value).some((entry) => replacePageNumberInValue(entry, pageNumber));
- }
- function filterReplayHeaders(headers, body) {
- const filteredHeaders = Object.fromEntries(
- Object.entries(headers ?? {}).filter(([key]) => {
- const normalizedKey = key.toLowerCase();
- return normalizedKey !== "content-length" && normalizedKey !== "host";
- })
- );
- if (body) {
- if (!hasHeader(filteredHeaders, "accept")) {
- filteredHeaders.Accept = "application/json, text/plain, */*";
- }
- if (!hasHeader(filteredHeaders, "content-type")) {
- filteredHeaders["Content-Type"] = "application/json";
- }
- if (!hasHeader(filteredHeaders, "x-login-source")) {
- filteredHeaders["x-login-source"] = "1";
- }
- if (!hasHeader(filteredHeaders, "agw-js-conv")) {
- filteredHeaders["Agw-Js-Conv"] = "str";
- }
- }
- return Object.keys(filteredHeaders).length > 0 ? filteredHeaders : void 0;
- }
- function hasHeader(headers, key) {
- return Object.keys(headers).some((headerKey) => headerKey.toLowerCase() === key);
- }
- function readNumericString(value) {
- if (!value) {
- return null;
- }
- const parsedValue = Number(value);
- return Number.isFinite(parsedValue) ? parsedValue : null;
- }
- async function defaultFetch5(input, init) {
- return fetch(input, init);
- }
- function isRecord8(value) {
- return typeof value === "object" && value !== null;
- }
- function mergeMarketRecord2(existingRecord, incomingRecord) {
- if (!existingRecord) {
- return {
- ...incomingRecord,
- exportFields: mergeFieldMap2(void 0, incomingRecord.exportFields),
- rates: mergeFieldMap2(void 0, incomingRecord.rates),
- status: incomingRecord.status ?? "idle"
- };
- }
- return {
- ...existingRecord,
- ...incomingRecord,
- authorName: mergeStringValue2(existingRecord.authorName, incomingRecord.authorName) ?? "",
- coreUserId: mergeStringValue2(existingRecord.coreUserId, incomingRecord.coreUserId),
- exportFields: mergeFieldMap2(
- existingRecord.exportFields,
- incomingRecord.exportFields
- ),
- failureReason: incomingRecord.failureReason ?? existingRecord.failureReason,
- hasDirectRatesSource: existingRecord.hasDirectRatesSource || incomingRecord.hasDirectRatesSource,
- location: mergeStringValue2(existingRecord.location, incomingRecord.location),
- price21To60s: mergeStringValue2(
- existingRecord.price21To60s,
- incomingRecord.price21To60s
- ),
- rates: mergeFieldMap2(existingRecord.rates, incomingRecord.rates),
- status: incomingRecord.status ?? existingRecord.status
- };
- }
- function mergeFieldMap2(current, incoming) {
- if (!current && !incoming) {
- return void 0;
- }
- const merged = {
- ...current ?? {}
- };
- Object.entries(incoming ?? {}).forEach(([key, value]) => {
- const currentValue = merged[key];
- if (hasTextValue4(value) || !hasTextValue4(currentValue)) {
- merged[key] = value;
- }
- });
- return merged;
- }
- function mergeStringValue2(current, incoming) {
- return hasTextValue4(incoming) ? incoming : current;
- }
- function hasTextValue4(value) {
- return Boolean(value && value.trim().length > 0);
- }
-
- // src/content/market/result-store.ts
- function createMarketResultStore() {
- const records = /* @__PURE__ */ new Map();
- return {
- getRecord(authorId) {
- return records.get(authorId) ?? null;
- },
- listRecords() {
- return Array.from(records.values());
- },
- setAuthorFailed(authorId, reason) {
- const existingRecord = ensureRecord(authorId);
- existingRecord.status = "failed";
- existingRecord.failureReason = reason;
- },
- setAuthorLoading(authorId) {
- const existingRecord = ensureRecord(authorId);
- existingRecord.status = "loading";
- delete existingRecord.failureReason;
- },
- setBackendMetricsFailed(authorId) {
- const existingRecord = ensureRecord(authorId);
- existingRecord.backendMetricsStatus = "failed";
- },
- setBackendMetricsLoading(authorId) {
- const existingRecord = ensureRecord(authorId);
- existingRecord.backendMetricsStatus = "loading";
- },
- setBackendMetricsMissing(authorId) {
- const existingRecord = ensureRecord(authorId);
- existingRecord.backendMetricsStatus = "missing";
- },
- setBackendMetricsSuccess(authorId, backendMetrics) {
- const existingRecord = ensureRecord(authorId);
- existingRecord.backendMetricsStatus = "success";
- existingRecord.backendMetrics = {
- ...existingRecord.backendMetrics,
- ...backendMetrics
- };
- },
- setAuthorSuccess(authorId, rates) {
- const existingRecord = ensureRecord(authorId);
- existingRecord.status = "success";
- existingRecord.rates = {
- ...existingRecord.rates,
- ...rates
- };
- delete existingRecord.failureReason;
- },
- upsertMarketRow(row) {
- const existingRecord = records.get(row.authorId);
- if (existingRecord) {
- existingRecord.authorName = mergeStringValue3(existingRecord.authorName, row.authorName) ?? existingRecord.authorName;
- existingRecord.coreUserId = mergeStringValue3(
- existingRecord.coreUserId,
- row.coreUserId
- );
- existingRecord.location = mergeStringValue3(
- existingRecord.location,
- row.location
- );
- existingRecord.price21To60s = mergeStringValue3(
- existingRecord.price21To60s,
- row.price21To60s
- );
- existingRecord.exportFields = mergeFieldMap3(
- existingRecord.exportFields,
- row.exportFields
- );
- existingRecord.backendMetrics = mergeFieldMap3(
- existingRecord.backendMetrics,
- row.backendMetrics
- );
- existingRecord.hasDirectRatesSource = existingRecord.hasDirectRatesSource || row.hasDirectRatesSource;
- existingRecord.rates = mergeFieldMap3(existingRecord.rates, row.rates);
- return existingRecord;
- }
- const nextRecord = {
- ...row,
- backendMetricsStatus: "idle",
- status: "idle"
- };
- records.set(row.authorId, nextRecord);
- return nextRecord;
- }
- };
- function ensureRecord(authorId) {
- const existingRecord = records.get(authorId);
- if (existingRecord) {
- return existingRecord;
- }
- const nextRecord = {
- authorId,
- authorName: authorId,
- backendMetricsStatus: "idle",
- status: "idle"
- };
- records.set(authorId, nextRecord);
- return nextRecord;
- }
- }
- function mergeFieldMap3(current, incoming) {
- if (!current && !incoming) {
- return void 0;
- }
- const merged = {
- ...current ?? {}
- };
- Object.entries(incoming ?? {}).forEach(([key, value]) => {
- const currentValue = merged[key];
- if (!hasTextValue5(currentValue)) {
- merged[key] = value;
- }
- });
- return merged;
- }
- function mergeStringValue3(current, incoming) {
- if (!hasTextValue5(current)) {
- return incoming ?? current;
- }
- return current;
- }
- function hasTextValue5(value) {
- return typeof value === "string" && value.trim().length > 0;
- }
-
- // src/shared/auth-messages.ts
- function isAuthResponseMessage(value) {
- if (!value || typeof value !== "object") {
- return false;
- }
- const candidate = value;
- if (candidate.ok === false) {
- return candidate.type === "auth:error" && typeof candidate.error === "string";
- }
- if (candidate.ok !== true || typeof candidate.type !== "string") {
- return false;
- }
- if (candidate.type === "auth:ack") {
- return true;
- }
- if (candidate.type === "auth:token") {
- return Boolean(
- candidate.value && typeof candidate.value === "object" && typeof candidate.value.accessToken === "string"
- );
- }
- if (candidate.type === "auth:state") {
- return Boolean(
- candidate.value && typeof candidate.value === "object" && typeof candidate.value.isAuthenticated === "boolean"
- );
- }
- return false;
- }
-
- // src/shared/backend-metrics-messages.ts
- function isBackendMetricsResponseMessage(value) {
- if (!value || typeof value !== "object") {
- return false;
- }
- const candidate = value;
- if (candidate.ok === false) {
- return candidate.type === "backend-metrics:error" && typeof candidate.error === "string";
- }
- return Boolean(
- candidate.ok === true && candidate.type === "backend-metrics:result" && candidate.value && typeof candidate.value === "object" && Array.isArray(candidate.value.rows)
- );
- }
-
- // src/content/market/index.ts
- var AUDIENCE_PROFILE_FIELD_SELECTION_STORAGE_KEY = "sces:audience-profile:selectedHeaders";
- function createMarketController(options) {
- const marketApiClient = createMarketApiClient();
- const audienceProfileClient = createAudienceProfileClient();
- const authorBaseClient = createAuthorBaseClient();
- const businessAbilityClient = createBusinessAbilityClient();
- const sendRuntimeMessage = createRuntimeMessageSender();
- const resultStore = options.resultStore ?? createMarketResultStore();
- const loadAuthorMetrics = options.loadAuthorMetrics ?? marketApiClient.loadAuthorAseInfo;
- const searchBackendMetrics = options.searchBackendMetrics ?? (hasRuntimeMessageSender() ? (starIds) => readBackendMetrics(sendRuntimeMessage, starIds) : null);
- const buildCsv = options.buildCsv ?? buildMarketCsv;
- const buildAudienceCsv = options.buildAudienceProfileCsv ?? buildAudienceProfileCsv;
- const loadAudienceProfile = options.loadAudienceProfile ?? audienceProfileClient.loadAudienceProfile;
- const loadAuthorBaseInfo = options.loadAuthorBaseInfo ?? authorBaseClient.loadAuthorBaseInfo;
- const loadBusinessAbility = options.loadBusinessAbility ?? businessAbilityClient.loadBusinessAbility;
- const getAuthState = options.getAuthState ?? (() => readAuthState(sendRuntimeMessage));
- const mutationObserverFactory = options.mutationObserverFactory ?? ((callback) => new MutationObserver(callback));
- const promptBatchName = options.promptBatchName ?? (() => promptForBatchName(options.document));
- const promptAuthorIds = options.promptAuthorIds ?? (() => promptForAuthorIds(options.document));
- const submitBatch = options.submitBatch ?? ((payload) => readBatchSubmitAck(sendRuntimeMessage, payload));
- const audienceProfileTargets = [
- { kind: "audience", target: AUDIENCE_PROFILE_TARGETS.audience },
- { kind: "fans", target: AUDIENCE_PROFILE_TARGETS.fans },
- {
- kind: "longtimeFans",
- target: AUDIENCE_PROFILE_TARGETS.longtimeFans
- }
- ];
- let activeProgressLabel = "\u5BFC\u51FA\u4E2D";
- let shouldShowDetailedProgress = true;
- const exportRangeController = createExportRangeController({
- document: options.document,
- onProgress: ({ currentPage, totalPages }) => {
- updateToolbarProgress(currentPage, totalPages);
- },
- prepareCurrentPageForExport,
- readCurrentPageRecords: () => getVisibleOrderedRecords(),
- readCurrentPageRowCount: () => countCurrentPageRows(options.document),
- window: options.window
- });
- const silentExportController = createSilentExportController({
- document: options.document,
- onProgress: ({ currentPage, totalPages }) => {
- updateToolbarProgress(currentPage, totalPages);
- }
- });
- let activeSort;
- let isDisposed = false;
- let isSyncRunning = false;
- let isSyncScheduled = false;
- let lastKnownPageSignature = "";
- let needsResync = false;
- let scheduledSyncTimeoutId = null;
- const selectedAuthorIds = /* @__PURE__ */ new Set();
- let toolbar;
- const observer = mutationObserverFactory(() => {
- if (isDisposed) {
- return;
- }
- let nextPageSignature = lastKnownPageSignature;
- try {
- nextPageSignature = readMarketPageSignature(options.document);
- } catch {
- return;
- }
- const toolbarNeedsRemount = !toolbar || !isPluginToolbarMounted(toolbar.root, options.document);
- const selectionControlsMissing = !options.document.querySelector('[data-market-selection-checkbox="row"]') || !options.document.querySelector('[data-market-selection-checkbox="header"]');
- if (nextPageSignature === lastKnownPageSignature && !toolbarNeedsRemount && !selectionControlsMissing) {
- return;
- }
- scheduleSync();
- });
- const observationRoot = options.document.body ?? options.document.documentElement;
- startObserving();
- const toolbarHandlers = {
- onExport: async () => {
- syncSelectionStateFromDom();
- const exportTarget = readToolbarExportTarget(toolbar);
- if (!exportTarget.target) {
- setToolbarExportStatus(toolbar, exportTarget.error ?? "\u5BFC\u51FA\u914D\u7F6E\u65E0\u6548");
- return;
- }
- setToolbarBusyState(toolbar, true);
- try {
- const records = filterRecordsBySelection(
- await exportRecords(exportTarget.target, "\u5BFC\u51FA\u4E2D", {
- showDetailedProgress: selectedAuthorIds.size === 0
- })
- );
- options.onCsvReady?.(buildCsv(records));
- setToolbarExportStatus(toolbar, "");
- } catch (error) {
- setToolbarExportStatus(
- toolbar,
- error instanceof Error ? error.message : "\u5BFC\u51FA\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"
- );
- } finally {
- setToolbarBusyState(toolbar, false);
- }
- },
- onExportAudienceProfile: async () => {
- syncSelectionStateFromDom();
- if (selectedAuthorIds.size === 0) {
- setToolbarExportStatus(toolbar, "\u8BF7\u5148\u52FE\u9009\u9700\u8981\u5BFC\u51FA\u6570\u636E\u7684\u8FBE\u4EBA");
- return;
- }
- const exportTarget = readToolbarExportTarget(toolbar);
- if (!exportTarget.target) {
- setToolbarExportStatus(toolbar, exportTarget.error ?? "\u5BFC\u51FA\u914D\u7F6E\u65E0\u6548");
- return;
- }
- setToolbarBusyState(toolbar, true);
- try {
- const selectedRecords = filterRecordsBySelectionStrict(
- await exportRecords(exportTarget.target, "\u753B\u50CF\u5BFC\u51FA\u4E2D", {
- showDetailedProgress: false
- })
- );
- if (selectedRecords.length === 0) {
- setToolbarExportStatus(toolbar, "\u5F53\u524D\u5BFC\u51FA\u8303\u56F4\u5185\u6CA1\u6709\u9009\u4E2D\u7684\u8FBE\u4EBA");
- return;
- }
- const rows = [];
- for (let index = 0; index < selectedRecords.length; index += 1) {
- const record = selectedRecords[index];
- setToolbarExportStatus(
- toolbar,
- `\u753B\u50CF\u5BFC\u51FA\u4E2D ${index + 1}/${selectedRecords.length}...`
- );
- const [profiles, businessAbility] = await Promise.all([
- loadAudienceProfileSet(record),
- loadBusinessAbilitySafe(record)
- ]);
- rows.push({
- businessAbility,
- profiles,
- record
- });
- }
- if (rows.every(
- (row) => Object.values(row.profiles).every((profile) => profile.status === "failed")
- )) {
- setToolbarExportStatus(toolbar, "\u753B\u50CF\u5BFC\u51FA\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
- return;
- }
- options.onCsvReady?.(
- buildAudienceCsv(rows, {
- selectedHeaders: readAudienceProfileSelectedHeaders()
- }),
- buildAudienceProfileFilename()
- );
- setToolbarExportStatus(toolbar, "");
- } catch (error) {
- setToolbarExportStatus(
- toolbar,
- error instanceof Error ? error.message : "\u753B\u50CF\u5BFC\u51FA\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"
- );
- } finally {
- setToolbarBusyState(toolbar, false);
- }
- },
- onExportAudienceProfileByIds: async () => {
- const input = await promptAuthorIds();
- if (input === null) {
- return;
- }
- const parsed = parseAuthorIds(input);
- if (parsed.ids.length === 0) {
- setToolbarExportStatus(toolbar, "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u8FBE\u4EBA\u661F\u56FEID");
- return;
- }
- setToolbarBusyState(toolbar, true);
- try {
- setToolbarExportStatus(
- toolbar,
- `\u8BC6\u522B ${parsed.ids.length + parsed.duplicates.length + parsed.invalidTokens.length} \u4E2A\uFF0C\u53BB\u91CD\u540E ${parsed.ids.length} \u4E2A\uFF0C\u975E\u6CD5 ${parsed.invalidTokens.length} \u4E2A`
- );
- const backendMetricsByAuthorId = await loadBackendMetricsMap(parsed.ids);
- const rows = [];
- for (let index = 0; index < parsed.ids.length; index += 1) {
- const authorId = parsed.ids[index];
- setToolbarExportStatus(
- toolbar,
- `\u6309ID\u753B\u50CF\u5BFC\u51FA\u4E2D ${index + 1}/${parsed.ids.length}...`
- );
- rows.push(
- await loadAudienceProfileRowById(
- authorId,
- backendMetricsByAuthorId.get(authorId)
- )
- );
- }
- options.onCsvReady?.(
- buildAudienceCsv(rows, {
- selectedHeaders: readAudienceProfileSelectedHeaders()
- }),
- buildAudienceProfileFilename(/* @__PURE__ */ new Date(), "\u6309ID\u5BFC\u51FA")
- );
- setToolbarExportStatus(toolbar, "");
- } catch (error) {
- setToolbarExportStatus(
- toolbar,
- error instanceof Error ? error.message : "\u6309ID\u5BFC\u51FA\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"
- );
- } finally {
- setToolbarBusyState(toolbar, false);
- }
- },
- onConfigureAudienceProfileFields: async () => {
- const groups = listAudienceProfileSelectableFieldGroups();
- const selectedHeaders = readAudienceProfileSelectedHeaders();
- const nextHeaders = await promptForAudienceProfileFields(
- options.document,
- groups,
- selectedHeaders
- );
- if (nextHeaders === null) {
- return;
- }
- saveAudienceProfileSelectedHeaders(nextHeaders);
- setToolbarExportStatus(
- toolbar,
- `\u5B57\u6BB5\u5DF2\u4FDD\u5B58\uFF08\u5DF2\u9009 ${nextHeaders.length}/${readAudienceProfileSelectableHeaders().length} \u4E2A\u5B57\u6BB5\uFF09`
- );
- },
- onSubmitBatch: async () => {
- syncSelectionStateFromDom();
- const exportTarget = readToolbarExportTarget(toolbar);
- if (!exportTarget.target) {
- setToolbarExportStatus(toolbar, exportTarget.error ?? "\u5BFC\u51FA\u914D\u7F6E\u65E0\u6548");
- return;
- }
- const batchName = await promptBatchName();
- if (batchName === null) {
- return;
- }
- if (!batchName.trim()) {
- setToolbarExportStatus(toolbar, "\u8BF7\u8F93\u5165\u6279\u6B21\u540D\u79F0");
- return;
- }
- setToolbarBusyState(toolbar, true);
- try {
- const hasSelectedAuthors = selectedAuthorIds.size > 0;
- const records = filterRecordsBySelection(
- await exportRecords(
- exportTarget.target,
- hasSelectedAuthors ? "\u63D0\u4EA4\u5DF2\u9009\u8FBE\u4EBA\u4E2D" : "\u63D0\u4EA4\u4E2D",
- {
- showDetailedProgress: !hasSelectedAuthors
- }
- )
- );
- const authState = await getAuthState();
- if (!authState.isAuthenticated) {
- throw new Error("\u8BF7\u5148\u767B\u5F55\u63D2\u4EF6");
- }
- const payload = createBatchPayload({
- authState,
- batchName,
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
- records
- });
- await submitBatch(payload);
- setToolbarExportStatus(toolbar, "\u6279\u6B21\u63D0\u4EA4\u6210\u529F");
- } catch (error) {
- setToolbarExportStatus(
- toolbar,
- error instanceof Error ? error.message : "\u6279\u6B21\u63D0\u4EA4\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"
- );
- } finally {
- setToolbarBusyState(toolbar, false);
- }
- }
- };
- toolbar = ensurePluginToolbar(options.document, toolbarHandlers);
- const ready = runSyncCycle();
- return {
- dispose() {
- isDisposed = true;
- observer.disconnect();
- if (scheduledSyncTimeoutId !== null) {
- options.window.clearTimeout(scheduledSyncTimeoutId);
- scheduledSyncTimeoutId = null;
- }
- },
- ready
- };
- async function hydrateCurrentPage() {
- const table = syncMarketTable(options.document);
- if (!table) {
- return;
- }
- const pageRows = [];
- for (const rowDom of table.rows) {
- const rowSnapshot = readRowSnapshot(rowDom);
- if (!rowSnapshot.authorId || !hasTextValue6(rowSnapshot.authorName)) {
- continue;
- }
- pageRows.push({
- rowDom,
- rowSnapshot
- });
- resultStore.upsertMarketRow(rowSnapshot);
- }
- const pendingRateRows = [];
- const rowsNeedingBackendMetrics = [];
- pageRows.forEach(({ rowDom, rowSnapshot }) => {
- if (rowSnapshot.hasDirectRatesSource) {
- resultStore.setAuthorSuccess(rowSnapshot.authorId, rowSnapshot.rates ?? {});
- }
- const existingRecord = resultStore.getRecord(rowSnapshot.authorId);
- const needsRateFetch = !hasSettledRateState(existingRecord) && !hasCompleteRates(existingRecord?.rates);
- const needsBackendMetrics = Boolean(searchBackendMetrics) && !hasSettledBackendMetricsState(existingRecord);
- if (needsRateFetch) {
- resultStore.setAuthorLoading(rowSnapshot.authorId);
- pendingRateRows.push({
- rowDom,
- rowSnapshot
- });
- }
- if (needsBackendMetrics) {
- resultStore.setBackendMetricsLoading(rowSnapshot.authorId);
- rowsNeedingBackendMetrics.push({
- rowDom,
- rowSnapshot
- });
- }
- if (needsRateFetch || needsBackendMetrics) {
- renderMarketRowState(rowDom, {
- ...existingRecord ?? {
- authorId: rowSnapshot.authorId,
- authorName: rowSnapshot.authorName,
- status: "idle"
- },
- ...rowSnapshot,
- backendMetricsStatus: needsBackendMetrics ? "loading" : existingRecord?.backendMetricsStatus,
- rates: existingRecord?.rates,
- status: needsRateFetch || needsBackendMetrics ? "loading" : existingRecord?.status ?? "idle"
- });
- return;
- }
- if (existingRecord) {
- renderMarketRowState(rowDom, existingRecord);
- }
- });
- await Promise.all([
- hydrateRatesForRows(pendingRateRows),
- hydrateBackendMetricsForPage(rowsNeedingBackendMetrics)
- ]);
- pageRows.forEach(({ rowDom, rowSnapshot }) => {
- const record = resultStore.getRecord(rowSnapshot.authorId);
- if (!record) {
- return;
- }
- renderMarketRowState(rowDom, record);
- });
- }
- async function hydrateRatesForRows(pageRows) {
- if (pageRows.length === 0) {
- return;
- }
- await Promise.all(
- pageRows.map(async ({ rowSnapshot }) => {
- const metricsResult = await loadAuthorMetrics(rowSnapshot.authorId);
- if (metricsResult.success) {
- resultStore.setAuthorSuccess(rowSnapshot.authorId, metricsResult.rates);
- return;
- }
- resultStore.setAuthorFailed(rowSnapshot.authorId, metricsResult.reason);
- })
- );
- }
- async function hydrateBackendMetricsForPage(pageRows) {
- if (!searchBackendMetrics || pageRows.length === 0) {
- return;
- }
- try {
- const rows = await searchBackendMetrics(
- pageRows.map(({ rowSnapshot }) => rowSnapshot.authorId)
- );
- const rowMap = new Map(rows.map((row) => [row.starId, row]));
- pageRows.forEach(({ rowSnapshot }) => {
- const backendMetrics = rowMap.get(rowSnapshot.authorId);
- if (backendMetrics) {
- resultStore.setBackendMetricsSuccess(rowSnapshot.authorId, backendMetrics);
- } else {
- resultStore.setBackendMetricsMissing(rowSnapshot.authorId);
- }
- });
- } catch {
- pageRows.forEach(({ rowSnapshot }) => {
- resultStore.setBackendMetricsFailed(rowSnapshot.authorId);
- });
- }
- }
- function applyCurrentView() {
- runWithoutMutationSync(() => {
- toolbar = ensurePluginToolbar(options.document, toolbarHandlers);
- const table = syncMarketTable(options.document);
- if (!table) {
- return;
- }
- syncPluginSortHeaders(options.document, {
- activeSort,
- onToggleSort: toggleSortFromHeader
- });
- const records = getVisibleOrderedRecords(table);
- applyRowVisibility(table, new Set(records.map((record) => record.authorId)));
- applyRowOrder(table, records.map((record) => record.authorId));
- bindSelectionControls(table);
- syncMarketSelectionState(table, selectedAuthorIds);
- lastKnownPageSignature = readMarketPageSignature(options.document);
- });
- }
- function bindSelectionControls(table) {
- if (!table) {
- return;
- }
- table.rows.forEach((rowDom) => {
- rowDom.selectionCheckbox.dataset.marketSelectionAuthorId = rowDom.authorId;
- if (rowDom.selectionCheckbox.dataset.marketSelectionBound === "true") {
- return;
- }
- rowDom.selectionCheckbox.dataset.marketSelectionBound = "true";
- rowDom.selectionCheckbox.addEventListener("change", () => {
- if (rowDom.selectionCheckbox.checked) {
- selectedAuthorIds.add(rowDom.authorId);
- } else {
- selectedAuthorIds.delete(rowDom.authorId);
- }
- refreshSelectionControls();
- });
- });
- if (!table.headerSelectionCheckbox) {
- return;
- }
- if (table.headerSelectionCheckbox.dataset.marketSelectionBound === "true") {
- return;
- }
- table.headerSelectionCheckbox.dataset.marketSelectionBound = "true";
- table.headerSelectionCheckbox.addEventListener("change", () => {
- const currentTable = syncMarketTable(options.document);
- if (!currentTable) {
- return;
- }
- const visibleRows = currentTable.rows.filter(
- (rowDom) => rowDom.visibilityTargets.some((target) => !target.hidden)
- );
- const scopedRows = visibleRows.length > 0 ? visibleRows : currentTable.rows;
- if (table.headerSelectionCheckbox?.checked) {
- scopedRows.forEach((rowDom) => {
- selectedAuthorIds.add(rowDom.authorId);
- });
- } else {
- scopedRows.forEach((rowDom) => {
- selectedAuthorIds.delete(rowDom.authorId);
- });
- }
- refreshSelectionControls();
- });
- }
- function refreshSelectionControls() {
- const table = syncMarketTable(options.document);
- if (!table) {
- return;
- }
- bindSelectionControls(table);
- syncMarketSelectionState(table, selectedAuthorIds);
- }
- function syncSelectionStateFromDom() {
- const rowSelectionCheckboxes = Array.from(
- options.document.querySelectorAll('[data-market-selection-checkbox="row"]')
- ).filter(
- (element) => element instanceof HTMLInputElement
- );
- if (rowSelectionCheckboxes.length === 0) {
- return;
- }
- rowSelectionCheckboxes.forEach((checkbox) => {
- const authorId = checkbox.dataset.marketSelectionAuthorId?.trim();
- if (!authorId) {
- return;
- }
- if (checkbox.checked) {
- selectedAuthorIds.add(authorId);
- } else {
- selectedAuthorIds.delete(authorId);
- }
- });
- refreshSelectionControls();
- }
- function toggleSortFromHeader(field) {
- activeSort = getNextSortState(activeSort, field);
- applyCurrentView();
- }
- function getVisibleOrderedRecords(table = syncMarketTable(options.document)) {
- const currentPageRecords = readCurrentPageRecords(table);
- return applyFilterAndSort(currentPageRecords, {
- sort: activeSort
- });
- }
- async function exportRecords(target, inProgressLabel = "\u5BFC\u51FA\u4E2D", progressOptions = {}) {
- activeProgressLabel = inProgressLabel;
- shouldShowDetailedProgress = progressOptions.showDetailedProgress ?? true;
- setToolbarExportStatus(toolbar, `${inProgressLabel}...`);
- if (target.mode === "count" && target.pageCount <= 1) {
- await prepareCurrentPageForExport();
- return getVisibleOrderedRecords();
- }
- const silentExportRecords = await silentExportController.exportRecords(target);
- if (silentExportRecords) {
- return hydrateExportRecords(
- silentExportRecords.map((record) => ({
- ...record,
- status: record.status ?? "idle"
- }))
- );
- }
- return exportRangeController.exportRecords(target);
- }
- function updateToolbarProgress(currentPage, totalPages) {
- if (!shouldShowDetailedProgress) {
- setToolbarExportStatus(toolbar, `${activeProgressLabel}...`);
- return;
- }
- setToolbarExportStatus(
- toolbar,
- totalPages ? `${activeProgressLabel} ${currentPage}/${totalPages} \u9875...` : `${activeProgressLabel} \u7B2C${currentPage}\u9875...`
- );
- }
- function filterRecordsBySelection(records) {
- if (selectedAuthorIds.size === 0) {
- return records;
- }
- const selectedRecords = records.filter(
- (record) => selectedAuthorIds.has(record.authorId)
- );
- return selectedRecords.length > 0 ? selectedRecords : records;
- }
- function filterRecordsBySelectionStrict(records) {
- if (selectedAuthorIds.size === 0) {
- return [];
- }
- return records.filter((record) => selectedAuthorIds.has(record.authorId));
- }
- async function loadAudienceProfileSet(record) {
- const profiles = {};
- for (const { kind, target } of audienceProfileTargets) {
- try {
- profiles[kind] = await loadAudienceProfile(record, target);
- } catch (error) {
- profiles[kind] = {
- failureReason: error instanceof Error ? error.message : "request-failed",
- status: "failed"
- };
- }
- }
- return profiles;
- }
- async function loadBusinessAbilitySafe(record) {
- try {
- return await loadBusinessAbility(record);
- } catch (error) {
- return {
- failureReason: error instanceof Error ? error.message : "request-failed",
- status: "failed"
- };
- }
- }
- async function loadAudienceProfileRowById(authorId, backendMetrics) {
- const [baseRecord, metricsResult] = await Promise.all([
- loadAuthorBaseInfoSafe(authorId),
- loadAuthorMetricsSafe(authorId)
- ]);
- const recordForRequests = {
- ...baseRecord,
- authorName: baseRecord.authorName || authorId,
- ...metricsResult.success ? { rates: metricsResult.rates } : {},
- ...backendMetrics ? { backendMetrics, backendMetricsStatus: "success" } : {}
- };
- const [profiles, businessAbility] = await Promise.all([
- loadAudienceProfileSet(recordForRequests),
- loadBusinessAbilitySafe(recordForRequests)
- ]);
- const failureReasons = collectAudienceProfileRowFailures(
- baseRecord,
- profiles,
- businessAbility
- );
- const rowStatus = failureReasons.length === 0 ? "\u6210\u529F" : hasAudienceProfileRowSuccess(baseRecord, profiles, businessAbility) ? "\u90E8\u5206\u6210\u529F" : "\u5931\u8D25";
- const authorName = baseRecord.authorName || "";
- return {
- businessAbility,
- profiles,
- record: {
- ...recordForRequests,
- exportFields: {
- \u8FBE\u4EBAID: authorId,
- \u8FBE\u4EBA\u540D\u79F0: authorName,
- \u5BFC\u51FA\u72B6\u6001: rowStatus,
- \u5931\u8D25\u539F\u56E0: failureReasons.join("; ")
- }
- }
- };
- }
- async function loadAuthorBaseInfoSafe(authorId) {
- try {
- return await loadAuthorBaseInfo(authorId);
- } catch (error) {
- return {
- authorId,
- authorName: "",
- failureReason: error instanceof Error ? "request-failed" : "request-failed",
- status: "failed"
- };
- }
- }
- async function loadAuthorMetricsSafe(authorId) {
- try {
- return await loadAuthorMetrics(authorId);
- } catch {
- return {
- reason: "request-failed",
- success: false
- };
- }
- }
- async function loadBackendMetricsMap(authorIds) {
- const metricsMap = /* @__PURE__ */ new Map();
- if (!searchBackendMetrics || authorIds.length === 0) {
- return metricsMap;
- }
- try {
- const rows = await searchBackendMetrics(authorIds);
- rows.forEach((row) => {
- const { starId, ...backendMetrics } = row;
- metricsMap.set(starId, backendMetrics);
- });
- } catch {
- return metricsMap;
- }
- return metricsMap;
- }
- function collectAudienceProfileRowFailures(baseRecord, profiles, businessAbility) {
- const failures = [];
- if (baseRecord.status === "failed") {
- failures.push(`\u57FA\u7840\u4FE1\u606F:${baseRecord.failureReason ?? "request-failed"}`);
- }
- Object.entries(profiles).forEach(([kind, profile]) => {
- if (profile.status === "failed") {
- failures.push(`${readAudienceProfileKindLabel(kind)}:${profile.failureReason ?? "request-failed"}`);
- }
- });
- if (businessAbility.status === "failed") {
- failures.push(`\u5546\u4E1A\u80FD\u529B:${businessAbility.failureReason ?? "request-failed"}`);
- }
- return failures;
- }
- function hasAudienceProfileRowSuccess(baseRecord, profiles, businessAbility) {
- return baseRecord.status === "success" || businessAbility.status === "success" || Object.values(profiles).some((profile) => profile.status === "success");
- }
- function readAudienceProfileKindLabel(kind) {
- if (kind === "audience") {
- return "\u89C2\u4F17\u753B\u50CF";
- }
- if (kind === "fans") {
- return "\u7C89\u4E1D\u753B\u50CF";
- }
- return "\u94C1\u7C89\u753B\u50CF";
- }
- function readAudienceProfileSelectableHeaders() {
- return listAudienceProfileSelectableFieldGroups().flatMap(
- (group) => group.headers
- );
- }
- function readAudienceProfileSelectedHeaders() {
- const selectableHeaders = readAudienceProfileSelectableHeaders();
- const selectableHeaderSet = new Set(selectableHeaders);
- try {
- const rawValue = options.window.localStorage?.getItem(
- AUDIENCE_PROFILE_FIELD_SELECTION_STORAGE_KEY
- );
- if (!rawValue) {
- return selectableHeaders;
- }
- const parsedValue = JSON.parse(rawValue);
- if (!Array.isArray(parsedValue)) {
- return selectableHeaders;
- }
- const selectedHeaders = parsedValue.filter(
- (header) => typeof header === "string" && selectableHeaderSet.has(header)
- );
- return selectedHeaders.length > 0 ? selectedHeaders : selectableHeaders;
- } catch {
- return selectableHeaders;
- }
- }
- function saveAudienceProfileSelectedHeaders(headers) {
- const selectableHeaderSet = new Set(readAudienceProfileSelectableHeaders());
- const selectedHeaders = headers.filter(
- (header) => selectableHeaderSet.has(header)
- );
- try {
- options.window.localStorage?.setItem(
- AUDIENCE_PROFILE_FIELD_SELECTION_STORAGE_KEY,
- JSON.stringify(selectedHeaders)
- );
- } catch {
- }
- }
- async function prepareCurrentPageForExport() {
- await runSyncCycle();
- await harvestCurrentPageForExport();
- await runSyncCycle();
- }
- async function hydrateExportRecords(records) {
- for (const record of records) {
- resultStore.upsertMarketRow(record);
- const existingRecord = resultStore.getRecord(record.authorId);
- if (existingRecord?.status === "success" && existingRecord.rates) {
- continue;
- }
- if (record.hasDirectRatesSource) {
- const directRates = record.rates ?? {};
- const hasAllRates = Boolean(directRates.singleVideoAfterSearchRate) && Boolean(directRates.personalVideoAfterSearchRate);
- resultStore.setAuthorSuccess(record.authorId, directRates);
- if (hasAllRates) {
- continue;
- }
- } else {
- resultStore.setAuthorLoading(record.authorId);
- }
- const metricsResult = await loadAuthorMetrics(record.authorId);
- if (metricsResult.success) {
- resultStore.setAuthorSuccess(record.authorId, metricsResult.rates);
- } else {
- resultStore.setAuthorFailed(record.authorId, metricsResult.reason);
- }
- }
- if (searchBackendMetrics) {
- const backendTargetRecords = records.filter((record) => {
- const existingRecord = resultStore.getRecord(record.authorId);
- return !(existingRecord?.backendMetricsStatus === "success" || existingRecord?.backendMetricsStatus === "missing");
- });
- if (backendTargetRecords.length > 0) {
- backendTargetRecords.forEach((record) => {
- resultStore.setBackendMetricsLoading(record.authorId);
- });
- try {
- const backendRows = await searchBackendMetrics(
- backendTargetRecords.map((record) => record.authorId)
- );
- const backendRowMap = new Map(backendRows.map((row) => [row.starId, row]));
- backendTargetRecords.forEach((record) => {
- const backendMetrics = backendRowMap.get(record.authorId);
- if (backendMetrics) {
- resultStore.setBackendMetricsSuccess(record.authorId, backendMetrics);
- } else {
- resultStore.setBackendMetricsMissing(record.authorId);
- }
- });
- } catch {
- backendTargetRecords.forEach((record) => {
- resultStore.setBackendMetricsFailed(record.authorId);
- });
- }
- }
- }
- return records.map((record) => toMarketRecord(record));
- }
- async function harvestCurrentPageForExport() {
- let hydrationSnapshot = await collectCurrentPageSnapshotsUntilSettled();
- if (hydrationSnapshot.missingDefaultFieldCount === 0 && hydrationSnapshot.blankExportFieldCount === 0) {
- return;
- }
- const table = syncMarketTable(options.document);
- const scrollContainer = findCurrentPageScrollContainer(table);
- if (!scrollContainer) {
- return;
- }
- const originalScrollTop = scrollContainer.scrollTop;
- const maxScrollTop = Math.max(
- 0,
- scrollContainer.scrollHeight - scrollContainer.clientHeight
- );
- if (maxScrollTop <= 0) {
- return;
- }
- const step = Math.max(scrollContainer.clientHeight, 240);
- for (let nextScrollTop = Math.min(originalScrollTop + step, maxScrollTop); nextScrollTop > originalScrollTop && nextScrollTop <= maxScrollTop; nextScrollTop = Math.min(nextScrollTop + step, maxScrollTop)) {
- setScrollTop(scrollContainer, nextScrollTop);
- hydrationSnapshot = await collectCurrentPageSnapshotsUntilSettled();
- if (hydrationSnapshot.missingDefaultFieldCount === 0 && hydrationSnapshot.blankExportFieldCount === 0) {
- break;
- }
- if (nextScrollTop === maxScrollTop) {
- break;
- }
- }
- if (scrollContainer.scrollTop !== originalScrollTop) {
- setScrollTop(scrollContainer, originalScrollTop);
- }
- }
- function readCurrentPageRecords(table) {
- if (!table) {
- return [];
- }
- return table.rows.map((rowDom) => {
- const rowSnapshot = readRowSnapshot(rowDom);
- if (!rowSnapshot.authorId || !hasTextValue6(rowSnapshot.authorName)) {
- return null;
- }
- return toMarketRecord(rowSnapshot);
- }).filter((record) => record !== null);
- }
- function toMarketRecord(rowSnapshot) {
- const existingRecord = resultStore.getRecord(rowSnapshot.authorId);
- const authorName = mergeStringValue4(existingRecord?.authorName, rowSnapshot.authorName) ?? "";
- const coreUserId = mergeStringValue4(
- existingRecord?.coreUserId,
- rowSnapshot.coreUserId
- );
- const location2 = mergeStringValue4(existingRecord?.location, rowSnapshot.location);
- const price21To60s = mergeStringValue4(
- existingRecord?.price21To60s,
- rowSnapshot.price21To60s
- );
- return {
- ...existingRecord,
- ...rowSnapshot,
- authorName,
- backendMetrics: mergeFieldMap4(
- existingRecord?.backendMetrics,
- rowSnapshot.backendMetrics
- ),
- backendMetricsStatus: existingRecord?.backendMetricsStatus ?? "idle",
- coreUserId,
- exportFields: withExportFieldFallbacks(
- mergeFieldMap4(existingRecord?.exportFields, rowSnapshot.exportFields),
- {
- authorName,
- location: location2,
- price21To60s
- }
- ),
- location: location2,
- price21To60s,
- rates: mergeFieldMap4(existingRecord?.rates, rowSnapshot.rates),
- status: existingRecord?.status ?? "idle"
- };
- }
- function collectCurrentPageSnapshots() {
- readCurrentPageRows(options.document).forEach((rowSnapshot) => {
- resultStore.upsertMarketRow(rowSnapshot);
- });
- }
- function findCurrentPageScrollContainer(table) {
- if (!table) {
- return null;
- }
- const candidateScores = /* @__PURE__ */ new Map();
- const candidateRoots = table.rows.map((rowDom) => rowDom.row).filter((row) => row instanceof options.window.HTMLElement);
- for (const rootElement of candidateRoots) {
- let currentElement = rootElement.parentElement;
- let depth = 0;
- while (currentElement) {
- if (isScrollableContainer(currentElement)) {
- const scrollRange = currentElement.scrollHeight - currentElement.clientHeight;
- const existingScore = candidateScores.get(currentElement);
- if (!existingScore || depth < existingScore.depth) {
- candidateScores.set(currentElement, {
- depth,
- scrollRange
- });
- }
- }
- depth += 1;
- currentElement = currentElement.parentElement;
- }
- }
- const rankedCandidates = Array.from(candidateScores.entries()).sort((left, right) => {
- const [, leftScore] = left;
- const [, rightScore] = right;
- if (rightScore.scrollRange !== leftScore.scrollRange) {
- return rightScore.scrollRange - leftScore.scrollRange;
- }
- return leftScore.depth - rightScore.depth;
- });
- return rankedCandidates[0]?.[0] ?? null;
- }
- function isScrollableContainer(element) {
- const computedStyle = options.window.getComputedStyle(element);
- return /auto|scroll|overlay/.test(computedStyle.overflowY) && element.scrollHeight > element.clientHeight;
- }
- async function waitForDomSettled() {
- await new Promise((resolve) => {
- options.window.setTimeout(resolve, 0);
- });
- await Promise.resolve();
- }
- async function collectCurrentPageSnapshotsUntilSettled() {
- let previousFingerprint = "";
- let stablePassCount = 0;
- let fingerprintStableSince = 0;
- let lastSnapshot = {
- blankExportFieldCount: 0,
- fingerprint: "",
- missingDefaultFieldCount: 0
- };
- for (let attempt = 0; attempt < 16; attempt += 1) {
- await waitForDomSettled();
- if (attempt > 0) {
- await new Promise((resolve) => {
- options.window.setTimeout(
- resolve,
- previousFingerprint.includes("|missing:0") ? 25 : 50
- );
- });
- await Promise.resolve();
- }
- collectCurrentPageSnapshots();
- const hydrationSnapshot = readVisibleRowHydrationSnapshot();
- lastSnapshot = hydrationSnapshot;
- if (!hydrationSnapshot.fingerprint) {
- stablePassCount = 0;
- previousFingerprint = "";
- continue;
- }
- if (hydrationSnapshot.fingerprint === previousFingerprint) {
- stablePassCount += 1;
- } else {
- previousFingerprint = hydrationSnapshot.fingerprint;
- stablePassCount = 1;
- fingerprintStableSince = options.window.Date.now();
- }
- const stableForMs = options.window.Date.now() - fingerprintStableSince;
- if (hydrationSnapshot.missingDefaultFieldCount === 0 && hydrationSnapshot.blankExportFieldCount === 0 && stablePassCount >= 2) {
- return hydrationSnapshot;
- }
- if (hydrationSnapshot.missingDefaultFieldCount === 0 && hydrationSnapshot.blankExportFieldCount > 0 && stablePassCount >= 2 && stableForMs >= 500) {
- return hydrationSnapshot;
- }
- }
- return lastSnapshot;
- }
- function readVisibleRowHydrationSnapshot() {
- const table = syncMarketTable(options.document);
- if (!table || table.rows.length === 0) {
- return {
- blankExportFieldCount: 0,
- fingerprint: "",
- missingDefaultFieldCount: 0
- };
- }
- const parts = table.rows.map((rowDom) => {
- const rowSnapshot = readRowSnapshot(rowDom);
- const populatedFieldCount = Object.values(rowSnapshot.exportFields ?? {}).filter(
- (value) => typeof value === "string" && value.trim().length > 0
- ).length;
- const blankExportFieldCount = Object.values(rowSnapshot.exportFields ?? {}).filter(
- (value) => typeof value !== "string" || value.trim().length === 0
- ).length;
- const hasAuthorField = hasTextValue6(rowSnapshot.exportFields?.["\u8FBE\u4EBA\u4FE1\u606F"]);
- const hasRepresentativeVideo = hasTextValue6(
- rowSnapshot.exportFields?.["\u4EE3\u8868\u89C6\u9891"]
- );
- const hasPriceField = hasTextValue6(rowSnapshot.price21To60s) || hasTextValue6(rowSnapshot.exportFields?.["21-60s\u62A5\u4EF7"]);
- const missingDefaultFieldCount = Number(!hasAuthorField) + Number(!hasRepresentativeVideo) + Number(!hasPriceField);
- return [
- rowSnapshot.authorId,
- populatedFieldCount,
- `blank:${blankExportFieldCount}`,
- hasAuthorField ? "author" : "no-author",
- hasRepresentativeVideo ? "video" : "no-video",
- hasPriceField ? "price" : "no-price",
- `missing:${missingDefaultFieldCount}`
- ].join(":");
- });
- return {
- blankExportFieldCount: parts.reduce((count, part) => {
- const match = part.match(/:blank:(\d+):/);
- return count + Number(match?.[1] ?? 0);
- }, 0),
- fingerprint: parts.join("|"),
- missingDefaultFieldCount: parts.reduce((count, part) => {
- const match = part.match(/missing:(\d+)$/);
- return count + Number(match?.[1] ?? 0);
- }, 0)
- };
- }
- function scheduleSync() {
- if (isDisposed) {
- return;
- }
- if (isSyncRunning) {
- needsResync = true;
- return;
- }
- if (isSyncScheduled) {
- return;
- }
- isSyncScheduled = true;
- scheduledSyncTimeoutId = options.window.setTimeout(() => {
- scheduledSyncTimeoutId = null;
- isSyncScheduled = false;
- if (isDisposed) {
- return;
- }
- void runSyncCycle();
- }, 0);
- }
- function runWithoutMutationSync(callback) {
- if (isDisposed) {
- return;
- }
- observer.disconnect();
- try {
- callback();
- } finally {
- startObserving();
- }
- }
- function startObserving() {
- if (isDisposed || !observationRoot) {
- return;
- }
- observer.observe(observationRoot, {
- childList: true,
- subtree: true
- });
- }
- async function runSyncCycle() {
- if (isDisposed) {
- return;
- }
- if (isSyncRunning) {
- needsResync = true;
- return;
- }
- isSyncRunning = true;
- try {
- toolbar = ensurePluginToolbar(options.document, toolbarHandlers);
- await hydrateCurrentPage();
- applyCurrentView();
- lastKnownPageSignature = readMarketPageSignature(options.document);
- } finally {
- isSyncRunning = false;
- if (isDisposed) {
- return;
- }
- if (needsResync) {
- needsResync = false;
- scheduleSync();
- }
- }
- }
- }
- function setScrollTop(element, top) {
- element.scrollTop = top;
- element.dispatchEvent(new Event("scroll"));
- }
- function readCurrentPageRows(document2) {
- const table = syncMarketTable(document2);
- if (!table) {
- return [];
- }
- return table.rows.map((rowDom) => readRowSnapshot(rowDom)).filter(
- (row) => Boolean(row.authorId) && hasTextValue6(row.authorName)
- );
- }
- function countCurrentPageRows(document2) {
- const table = syncMarketTable(document2);
- if (!table) {
- return 0;
- }
- return table.rows.filter((rowDom) => Boolean(rowDom.authorId)).length;
- }
- function readRowSnapshot(rowDom) {
- return {
- authorId: rowDom.authorId,
- authorName: rowDom.authorName,
- exportFields: rowDom.exportFields,
- hasDirectRatesSource: rowDom.hasDirectRatesSource,
- location: rowDom.location,
- price21To60s: rowDom.price21To60s,
- rates: rowDom.rates
- };
- }
- function getNextSortState(currentSort, field) {
- if (!currentSort || currentSort.field !== field) {
- return {
- direction: "desc",
- field
- };
- }
- if (currentSort.direction === "desc") {
- return {
- direction: "asc",
- field
- };
- }
- return void 0;
- }
- function hasCompleteRates(rates) {
- return Boolean(
- rates?.singleVideoAfterSearchRate && rates?.personalVideoAfterSearchRate
- );
- }
- function hasSettledRateState(record) {
- if (!record) {
- return false;
- }
- return record.status === "failed" || hasCompleteRates(record.rates);
- }
- function hasSettledBackendMetricsState(record) {
- if (!record) {
- return false;
- }
- return record.backendMetricsStatus === "success" || record.backendMetricsStatus === "missing" || record.backendMetricsStatus === "failed";
- }
- function mergeFieldMap4(current, incoming) {
- if (!current && !incoming) {
- return void 0;
- }
- const merged = {
- ...current ?? {}
- };
- Object.entries(incoming ?? {}).forEach(([key, value]) => {
- const currentValue = merged[key];
- if (hasTextValue6(value) || !hasTextValue6(currentValue)) {
- merged[key] = value;
- }
- });
- return merged;
- }
- function createRuntimeMessageSender() {
- return (message) => Promise.resolve(
- globalThis.chrome?.runtime?.sendMessage?.(message)
- );
- }
- async function readAuthState(sendMessage) {
- const response = await sendMessage({ type: "auth:get-state" });
- if (!isAuthResponseMessage(response) || !response.ok || response.type !== "auth:state") {
- throw new Error("\u8BF7\u5148\u767B\u5F55\u63D2\u4EF6");
- }
- return response.value;
- }
- async function readBatchSubmitAck(sendMessage, payload) {
- const response = await sendMessage({
- payload,
- type: "batch:submit"
- });
- if (response && typeof response === "object" && response.ok === true) {
- return response.value;
- }
- if (response && typeof response === "object" && response.ok === false && typeof response.error === "string") {
- throw new Error(response.error);
- }
- throw new Error("\u6279\u6B21\u63D0\u4EA4\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
- }
- async function readBackendMetrics(sendMessage, starIds) {
- const response = await sendMessage({
- type: "backend-metrics:search",
- value: {
- starIds
- }
- });
- if (isBackendMetricsResponseMessage(response) && response.ok && response.type === "backend-metrics:result") {
- return response.value.rows;
- }
- throw new Error("\u540E\u7AEF\u6307\u6807\u52A0\u8F7D\u5931\u8D25");
- }
- function mergeStringValue4(current, incoming) {
- if (hasTextValue6(incoming) || !hasTextValue6(current)) {
- return incoming ?? current;
- }
- return current;
- }
- function withExportFieldFallbacks(exportFields, fallbackValues) {
- if (!exportFields) {
- return void 0;
- }
- const nextExportFields = {
- ...exportFields
- };
- if ("\u8FBE\u4EBA\u4FE1\u606F" in nextExportFields && !hasTextValue6(nextExportFields["\u8FBE\u4EBA\u4FE1\u606F"]) && hasTextValue6(fallbackValues.authorName)) {
- nextExportFields["\u8FBE\u4EBA\u4FE1\u606F"] = fallbackValues.authorName;
- }
- if ("\u5730\u533A" in nextExportFields && !hasTextValue6(nextExportFields["\u5730\u533A"]) && hasTextValue6(fallbackValues.location)) {
- nextExportFields["\u5730\u533A"] = fallbackValues.location;
- }
- if ("21-60s\u62A5\u4EF7" in nextExportFields && !hasTextValue6(nextExportFields["21-60s\u62A5\u4EF7"]) && hasTextValue6(fallbackValues.price21To60s)) {
- nextExportFields["21-60s\u62A5\u4EF7"] = fallbackValues.price21To60s;
- }
- return nextExportFields;
- }
- function hasTextValue6(value) {
- return typeof value === "string" && value.trim().length > 0;
- }
- function hasRuntimeMessageSender() {
- return Boolean(
- globalThis.chrome?.runtime?.sendMessage
- );
- }
- function buildAudienceProfileFilename(date = /* @__PURE__ */ new Date(), label) {
- const year = date.getFullYear();
- const month = String(date.getMonth() + 1).padStart(2, "0");
- const day = String(date.getDate()).padStart(2, "0");
- const hour = String(date.getHours()).padStart(2, "0");
- const minute = String(date.getMinutes()).padStart(2, "0");
- const labelPart = label ? `_${label}` : "";
- return `\u8FBE\u4EBA\u8FDE\u63A5\u7528\u6237\u753B\u50CF${labelPart}_${year}${month}${day}_${hour}${minute}.csv`;
- }
-
- // src/content/market/auth-gate.ts
- function renderMarketAuthGate(document2, currentWindow, message = "\u8BF7\u5148\u767B\u5F55\u63D2\u4EF6") {
- const existingGate = document2.querySelector(
- '[data-market-auth-gate="root"]'
- );
- if (existingGate) {
- return existingGate;
- }
- const root = document2.createElement("section");
- root.dataset.marketAuthGate = "root";
- root.innerHTML = `
-
- \u6253\u5F00\u6269\u5C55\u5F39\u7A97\u5B8C\u6210\u767B\u5F55\u540E\u5237\u65B0\u672C\u9875
-
- `;
- const title = root.querySelector("strong");
- if (title) {
- title.textContent = message;
- }
- root.querySelector('[data-market-auth-help="button"]')?.addEventListener("click", () => {
- currentWindow.alert("\u8BF7\u70B9\u51FB\u6D4F\u89C8\u5668\u5DE5\u5177\u680F\u4E2D\u7684\u6269\u5C55\u56FE\u6807\u5B8C\u6210\u767B\u5F55");
- });
- document2.body.prepend(root);
- return root;
- }
-
- // src/content/index.ts
- var DOWNLOAD_MARKET_CSV_MESSAGE = "download-market-csv";
- async function bootContentScript(options = {}) {
- const currentWindow = options.window ?? window;
- const currentDocument = options.document ?? document;
- const controllerFactory = options.createMarketController ?? createMarketController;
- const sendAuthMessage = options.sendAuthMessage ?? createRuntimeMessageSender2();
- if (!isMarketPage(currentWindow.location.href)) {
- return null;
- }
- installMarketPageBridge(currentDocument);
- const authState = await readAuthState2(sendAuthMessage);
- if (!authState?.isAuthenticated) {
- await waitForBodyReady(currentDocument, currentWindow);
- renderMarketAuthGate(
- currentDocument,
- currentWindow,
- isExpiredAuthState(authState) ? "\u767B\u5F55\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55" : void 0
- );
- return {
- ready: Promise.resolve()
- };
- }
- await waitForBodyReady(currentDocument, currentWindow);
- return controllerFactory({
- document: currentDocument,
- onCsvReady: (csv, filename) => {
- if (filename) {
- downloadCsv(currentDocument, currentWindow, csv, filename);
- return;
- }
- if (requestCsvDownload(csv)) {
- return;
- }
- downloadCsv(currentDocument, currentWindow, csv, filename);
- },
- window: currentWindow
- });
- }
- async function readAuthState2(sendMessage) {
- const response = await sendMessage({ type: "auth:get-state" });
- if (!isAuthResponseMessage(response) || !response.ok || response.type !== "auth:state") {
- return null;
- }
- return response.value;
- }
- function isMarketPage(url) {
- const parsedUrl = new URL(url);
- const isXingtuHost = parsedUrl.hostname === "xingtu.cn" || parsedUrl.hostname.endsWith(".xingtu.cn");
- return isXingtuHost && parsedUrl.pathname.startsWith("/ad/creator/market");
- }
- function bootstrapContentScript() {
- const runtime = globalThis.chrome?.runtime;
- if (!runtime || typeof window === "undefined" || typeof document === "undefined") {
- return;
- }
- const marker = "__starChartSearchEnhancerContentController";
- const scopedWindow = window;
- if (scopedWindow[marker]) {
- return;
- }
- scopedWindow[marker] = true;
- void bootContentScript().then((controller) => {
- scopedWindow[marker] = controller;
- });
- }
- bootstrapContentScript();
- function requestCsvDownload(csv, filename) {
- const runtime = globalThis.chrome?.runtime;
- if (!runtime?.id || typeof runtime.sendMessage !== "function") {
- return false;
- }
- runtime.sendMessage({
- csv,
- filename: filename ?? `star-chart-search-enhancer-${formatTimestampForFilename()}.csv`,
- type: DOWNLOAD_MARKET_CSV_MESSAGE
- });
- return true;
- }
- function createRuntimeMessageSender2() {
- return async (message) => {
- const runtime = globalThis.chrome?.runtime;
- if (typeof runtime?.sendMessage !== "function") {
- return null;
- }
- return runtime.sendMessage(message);
- };
- }
- async function waitForBodyReady(document2, currentWindow) {
- if (document2.body) {
- return;
- }
- await new Promise((resolve) => {
- const handleReady = () => {
- if (document2.body) {
- document2.removeEventListener("DOMContentLoaded", handleReady);
- resolve();
- }
- };
- document2.addEventListener("DOMContentLoaded", handleReady);
- currentWindow.setTimeout(handleReady, 0);
- });
- }
- function downloadCsv(document2, window2, csv, filename) {
- const blob = new Blob(["\uFEFF", csv], {
- type: "text/csv;charset=utf-8"
- });
- const objectUrl = window2.URL.createObjectURL(blob);
- const link = document2.createElement("a");
- link.href = objectUrl;
- link.download = filename ?? `star-chart-search-enhancer-${formatTimestampForFilename()}.csv`;
- document2.body.appendChild(link);
- link.click();
- link.remove();
- window2.URL.revokeObjectURL(objectUrl);
- }
- function formatTimestampForFilename() {
- return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
- }
- function isExpiredAuthState(authState) {
- const lastError = authState?.lastError;
- return typeof lastError === "string" && (/token/i.test(lastError) || lastError.includes("\u8FC7\u671F"));
- }
- function installMarketPageBridge(document2) {
- if (document2.documentElement.querySelector(
- '[data-sces-market-bridge="script"]'
- )) {
- return;
- }
- const script = document2.createElement("script");
- script.dataset.scesMarketBridge = "script";
- const runtime = globalThis.chrome?.runtime;
- const bridgeUrl = runtime?.getURL?.("content/market-page-bridge.js");
- if (bridgeUrl) {
- script.src = bridgeUrl;
- } else {
- script.textContent = "";
- }
- (document2.head ?? document2.documentElement).appendChild(script);
- }
-})();
diff --git a/dist-release/content/market-page-bridge.js b/dist-release/content/market-page-bridge.js
deleted file mode 100644
index f8799fe..0000000
--- a/dist-release/content/market-page-bridge.js
+++ /dev/null
@@ -1,585 +0,0 @@
-"use strict";
-(() => {
- // src/shared/rate-normalizer.ts
- function normalizeFractionRateDisplay(value) {
- const numericValue = Number(value);
- if (!Number.isFinite(numericValue)) {
- return null;
- }
- const percentageValue = numericValue * 100;
- return `${trimTrailingZeros(percentageValue.toFixed(6))}%`;
- }
- function trimTrailingZeros(value) {
- return value.replace(/\.?0+$/, "");
- }
-
- // src/content/market/market-list-request-snapshot.ts
- var MARKET_REQUEST_SNAPSHOT_ATTRIBUTE = "data-sces-market-request-snapshot";
- function writeMarketListRequestSnapshot(document2, snapshot) {
- document2.documentElement.setAttribute(
- MARKET_REQUEST_SNAPSHOT_ATTRIBUTE,
- JSON.stringify(snapshot)
- );
- }
-
- // src/content/market/market-list-row.ts
- var PAGE_NUMBER_KEYS = [
- "currentPage",
- "page",
- "pageNo",
- "pageNum",
- "page_no",
- "page_num"
- ];
- var PAGE_SIZE_KEYS = [
- "limit",
- "pageSize",
- "page_size",
- "size"
- ];
- var TOTAL_COUNT_KEYS = [
- "total",
- "totalCount",
- "total_count"
- ];
- var TOTAL_PAGE_KEYS = [
- "pageCount",
- "page_count",
- "totalPage",
- "totalPages",
- "total_page",
- "total_pages"
- ];
- function mapMarketListRow(row) {
- const attributeDatas = readMarketAttributeDatas(row);
- const singleVideoAfterSearchRate = normalizeMarketListRate(
- readMarketFieldValue(row, attributeDatas, "avg_search_after_view_rate_30d")
- );
- return {
- authorId: readString(readMarketFieldValue(row, attributeDatas, "star_id")) ?? readString(readMarketFieldValue(row, attributeDatas, "id")) ?? "",
- authorName: readString(readMarketFieldValue(row, attributeDatas, "nickname")) ?? readString(readMarketFieldValue(row, attributeDatas, "nick_name")) ?? "",
- coreUserId: readString(readMarketFieldValue(row, attributeDatas, "core_user_id")) ?? void 0,
- exportFields: buildMarketExportFieldFallbacks(row, attributeDatas),
- hasDirectRatesSource: true,
- location: readMarketLocation(row, attributeDatas),
- price21To60s: readMarketPrice21To60s(row, attributeDatas),
- rates: singleVideoAfterSearchRate ? {
- singleVideoAfterSearchRate
- } : void 0
- };
- }
- function parseMarketListResponse(payload) {
- const container = findMarketListContainer(payload);
- if (!container) {
- return null;
- }
- const marketList = readMarketListArray(container);
- if (!marketList) {
- return null;
- }
- return {
- currentPage: readKnownNumberDeep(container, PAGE_NUMBER_KEYS) ?? void 0,
- pageSize: readKnownNumberDeep(container, PAGE_SIZE_KEYS) ?? void 0,
- records: marketList.map((row) => isRecord(row) ? mapMarketListRow(row) : null).filter(
- (row) => row !== null && Boolean(row.authorId || row.authorName)
- ),
- totalCount: readKnownNumberDeep(container, TOTAL_COUNT_KEYS) ?? void 0,
- totalPages: readKnownNumberDeep(container, TOTAL_PAGE_KEYS) ?? void 0
- };
- }
- function findMarketListContainer(value) {
- const queue = [value];
- while (queue.length > 0) {
- const current = queue.shift();
- if (!isRecord(current)) {
- continue;
- }
- if (readMarketListArray(current)) {
- return current;
- }
- Object.values(current).forEach((entry) => {
- queue.push(unwrapVueRef(entry));
- });
- }
- return null;
- }
- function readMarketListArray(record) {
- const marketList = unwrapVueRef(record.marketList);
- if (Array.isArray(marketList)) {
- return marketList;
- }
- const authors = unwrapVueRef(record.authors);
- if (Array.isArray(authors)) {
- return authors;
- }
- return null;
- }
- function unwrapVueRef(value) {
- if (isRecord(value) && "value" in value) {
- return value.value;
- }
- return value;
- }
- function isRecord(value) {
- return typeof value === "object" && value !== null;
- }
- function readMarketAttributeDatas(record) {
- return isRecord(record.attribute_datas) ? record.attribute_datas : {};
- }
- function readMarketFieldValue(record, attributeDatas, field) {
- return record[field] ?? attributeDatas[field];
- }
- function readString(value) {
- return typeof value === "string" ? value : null;
- }
- function normalizeMarketListRate(value) {
- if (typeof value === "number") {
- return normalizeFractionRateDisplay(String(value));
- }
- return typeof value === "string" ? normalizeFractionRateDisplay(value) : null;
- }
- function normalizeExportCellText(value) {
- return value?.replace(/\s+/g, " ").trim() ?? "";
- }
- function buildMarketExportFieldFallbacks(record, attributeDatas) {
- const exportFields = {};
- const authorInfo = buildMarketAuthorInfo(record, attributeDatas);
- const authorType = buildMarketAuthorType(record, attributeDatas);
- const contentTheme = buildMarketContentTheme(record, attributeDatas);
- const connectedUsers = formatWanValue(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "link_link_cnt_by_industry"))
- );
- const followerCount = formatWanValue(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "follower"))
- );
- const expectedCpm = formatDecimalDisplay(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "prospective_20_60_cpm"))
- );
- const expectedPlayCount = formatWanValue(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "expected_play_num"))
- );
- const interactionRate = formatFractionPercent(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "interact_rate_within_30d"))
- );
- const finishRate = formatFractionPercent(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "play_over_rate_within_30d"))
- );
- const burstRate = readBurstRateDisplay(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "burst_text_rate"))
- );
- const price21To60s = readMarketPrice21To60s(record, attributeDatas);
- const representativeVideo = readMarketRepresentativeVideo(record, attributeDatas);
- assignExportField(exportFields, "\u8FBE\u4EBA\u4FE1\u606F", authorInfo);
- assignExportField(exportFields, "\u4EE3\u8868\u89C6\u9891", representativeVideo);
- assignExportField(exportFields, "\u8FBE\u4EBA\u7C7B\u578B", authorType);
- assignExportField(exportFields, "\u5185\u5BB9\u4E3B\u9898", contentTheme);
- assignExportField(exportFields, "\u8FDE\u63A5\u7528\u6237\u6570", connectedUsers);
- assignExportField(exportFields, "\u7C89\u4E1D\u6570", followerCount);
- assignExportField(exportFields, "\u9884\u671FCPM", expectedCpm);
- assignExportField(exportFields, "\u9884\u671F\u64AD\u653E\u91CF", expectedPlayCount);
- assignExportField(exportFields, "\u4E92\u52A8\u7387", interactionRate);
- assignExportField(exportFields, "\u5B8C\u64AD\u7387", finishRate);
- assignExportField(exportFields, "\u7206\u6587\u7387", burstRate);
- assignExportField(exportFields, "21-60s\u62A5\u4EF7", price21To60s);
- return Object.keys(exportFields).length > 0 ? exportFields : void 0;
- }
- function assignExportField(exportFields, key, value) {
- if (hasTextValue(value)) {
- exportFields[key] = value;
- }
- }
- function hasTextValue(value) {
- return Boolean(value && value.trim().length > 0);
- }
- function buildMarketAuthorInfo(record, attributeDatas) {
- const nickname = readString(readMarketFieldValue(record, attributeDatas, "nickname")) ?? readString(readMarketFieldValue(record, attributeDatas, "nick_name")) ?? "";
- const parts = [
- nickname,
- readMarketGenderLabel(readMarketFieldValue(record, attributeDatas, "gender")),
- readString(readMarketFieldValue(record, attributeDatas, "city")) ?? ""
- ].filter((value) => Boolean(value));
- return parts.length > 0 ? parts.join(" ") : void 0;
- }
- function buildMarketAuthorType(record, attributeDatas) {
- const tagsRelation = readRecordLike(
- readMarketFieldValue(record, attributeDatas, "tags_relation")
- );
- if (tagsRelation) {
- const primaryTag = Object.keys(tagsRelation)[0];
- if (hasTextValue(primaryTag)) {
- return primaryTag;
- }
- }
- return void 0;
- }
- function buildMarketContentTheme(record, attributeDatas) {
- const themes = readStringArray(
- readMarketFieldValue(record, attributeDatas, "content_theme_labels_180d")
- );
- if (themes.length === 0) {
- return void 0;
- }
- if (themes.length <= 2) {
- return themes.join(" ");
- }
- return `${themes.slice(0, 2).join(" ")} ${themes.length - 2}+`;
- }
- function readMarketLocation(record, attributeDatas) {
- return readString(readMarketFieldValue(record, attributeDatas, "city")) ?? void 0;
- }
- function readMarketPrice21To60s(record, attributeDatas) {
- return formatCurrencyValue(
- readNumericValue(readMarketFieldValue(record, attributeDatas, "price_20_60"))
- );
- }
- function readMarketRepresentativeVideo(record, attributeDatas) {
- const items = readArrayLike(readMarketFieldValue(record, attributeDatas, "items"));
- for (const item of items) {
- if (!isRecord(item)) {
- continue;
- }
- const title = readString(item.title);
- if (hasTextValue(title)) {
- return normalizeExportCellText(title);
- }
- }
- return void 0;
- }
- function readMarketGenderLabel(value) {
- const rawValue = typeof value === "number" ? String(value) : readString(value);
- if (rawValue === "1") {
- return "\u7537";
- }
- if (rawValue === "2") {
- return "\u5973";
- }
- return void 0;
- }
- function readBurstRateDisplay(value) {
- if (value === null) {
- return void 0;
- }
- if (value <= 0) {
- return "-";
- }
- return formatFractionPercent(value);
- }
- function formatCurrencyValue(value) {
- if (value === null) {
- return void 0;
- }
- return `\xA5${value.toLocaleString("en-US", {
- maximumFractionDigits: 0
- })}`;
- }
- function formatWanValue(value) {
- if (value === null) {
- return void 0;
- }
- return `${formatDecimalWithGrouping(value / 1e4)}w`;
- }
- function formatFractionPercent(value) {
- if (value === null) {
- return void 0;
- }
- return `${formatDecimalDisplay(value * 100)}%`;
- }
- function formatDecimalDisplay(value) {
- if (value === null) {
- return void 0;
- }
- return value.toLocaleString("en-US", {
- maximumFractionDigits: 1,
- minimumFractionDigits: 0,
- useGrouping: false
- });
- }
- function formatDecimalWithGrouping(value) {
- return value.toLocaleString("en-US", {
- maximumFractionDigits: 1,
- minimumFractionDigits: 0
- });
- }
- function readNumericValue(value) {
- if (typeof value === "number" && Number.isFinite(value)) {
- return value;
- }
- if (typeof value === "string") {
- const trimmedValue = value.trim();
- if (!trimmedValue) {
- return null;
- }
- const parsedValue = Number(trimmedValue);
- return Number.isFinite(parsedValue) ? parsedValue : null;
- }
- return null;
- }
- function readStringArray(value) {
- if (Array.isArray(value)) {
- return value.filter((item) => typeof item === "string");
- }
- if (typeof value === "string") {
- try {
- const parsedValue = JSON.parse(value);
- return Array.isArray(parsedValue) ? parsedValue.filter((item) => typeof item === "string") : [];
- } catch {
- return [];
- }
- }
- return [];
- }
- function readArrayLike(value) {
- if (Array.isArray(value)) {
- return value;
- }
- if (typeof value === "string") {
- try {
- const parsedValue = JSON.parse(value);
- return Array.isArray(parsedValue) ? parsedValue : [];
- } catch {
- return [];
- }
- }
- return [];
- }
- function readRecordLike(value) {
- if (isRecord(value)) {
- return value;
- }
- if (typeof value === "string") {
- try {
- const parsedValue = JSON.parse(value);
- return isRecord(parsedValue) ? parsedValue : null;
- } catch {
- return null;
- }
- }
- return null;
- }
- function readKnownNumber(record, keys) {
- for (const key of keys) {
- const value = readNumericValue(record[key]);
- if (value !== null) {
- return value;
- }
- }
- return void 0;
- }
- function readKnownNumberDeep(value, keys) {
- if (!isRecord(value)) {
- return null;
- }
- const directValue = readKnownNumber(value, keys);
- if (typeof directValue === "number") {
- return directValue;
- }
- for (const nestedValue of Object.values(value)) {
- const candidate = readKnownNumberDeep(unwrapVueRef(nestedValue), keys);
- if (typeof candidate === "number") {
- return candidate;
- }
- }
- return null;
- }
-
- // src/content/market/page-bridge.ts
- var BRIDGE_MARKER = "__SCES_MARKET_PAGE_BRIDGE_INSTALLED__";
- var MARKET_SEARCH_REQUEST_PATH = "/gw/api/gsearch/search_for_author_square";
- var SERIALIZED_MARKET_ROWS_ATTRIBUTE = "data-sces-market-rows";
- installMarketPageBridge();
- function installMarketPageBridge() {
- if (window[BRIDGE_MARKER]) {
- syncSerializedMarketRows();
- return;
- }
- window[BRIDGE_MARKER] = true;
- installMarketRequestSnapshotBridge();
- syncSerializedMarketRows();
- const observer = new MutationObserver(() => {
- syncSerializedMarketRows();
- });
- observer.observe(document.documentElement, {
- childList: true,
- subtree: true
- });
- window.setInterval(() => {
- syncSerializedMarketRows();
- }, 1e3);
- }
- function installMarketRequestSnapshotBridge() {
- installFetchSnapshotBridge();
- installXmlHttpRequestSnapshotBridge();
- }
- function syncSerializedMarketRows() {
- if (typeof document === "undefined") {
- return;
- }
- const nextSerializedRows = JSON.stringify(readSerializedMarketRows());
- if (document.documentElement.getAttribute(SERIALIZED_MARKET_ROWS_ATTRIBUTE) !== nextSerializedRows) {
- document.documentElement.setAttribute(
- SERIALIZED_MARKET_ROWS_ATTRIBUTE,
- nextSerializedRows
- );
- }
- }
- function installFetchSnapshotBridge() {
- if (typeof window.fetch !== "function") {
- return;
- }
- const originalFetch = window.fetch.bind(window);
- window.fetch = async (input, init) => {
- const requestSnapshot = readFetchSnapshot(input, init);
- const response = await originalFetch(input, init);
- if (requestSnapshot) {
- const clonedResponse = response.clone();
- void captureMarketSnapshotFromResponse(
- requestSnapshot,
- () => clonedResponse.json()
- );
- }
- return response;
- };
- }
- function installXmlHttpRequestSnapshotBridge() {
- const OriginalXmlHttpRequest = window.XMLHttpRequest;
- if (!OriginalXmlHttpRequest) {
- return;
- }
- const originalOpen = OriginalXmlHttpRequest.prototype.open;
- const originalSend = OriginalXmlHttpRequest.prototype.send;
- const originalSetRequestHeader = OriginalXmlHttpRequest.prototype.setRequestHeader;
- OriginalXmlHttpRequest.prototype.open = function(method, url, ...rest) {
- this.__scesMarketSnapshot = {
- headers: {},
- method,
- url: String(url)
- };
- return originalOpen.call(this, method, url, ...rest);
- };
- OriginalXmlHttpRequest.prototype.setRequestHeader = function(name, value) {
- this.__scesMarketSnapshot?.headers && (this.__scesMarketSnapshot.headers[name] = value);
- return originalSetRequestHeader.call(this, name, value);
- };
- OriginalXmlHttpRequest.prototype.send = function(body) {
- const snapshotState = this.__scesMarketSnapshot;
- if (snapshotState) {
- snapshotState.body = typeof body === "string" ? body : void 0;
- this.addEventListener("load", () => {
- if (this.status < 200 || this.status >= 300 || typeof this.responseText !== "string") {
- return;
- }
- void captureMarketSnapshotFromResponse(
- snapshotState,
- async () => JSON.parse(this.responseText)
- );
- });
- }
- return originalSend.call(this, body);
- };
- }
- async function captureMarketSnapshotFromResponse(snapshot, readPayload) {
- if (!isMarketSearchRequest(snapshot.url)) {
- return;
- }
- try {
- const payload = await readPayload();
- if (!parseMarketListResponse(payload)) {
- return;
- }
- writeMarketListRequestSnapshot(document, {
- body: snapshot.body,
- headers: snapshot.headers,
- method: snapshot.method,
- url: snapshot.url
- });
- } catch {
- }
- }
- function readSerializedMarketRows() {
- const marketList = readMarketList();
- return marketList.map((row) => {
- const attributeDatas = isRecord2(row.attribute_datas) ? row.attribute_datas : {};
- const singleVideoAfterSearchRate = readNormalizedFractionRate(
- attributeDatas.avg_search_after_view_rate_30d
- );
- return {
- authorId: readString2(row.star_id) ?? readString2(attributeDatas.id) ?? "",
- authorName: readString2(attributeDatas.nickname) ?? readString2(row.nick_name) ?? "",
- coreUserId: readString2(attributeDatas.core_user_id) ?? void 0,
- singleVideoAfterSearchRate
- };
- }).filter((row) => Boolean(row.authorId || row.authorName));
- }
- function readFetchSnapshot(input, init) {
- const request = input instanceof Request ? input : null;
- const method = init?.method ?? request?.method ?? "GET";
- const url = request?.url ?? String(input);
- const body = typeof init?.body === "string" ? init.body : typeof request?.bodyUsed === "boolean" && request.bodyUsed ? void 0 : void 0;
- const headers = serializeHeaders(init?.headers ?? request?.headers);
- return {
- body,
- headers,
- method,
- url
- };
- }
- function serializeHeaders(headers) {
- if (!headers) {
- return void 0;
- }
- if (headers instanceof Headers) {
- return Object.fromEntries(headers.entries());
- }
- if (Array.isArray(headers)) {
- return Object.fromEntries(headers);
- }
- return Object.fromEntries(
- Object.entries(headers).map(([key, value]) => [key, String(value)])
- );
- }
- function readMarketList() {
- if (typeof document === "undefined") {
- return [];
- }
- const marketRoot = document.querySelector(".base-author-list");
- const setupState = marketRoot?.__vue__?._setupState;
- if (!setupState) {
- return [];
- }
- for (const value of Object.values(setupState)) {
- const candidate = unwrapVueRef2(value);
- if (Array.isArray(candidate) && looksLikeMarketList(candidate)) {
- return candidate;
- }
- if (!isRecord2(candidate) || !Array.isArray(candidate.marketList)) {
- continue;
- }
- if (looksLikeMarketList(candidate.marketList)) {
- return candidate.marketList;
- }
- }
- return [];
- }
- function isMarketSearchRequest(url) {
- return url === MARKET_SEARCH_REQUEST_PATH || url.startsWith(`${MARKET_SEARCH_REQUEST_PATH}?`) || url.includes(`${MARKET_SEARCH_REQUEST_PATH}?`) || url.endsWith(MARKET_SEARCH_REQUEST_PATH);
- }
- function looksLikeMarketList(value) {
- const firstRow = value[0];
- return isRecord2(firstRow) && ("star_id" in firstRow || "attribute_datas" in firstRow);
- }
- function unwrapVueRef2(value) {
- if (isRecord2(value) && "value" in value) {
- return value.value;
- }
- return value;
- }
- function isRecord2(value) {
- return typeof value === "object" && value !== null;
- }
- function readString2(value) {
- return typeof value === "string" ? value : null;
- }
- function readNormalizedFractionRate(value) {
- return typeof value === "string" ? normalizeFractionRateDisplay(value) ?? void 0 : void 0;
- }
-})();
diff --git a/dist-release/manifest.json b/dist-release/manifest.json
deleted file mode 100644
index ef39e2b..0000000
--- a/dist-release/manifest.json
+++ /dev/null
@@ -1,59 +0,0 @@
-{
- "action": {
- "default_icon": {
- "16": "assets/icons/icon-16.png",
- "32": "assets/icons/icon-32.png"
- },
- "default_popup": "popup/index.html"
- },
- "background": {
- "service_worker": "background/index.js"
- },
- "content_scripts": [
- {
- "js": [
- "content/index.js"
- ],
- "matches": [
- "https://xingtu.cn/ad/creator/market*",
- "https://*.xingtu.cn/ad/creator/market*"
- ],
- "run_at": "document_start"
- }
- ],
- "description": "Bootstraps the Xingtu creator market content script.",
- "icons": {
- "16": "assets/icons/icon-16.png",
- "32": "assets/icons/icon-32.png",
- "48": "assets/icons/icon-48.png",
- "128": "assets/icons/icon-128.png"
- },
- "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0CaZJxcX97TbRXCR08L10t9EZFV31+wPnUgDf21j2f0qYaWdblzWXfVkeU9jGb2Hr2Etpp7F/XuBa6pcipUXkzMMBkJ42KOkciAwbuzTBoAtGB8o9aoWigtax+gGfSz+T3BjqxKBJtXqeqbIAKCDIlxRKIrY+KcY1Z+mD5BKcBHKsUDQPlHsrjc1g0wIBD5doz9LoOk1Wso6gK5cSeOp9lw5YHcu4TImR4yqxGiL6pZwnpciuX/g7qjWBZXn5gf0YBlDsBDDTt5upbP3NguUKgO2qA9M77LyeUwXl3aqbIxYi/VwsQ2t5w9PGWtnOUQQDWUcEg/9dfTb89esZXKATwIDAQAB",
- "manifest_version": 3,
- "name": "Star Chart Search Enhancer",
- "permissions": [
- "downloads",
- "identity",
- "storage"
- ],
- "web_accessible_resources": [
- {
- "matches": [
- "https://xingtu.cn/*",
- "https://*.xingtu.cn/*"
- ],
- "resources": [
- "content/market-page-bridge.js"
- ]
- }
- ],
- "version": "0.0525.6",
- "host_permissions": [
- "https://xingtu.cn/ad/creator/market*",
- "https://*.xingtu.cn/ad/creator/market*",
- "https://login-api.intelligrow.cn/*",
- "https://talent-search.intelligrow.cn/*",
- "http://192.168.31.21:8083/*",
- "https://*/*"
- ]
-}
diff --git a/dist-release/popup/index.html b/dist-release/popup/index.html
deleted file mode 100644
index 6e17f31..0000000
--- a/dist-release/popup/index.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
- Star Chart Search Enhancer
-
-
-
-
-
-
diff --git a/dist-release/popup/index.js b/dist-release/popup/index.js
deleted file mode 100644
index 49af92e..0000000
--- a/dist-release/popup/index.js
+++ /dev/null
@@ -1,422 +0,0 @@
-"use strict";
-(() => {
- // src/popup/view.ts
- function renderLoggedOut(root, error) {
- root.innerHTML = `
-
- Star Chart Search Enhancer
- \u767B\u5F55\u540E\u624D\u80FD\u4F7F\u7528\u661F\u56FE\u589E\u5F3A\u529F\u80FD
- ${error ? `${error}
` : ""}
-
-
- `;
- }
- function renderLoggedIn(root, authState) {
- const userInfo = authState.userInfo;
- root.innerHTML = `
-
- Star Chart Search Enhancer
- \u5DF2\u767B\u5F55
- ${userInfo?.name ?? userInfo?.username ?? "\u672A\u77E5\u7528\u6237"}
- ${userInfo?.email ?? ""}
-
- \u7248\u672C\u66F4\u65B0
- \u6B63\u5728\u68C0\u67E5\u66F4\u65B0...
-
-
-
- `;
- }
- function renderUpdateStatus(root, options) {
- const container = root.querySelector('[data-popup-update="root"]');
- if (!container) {
- return;
- }
- if (options.status === "checking") {
- container.innerHTML = `
- \u7248\u672C\u66F4\u65B0
- \u5F53\u524D\u7248\u672C\uFF1A${options.currentVersion}
- \u6B63\u5728\u68C0\u67E5\u66F4\u65B0...
- `;
- return;
- }
- if (options.status === "error") {
- container.innerHTML = `
- \u7248\u672C\u66F4\u65B0
- \u5F53\u524D\u7248\u672C\uFF1A${options.currentVersion}
- \u6682\u65F6\u65E0\u6CD5\u68C0\u67E5\u66F4\u65B0
- \u5982\u679C\u9700\u8981\u65B0\u7248\uFF0C\u8BF7\u8054\u7CFB\u7EF4\u62A4\u540C\u4E8B\u83B7\u53D6\u66F4\u65B0\u5305\u3002
- `;
- return;
- }
- if (options.status === "latest" || !options.manifest) {
- container.innerHTML = `
- \u7248\u672C\u66F4\u65B0
- \u5F53\u524D\u7248\u672C\uFF1A${options.currentVersion}
- \u5F53\u524D\u5DF2\u662F\u6700\u65B0\u7248\u672C
- `;
- return;
- }
- container.innerHTML = `
- \u7248\u672C\u66F4\u65B0
- \u5F53\u524D\u7248\u672C\uFF1A${options.currentVersion}
- \u53D1\u73B0\u65B0\u7248\u672C\uFF1A${options.manifest.latestVersion}
- ${renderReleaseNotes(options.manifest.releaseNotes)}
-
-
- \u4E0B\u8F7D\u540E\u8BF7\u89E3\u538B\u65B0\u7248 zip\uFF0C\u5E76\u5728 chrome://extensions \u91CC\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6\u3002
- `;
- }
- function setUpdateDownloadStatus(root, value) {
- const output = root.querySelector('[data-popup-update-download-status="text"]');
- if (!output) {
- return;
- }
- output.textContent = value;
- }
- function renderReleaseNotes(releaseNotes) {
- if (releaseNotes.length === 0) {
- return "";
- }
- return `
-
- ${releaseNotes.map((note) => `- ${escapeHtml(note)}
`).join("")}
-
- `;
- }
- function escapeHtml(value) {
- return value.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """);
- }
- function renderDevPanel(root, authState) {
- const panel = root.ownerDocument.createElement("section");
- panel.dataset.popupDevPanel = "root";
- panel.innerHTML = `
- dev auth panel
- resource: ${authState.resource ?? ""}
- scopes: ${(authState.scopes ?? []).join(", ")}
- token: ${authState.tokenAvailable ? "available" : "missing"}
- expires: ${authState.accessTokenExpiresAt ?? "unknown"}
- error: ${authState.lastError ?? ""}
-
-
- `;
- root.appendChild(panel);
- }
- function setProtectedApiResult(root, value) {
- const output = root.querySelector(
- '[data-popup-protected-api-result="output"]'
- );
- if (!output) {
- return;
- }
- output.textContent = value;
- }
-
- // src/shared/auth-config.ts
- var defaultAuthConfig = {
- apiResource: "https://talent-search.intelligrow.cn",
- appId: "i4jkllbvih0554r4n0fd3",
- enableDevAuthPanel: false,
- logtoEndpoint: "https://login-api.intelligrow.cn",
- scopes: ["openid", "profile", "offline_access", "talent-search:read"]
- };
- function readAuthConfig(overrides = {}) {
- const nextConfig = {
- ...defaultAuthConfig,
- ...overrides
- };
- if (!nextConfig.logtoEndpoint.trim()) {
- throw new Error("auth config logtoEndpoint is required");
- }
- if (!nextConfig.appId.trim()) {
- throw new Error("auth config appId is required");
- }
- if (!nextConfig.apiResource.trim()) {
- throw new Error("auth config apiResource is required");
- }
- return nextConfig;
- }
-
- // src/shared/auth-messages.ts
- function isAuthResponseMessage(value) {
- if (!value || typeof value !== "object") {
- return false;
- }
- const candidate = value;
- if (candidate.ok === false) {
- return candidate.type === "auth:error" && typeof candidate.error === "string";
- }
- if (candidate.ok !== true || typeof candidate.type !== "string") {
- return false;
- }
- if (candidate.type === "auth:ack") {
- return true;
- }
- if (candidate.type === "auth:token") {
- return Boolean(
- candidate.value && typeof candidate.value === "object" && typeof candidate.value.accessToken === "string"
- );
- }
- if (candidate.type === "auth:state") {
- return Boolean(
- candidate.value && typeof candidate.value === "object" && typeof candidate.value.isAuthenticated === "boolean"
- );
- }
- return false;
- }
-
- // src/shared/protected-api-client.ts
- function createProtectedApiClient(options) {
- const fetchImpl = options.fetchImpl ?? fetch;
- return {
- async loadProtectedMockData() {
- const token = await readAccessToken(options.sendMessage);
- const response = await fetchImpl(
- new URL("/api/mock/protected", options.baseUrl).toString(),
- {
- headers: {
- Authorization: `Bearer ${token}`
- },
- method: "GET"
- }
- );
- if (response.status === 401 || response.status === 403) {
- throw new Error("protected api unauthorized");
- }
- if (!response.ok) {
- throw new Error(`protected api request failed: ${response.status}`);
- }
- return response.json();
- }
- };
- }
- async function readAccessToken(sendMessage) {
- const response = await sendMessage({ type: "auth:get-access-token" });
- if (!isAuthResponseMessage(response) || !response.ok || response.type !== "auth:token" || !response.value.accessToken.trim()) {
- throw new Error("protected api token unavailable");
- }
- return response.value.accessToken;
- }
-
- // src/shared/update-check.ts
- function compareExtensionVersions(left, right) {
- const leftParts = parseVersionParts(left);
- const rightParts = parseVersionParts(right);
- const maxLength = Math.max(leftParts.length, rightParts.length);
- for (let index = 0; index < maxLength; index += 1) {
- const leftValue = leftParts[index] ?? 0;
- const rightValue = rightParts[index] ?? 0;
- if (leftValue !== rightValue) {
- return leftValue - rightValue;
- }
- }
- return 0;
- }
- function parseUpdateManifest(value) {
- if (!value || typeof value !== "object") {
- return null;
- }
- const candidate = value;
- if (!isVersionString(candidate.latestVersion) || !isVersionString(candidate.minSupportedVersion) || !isHttpsUrl(candidate.zipUrl) || !isHttpsUrl(candidate.guideUrl) || typeof candidate.publishedAt !== "string" || !Array.isArray(candidate.releaseNotes) || !candidate.releaseNotes.every((note) => typeof note === "string")) {
- return null;
- }
- return {
- guideUrl: candidate.guideUrl,
- latestVersion: candidate.latestVersion,
- minSupportedVersion: candidate.minSupportedVersion,
- publishedAt: candidate.publishedAt,
- releaseNotes: candidate.releaseNotes,
- zipUrl: candidate.zipUrl
- };
- }
- async function fetchUpdateManifest(manifestUrl, fetchImpl = fetch) {
- const response = await fetchImpl(manifestUrl, {
- cache: "no-store"
- });
- if (!response.ok) {
- throw new Error(`update manifest request failed: ${response.status}`);
- }
- const manifest = parseUpdateManifest(await response.json());
- if (!manifest) {
- throw new Error("update manifest is invalid");
- }
- return manifest;
- }
- function parseVersionParts(value) {
- return value.split(".").map((part) => {
- const parsed = Number.parseInt(part, 10);
- return Number.isFinite(parsed) ? parsed : 0;
- });
- }
- function isVersionString(value) {
- return typeof value === "string" && /^\d+(?:\.\d+)*$/.test(value);
- }
- function isHttpsUrl(value) {
- if (typeof value !== "string") {
- return false;
- }
- try {
- return new URL(value).protocol === "https:";
- } catch {
- return false;
- }
- }
-
- // src/shared/update-config.ts
- var UPDATE_MANIFEST_URL = "https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/latest.json";
-
- // src/popup/index.ts
- async function bootPopup(options = {}) {
- const currentDocument = options.document ?? document;
- const popupConfig = readAuthConfig(options.config);
- const currentVersion = options.currentVersion ?? readCurrentVersion();
- const root = currentDocument.querySelector("#app");
- const HTMLElementCtor = currentDocument.defaultView?.HTMLElement;
- if (!root || HTMLElementCtor && !(root instanceof HTMLElementCtor)) {
- throw new Error("popup root #app is required");
- }
- const sendMessage = options.sendMessage ?? ((message) => Promise.resolve(
- globalThis.chrome?.runtime?.sendMessage?.(message)
- ));
- const fetchProtectedApi = options.fetchProtectedApi ?? createProtectedApiClient({
- baseUrl: "http://127.0.0.1:4319",
- sendMessage
- }).loadProtectedMockData;
- const fetchUpdateManifest2 = options.fetchUpdateManifest ?? (() => fetchUpdateManifest(
- options.updateManifestUrl ?? UPDATE_MANIFEST_URL
- ));
- await renderCurrentAuthState(root, popupConfig, sendMessage, fetchProtectedApi, {
- currentVersion,
- fetchUpdateManifest: fetchUpdateManifest2
- });
- }
- async function renderCurrentAuthState(root, popupConfig, sendMessage, fetchProtectedApi, updateOptions) {
- const response = await sendMessage({ type: "auth:get-state" });
- if (!isAuthResponseMessage(response) || !response.ok || response.type !== "auth:state") {
- renderLoggedOut(root, "\u8BA4\u8BC1\u72B6\u6001\u8BFB\u53D6\u5931\u8D25");
- return;
- }
- if (!response.value.isAuthenticated) {
- renderLoggedOut(root, response.value.lastError);
- root.querySelector('[data-popup-sign-in="button"]')?.addEventListener("click", () => {
- void runAuthAction(root, popupConfig, sendMessage, {
- actionMessage: { type: "auth:sign-in" },
- fetchProtectedApi,
- updateOptions
- });
- });
- return;
- }
- renderLoggedIn(root, response.value);
- void runUpdateCheck(root, sendMessage, updateOptions);
- root.querySelector('[data-popup-sign-out="button"]')?.addEventListener("click", () => {
- void runAuthAction(root, popupConfig, sendMessage, {
- actionMessage: { type: "auth:sign-out" },
- fetchProtectedApi,
- updateOptions
- });
- });
- if (popupConfig.enableDevAuthPanel) {
- renderDevPanel(root, response.value);
- root.querySelector('[data-popup-test-protected-api="button"]')?.addEventListener("click", () => {
- void runProtectedApiProbe(root, fetchProtectedApi);
- });
- }
- }
- async function runAuthAction(root, popupConfig, sendMessage, options) {
- const response = await sendMessage(options.actionMessage);
- if (isActionError(response)) {
- renderLoggedOut(root, response.error);
- root.querySelector('[data-popup-sign-in="button"]')?.addEventListener("click", () => {
- void runAuthAction(root, popupConfig, sendMessage, options);
- });
- return;
- }
- await renderCurrentAuthState(
- root,
- popupConfig,
- sendMessage,
- options.fetchProtectedApi,
- options.updateOptions
- );
- }
- function isActionError(response) {
- return isAuthResponseMessage(response) && !response.ok && response.type === "auth:error";
- }
- async function runUpdateCheck(root, sendMessage, options) {
- renderUpdateStatus(root, {
- currentVersion: options.currentVersion,
- status: "checking"
- });
- try {
- const manifest = await options.fetchUpdateManifest();
- if (compareExtensionVersions(manifest.latestVersion, options.currentVersion) <= 0) {
- renderUpdateStatus(root, {
- currentVersion: options.currentVersion,
- status: "latest"
- });
- return;
- }
- renderUpdateStatus(root, {
- currentVersion: options.currentVersion,
- manifest,
- status: "available"
- });
- bindUpdateDownloadButtons(root, sendMessage, manifest);
- } catch {
- renderUpdateStatus(root, {
- currentVersion: options.currentVersion,
- status: "error"
- });
- }
- }
- function bindUpdateDownloadButtons(root, sendMessage, manifest) {
- root.querySelector('[data-popup-download-update="button"]')?.addEventListener("click", () => {
- void downloadUpdateAsset(root, sendMessage, {
- filename: "star-chart-search-enhancer-internal.zip",
- url: manifest.zipUrl
- });
- });
- root.querySelector('[data-popup-download-guide="button"]')?.addEventListener("click", () => {
- void downloadUpdateAsset(root, sendMessage, {
- filename: "\u661F\u56FE\u589E\u5F3A\u63D2\u4EF6-\u8D85\u7B80\u5355\u5B89\u88C5\u4F7F\u7528\u6307\u5357.pdf",
- url: manifest.guideUrl
- });
- });
- }
- async function downloadUpdateAsset(root, sendMessage, options) {
- setUpdateDownloadStatus(root, "\u6B63\u5728\u4E0B\u8F7D...");
- try {
- await sendMessage({
- filename: options.filename,
- type: "update:download",
- url: options.url
- });
- setUpdateDownloadStatus(root, "\u5DF2\u89E6\u53D1\u4E0B\u8F7D\u3002\u4E0B\u8F7D\u540E\u8BF7\u89E3\u538B\u65B0\u7248 zip\uFF0C\u5E76\u5728 chrome://extensions \u91CC\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6\u3002");
- } catch (error) {
- setUpdateDownloadStatus(
- root,
- error instanceof Error ? error.message : "\u4E0B\u8F7D\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"
- );
- }
- }
- function readCurrentVersion() {
- const runtime = globalThis.chrome?.runtime;
- return runtime?.getManifest?.().version ?? "0.0.0";
- }
- async function runProtectedApiProbe(root, fetchProtectedApi) {
- setProtectedApiResult(root, "\u8BF7\u6C42\u4E2D...");
- try {
- const result = await fetchProtectedApi();
- setProtectedApiResult(root, JSON.stringify(result, null, 2));
- } catch (error) {
- setProtectedApiResult(
- root,
- error instanceof Error ? error.message : String(error)
- );
- }
- }
- if (typeof document !== "undefined") {
- void bootPopup();
- }
-})();