/upload-resumable w/ `thumbnailfile` and/or `previewfile`

hi there,

I managed to implement uploading videos through the /videos/upload endpoint (with thumbnailfile and previewfile in the multipart/form-data. BTW, one has to explicitly add a Content-Type : the Go multipart library automatically adds application/octet-stream.)

now, I am trying to implement uploading videos through /videos/upload-resumable.
it works when I am not adding the thumbnailfile and/or previewfile keys to the initial JSON payload.

when I am adding those (with the raw byte content of these files) to the initial payload, I get :

peertube-1  | [localhost:9000] 2024-04-04 08:34:20.822 info: 127.0.0.1 - - [04/Apr/2024:08:34:20 +0000] "GET /api/v1/oauth-clients/local HTTP/1.1" 200 99 "-" "Go-http-client/1.1"
peertube-1  | [localhost:9000] 2024-04-04 08:34:20.888 info: 127.0.0.1 - - [04/Apr/2024:08:34:20 +0000] "POST /api/v1/users/token HTTP/1.1" 200 194 "-" "Go-http-client/1.1"
peertube-1  | [localhost:9000] 2024-04-04 08:34:20.901 info: 127.0.0.1 - - [04/Apr/2024:08:34:20 +0000] "GET /api/v1/accounts/root/video-channels HTTP/1.1" 200 717 "-" "Go-http-client/1.1"
peertube-1  | [localhost:9000] 2024-04-04 08:34:20.914 info: 127.0.0.1 - - [04/Apr/2024:08:34:20 +0000] "POST /api/v1/videos/upload-resumable HTTP/1.1" 201 - "-" "Go-http-client/1.1"
peertube-1  | [localhost:9000] 2024-04-04 08:34:20.918 info: 127.0.0.1 - - [04/Apr/2024:08:34:20 +0000] "PUT /api/v1/videos/upload-resumable?upload_id=af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-fcd55ff9b920f996 HTTP/1.1" 308 - "-" "Go-http-client/1.1"
peertube-1  | [localhost:9000] 2024-04-04 08:34:20.919 info: 127.0.0.1 - - [04/Apr/2024:08:34:20 +0000] "PUT /api/v1/videos/upload-resumable?upload_id=af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-fcd55ff9b920f996 HTTP/1.1" 308 - "-" "Go-http-client/1.1"
peertube-1  | [localhost:9000] 2024-04-04 08:34:20.921 info: 127.0.0.1 - - [04/Apr/2024:08:34:20 +0000] "PUT /api/v1/videos/upload-resumable?upload_id=af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-fcd55ff9b920f996 HTTP/1.1" 308 - "-" "Go-http-client/1.1"
peertube-1  | [localhost:9000] 2024-04-04 08:34:21.083 error: Error in controller. {
peertube-1  |   "err": "TypeError [ERR_INVALID_ARG_TYPE]: The \"path\" argument must be of type string. Received undefined\n    at new NodeError (node:internal/errors:405:5)\n    at validateString (node:internal/validators:162:11)\n    at extname (node:path:1385:5)\n    at getLowercaseExtension (file:///app/packages/node-utils/dist/path.js:33:17)\n    at processImage (file:///app/dist/core/helpers/image-utils.js:12:23)\n    at /app/node_modules/piscina/dist/src/worker.js:141:32"
peertube-1  | }
peertube-1  | [localhost:9000] 2024-04-04 08:34:21.083 info: 127.0.0.1 - - [04/Apr/2024:08:34:21 +0000] "PUT /api/v1/videos/upload-resumable?upload_id=af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-fcd55ff9b920f996 HTTP/1.1" 500 361 "-" "Go-http-client/1.1"

on the client end, I get :

go-piretube: ERROR: [500] "500 Internal Server Error"
go-piretube: could not run piretube-cli: could not upload video "./testdata/blank-5s.mp3": could not upload video:  (500): The "path" argument must be of type string. Received undefined

here is the initial payload (with the raw bytes from previewfile elided for brevity):

{
  "channelId": 1,
  "commentsEnabled": true,
  "description": "some description",
  "downloadEnabled": true,
  "filename": "blank-5s.mp3",
  "name": "with-thumbnail-9",
  "previewfile": "iVBORw0KGgoAAAANSUhEUgAAAMg...",
  "privacy": 1,
  "scheduleUpdate": {
    "updateAt": "2024-04-04T08:34:20.901416223Z",
    "privacy": 1
  },
  "tags": [
    "go",
    "golang",
    "peertube"
  ],
  "waitTranscoding": true
}

I tried to git grep for the error in the PeerTube sources but it didn’t pan out.

what am I doing wrong ?

cheers,
Sébastien

Can you retry with peertube having debug logs enabled?

here it is (with PEERTUBE_LOG_LEVEL=debug)

