-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathrun.js
More file actions
196 lines (185 loc) · 7.99 KB
/
run.js
File metadata and controls
196 lines (185 loc) · 7.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
((async (fastMode /** 快速模式:以时间降序查询物品,若超过3页都没有能添加的物品,则认为该分类已经没有新增的 */) => {
const getCookie = (name) => {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
/**
* 获取物品
*
* @returns [next,[uid]]
*/
const getItemsApi = async (cookies, next, url_base) => {
//const response = await fetch(`&cursor=${next}`, {
const response = await fetch(`${url_base}&cursor=${next}`, {
"headers": {
"accept": "application/json, text/plain, */*",
"cookie": cookies
},
"method": "GET",
})
let data = await response.json()
let nextPage = data.cursors?.next ?? null
let uidList = data.results?.map(result => result.uid) ?? []
//console.log(data.cursors.previous)
//console.log(`测试物品数据: ${JSON.stringify(data)}`)
return [nextPage, uidList]
}
/**
* 添加到库
*/
const addLibApi = async (cookies, token, uid, offerId) => {
const response = await fetch(`https://www.fab.com/i/listings/${uid}/add-to-library`, {
"headers": {
"Cookies": cookies,
"accept": "application/json, text/plain, */*",
"accept-language": "en",
"content-type": "multipart/form-data; boundary=----WebKitFormBoundary1",
"priority": "u=1, i",
"sec-ch-ua": "\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-csrftoken": token,
"x-requested-with": "XMLHttpRequest"
},
"referrer": `https://www.fab.com/zh-cn/listings/${uid}`,
"referrerPolicy": "strict-origin-when-cross-origin",
"body": `------WebKitFormBoundary1\r\nContent-Disposition: form-data; name=\"offer_id\"\r\n\r\n${offerId}\r\n------WebKitFormBoundary1--\r\n`,
"method": "POST",
"mode": "cors",
"credentials": "include"
});
return response.status == 204
}
/**
* 获取详细信息,主要要取得offerId
*/
const listingsApi = async (cookies, token, uid) => {
const response = await fetch(`https://www.fab.com/i/listings/${uid}`, {
"headers": {
"accept": "application/json, text/plain, */*",
"cookie": cookies,
"referer": "https://www.fab.com/",
"x-csrftoken": token
},
"method": "GET",
})
let data = await response.json()
let title = data.title
let offerId = null
let type = null
//尽量专业版
if (data.licenses)
for (licenseInfo of data.licenses) {
if (licenseInfo.priceTier.price == 0.0) {
offerId = licenseInfo.offerId
type = licenseInfo.slug
if (licenseInfo.slug == "professional") {
break
}
}
}
//console.log(`测试数据: ${JSON.stringify(data)}`)
return [offerId, type, title]
}
/**
* 获取许可状态
*/
const listingsStateApi = async (cookies, token, uids) => {
if (!Array.isArray(uids) || !uids.length)
return {}
let uidParams = uids.map(uid => `listing_ids=${uid}`).join("&")
const response = await fetch(`https://www.fab.com/i/users/me/listings-states?${uidParams}`, {
"headers": {
"cookie": cookies,
"accept": "application/json, text/plain, */*",
"x-csrftoken": token,
"x-requested-with": "XMLHttpRequest"
},
"referrer": "https://www.fab.com/",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": null,
"method": "GET",
"mode": "cors",
"credentials": "include"
})
let data = await response.json()
return data.reduce((acc, item) => {
acc[item.uid] = item.acquired;
return acc;
}, {})
}
console.log("⭐ UnrealFabAssistant: Automatically add all free resources from Fab to your account")
console.log("⭐ Powered by https://github.com/RyensX/UnrealFabAssistant")
// 获取cookies和xtoken
console.log("-> Checking User Info...")
let csrftoken = ""
let cookies = document.cookie
try {
csrftoken = getCookie("fab_csrftoken") ?? "{}"
if (!csrftoken) {
return console.error("-> Error: cannot find csrftoken. Please login again.")
}
} catch (_) {
return console.error("-> Error: cannot find csrftoken. Please login again.")
}
console.log(`cookies=${cookies}`)
console.log(`csrftoken=${csrftoken}`)
console.log("-> Start Process Items...")
let totalCount = 0
const MAX_EMPTY_PAGE = 3
let countMap = {}
let urls = {
"UE": "https://www.fab.com/i/listings/search?channels=unreal-engine&is_free=1&sort_by=-createdAt",
"Unity": "https://www.fab.com/i/listings/search?channels=unity&is_free=1&sort_by=-createdAt",
"UEFN": "https://www.fab.com/i/listings/search?channels=uefn&is_free=1&sort_by=-createdAt",
"Quixel": "https://www.fab.com/i/listings/search?currency=USD&seller=Quixel&sort_by=listingTypeWeight"
//这里如果仅仅只需要其中一种类型资源,比如只需要UE的,那可以只保留UE的链接
}
const mainTasks = Object.entries(urls).map(async ([name, url]) => {
console.log(`start by name=${name} url=${url}`)
let nextPage = null
let currentPageIndex = 1
let currentCount = 0
let lastPage = 0
do {
lastPage++
const page = await getItemsApi(cookies, nextPage, url)
console.log(`${name} page=${currentPageIndex++}(${page[0]}) ,count=${page[1].length}`)
nextPage = page[0]
//先获取许可状态
const states = await listingsStateApi(cookies, csrftoken, page[1])
//获取详情
const tasks = page[1].map(async (uid) => {
//已入库的不再重复。不过如果需要自动更新许可类型(尽量换成专业版)可以把这个限制去掉
if (states[uid] == false) {
const info = await listingsApi(cookies, csrftoken, uid)
const [offerId, type, title] = info
if (offerId != null) {
console.log(`No.${currentCount} ${name} Item: name=${title} , offerId=${offerId}`)
//入库
const result = await addLibApi(cookies, csrftoken, uid, offerId)
console.log(`addLib No.${currentCount} ${title} from ${name} result=${result} page=${page[0]} type=${type}`)
if (result) {
lastPage = 0
currentCount++
}
}
}
})
await Promise.allSettled(tasks)
//break //测试用
} while ((!fastMode || lastPage < MAX_EMPTY_PAGE) && nextPage != null && nextPage != "")
console.log(`✅ ${name} done! ${currentCount} items added.`)
totalCount += currentCount
countMap[name] = currentCount
})
await Promise.allSettled(mainTasks)
let countDetail = totalCount > 0 ?
"(" + Object.entries(countMap).map(([key, value]) => `${key}:${value}`).join(" ") + ")"
: ""
console.log(`\n✅ All done! ${totalCount}${countDetail} items added.`)
})(console.fastMode ?? true))