mirror of
https://github.com/Jonnyan404/memos-bber.git
synced 2026-08-22 16:31:40 +09:00
fix: 适配 Memos 0.29.x 附件 API
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
一款通过浏览器插件发布 [Memos](https://usememos.com/) 的插件。基于 iSpeak-bber 修改,原作者为 [DreamyTZK](https://www.antmoe.com/)。
|
||||
|
||||
兼容 Memos v0.15.0 - v0.29.x,支持新版 Attachment API。
|
||||
|
||||
[](https://ko-fi.com/jonnyan404)
|
||||
|
||||
## 在线商店安装
|
||||
@@ -18,4 +20,4 @@ Android
|
||||
### 使用教程
|
||||
|
||||
- 在文本框输入想搜索的关键字,点击搜索按钮
|
||||
- 随机和搜索功能在`0.24`以上版本支持私有权限的 memo,其它版本不支持
|
||||
- 随机和搜索功能在 `0.24` 以上版本支持私有权限的 memo,其他版本不支持
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
## 更新日志
|
||||
- 20260805 适配 Memos 0.29.x Attachment API,修复附件上传、绑定和预览地址
|
||||
- 20260425 新增德语/法语/西班牙语支持
|
||||
- 20260423 优化 firefox 抖动问题和支持手机版,支持 edge 浏览器扩展
|
||||
- 20260422 调整发送设置,支持仅发送附件
|
||||
@@ -69,4 +70,4 @@
|
||||
|
||||
2022.10.24 添加 visiblity 发送设置。
|
||||
|
||||
</details>
|
||||
</details>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "Speichern"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Kompatibel mit Memos v0.15.0 - 0.27.x"
|
||||
"message": "Kompatibel mit Memos v0.15.0 - 0.29.x"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "Verbindung"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "Save"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Compatible with Memos v0.15.0 - 0.27.x"
|
||||
"message": "Compatible with Memos v0.15.0 - 0.29.x"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "Connection"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "Guardar"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Compatible con Memos v0.15.0 - 0.27.x"
|
||||
"message": "Compatible con Memos v0.15.0 - 0.29.x"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "Conexión"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "Enregistrer"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Compatible avec Memos v0.15.0 - 0.27.x"
|
||||
"message": "Compatible avec Memos v0.15.0 - 0.29.x"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "Connexion"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "保存"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Memos v0.15.0 - 0.27.x に対応"
|
||||
"message": "Memos v0.15.0 - 0.29.x に対応"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "接続設定"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "저장"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Memos v0.15.0 - 0.27.x 호환"
|
||||
"message": "Memos v0.15.0 - 0.29.x 호환"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "연결 설정"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "保存"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "兼容 Memos v0.15.0 - 0.27.x"
|
||||
"message": "兼容 Memos v0.15.0 - 0.29.x"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "连接设置"
|
||||
|
||||
@@ -187,6 +187,7 @@
|
||||
|
||||
function normalizeUploadedItem(entity, fallbackFilename) {
|
||||
if (!entity) return null
|
||||
entity = entity.attachment || entity.resource || entity
|
||||
const inferredId = (function () {
|
||||
const value = entity.id != null ? entity.id : entity.ID != null ? entity.ID : entity.Id
|
||||
if (typeof value === 'number' && Number.isFinite(value)) return Math.floor(value)
|
||||
@@ -204,7 +205,11 @@
|
||||
name: name,
|
||||
filename: entity.filename || fallbackFilename || name,
|
||||
createTime: entity.createTime || entity.createdTs || entity.createdAt,
|
||||
type: entity.type
|
||||
type: entity.type,
|
||||
uid: entity.uid,
|
||||
size: entity.size,
|
||||
memo: entity.memo,
|
||||
externalLink: entity.externalLink
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +401,7 @@
|
||||
info,
|
||||
payload,
|
||||
function (resp) {
|
||||
const entity = (resp && resp.resource) || resp
|
||||
const entity = (resp && (resp.attachment || resp.resource)) || resp
|
||||
if (success) success(normalizeUploadedItem(entity, nextName))
|
||||
},
|
||||
fail
|
||||
@@ -458,24 +463,35 @@
|
||||
return
|
||||
}
|
||||
|
||||
const uploadedItems = Array.isArray(payload.resourceIdList) ? payload.resourceIdList : []
|
||||
const attachmentReferences = uploadedItems
|
||||
.filter(function (item) {
|
||||
return item && typeof item.name === 'string' && item.name.indexOf('attachments/') === 0
|
||||
})
|
||||
.map(function (item) {
|
||||
return { name: item.name }
|
||||
})
|
||||
const canCreateWithAttachments = flavor === 'modern' && attachmentReferences.length === uploadedItems.length
|
||||
|
||||
requestJson({
|
||||
url: info.apiUrl + 'api/v1/memos',
|
||||
type: 'POST',
|
||||
data: JSON.stringify({
|
||||
content: payload.content,
|
||||
visibility: payload.visibility
|
||||
visibility: payload.visibility,
|
||||
...(canCreateWithAttachments ? { attachments: attachmentReferences } : {})
|
||||
}),
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: { Authorization: 'Bearer ' + info.apiTokens }
|
||||
}, function (data) {
|
||||
const createdName = data && data.name ? data.name : data && data.memo && data.memo.name ? data.memo.name : ''
|
||||
const resources = Array.isArray(payload.resourceIdList) ? payload.resourceIdList : []
|
||||
const resources = uploadedItems
|
||||
if (!createdName) {
|
||||
if (success) success(data)
|
||||
return
|
||||
}
|
||||
if (resources.length === 0) {
|
||||
if (resources.length === 0 || canCreateWithAttachments) {
|
||||
getMemo(createdName, success, fail)
|
||||
return
|
||||
}
|
||||
@@ -497,7 +513,7 @@
|
||||
return {
|
||||
flavor: flavor,
|
||||
needsAuthenticatedImagePreview: function () {
|
||||
return flavor === FLAVOR_V020_V021
|
||||
return true
|
||||
},
|
||||
listTags: listTags,
|
||||
searchMemos: searchMemos,
|
||||
@@ -518,4 +534,4 @@
|
||||
probeFlavor: probeFlavor,
|
||||
resolve: resolve
|
||||
}
|
||||
})(window)
|
||||
})(window)
|
||||
|
||||
@@ -151,10 +151,53 @@
|
||||
return copy
|
||||
}
|
||||
|
||||
function uploadResource() {
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(payload),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
if (!xhr || xhr.status !== 400) {
|
||||
if (onFail) onFail(xhr)
|
||||
return
|
||||
}
|
||||
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(stripVisibility(payload)),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr2) {
|
||||
if (onFail) onFail(xhr2)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Attachment visibility was removed in Memos v0.26. Send the current
|
||||
// attachment shape first and retain the resource endpoint as a legacy fallback.
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlAttachments,
|
||||
data: JSON.stringify(payload),
|
||||
data: JSON.stringify(stripVisibility(payload)),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
@@ -166,115 +209,11 @@
|
||||
onSuccess(data, 'attachments')
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
if (xhr && xhr.status === 400) {
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlAttachments,
|
||||
data: JSON.stringify(stripVisibility(payload)),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'attachments')
|
||||
})
|
||||
.fail(function (xhrRetry) {
|
||||
if (!isNotFoundLikeXhr(xhrRetry)) {
|
||||
if (onFail) onFail(xhrRetry)
|
||||
return
|
||||
}
|
||||
// fall through to resources below
|
||||
xhr = xhrRetry
|
||||
if (!isNotFoundLikeXhr(xhr)) {
|
||||
if (onFail) onFail(xhr)
|
||||
return
|
||||
}
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(payload),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr2) {
|
||||
if (xhr2 && xhr2.status === 400) {
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(stripVisibility(payload)),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr3) {
|
||||
if (onFail) onFail(xhr3)
|
||||
})
|
||||
return
|
||||
}
|
||||
if (onFail) onFail(xhr2)
|
||||
})
|
||||
})
|
||||
if (isNotFoundLikeXhr(xhr)) {
|
||||
uploadResource()
|
||||
return
|
||||
}
|
||||
|
||||
if (!isNotFoundLikeXhr(xhr)) {
|
||||
if (onFail) onFail(xhr)
|
||||
return
|
||||
}
|
||||
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(payload),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr2) {
|
||||
if (xhr2 && xhr2.status === 400) {
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(stripVisibility(payload)),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr3) {
|
||||
if (onFail) onFail(xhr3)
|
||||
})
|
||||
return
|
||||
}
|
||||
if (onFail) onFail(xhr2)
|
||||
})
|
||||
if (onFail) onFail(xhr)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+11
-4
@@ -480,6 +480,15 @@ function buildV1ResourceStreamUrl(info, resource) {
|
||||
}
|
||||
if (root && !root.endsWith('/')) root += '/'
|
||||
|
||||
if (resource.externalLink) return resource.externalLink
|
||||
|
||||
const name = typeof resource.name === 'string' ? resource.name.trim() : ''
|
||||
const filename = typeof resource.filename === 'string' ? resource.filename : ''
|
||||
if (name.indexOf('attachments/') === 0 && filename) {
|
||||
const url = root + 'file/' + name.split('/').map(encodeURIComponent).join('/') + '/' + encodeURIComponent(filename)
|
||||
return isImageResource(resource) ? url + '?thumbnail=true' : url
|
||||
}
|
||||
|
||||
function isImageResource(r) {
|
||||
if (!r) return false
|
||||
const t = typeof r.type === 'string' ? r.type.toLowerCase() : ''
|
||||
@@ -517,13 +526,11 @@ function buildV1ResourceStreamUrl(info, resource) {
|
||||
if (id) return buildStreamUrl(id)
|
||||
|
||||
// Fallback for older resource shapes.
|
||||
const name = typeof resource.name === 'string' ? resource.name : ''
|
||||
|
||||
// In some memo payloads, the uid may appear as `name` directly.
|
||||
// Example: name="ETU6hjuR..." should map to /o/r/:uid, not /file/:name/:filename.
|
||||
if (isProbablyUid(name)) return buildStreamUrl(name.trim())
|
||||
|
||||
const fileId = resource.publicId || resource.filename
|
||||
const fileId = resource.publicId || filename
|
||||
if (name && fileId) return root + 'file/' + name + '/' + fileId
|
||||
return ''
|
||||
}
|
||||
@@ -1283,4 +1290,4 @@ function sendText(preparedContent) {
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 3,
|
||||
"name": "__MSG_extName__",
|
||||
"default_locale": "en",
|
||||
"version": "2026.04.25",
|
||||
"version": "2026.08.05",
|
||||
"action": {
|
||||
"default_popup": "popup.html",
|
||||
"default_icon": "assets/logo_24x24.png",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "Speichern"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Kompatibel mit Memos v0.15.0 - 0.27.x"
|
||||
"message": "Kompatibel mit Memos v0.15.0 - 0.29.x"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "Verbindung"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "Save"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Compatible with Memos v0.15.0 - 0.27.x"
|
||||
"message": "Compatible with Memos v0.15.0 - 0.29.x"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "Connection"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "Guardar"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Compatible con Memos v0.15.0 - 0.27.x"
|
||||
"message": "Compatible con Memos v0.15.0 - 0.29.x"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "Conexión"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "Enregistrer"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Compatible avec Memos v0.15.0 - 0.27.x"
|
||||
"message": "Compatible avec Memos v0.15.0 - 0.29.x"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "Connexion"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "保存"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Memos v0.15.0 - 0.27.x に対応"
|
||||
"message": "Memos v0.15.0 - 0.29.x に対応"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "接続設定"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "저장"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Memos v0.15.0 - 0.27.x 호환"
|
||||
"message": "Memos v0.15.0 - 0.29.x 호환"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "연결 설정"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "保存"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "兼容 Memos v0.15.0 - 0.27.x"
|
||||
"message": "兼容 Memos v0.15.0 - 0.29.x"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "连接设置"
|
||||
|
||||
@@ -187,6 +187,7 @@
|
||||
|
||||
function normalizeUploadedItem(entity, fallbackFilename) {
|
||||
if (!entity) return null
|
||||
entity = entity.attachment || entity.resource || entity
|
||||
const inferredId = (function () {
|
||||
const value = entity.id != null ? entity.id : entity.ID != null ? entity.ID : entity.Id
|
||||
if (typeof value === 'number' && Number.isFinite(value)) return Math.floor(value)
|
||||
@@ -204,7 +205,11 @@
|
||||
name: name,
|
||||
filename: entity.filename || fallbackFilename || name,
|
||||
createTime: entity.createTime || entity.createdTs || entity.createdAt,
|
||||
type: entity.type
|
||||
type: entity.type,
|
||||
uid: entity.uid,
|
||||
size: entity.size,
|
||||
memo: entity.memo,
|
||||
externalLink: entity.externalLink
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +401,7 @@
|
||||
info,
|
||||
payload,
|
||||
function (resp) {
|
||||
const entity = (resp && resp.resource) || resp
|
||||
const entity = (resp && (resp.attachment || resp.resource)) || resp
|
||||
if (success) success(normalizeUploadedItem(entity, nextName))
|
||||
},
|
||||
fail
|
||||
@@ -458,24 +463,35 @@
|
||||
return
|
||||
}
|
||||
|
||||
const uploadedItems = Array.isArray(payload.resourceIdList) ? payload.resourceIdList : []
|
||||
const attachmentReferences = uploadedItems
|
||||
.filter(function (item) {
|
||||
return item && typeof item.name === 'string' && item.name.indexOf('attachments/') === 0
|
||||
})
|
||||
.map(function (item) {
|
||||
return { name: item.name }
|
||||
})
|
||||
const canCreateWithAttachments = flavor === 'modern' && attachmentReferences.length === uploadedItems.length
|
||||
|
||||
requestJson({
|
||||
url: info.apiUrl + 'api/v1/memos',
|
||||
type: 'POST',
|
||||
data: JSON.stringify({
|
||||
content: payload.content,
|
||||
visibility: payload.visibility
|
||||
visibility: payload.visibility,
|
||||
...(canCreateWithAttachments ? { attachments: attachmentReferences } : {})
|
||||
}),
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: { Authorization: 'Bearer ' + info.apiTokens }
|
||||
}, function (data) {
|
||||
const createdName = data && data.name ? data.name : data && data.memo && data.memo.name ? data.memo.name : ''
|
||||
const resources = Array.isArray(payload.resourceIdList) ? payload.resourceIdList : []
|
||||
const resources = uploadedItems
|
||||
if (!createdName) {
|
||||
if (success) success(data)
|
||||
return
|
||||
}
|
||||
if (resources.length === 0) {
|
||||
if (resources.length === 0 || canCreateWithAttachments) {
|
||||
getMemo(createdName, success, fail)
|
||||
return
|
||||
}
|
||||
@@ -497,7 +513,7 @@
|
||||
return {
|
||||
flavor: flavor,
|
||||
needsAuthenticatedImagePreview: function () {
|
||||
return flavor === FLAVOR_V020_V021
|
||||
return true
|
||||
},
|
||||
listTags: listTags,
|
||||
searchMemos: searchMemos,
|
||||
@@ -518,4 +534,4 @@
|
||||
probeFlavor: probeFlavor,
|
||||
resolve: resolve
|
||||
}
|
||||
})(window)
|
||||
})(window)
|
||||
|
||||
@@ -151,10 +151,53 @@
|
||||
return copy
|
||||
}
|
||||
|
||||
function uploadResource() {
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(payload),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
if (!xhr || xhr.status !== 400) {
|
||||
if (onFail) onFail(xhr)
|
||||
return
|
||||
}
|
||||
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(stripVisibility(payload)),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr2) {
|
||||
if (onFail) onFail(xhr2)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Attachment visibility was removed in Memos v0.26. Send the current
|
||||
// attachment shape first and retain the resource endpoint as a legacy fallback.
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlAttachments,
|
||||
data: JSON.stringify(payload),
|
||||
data: JSON.stringify(stripVisibility(payload)),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
@@ -166,115 +209,11 @@
|
||||
onSuccess(data, 'attachments')
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
if (xhr && xhr.status === 400) {
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlAttachments,
|
||||
data: JSON.stringify(stripVisibility(payload)),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'attachments')
|
||||
})
|
||||
.fail(function (xhrRetry) {
|
||||
if (!isNotFoundLikeXhr(xhrRetry)) {
|
||||
if (onFail) onFail(xhrRetry)
|
||||
return
|
||||
}
|
||||
// fall through to resources below
|
||||
xhr = xhrRetry
|
||||
if (!isNotFoundLikeXhr(xhr)) {
|
||||
if (onFail) onFail(xhr)
|
||||
return
|
||||
}
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(payload),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr2) {
|
||||
if (xhr2 && xhr2.status === 400) {
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(stripVisibility(payload)),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr3) {
|
||||
if (onFail) onFail(xhr3)
|
||||
})
|
||||
return
|
||||
}
|
||||
if (onFail) onFail(xhr2)
|
||||
})
|
||||
})
|
||||
if (isNotFoundLikeXhr(xhr)) {
|
||||
uploadResource()
|
||||
return
|
||||
}
|
||||
|
||||
if (!isNotFoundLikeXhr(xhr)) {
|
||||
if (onFail) onFail(xhr)
|
||||
return
|
||||
}
|
||||
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(payload),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr2) {
|
||||
if (xhr2 && xhr2.status === 400) {
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(stripVisibility(payload)),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr3) {
|
||||
if (onFail) onFail(xhr3)
|
||||
})
|
||||
return
|
||||
}
|
||||
if (onFail) onFail(xhr2)
|
||||
})
|
||||
if (onFail) onFail(xhr)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+11
-4
@@ -480,6 +480,15 @@ function buildV1ResourceStreamUrl(info, resource) {
|
||||
}
|
||||
if (root && !root.endsWith('/')) root += '/'
|
||||
|
||||
if (resource.externalLink) return resource.externalLink
|
||||
|
||||
const name = typeof resource.name === 'string' ? resource.name.trim() : ''
|
||||
const filename = typeof resource.filename === 'string' ? resource.filename : ''
|
||||
if (name.indexOf('attachments/') === 0 && filename) {
|
||||
const url = root + 'file/' + name.split('/').map(encodeURIComponent).join('/') + '/' + encodeURIComponent(filename)
|
||||
return isImageResource(resource) ? url + '?thumbnail=true' : url
|
||||
}
|
||||
|
||||
function isImageResource(r) {
|
||||
if (!r) return false
|
||||
const t = typeof r.type === 'string' ? r.type.toLowerCase() : ''
|
||||
@@ -517,13 +526,11 @@ function buildV1ResourceStreamUrl(info, resource) {
|
||||
if (id) return buildStreamUrl(id)
|
||||
|
||||
// Fallback for older resource shapes.
|
||||
const name = typeof resource.name === 'string' ? resource.name : ''
|
||||
|
||||
// In some memo payloads, the uid may appear as `name` directly.
|
||||
// Example: name="ETU6hjuR..." should map to /o/r/:uid, not /file/:name/:filename.
|
||||
if (isProbablyUid(name)) return buildStreamUrl(name.trim())
|
||||
|
||||
const fileId = resource.publicId || resource.filename
|
||||
const fileId = resource.publicId || filename
|
||||
if (name && fileId) return root + 'file/' + name + '/' + fileId
|
||||
return ''
|
||||
}
|
||||
@@ -1283,4 +1290,4 @@ function sendText(preparedContent) {
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 3,
|
||||
"name": "__MSG_extName__",
|
||||
"default_locale": "en",
|
||||
"version": "2026.04.25",
|
||||
"version": "2026.08.05",
|
||||
"action": {
|
||||
"default_popup": "popup.html",
|
||||
"default_icon": "assets/logo_24x24.png",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "Speichern"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Kompatibel mit Memos v0.15.0 - 0.27.x"
|
||||
"message": "Kompatibel mit Memos v0.15.0 - 0.29.x"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "Verbindung"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "Save"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Compatible with Memos v0.15.0 - 0.27.x"
|
||||
"message": "Compatible with Memos v0.15.0 - 0.29.x"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "Connection"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "Guardar"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Compatible con Memos v0.15.0 - 0.27.x"
|
||||
"message": "Compatible con Memos v0.15.0 - 0.29.x"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "Conexión"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "Enregistrer"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Compatible avec Memos v0.15.0 - 0.27.x"
|
||||
"message": "Compatible avec Memos v0.15.0 - 0.29.x"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "Connexion"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "保存"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Memos v0.15.0 - 0.27.x に対応"
|
||||
"message": "Memos v0.15.0 - 0.29.x に対応"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "接続設定"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "저장"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "Memos v0.15.0 - 0.27.x 호환"
|
||||
"message": "Memos v0.15.0 - 0.29.x 호환"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "연결 설정"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"message": "保存"
|
||||
},
|
||||
"supportedMemosVersion": {
|
||||
"message": "兼容 Memos v0.15.0 - 0.27.x"
|
||||
"message": "兼容 Memos v0.15.0 - 0.29.x"
|
||||
},
|
||||
"settingsConnectionTitle": {
|
||||
"message": "连接设置"
|
||||
|
||||
@@ -187,6 +187,7 @@
|
||||
|
||||
function normalizeUploadedItem(entity, fallbackFilename) {
|
||||
if (!entity) return null
|
||||
entity = entity.attachment || entity.resource || entity
|
||||
const inferredId = (function () {
|
||||
const value = entity.id != null ? entity.id : entity.ID != null ? entity.ID : entity.Id
|
||||
if (typeof value === 'number' && Number.isFinite(value)) return Math.floor(value)
|
||||
@@ -204,7 +205,11 @@
|
||||
name: name,
|
||||
filename: entity.filename || fallbackFilename || name,
|
||||
createTime: entity.createTime || entity.createdTs || entity.createdAt,
|
||||
type: entity.type
|
||||
type: entity.type,
|
||||
uid: entity.uid,
|
||||
size: entity.size,
|
||||
memo: entity.memo,
|
||||
externalLink: entity.externalLink
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +401,7 @@
|
||||
info,
|
||||
payload,
|
||||
function (resp) {
|
||||
const entity = (resp && resp.resource) || resp
|
||||
const entity = (resp && (resp.attachment || resp.resource)) || resp
|
||||
if (success) success(normalizeUploadedItem(entity, nextName))
|
||||
},
|
||||
fail
|
||||
@@ -458,24 +463,35 @@
|
||||
return
|
||||
}
|
||||
|
||||
const uploadedItems = Array.isArray(payload.resourceIdList) ? payload.resourceIdList : []
|
||||
const attachmentReferences = uploadedItems
|
||||
.filter(function (item) {
|
||||
return item && typeof item.name === 'string' && item.name.indexOf('attachments/') === 0
|
||||
})
|
||||
.map(function (item) {
|
||||
return { name: item.name }
|
||||
})
|
||||
const canCreateWithAttachments = flavor === 'modern' && attachmentReferences.length === uploadedItems.length
|
||||
|
||||
requestJson({
|
||||
url: info.apiUrl + 'api/v1/memos',
|
||||
type: 'POST',
|
||||
data: JSON.stringify({
|
||||
content: payload.content,
|
||||
visibility: payload.visibility
|
||||
visibility: payload.visibility,
|
||||
...(canCreateWithAttachments ? { attachments: attachmentReferences } : {})
|
||||
}),
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: { Authorization: 'Bearer ' + info.apiTokens }
|
||||
}, function (data) {
|
||||
const createdName = data && data.name ? data.name : data && data.memo && data.memo.name ? data.memo.name : ''
|
||||
const resources = Array.isArray(payload.resourceIdList) ? payload.resourceIdList : []
|
||||
const resources = uploadedItems
|
||||
if (!createdName) {
|
||||
if (success) success(data)
|
||||
return
|
||||
}
|
||||
if (resources.length === 0) {
|
||||
if (resources.length === 0 || canCreateWithAttachments) {
|
||||
getMemo(createdName, success, fail)
|
||||
return
|
||||
}
|
||||
@@ -497,7 +513,7 @@
|
||||
return {
|
||||
flavor: flavor,
|
||||
needsAuthenticatedImagePreview: function () {
|
||||
return flavor === FLAVOR_V020_V021
|
||||
return true
|
||||
},
|
||||
listTags: listTags,
|
||||
searchMemos: searchMemos,
|
||||
@@ -518,4 +534,4 @@
|
||||
probeFlavor: probeFlavor,
|
||||
resolve: resolve
|
||||
}
|
||||
})(window)
|
||||
})(window)
|
||||
|
||||
@@ -151,10 +151,53 @@
|
||||
return copy
|
||||
}
|
||||
|
||||
function uploadResource() {
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(payload),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
if (!xhr || xhr.status !== 400) {
|
||||
if (onFail) onFail(xhr)
|
||||
return
|
||||
}
|
||||
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(stripVisibility(payload)),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr2) {
|
||||
if (onFail) onFail(xhr2)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Attachment visibility was removed in Memos v0.26. Send the current
|
||||
// attachment shape first and retain the resource endpoint as a legacy fallback.
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlAttachments,
|
||||
data: JSON.stringify(payload),
|
||||
data: JSON.stringify(stripVisibility(payload)),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
@@ -166,115 +209,11 @@
|
||||
onSuccess(data, 'attachments')
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
if (xhr && xhr.status === 400) {
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlAttachments,
|
||||
data: JSON.stringify(stripVisibility(payload)),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'attachments')
|
||||
})
|
||||
.fail(function (xhrRetry) {
|
||||
if (!isNotFoundLikeXhr(xhrRetry)) {
|
||||
if (onFail) onFail(xhrRetry)
|
||||
return
|
||||
}
|
||||
// fall through to resources below
|
||||
xhr = xhrRetry
|
||||
if (!isNotFoundLikeXhr(xhr)) {
|
||||
if (onFail) onFail(xhr)
|
||||
return
|
||||
}
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(payload),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr2) {
|
||||
if (xhr2 && xhr2.status === 400) {
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(stripVisibility(payload)),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr3) {
|
||||
if (onFail) onFail(xhr3)
|
||||
})
|
||||
return
|
||||
}
|
||||
if (onFail) onFail(xhr2)
|
||||
})
|
||||
})
|
||||
if (isNotFoundLikeXhr(xhr)) {
|
||||
uploadResource()
|
||||
return
|
||||
}
|
||||
|
||||
if (!isNotFoundLikeXhr(xhr)) {
|
||||
if (onFail) onFail(xhr)
|
||||
return
|
||||
}
|
||||
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(payload),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr2) {
|
||||
if (xhr2 && xhr2.status === 400) {
|
||||
global.$
|
||||
.ajax({
|
||||
url: urlResources,
|
||||
data: JSON.stringify(stripVisibility(payload)),
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
headers: headers
|
||||
})
|
||||
.done(function (data) {
|
||||
onSuccess(data, 'resources')
|
||||
})
|
||||
.fail(function (xhr3) {
|
||||
if (onFail) onFail(xhr3)
|
||||
})
|
||||
return
|
||||
}
|
||||
if (onFail) onFail(xhr2)
|
||||
})
|
||||
if (onFail) onFail(xhr)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+11
-4
@@ -505,6 +505,15 @@ function buildV1ResourceStreamUrl(info, resource) {
|
||||
}
|
||||
if (root && !root.endsWith('/')) root += '/'
|
||||
|
||||
if (resource.externalLink) return resource.externalLink
|
||||
|
||||
const name = typeof resource.name === 'string' ? resource.name.trim() : ''
|
||||
const filename = typeof resource.filename === 'string' ? resource.filename : ''
|
||||
if (name.indexOf('attachments/') === 0 && filename) {
|
||||
const url = root + 'file/' + name.split('/').map(encodeURIComponent).join('/') + '/' + encodeURIComponent(filename)
|
||||
return isImageResource(resource) ? url + '?thumbnail=true' : url
|
||||
}
|
||||
|
||||
function isImageResource(r) {
|
||||
if (!r) return false
|
||||
const t = typeof r.type === 'string' ? r.type.toLowerCase() : ''
|
||||
@@ -542,13 +551,11 @@ function buildV1ResourceStreamUrl(info, resource) {
|
||||
if (id) return buildStreamUrl(id)
|
||||
|
||||
// Fallback for older resource shapes.
|
||||
const name = typeof resource.name === 'string' ? resource.name : ''
|
||||
|
||||
// In some memo payloads, the uid may appear as `name` directly.
|
||||
// Example: name="ETU6hjuR..." should map to /o/r/:uid, not /file/:name/:filename.
|
||||
if (isProbablyUid(name)) return buildStreamUrl(name.trim())
|
||||
|
||||
const fileId = resource.publicId || resource.filename
|
||||
const fileId = resource.publicId || filename
|
||||
if (name && fileId) return root + 'file/' + name + '/' + fileId
|
||||
return ''
|
||||
}
|
||||
@@ -1308,4 +1315,4 @@ function sendText(preparedContent) {
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "__MSG_extName__",
|
||||
"default_locale": "en",
|
||||
"version": "2026.4.25",
|
||||
"version": "2026.8.5",
|
||||
"browser_action": {
|
||||
"default_popup": "popup.html",
|
||||
"default_icon": "assets/logo_24x24.png",
|
||||
|
||||
Reference in New Issue
Block a user