peertube-1  | [localhost:9000] 2024-04-04 09:24:56.180 debug: Executed SQL request - Executing (default): SELECT "id", "clientId", "clientSecret", "grants", "redirectUris", "createdAt", "updatedAt" FROM "oAuthClient" AS "OAuthClientModel" LIMIT 1;
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.184 info: 127.0.0.1 - - [04/Apr/2024:09:24:56 +0000] "GET /api/v1/oauth-clients/local HTTP/1.1" 200 99 "-" "Go-http-client/1.1"
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.194 debug: Getting Client (clientId: sqt6kcqlcy70pf0ihif97l86ev32soum, clientSecret: 85l8AvFa0CG9zAb9RLLUsPQSFxxc21NE).
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.195 debug: Executed SQL request - Executing (default): SELECT "id", "clientId", "clientSecret", "grants", "redirectUris", "createdAt", "updatedAt" FROM "oAuthClient" AS "OAuthClientModel" WHERE "OAuthClientModel"."clientId" = 'sqt6kcqlcy70pf0ihif97l86ev32soum' AND "OAuthClientModel"."clientSecret" = '85l8AvFa0CG9zAb9RLLUsPQSFxxc21NE' LIMIT 1;
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.195 debug: Getting User (username/email: root, password: ******).
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.198 debug: Executed SQL request - Executing (default): SELECT "UserModel".*, "Account->Actor->Server"."id" AS "Account.Actor.Server.id", "Account->Actor->Server"."host" AS "Account.Actor.Server.host", "Account->Actor->Server"."redundancyAllowed" AS "Account.Actor.Server.redundancyAllowed", "Account->Actor->Server"."createdAt" AS "Account.Actor.Server.createdAt", "Account->Actor->Server"."updatedAt" AS "Account.Actor.Server.updatedAt", "Account->Actor->Avatars"."id" AS "Account.Actor.Avatars.id", "Account->Actor->Avatars"."filename" AS "Account.Actor.Avatars.filename", "Account->Actor->Avatars"."height" AS "Account.Actor.Avatars.height", "Account->Actor->Avatars"."width" AS "Account.Actor.Avatars.width", "Account->Actor->Avatars"."fileUrl" AS "Account.Actor.Avatars.fileUrl", "Account->Actor->Avatars"."onDisk" AS "Account.Actor.Avatars.onDisk", "Account->Actor->Avatars"."type" AS "Account.Actor.Avatars.type", "Account->Actor->Avatars"."actorId" AS "Account.Actor.Avatars.actorId", "Account->Actor->Avatars"."createdAt" AS "Account.Actor.Avatars.createdAt", "Account->Actor->Avatars"."updatedAt" AS "Account.Actor.Avatars.updatedAt" FROM (SELECT "UserModel"."id", "UserModel"."password", "UserModel"."username", "UserModel"."email", "UserModel"."pendingEmail", "UserModel"."emailVerified", "UserModel"."nsfwPolicy", "UserModel"."p2pEnabled", "UserModel"."videosHistoryEnabled", "UserModel"."autoPlayVideo", "UserModel"."autoPlayNextVideo", "UserModel"."autoPlayNextVideoPlaylist", "UserModel"."videoLanguages", "UserModel"."adminFlags", "UserModel"."blocked", "UserModel"."blockedReason", "UserModel"."role", "UserModel"."videoQuota", "UserModel"."videoQuotaDaily", "UserModel"."theme", "UserModel"."noInstanceConfigWarningModal", "UserModel"."noWelcomeModal", "UserModel"."noAccountSetupWarningModal", "UserModel"."pluginAuth", "UserModel"."feedToken", "UserModel"."lastLoginDate", "UserModel"."emailPublic", "UserModel"."otpSecret", "UserModel"."createdAt", "UserModel"."updatedAt", "Account"."id" AS "Account.id", "Account"."name" AS "Account.name", "Account"."description" AS "Account.description", "Account"."actorId" AS "Account.actorId", "Account"."userId" AS "Account.userId", "Account"."applicationId" AS "Account.applicationId", "Account"."createdAt" AS "Account.createdAt", "Account"."updatedAt" AS "Account.updatedAt", "Account->Actor"."id" AS "Account.Actor.id", "Account->Actor"."type" AS "Account.Actor.type", "Account->Actor"."preferredUsername" AS "Account.Actor.preferredUsername", "Account->Actor"."url" AS "Account.Actor.url", "Account->Actor"."publicKey" AS "Account.Actor.publicKey", "Account->Actor"."privateKey" AS "Account.Actor.privateKey", "Account->Actor"."followersCount" AS "Account.Actor.followersCount", "Account->Actor"."followingCount" AS "Account.Actor.followingCount", "Account->Actor"."inboxUrl" AS "Account.Actor.inboxUrl", "Account->Actor"."outboxUrl" AS "Account.Actor.outboxUrl", "Account->Actor"."sharedInboxUrl" AS "Account.Actor.sharedInboxUrl", "Account->Actor"."followersUrl" AS "Account.Actor.followersUrl", "Account->Actor"."followingUrl" AS "Account.Actor.followingUrl", "Account->Actor"."remoteCreatedAt" AS "Account.Actor.remoteCreatedAt", "Account->Actor"."serverId" AS "Account.Actor.serverId", "Account->Actor"."createdAt" AS "Account.Actor.createdAt", "Account->Actor"."updatedAt" AS "Account.Actor.updatedAt", "NotificationSetting"."id" AS "NotificationSetting.id", "NotificationSetting"."newVideoFromSubscription" AS "NotificationSetting.newVideoFromSubscription", "NotificationSetting"."newCommentOnMyVideo" AS "NotificationSetting.newCommentOnMyVideo", "NotificationSetting"."abuseAsModerator" AS "NotificationSetting.abuseAsModerator", "NotificationSetting"."videoAutoBlacklistAsModerator" AS "NotificationSetting.videoAutoBlacklistAsModerator", "NotificationSetting"."blacklistOnMyVideo" AS "NotificationSetting.blacklistOnMyVideo", "NotificationSetting"."myVideoPublished" AS "NotificationSetting.myVideoPublished", "NotificationSetting"."myVideoImportFinished" AS "NotificationSetting.myVideoImportFinished", "NotificationSetting"."newUserRegistration" AS "NotificationSetting.newUserRegistration", "NotificationSetting"."newInstanceFollower" AS "NotificationSetting.newInstanceFollower", "NotificationSetting"."autoInstanceFollowing" AS "NotificationSetting.autoInstanceFollowing", "NotificationSetting"."newFollow" AS "NotificationSetting.newFollow", "NotificationSetting"."commentMention" AS "NotificationSetting.commentMention", "NotificationSetting"."abuseStateChange" AS "NotificationSetting.abuseStateChange", "NotificationSetting"."abuseNewMessage" AS "NotificationSetting.abuseNewMessage", "NotificationSetting"."newPeerTubeVersion" AS "NotificationSetting.newPeerTubeVersion", "NotificationSetting"."newPluginVersion" AS "NotificationSetting.newPluginVersion", "NotificationSetting"."myVideoStudioEditionFinished" AS "NotificationSetting.myVideoStudioEditionFinished", "NotificationSetting"."userId" AS "NotificationSetting.userId", "NotificationSetting"."createdAt" AS "NotificationSetting.createdAt", "NotificationSetting"."updatedAt" AS "NotificationSetting.updatedAt" FROM "user" AS "UserModel" INNER JOIN "account" AS "Account" ON "UserModel"."id" = "Account"."userId" INNER JOIN "actor" AS "Account->Actor" ON "Account"."actorId" = "Account->Actor"."id" INNER JOIN "userNotificationSetting" AS "NotificationSetting" ON "UserModel"."id" = "NotificationSetting"."userId" WHERE (lower("username") = lower('root') OR "UserModel"."email" = 'root') LIMIT 1) AS "UserModel" LEFT OUTER JOIN "server" AS "Account->Actor->Server" ON "Account.Actor.serverId" = "Account->Actor->Server"."id" LEFT OUTER JOIN "actorImage" AS "Account->Actor->Avatars" ON "Account.Actor.id" = "Account->Actor->Avatars"."actorId" AND "Account->Actor->Avatars"."type" = 1;
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.278 debug: Saving token 8fe31f1d8deecc7c85f729bc9badf8d70c442f18 for client 1 and user 1.
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.281 debug: Executed SQL request - Executing (default): INSERT INTO "oAuthToken" ("id","accessToken","accessTokenExpiresAt","refreshToken","refreshTokenExpiresAt","authName","userId","oAuthClientId","createdAt","updatedAt") VALUES (DEFAULT,$1,$2,$3,$4,$5,$6,$7,$8,$9) RETURNING "id","accessToken","accessTokenExpiresAt","refreshToken","refreshTokenExpiresAt","authName","userId","oAuthClientId","createdAt","updatedAt";
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.286 debug: Executed SQL request - Executing (default): UPDATE "user" SET "lastLoginDate"=$1,"updatedAt"=$2 WHERE "id" = $3
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.288 info: 127.0.0.1 - - [04/Apr/2024:09:24:56 +0000] "POST /api/v1/users/token HTTP/1.1" 200 194 "-" "Go-http-client/1.1"
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.293 debug: Checking GET - /api/v1/accounts/root/video-channels parameters {
peertube-1  |   "body": {},
peertube-1  |   "params": {
peertube-1  |     "accountName": "root"
peertube-1  |   },
peertube-1  |   "query": {}
peertube-1  | }
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.296 debug: Executed SQL request - Executing (default): SELECT "AccountModel".*, "Actor->Server"."id" AS "Actor.Server.id", "Actor->Server"."host" AS "Actor.Server.host", "Actor->Server"."redundancyAllowed" AS "Actor.Server.redundancyAllowed", "Actor->Server"."createdAt" AS "Actor.Server.createdAt", "Actor->Server"."updatedAt" AS "Actor.Server.updatedAt", "Actor->Avatars"."id" AS "Actor.Avatars.id", "Actor->Avatars"."filename" AS "Actor.Avatars.filename", "Actor->Avatars"."height" AS "Actor.Avatars.height", "Actor->Avatars"."width" AS "Actor.Avatars.width", "Actor->Avatars"."fileUrl" AS "Actor.Avatars.fileUrl", "Actor->Avatars"."onDisk" AS "Actor.Avatars.onDisk", "Actor->Avatars"."type" AS "Actor.Avatars.type", "Actor->Avatars"."actorId" AS "Actor.Avatars.actorId", "Actor->Avatars"."createdAt" AS "Actor.Avatars.createdAt", "Actor->Avatars"."updatedAt" AS "Actor.Avatars.updatedAt" FROM (SELECT "AccountModel"."id", "AccountModel"."name", "AccountModel"."description", "AccountModel"."actorId", "AccountModel"."userId", "AccountModel"."applicationId", "AccountModel"."createdAt", "AccountModel"."updatedAt", "Actor"."id" AS "Actor.id", "Actor"."type" AS "Actor.type", "Actor"."preferredUsername" AS "Actor.preferredUsername", "Actor"."url" AS "Actor.url", "Actor"."publicKey" AS "Actor.publicKey", "Actor"."privateKey" AS "Actor.privateKey", "Actor"."followersCount" AS "Actor.followersCount", "Actor"."followingCount" AS "Actor.followingCount", "Actor"."inboxUrl" AS "Actor.inboxUrl", "Actor"."outboxUrl" AS "Actor.outboxUrl", "Actor"."sharedInboxUrl" AS "Actor.sharedInboxUrl", "Actor"."followersUrl" AS "Actor.followersUrl", "Actor"."followingUrl" AS "Actor.followingUrl", "Actor"."remoteCreatedAt" AS "Actor.remoteCreatedAt", "Actor"."serverId" AS "Actor.serverId", "Actor"."createdAt" AS "Actor.createdAt", "Actor"."updatedAt" AS "Actor.updatedAt" FROM "account" AS "AccountModel" INNER JOIN "actor" AS "Actor" ON "AccountModel"."actorId" = "Actor"."id" AND lower("preferredUsername") = 'root' WHERE ("AccountModel"."userId" IS NOT NULL OR "AccountModel"."applicationId" IS NOT NULL) LIMIT 1) AS "AccountModel" LEFT OUTER JOIN "server" AS "Actor->Server" ON "Actor.serverId" = "Actor->Server"."id" LEFT OUTER JOIN "actorImage" AS "Actor->Avatars" ON "Actor.id" = "Actor->Avatars"."actorId" AND "Actor->Avatars"."type" = 1;
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.298 debug: Checking GET - /api/v1/accounts/root/video-channels parameters {
peertube-1  |   "body": {},
peertube-1  |   "params": {
peertube-1  |     "accountName": "root"
peertube-1  |   },
peertube-1  |   "query": {}
peertube-1  | }
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.298 debug: Checking GET - /api/v1/accounts/root/video-channels parameters {
peertube-1  |   "body": {},
peertube-1  |   "params": {
peertube-1  |     "accountName": "root"
peertube-1  |   },
peertube-1  |   "query": {}
peertube-1  | }
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.298 debug: Checking GET - /api/v1/accounts/root/video-channels parameters {
peertube-1  |   "body": {},
peertube-1  |   "params": {
peertube-1  |     "accountName": "root"
peertube-1  |   },
peertube-1  |   "query": {}
peertube-1  | }
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.303 debug: Executed SQL request - Executing (default): SELECT count("VideoChannelModel"."id") AS "count" FROM "videoChannel" AS "VideoChannelModel" INNER JOIN "account" AS "Account" ON "VideoChannelModel"."accountId" = "Account"."id" AND "Account"."id" = 2;
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.303 debug: Executed SQL request - Executing (default): SELECT "VideoChannelModel".*, "Actor"."id" AS "Actor.id", "Actor"."type" AS "Actor.type", "Actor"."preferredUsername" AS "Actor.preferredUsername", "Actor"."url" AS "Actor.url", "Actor"."publicKey" AS "Actor.publicKey", "Actor"."privateKey" AS "Actor.privateKey", "Actor"."followersCount" AS "Actor.followersCount", "Actor"."followingCount" AS "Actor.followingCount", "Actor"."inboxUrl" AS "Actor.inboxUrl", "Actor"."outboxUrl" AS "Actor.outboxUrl", "Actor"."sharedInboxUrl" AS "Actor.sharedInboxUrl", "Actor"."followersUrl" AS "Actor.followersUrl", "Actor"."followingUrl" AS "Actor.followingUrl", "Actor"."remoteCreatedAt" AS "Actor.remoteCreatedAt", "Actor"."serverId" AS "Actor.serverId", "Actor"."createdAt" AS "Actor.createdAt", "Actor"."updatedAt" AS "Actor.updatedAt", "Actor->Server"."id" AS "Actor.Server.id", "Actor->Server"."host" AS "Actor.Server.host", "Actor->Server"."redundancyAllowed" AS "Actor.Server.redundancyAllowed", "Actor->Server"."createdAt" AS "Actor.Server.createdAt", "Actor->Server"."updatedAt" AS "Actor.Server.updatedAt", "Actor->Avatars"."id" AS "Actor.Avatars.id", "Actor->Avatars"."filename" AS "Actor.Avatars.filename", "Actor->Avatars"."height" AS "Actor.Avatars.height", "Actor->Avatars"."width" AS "Actor.Avatars.width", "Actor->Avatars"."fileUrl" AS "Actor.Avatars.fileUrl", "Actor->Avatars"."onDisk" AS "Actor.Avatars.onDisk", "Actor->Avatars"."type" AS "Actor.Avatars.type", "Actor->Avatars"."actorId" AS "Actor.Avatars.actorId", "Actor->Avatars"."createdAt" AS "Actor.Avatars.createdAt", "Actor->Avatars"."updatedAt" AS "Actor.Avatars.updatedAt", "Actor->Banners"."id" AS "Actor.Banners.id", "Actor->Banners"."filename" AS "Actor.Banners.filename", "Actor->Banners"."height" AS "Actor.Banners.height", "Actor->Banners"."width" AS "Actor.Banners.width", "Actor->Banners"."fileUrl" AS "Actor.Banners.fileUrl", "Actor->Banners"."onDisk" AS "Actor.Banners.onDisk", "Actor->Banners"."type" AS "Actor.Banners.type", "Actor->Banners"."actorId" AS "Actor.Banners.actorId", "Actor->Banners"."createdAt" AS "Actor.Banners.createdAt", "Actor->Banners"."updatedAt" AS "Actor.Banners.updatedAt", "Account->Actor->Server"."id" AS "Account.Actor.Server.id", "Account->Actor->Server"."host" AS "Account.Actor.Server.host", "Account->Actor->Server"."redundancyAllowed" AS "Account.Actor.Server.redundancyAllowed", "Account->Actor->Server"."createdAt" AS "Account.Actor.Server.createdAt", "Account->Actor->Server"."updatedAt" AS "Account.Actor.Server.updatedAt", "Account->Actor->Avatars"."id" AS "Account.Actor.Avatars.id", "Account->Actor->Avatars"."filename" AS "Account.Actor.Avatars.filename", "Account->Actor->Avatars"."height" AS "Account.Actor.Avatars.height", "Account->Actor->Avatars"."width" AS "Account.Actor.Avatars.width", "Account->Actor->Avatars"."fileUrl" AS "Account.Actor.Avatars.fileUrl", "Account->Actor->Avatars"."onDisk" AS "Account.Actor.Avatars.onDisk", "Account->Actor->Avatars"."type" AS "Account.Actor.Avatars.type", "Account->Actor->Avatars"."actorId" AS "Account.Actor.Avatars.actorId", "Account->Actor->Avatars"."createdAt" AS "Account.Actor.Avatars.createdAt", "Account->Actor->Avatars"."updatedAt" AS "Account.Actor.Avatars.updatedAt" FROM (SELECT "VideoChannelModel"."id", "VideoChannelModel"."name", "VideoChannelModel"."description", "VideoChannelModel"."support", "VideoChannelModel"."actorId", "VideoChannelModel"."accountId", "VideoChannelModel"."createdAt", "VideoChannelModel"."updatedAt", "Account"."id" AS "Account.id", "Account"."name" AS "Account.name", "Account"."description" AS "Account.description", "Account"."actorId" AS "Account.actorId", "Account"."userId" AS "Account.userId", "Account"."applicationId" AS "Account.applicationId", "Account"."createdAt" AS "Account.createdAt", "Account"."updatedAt" AS "Account.updatedAt", "Account->Actor"."id" AS "Account.Actor.id", "Account->Actor"."type" AS "Account.Actor.type", "Account->Actor"."preferredUsername" AS "Account.Actor.preferredUsername", "Account->Actor"."url" AS "Account.Actor.url", "Account->Actor"."publicKey" AS "Account.Actor.publicKey", "Account->Actor"."privateKey" AS "Account.Actor.privateKey", "Account->Actor"."followersCount" AS "Account.Actor.followersCount", "Account->Actor"."followingCount" AS "Account.Actor.followingCount", "Account->Actor"."inboxUrl" AS "Account.Actor.inboxUrl", "Account->Actor"."outboxUrl" AS "Account.Actor.outboxUrl", "Account->Actor"."sharedInboxUrl" AS "Account.Actor.sharedInboxUrl", "Account->Actor"."followersUrl" AS "Account.Actor.followersUrl", "Account->Actor"."followingUrl" AS "Account.Actor.followingUrl", "Account->Actor"."remoteCreatedAt" AS "Account.Actor.remoteCreatedAt", "Account->Actor"."serverId" AS "Account.Actor.serverId", "Account->Actor"."createdAt" AS "Account.Actor.createdAt", "Account->Actor"."updatedAt" AS "Account.Actor.updatedAt" FROM "videoChannel" AS "VideoChannelModel" INNER JOIN "account" AS "Account" ON "VideoChannelModel"."accountId" = "Account"."id" AND "Account"."id" = 2 INNER JOIN "actor" AS "Account->Actor" ON "Account"."actorId" = "Account->Actor"."id" ORDER BY "VideoChannelModel"."createdAt" DESC, "VideoChannelModel"."id" ASC LIMIT 15 OFFSET 0) AS "VideoChannelModel" LEFT OUTER JOIN "actor" AS "Actor" ON "VideoChannelModel"."actorId" = "Actor"."id" LEFT OUTER JOIN "server" AS "Actor->Server" ON "Actor"."serverId" = "Actor->Server"."id" LEFT OUTER JOIN "actorImage" AS "Actor->Avatars" ON "Actor"."id" = "Actor->Avatars"."actorId" AND "Actor->Avatars"."type" = 1 LEFT OUTER JOIN "actorImage" AS "Actor->Banners" ON "Actor"."id" = "Actor->Banners"."actorId" AND "Actor->Banners"."type" = 2 LEFT OUTER JOIN "server" AS "Account->Actor->Server" ON "Account.Actor.serverId" = "Account->Actor->Server"."id" LEFT OUTER JOIN "actorImage" AS "Account->Actor->Avatars" ON "Account.Actor.id" = "Account->Actor->Avatars"."actorId" AND "Account->Actor->Avatars"."type" = 1 ORDER BY "VideoChannelModel"."createdAt" DESC, "VideoChannelModel"."id" ASC;
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.311 info: 127.0.0.1 - - [04/Apr/2024:09:24:56 +0000] "GET /api/v1/accounts/root/video-channels HTTP/1.1" 200 717 "-" "Go-http-client/1.1"
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.315 debug: Getting access token.
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.318 debug: Executed SQL request - Executing (default): SELECT "OAuthTokenModel"."id", "OAuthTokenModel"."accessToken", "OAuthTokenModel"."accessTokenExpiresAt", "OAuthTokenModel"."refreshToken", "OAuthTokenModel"."refreshTokenExpiresAt", "OAuthTokenModel"."authName", "OAuthTokenModel"."userId", "OAuthTokenModel"."oAuthClientId", "OAuthTokenModel"."createdAt", "OAuthTokenModel"."updatedAt", "User"."id" AS "User.id", "User"."password" AS "User.password", "User"."username" AS "User.username", "User"."email" AS "User.email", "User"."pendingEmail" AS "User.pendingEmail", "User"."emailVerified" AS "User.emailVerified", "User"."nsfwPolicy" AS "User.nsfwPolicy", "User"."p2pEnabled" AS "User.p2pEnabled", "User"."videosHistoryEnabled" AS "User.videosHistoryEnabled", "User"."autoPlayVideo" AS "User.autoPlayVideo", "User"."autoPlayNextVideo" AS "User.autoPlayNextVideo", "User"."autoPlayNextVideoPlaylist" AS "User.autoPlayNextVideoPlaylist", "User"."videoLanguages" AS "User.videoLanguages", "User"."adminFlags" AS "User.adminFlags", "User"."blocked" AS "User.blocked", "User"."blockedReason" AS "User.blockedReason", "User"."role" AS "User.role", "User"."videoQuota" AS "User.videoQuota", "User"."videoQuotaDaily" AS "User.videoQuotaDaily", "User"."theme" AS "User.theme", "User"."noInstanceConfigWarningModal" AS "User.noInstanceConfigWarningModal", "User"."noWelcomeModal" AS "User.noWelcomeModal", "User"."noAccountSetupWarningModal" AS "User.noAccountSetupWarningModal", "User"."pluginAuth" AS "User.pluginAuth", "User"."feedToken" AS "User.feedToken", "User"."lastLoginDate" AS "User.lastLoginDate", "User"."emailPublic" AS "User.emailPublic", "User"."otpSecret" AS "User.otpSecret", "User"."createdAt" AS "User.createdAt", "User"."updatedAt" AS "User.updatedAt", "User->Account"."id" AS "User.Account.id", "User->Account->Actor"."id" AS "User.Account.Actor.id", "User->Account->Actor"."url" AS "User.Account.Actor.url" FROM "oAuthToken" AS "OAuthTokenModel" INNER JOIN "user" AS "User" ON "OAuthTokenModel"."userId" = "User"."id" INNER JOIN "account" AS "User->Account" ON "User"."id" = "User->Account"."userId" INNER JOIN "actor" AS "User->Account->Actor" ON "User->Account"."actorId" = "User->Account->Actor"."id" WHERE "OAuthTokenModel"."accessToken" = '8fe31f1d8deecc7c85f729bc9badf8d70c442f18' LIMIT 1;
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.329 debug: Checking videosAddResumableInitValidator parameters and headers {
peertube-1  |   "parameters": {
peertube-1  |     "channelId": 1,
peertube-1  |     "commentsEnabled": true,
peertube-1  |     "description": "some description",
peertube-1  |     "downloadEnabled": true,
peertube-1  |     "filename": "blank-5s.mp3",
peertube-1  |     "name": "with-thumbnail-9",
peertube-1  |     "previewfile": "iVBOR...",
peertube-1  |     "privacy": 1,
peertube-1  |     "scheduleUpdate": {
peertube-1  |       "updateAt": "2024-04-04T09:24:56.311942008Z",
peertube-1  |       "privacy": 1
peertube-1  |     },
peertube-1  |     "tags": [
peertube-1  |       "go",
peertube-1  |       "golang",
peertube-1  |       "peertube"
peertube-1  |     ],
peertube-1  |     "waitTranscoding": true
peertube-1  |   },
peertube-1  |   "headers": {
peertube-1  |     "host": "localhost:9000",
peertube-1  |     "user-agent": "Go-http-client/1.1",
peertube-1  |     "content-length": "32956",
peertube-1  |     "accept-encoding": "gzip",
peertube-1  |     "authorization": "Bearer 8fe31f1d8deecc7c85f729bc9badf8d70c442f18",
peertube-1  |     "content-type": "application/json",
peertube-1  |     "x-forwarded-for": "127.0.0.1",
peertube-1  |     "x-upload-content-length": "81128",
peertube-1  |     "x-upload-content-type": "audio/mpeg"
peertube-1  |   }
peertube-1  | }
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.333 debug: Executed SQL request - Executing (default): SELECT "VideoChannelModel"."id", "VideoChannelModel"."name", "VideoChannelModel"."description", "VideoChannelModel"."support", "VideoChannelModel"."actorId", "VideoChannelModel"."accountId", "VideoChannelModel"."createdAt", "VideoChannelModel"."updatedAt", "Actor"."id" AS "Actor.id", "Actor"."type" AS "Actor.type", "Actor"."preferredUsername" AS "Actor.preferredUsername", "Actor"."url" AS "Actor.url", "Actor"."publicKey" AS "Actor.publicKey", "Actor"."privateKey" AS "Actor.privateKey", "Actor"."followersCount" AS "Actor.followersCount", "Actor"."followingCount" AS "Actor.followingCount", "Actor"."inboxUrl" AS "Actor.inboxUrl", "Actor"."outboxUrl" AS "Actor.outboxUrl", "Actor"."sharedInboxUrl" AS "Actor.sharedInboxUrl", "Actor"."followersUrl" AS "Actor.followersUrl", "Actor"."followingUrl" AS "Actor.followingUrl", "Actor"."remoteCreatedAt" AS "Actor.remoteCreatedAt", "Actor"."serverId" AS "Actor.serverId", "Actor"."createdAt" AS "Actor.createdAt", "Actor"."updatedAt" AS "Actor.updatedAt", "Actor->Server"."id" AS "Actor.Server.id", "Actor->Server"."host" AS "Actor.Server.host", "Actor->Server"."redundancyAllowed" AS "Actor.Server.redundancyAllowed", "Actor->Server"."createdAt" AS "Actor.Server.createdAt", "Actor->Server"."updatedAt" AS "Actor.Server.updatedAt", "Actor->Avatars"."id" AS "Actor.Avatars.id", "Actor->Avatars"."filename" AS "Actor.Avatars.filename", "Actor->Avatars"."height" AS "Actor.Avatars.height", "Actor->Avatars"."width" AS "Actor.Avatars.width", "Actor->Avatars"."fileUrl" AS "Actor.Avatars.fileUrl", "Actor->Avatars"."onDisk" AS "Actor.Avatars.onDisk", "Actor->Avatars"."type" AS "Actor.Avatars.type", "Actor->Avatars"."actorId" AS "Actor.Avatars.actorId", "Actor->Avatars"."createdAt" AS "Actor.Avatars.createdAt", "Actor->Avatars"."updatedAt" AS "Actor.Avatars.updatedAt", "Actor->Banners"."id" AS "Actor.Banners.id", "Actor->Banners"."filename" AS "Actor.Banners.filename", "Actor->Banners"."height" AS "Actor.Banners.height", "Actor->Banners"."width" AS "Actor.Banners.width", "Actor->Banners"."fileUrl" AS "Actor.Banners.fileUrl", "Actor->Banners"."onDisk" AS "Actor.Banners.onDisk", "Actor->Banners"."type" AS "Actor.Banners.type", "Actor->Banners"."actorId" AS "Actor.Banners.actorId", "Actor->Banners"."createdAt" AS "Actor.Banners.createdAt", "Actor->Banners"."updatedAt" AS "Actor.Banners.updatedAt", "Account"."id" AS "Account.id", "Account"."name" AS "Account.name", "Account"."description" AS "Account.description", "Account"."actorId" AS "Account.actorId", "Account"."userId" AS "Account.userId", "Account"."applicationId" AS "Account.applicationId", "Account"."createdAt" AS "Account.createdAt", "Account"."updatedAt" AS "Account.updatedAt", "Account->Actor"."id" AS "Account.Actor.id", "Account->Actor"."type" AS "Account.Actor.type", "Account->Actor"."preferredUsername" AS "Account.Actor.preferredUsername", "Account->Actor"."url" AS "Account.Actor.url", "Account->Actor"."publicKey" AS "Account.Actor.publicKey", "Account->Actor"."privateKey" AS "Account.Actor.privateKey", "Account->Actor"."followersCount" AS "Account.Actor.followersCount", "Account->Actor"."followingCount" AS "Account.Actor.followingCount", "Account->Actor"."inboxUrl" AS "Account.Actor.inboxUrl", "Account->Actor"."outboxUrl" AS "Account.Actor.outboxUrl", "Account->Actor"."sharedInboxUrl" AS "Account.Actor.sharedInboxUrl", "Account->Actor"."followersUrl" AS "Account.Actor.followersUrl", "Account->Actor"."followingUrl" AS "Account.Actor.followingUrl", "Account->Actor"."remoteCreatedAt" AS "Account.Actor.remoteCreatedAt", "Account->Actor"."serverId" AS "Account.Actor.serverId", "Account->Actor"."createdAt" AS "Account.Actor.createdAt", "Account->Actor"."updatedAt" AS "Account.Actor.updatedAt", "Account->Actor->Server"."id" AS "Account.Actor.Server.id", "Account->Actor->Server"."host" AS "Account.Actor.Server.host", "Account->Actor->Server"."redundancyAllowed" AS "Account.Actor.Server.redundancyAllowed", "Account->Actor->Server"."createdAt" AS "Account.Actor.Server.createdAt", "Account->Actor->Server"."updatedAt" AS "Account.Actor.Server.updatedAt", "Account->Actor->Avatars"."id" AS "Account.Actor.Avatars.id", "Account->Actor->Avatars"."filename" AS "Account.Actor.Avatars.filename", "Account->Actor->Avatars"."height" AS "Account.Actor.Avatars.height", "Account->Actor->Avatars"."width" AS "Account.Actor.Avatars.width", "Account->Actor->Avatars"."fileUrl" AS "Account.Actor.Avatars.fileUrl", "Account->Actor->Avatars"."onDisk" AS "Account.Actor.Avatars.onDisk", "Account->Actor->Avatars"."type" AS "Account.Actor.Avatars.type", "Account->Actor->Avatars"."actorId" AS "Account.Actor.Avatars.actorId", "Account->Actor->Avatars"."createdAt" AS "Account.Actor.Avatars.createdAt", "Account->Actor->Avatars"."updatedAt" AS "Account.Actor.Avatars.updatedAt" FROM "videoChannel" AS "VideoChannelModel" LEFT OUTER JOIN "actor" AS "Actor" ON "VideoChannelModel"."actorId" = "Actor"."id" LEFT OUTER JOIN "server" AS "Actor->Server" ON "Actor"."serverId" = "Actor->Server"."id" LEFT OUTER JOIN "actorImage" AS "Actor->Avatars" ON "Actor"."id" = "Actor->Avatars"."actorId" AND "Actor->Avatars"."type" = 1 LEFT OUTER JOIN "actorImage" AS "Actor->Banners" ON "Actor"."id" = "Actor->Banners"."actorId" AND "Actor->Banners"."type" = 2 INNER JOIN "account" AS "Account" ON "VideoChannelModel"."accountId" = "Account"."id" INNER JOIN "actor" AS "Account->Actor" ON "Account"."actorId" = "Account->Actor"."id" LEFT OUTER JOIN "server" AS "Account->Actor->Server" ON "Account->Actor"."serverId" = "Account->Actor->Server"."id" LEFT OUTER JOIN "actorImage" AS "Account->Actor->Avatars" ON "Account->Actor"."id" = "Account->Actor->Avatars"."actorId" AND "Account->Actor->Avatars"."type" = 1 WHERE "VideoChannelModel"."id" = 1;
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.340 debug: Executed SQL request - Executing (default): SELECT "id", "password", "username", "email", "pendingEmail", "emailVerified", "nsfwPolicy", "p2pEnabled", "videosHistoryEnabled", "autoPlayVideo", "autoPlayNextVideo", "autoPlayNextVideoPlaylist", "videoLanguages", "adminFlags", "blocked", "blockedReason", "role", "videoQuota", "videoQuotaDaily", "theme", "noInstanceConfigWarningModal", "noWelcomeModal", "noAccountSetupWarningModal", "pluginAuth", "feedToken", "lastLoginDate", "emailPublic", "otpSecret", "createdAt", "updatedAt" FROM "user" AS "UserModel" WHERE "UserModel"."id" = 1;
peertube-1  | [localhost:9000 uploadx] 2024-04-04 09:24:56.341 debug: [request]: POST /upload-resumable
peertube-1  | [localhost:9000 uploadx] 2024-04-04 09:24:56.344 debug: [created]: 1-af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-9a28382d30cdf241.mp3: 0/81128
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.344 info: 127.0.0.1 - - [04/Apr/2024:09:24:56 +0000] "POST /api/v1/videos/upload-resumable HTTP/1.1" 201 - "-" "Go-http-client/1.1"
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.349 debug: Getting access token.
peertube-1  | [localhost:9000 uploadx] 2024-04-04 09:24:56.350 debug: [request]: PUT /upload-resumable?upload_id=af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-9a28382d30cdf241
peertube-1  | [localhost:9000 uploadx] 2024-04-04 09:24:56.355 debug: [part]: 1-af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-9a28382d30cdf241.mp3: 20480/81128
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.355 info: 127.0.0.1 - - [04/Apr/2024:09:24:56 +0000] "PUT /api/v1/videos/upload-resumable?upload_id=af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-9a28382d30cdf241 HTTP/1.1" 308 - "-" "Go-http-client/1.1"
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.357 debug: Getting access token.
peertube-1  | [localhost:9000 uploadx] 2024-04-04 09:24:56.357 debug: [request]: PUT /upload-resumable?upload_id=af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-9a28382d30cdf241
peertube-1  | [localhost:9000 uploadx] 2024-04-04 09:24:56.359 debug: [part]: 1-af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-9a28382d30cdf241.mp3: 40960/81128
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.359 info: 127.0.0.1 - - [04/Apr/2024:09:24:56 +0000] "PUT /api/v1/videos/upload-resumable?upload_id=af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-9a28382d30cdf241 HTTP/1.1" 308 - "-" "Go-http-client/1.1"
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.360 debug: Getting access token.
peertube-1  | [localhost:9000 uploadx] 2024-04-04 09:24:56.361 debug: [request]: PUT /upload-resumable?upload_id=af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-9a28382d30cdf241
peertube-1  | [localhost:9000 uploadx] 2024-04-04 09:24:56.362 debug: [part]: 1-af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-9a28382d30cdf241.mp3: 61440/81128
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.362 info: 127.0.0.1 - - [04/Apr/2024:09:24:56 +0000] "PUT /api/v1/videos/upload-resumable?upload_id=af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-9a28382d30cdf241 HTTP/1.1" 308 - "-" "Go-http-client/1.1"
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.363 debug: Getting access token.
peertube-1  | [localhost:9000 uploadx] 2024-04-04 09:24:56.363 debug: [request]: PUT /upload-resumable?upload_id=af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-9a28382d30cdf241
peertube-1  | [localhost:9000 uploadx] 2024-04-04 09:24:56.365 debug: [completed]: 1-af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-9a28382d30cdf241.mp3: 81128/81128
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.370 debug: Executed SQL request - Executing (default): SELECT "VideoChannelModel"."id", "VideoChannelModel"."name", "VideoChannelModel"."description", "VideoChannelModel"."support", "VideoChannelModel"."actorId", "VideoChannelModel"."accountId", "VideoChannelModel"."createdAt", "VideoChannelModel"."updatedAt", "Actor"."id" AS "Actor.id", "Actor"."type" AS "Actor.type", "Actor"."preferredUsername" AS "Actor.preferredUsername", "Actor"."url" AS "Actor.url", "Actor"."publicKey" AS "Actor.publicKey", "Actor"."privateKey" AS "Actor.privateKey", "Actor"."followersCount" AS "Actor.followersCount", "Actor"."followingCount" AS "Actor.followingCount", "Actor"."inboxUrl" AS "Actor.inboxUrl", "Actor"."outboxUrl" AS "Actor.outboxUrl", "Actor"."sharedInboxUrl" AS "Actor.sharedInboxUrl", "Actor"."followersUrl" AS "Actor.followersUrl", "Actor"."followingUrl" AS "Actor.followingUrl", "Actor"."remoteCreatedAt" AS "Actor.remoteCreatedAt", "Actor"."serverId" AS "Actor.serverId", "Actor"."createdAt" AS "Actor.createdAt", "Actor"."updatedAt" AS "Actor.updatedAt", "Actor->Server"."id" AS "Actor.Server.id", "Actor->Server"."host" AS "Actor.Server.host", "Actor->Server"."redundancyAllowed" AS "Actor.Server.redundancyAllowed", "Actor->Server"."createdAt" AS "Actor.Server.createdAt", "Actor->Server"."updatedAt" AS "Actor.Server.updatedAt", "Actor->Avatars"."id" AS "Actor.Avatars.id", "Actor->Avatars"."filename" AS "Actor.Avatars.filename", "Actor->Avatars"."height" AS "Actor.Avatars.height", "Actor->Avatars"."width" AS "Actor.Avatars.width", "Actor->Avatars"."fileUrl" AS "Actor.Avatars.fileUrl", "Actor->Avatars"."onDisk" AS "Actor.Avatars.onDisk", "Actor->Avatars"."type" AS "Actor.Avatars.type", "Actor->Avatars"."actorId" AS "Actor.Avatars.actorId", "Actor->Avatars"."createdAt" AS "Actor.Avatars.createdAt", "Actor->Avatars"."updatedAt" AS "Actor.Avatars.updatedAt", "Actor->Banners"."id" AS "Actor.Banners.id", "Actor->Banners"."filename" AS "Actor.Banners.filename", "Actor->Banners"."height" AS "Actor.Banners.height", "Actor->Banners"."width" AS "Actor.Banners.width", "Actor->Banners"."fileUrl" AS "Actor.Banners.fileUrl", "Actor->Banners"."onDisk" AS "Actor.Banners.onDisk", "Actor->Banners"."type" AS "Actor.Banners.type", "Actor->Banners"."actorId" AS "Actor.Banners.actorId", "Actor->Banners"."createdAt" AS "Actor.Banners.createdAt", "Actor->Banners"."updatedAt" AS "Actor.Banners.updatedAt", "Account"."id" AS "Account.id", "Account"."name" AS "Account.name", "Account"."description" AS "Account.description", "Account"."actorId" AS "Account.actorId", "Account"."userId" AS "Account.userId", "Account"."applicationId" AS "Account.applicationId", "Account"."createdAt" AS "Account.createdAt", "Account"."updatedAt" AS "Account.updatedAt", "Account->Actor"."id" AS "Account.Actor.id", "Account->Actor"."type" AS "Account.Actor.type", "Account->Actor"."preferredUsername" AS "Account.Actor.preferredUsername", "Account->Actor"."url" AS "Account.Actor.url", "Account->Actor"."publicKey" AS "Account.Actor.publicKey", "Account->Actor"."privateKey" AS "Account.Actor.privateKey", "Account->Actor"."followersCount" AS "Account.Actor.followersCount", "Account->Actor"."followingCount" AS "Account.Actor.followingCount", "Account->Actor"."inboxUrl" AS "Account.Actor.inboxUrl", "Account->Actor"."outboxUrl" AS "Account.Actor.outboxUrl", "Account->Actor"."sharedInboxUrl" AS "Account.Actor.sharedInboxUrl", "Account->Actor"."followersUrl" AS "Account.Actor.followersUrl", "Account->Actor"."followingUrl" AS "Account.Actor.followingUrl", "Account->Actor"."remoteCreatedAt" AS "Account.Actor.remoteCreatedAt", "Account->Actor"."serverId" AS "Account.Actor.serverId", "Account->Actor"."createdAt" AS "Account.Actor.createdAt", "Account->Actor"."updatedAt" AS "Account.Actor.updatedAt", "Account->Actor->Server"."id" AS "Account.Actor.Server.id", "Account->Actor->Server"."host" AS "Account.Actor.Server.host", "Account->Actor->Server"."redundancyAllowed" AS "Account.Actor.Server.redundancyAllowed", "Account->Actor->Server"."createdAt" AS "Account.Actor.Server.createdAt", "Account->Actor->Server"."updatedAt" AS "Account.Actor.Server.updatedAt", "Account->Actor->Avatars"."id" AS "Account.Actor.Avatars.id", "Account->Actor->Avatars"."filename" AS "Account.Actor.Avatars.filename", "Account->Actor->Avatars"."height" AS "Account.Actor.Avatars.height", "Account->Actor->Avatars"."width" AS "Account.Actor.Avatars.width", "Account->Actor->Avatars"."fileUrl" AS "Account.Actor.Avatars.fileUrl", "Account->Actor->Avatars"."onDisk" AS "Account.Actor.Avatars.onDisk", "Account->Actor->Avatars"."type" AS "Account.Actor.Avatars.type", "Account->Actor->Avatars"."actorId" AS "Account.Actor.Avatars.actorId", "Account->Actor->Avatars"."createdAt" AS "Account.Actor.Avatars.createdAt", "Account->Actor->Avatars"."updatedAt" AS "Account.Actor.Avatars.updatedAt" FROM "videoChannel" AS "VideoChannelModel" LEFT OUTER JOIN "actor" AS "Actor" ON "VideoChannelModel"."actorId" = "Actor"."id" LEFT OUTER JOIN "server" AS "Actor->Server" ON "Actor"."serverId" = "Actor->Server"."id" LEFT OUTER JOIN "actorImage" AS "Actor->Avatars" ON "Actor"."id" = "Actor->Avatars"."actorId" AND "Actor->Avatars"."type" = 1 LEFT OUTER JOIN "actorImage" AS "Actor->Banners" ON "Actor"."id" = "Actor->Banners"."actorId" AND "Actor->Banners"."type" = 2 INNER JOIN "account" AS "Account" ON "VideoChannelModel"."accountId" = "Account"."id" INNER JOIN "actor" AS "Account->Actor" ON "Account"."actorId" = "Account->Actor"."id" LEFT OUTER JOIN "server" AS "Account->Actor->Server" ON "Account->Actor"."serverId" = "Account->Actor->Server"."id" LEFT OUTER JOIN "actorImage" AS "Account->Actor->Avatars" ON "Account->Actor"."id" = "Account->Actor->Avatars"."actorId" AND "Account->Actor->Avatars"."type" = 1 WHERE "VideoChannelModel"."id" = 1;
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.515 debug: Got 0 chapters from video "with-thumbnail-9" container {
peertube-1  |   "containerChapters": []
peertube-1  | }
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.827 error: Error in controller. {
peertube-1  |   "err": "TypeError [ERR_INVALID_ARG_TYPE]: The \"path\" argument must be of type string. Received undefined\n    at new NodeError (node:internal/errors:405:5)\n    at validateString (node:internal/validators:162:11)\n    at extname (node:path:1385:5)\n    at getLowercaseExtension (file:///app/packages/node-utils/dist/path.js:33:17)\n    at processImage (file:///app/dist/core/helpers/image-utils.js:12:23)\n    at /app/node_modules/piscina/dist/src/worker.js:141:32"
peertube-1  | }
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.827 debug: Bad HTTP request. {
peertube-1  |   "json": {
peertube-1  |     "type": "https://docs.joinpeertube.org/api-rest-reference.html#section/Errors/TypeError",
peertube-1  |     "detail": "The \"path\" argument must be of type string. Received undefined",
peertube-1  |     "status": 500,
peertube-1  |     "docs": "https://docs.joinpeertube.org/api-rest-reference.html#operation/uploadResumable",
peertube-1  |     "code": "TypeError",
peertube-1  |     "error": "The \"path\" argument must be of type string. Received undefined"
peertube-1  |   }
peertube-1  | }
peertube-1  | [localhost:9000] 2024-04-04 09:24:56.828 info: 127.0.0.1 - - [04/Apr/2024:09:24:56 +0000] "PUT /api/v1/videos/upload-resumable?upload_id=af63ac4c86019afc-75d8ca9233872e02-868bbb0dfecd79bb-9a28382d30cdf241 HTTP/1.1" 500 361 "-" "Go-http-client/1.1"
peertube-1  | [localhost:9000] 2024-04-04 09:25:16.273 debug: Executed SQL request - Executing (default): SELECT "id", "uuid", "type", "payload", "privatePayload", "state", "failures", "error", "priority", "processingJobToken", "progress", "startedAt", "finishedAt", "dependsOnRunnerJobId", "runnerId", "createdAt", "updatedAt" FROM "runnerJob" AS "RunnerJobModel" WHERE "RunnerJobModel"."type" IN ('vod-audio-merge-transcoding', 'vod-hls-transcoding', 'vod-web-video-transcoding') AND "RunnerJobModel"."state" = 2 AND "RunnerJobModel"."updatedAt" < '2024-04-04 09:23:16.262 +00:00';
peertube-1  | [localhost:9000] 2024-04-04 09:25:16.278 debug: Executed SQL request - Executing (default): SELECT "id", "uuid", "type", "payload", "privatePayload", "state", "failures", "error", "priority", "processingJobToken", "progress", "startedAt", "finishedAt", "dependsOnRunnerJobId", "runnerId", "createdAt", "updatedAt" FROM "runnerJob" AS "RunnerJobModel" WHERE "RunnerJobModel"."type" IN ('live-rtmp-hls-transcoding') AND "RunnerJobModel"."state" = 2 AND "RunnerJobModel"."updatedAt" < '2024-04-04 09:24:46.277 +00:00';


interestingly, when using the web interface (and interposing a proxy to dump and save the requests/responses flying around), I see the following request:

POST /api/v1/videos/upload-resumable HTTP/1.1
Host: localhost:9000
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.5
Authorization: Bearer XXX-s3cr3t-XXX
Cache-Control: no-cache
Content-Length: 20953
Content-Type: multipart/form-data; boundary=---------------------------179385139242534464011223794632
Cookie: cc={%22disagreement%22:[%22ad%22%2C%22visit%22]%2C%22creation%22:1706631674396%2C%22update%22:1706631675927}; pa_priva
cy=%22optin%22; lmd_cap=_e8o9vcp37; pa_user=%7B%22id%22%3A%2229715358%22%2C%22category%22%3A%22ESS%22%7D; VGOGH_TOKEN=73cf093e
-3b2c-80c7-8abf-e70a243dc78d; has_js=1; lmd_onboarding_favorite=%7B%22visitsNumber%22%3A13%2C%22step%22%3A%22step_one%22%2C%22
lastDisplayed%22%3A%222024-02-02T14%3A51%3A05.391Z%22%2C%22resetDone%22%3Afalse%7D
Origin: http://localhost:9191
Pragma: no-cache
Referer: http://localhost:9191/videos/upload
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0
X-Forwarded-For: 127.0.0.1
X-Upload-Content-Length: 81128
X-Upload-Content-Type: audio/mpeg

-----------------------------179385139242534464011223794632
Content-Disposition: form-data; name="name"

blank-5s
-----------------------------179385139242534464011223794632
Content-Disposition: form-data; name="mimeType"

audio/mpeg
-----------------------------179385139242534464011223794632
Content-Disposition: form-data; name="size"

81128
-----------------------------179385139242534464011223794632
Content-Disposition: form-data; name="lastModified"

1712067145457
-----------------------------179385139242534464011223794632
Content-Disposition: form-data; name="waitTranscoding"

true
-----------------------------179385139242534464011223794632
Content-Disposition: form-data; name="channelId"

1
-----------------------------179385139242534464011223794632
Content-Disposition: form-data; name="nsfw"

false
-----------------------------179385139242534464011223794632
Content-Disposition: form-data; name="privacy"

3
-----------------------------179385139242534464011223794632
Content-Disposition: form-data; name="filename"

blank-5s.mp3
-----------------------------179385139242534464011223794632
Content-Disposition: form-data; name="previewfile"; filename="peertube-logo.png"
Content-Type: image/png

<89>PNG
^Z
^@^@^@^MIHDR [...]

-----------------------------179385139242534464011223794632
Content-Disposition: form-data; name="thumbnailfile"; filename="peertube-logo.png"
Content-Type: image/png

<89>PNG
^Z
^@^@^@^MIHDR [...]

-----------------------------179385139242534464011223794632--

and the following reply:

HTTP/1.1 200 OK
Content-Length: 1112
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Retry-After,Location,Range
Cache-Control: no-store
Connection: keep-alive
Content-Type: application/json
Date: Thu, 04 Apr 2024 12:06:03 GMT
Keep-Alive: timeout=5
Location: //localhost:9000/api/v1/videos/upload-resumable?upload_id=af63ac4c86019afc-c70420ad4ee57890-868bbb0dfecd79bb-9d62b445a7daccdd
Range: bytes=0-81127
Tk: N
X-Frame-Options: DENY
X-Powered-By: PeerTube
X-Ratelimit-Limit: 50
X-Ratelimit-Remaining: 49
X-Ratelimit-Reset: 1712232374

{
  "bytesWritten": 81128,
  "name": "1-af63ac4c86019afc-c70420ad4ee57890-868bbb0dfecd79bb-9d62b445a7daccdd.mp3",
  "metadata": {
    "name": "blank-5s",
    "mimeType": "audio/mpeg",
    "size": "81128",
    "lastModified": "1712067145457",
    "waitTranscoding": true,
    "channelId": 1,
    "nsfw": false,
    "privacy": 3,
    "filename": "blank-5s.mp3",
    "previewfile": [
      {
        "fieldname": "previewfile",
        "originalname": "peertube-logo.png",
        "encoding": "7bit",
        "mimetype": "image/png",
        "destination": "/data/tmp/resumable-uploads",
        "filename": "e9267d0a26eb0dfa6ad115fc6129ec1a.png",
        "path": "/data/tmp/resumable-uploads/e9267d0a26eb0dfa6ad115fc6129ec1a.png",
        "size": 9730
      }
    ],
    "thumbnailfile": [
      {
        "fieldname": "thumbnailfile",
        "originalname": "peertube-logo.png",
        "encoding": "7bit",
        "mimetype": "image/png",
        "destination": "/data/tmp/resumable-uploads",
        "filename": "dd5cb79cdc8b40bd2566845d1174dab2.png",
        "path": "/data/tmp/resumable-uploads/dd5cb79cdc8b40bd2566845d1174dab2.png",
        "size": 9730
      }
    ]
  },
  "originalName": "blank-5s",
  "contentType": "audio/mpeg",
  "size": 81128,
  "userId": "1",
  "id": "af63ac4c86019afc-c70420ad4ee57890-868bbb0dfecd79bb-9d62b445a7daccdd",
  "status": "completed",
  "createdAt": "2024-04-04T12:06:03.038Z"
}

on the peertube server, I get:

peertube-1  | [localhost:9000] 2024-04-04 12:06:03.009 debug: Getting access token.
peertube-1  | [localhost:9000] 2024-04-04 12:06:03.017 debug: Checking videosAddResumableInitValidator parameters and headers {
peertube-1  |   "parameters": {
peertube-1  |     "name": "blank-5s",
peertube-1  |     "mimeType": "audio/mpeg",
peertube-1  |     "size": "81128",
peertube-1  |     "lastModified": "1712067145457",
peertube-1  |     "waitTranscoding": true,
peertube-1  |     "channelId": 1,
peertube-1  |     "nsfw": false,
peertube-1  |     "privacy": 3,
peertube-1  |     "filename": "blank-5s.mp3"
peertube-1  |   },
peertube-1  |   "headers": {
peertube-1  |     "host": "localhost:9000",
peertube-1  |     "user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0",
peertube-1  |     "content-length": "20953",
peertube-1  |     "accept": "*/*",
peertube-1  |     "accept-encoding": "gzip, deflate, br",
peertube-1  |     "accept-language": "en-US,en;q=0.5",
peertube-1  |     "authorization": "Bearer b58b664b91dab9a3e5485ec5972a0fbfe5f1c156",
peertube-1  |     "cache-control": "no-cache",
peertube-1  |     "content-type": "multipart/form-data; boundary=---------------------------179385139242534464011223794632",
peertube-1  |     "cookie": "cc={%22disagreement%22:[%22ad%22%2C%22visit%22]%2C%22creation%22:1706631674396%2C%22update%22:1706631675927}; pa_privacy=%22optin%22; lmd_cap=_e8o9vcp37; pa_user=%7B%22id%22%3A%2229715358%22%2C%22category%22%3A%22ESS%22%7D; VGOGH_TOKEN=73cf093e-3b2c-80c7-8abf-e70a243dc78d; has_js=1; lmd_onboarding_favorite=%7B%22visitsNumber%22%3A13%2C%22step%22%3A%22step_one%22%2C%22lastDisplayed%22%3A%222024-02-02T14%3A51%3A05.391Z%22%2C%22resetDone%22%3Afalse%7D",
peertube-1  |     "origin": "http://localhost:9191",
peertube-1  |     "pragma": "no-cache",
peertube-1  |     "referer": "http://localhost:9191/videos/upload",
peertube-1  |     "sec-fetch-dest": "empty",
peertube-1  |     "sec-fetch-mode": "cors",
peertube-1  |     "sec-fetch-site": "same-origin",
peertube-1  |     "x-forwarded-for": "127.0.0.1",
peertube-1  |     "x-upload-content-length": "81128",
peertube-1  |     "x-upload-content-type": "audio/mpeg"
peertube-1  |   },
peertube-1  |   "files": {
peertube-1  |     "previewfile": [
peertube-1  |       {
peertube-1  |         "fieldname": "previewfile",
peertube-1  |         "originalname": "peertube-logo.png",
peertube-1  |         "encoding": "7bit",
peertube-1  |         "mimetype": "image/png",
peertube-1  |         "destination": "/data/tmp/resumable-uploads",
peertube-1  |         "filename": "e9267d0a26eb0dfa6ad115fc6129ec1a.png",
peertube-1  |         "path": "/data/tmp/resumable-uploads/e9267d0a26eb0dfa6ad115fc6129ec1a.png",
peertube-1  |         "size": 9730
peertube-1  |       }
peertube-1  |     ],
peertube-1  |     "thumbnailfile": [
peertube-1  |       {
peertube-1  |         "fieldname": "thumbnailfile",
peertube-1  |         "originalname": "peertube-logo.png",
peertube-1  |         "encoding": "7bit",
peertube-1  |         "mimetype": "image/png",
peertube-1  |         "destination": "/data/tmp/resumable-uploads",
peertube-1  |         "filename": "dd5cb79cdc8b40bd2566845d1174dab2.png",
peertube-1  |         "path": "/data/tmp/resumable-uploads/dd5cb79cdc8b40bd2566845d1174dab2.png",
peertube-1  |         "size": 9730
peertube-1  |       }
peertube-1  |     ]
peertube-1  |   }
peertube-1  | }

ie: there are additional fields in the JSON payload as displayed on the server side ("files" with "previewfile" and "thumbnailfile", with that pesky "path" key).

would this imply that the REST API as documented isn’t completely accurate in requesting an application/json as "Content-Type"? (should we send a multipart instead, like /videos/upload « legacy » does ?)

You can use application/json if you don’t send files (thumbnail/preview). But if you send them, you have to send a multipart/form-data request

ok, thanks.

I may very well have missed where this is spelled out, but this could perhaps be clarified on the REST API documentation :}

so, if I send a thumbnail/preview file, I can send the same multipart/form-data payload than for the legacy case, except for the "videofile" field that becomes "filename".

anything else that should be modified ?