Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support async media upload #37

Merged
merged 2 commits into from
Mar 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 11 additions & 15 deletions bin/tsl-mastodon.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const HELP = [
' --api [str] API address of the Mastodon server.',
' --api2 [str] API v2 address of the Mastodon server.',
' --help -h Show this help.',
' --image [str] Image path.',
' --media [str] Media path.',
' --text [str] Text string.',
' --token [hex] API token.',
' --version -v Show the version of tsl-mastodon.',
Expand Down Expand Up @@ -138,31 +138,27 @@ async function post (
/** @type {Mastodon.JSON.StatusPost} */
let status;

if ( args.image ) {
const images = (
args.image instanceof Array ?
args.image :
[args.image]
if ( args.media ) {
const mediums = (
args.media instanceof Array ?
args.media :
[args.media]
);
/** @type {Mastodon.JSON.MediaStatusPost} */
const post = {
media_ids: []
};

for ( const image of images ) {
const file = await Mastodon.Utilities.fileFrom( '' + image );
for ( const media of mediums ) {
const file = await Mastodon.Utilities.fileFrom( '' + media );

console.log( file.name, file.size + ' bytes' );

let attachment = await api2.postMediaAttachment( {
const result = await api2.postMediaAttachment( {
file
} );
}, true );

if ( !attachment.json ) {
throw attachment;
}

post.media_ids.push( attachment.json.id );
post.media_ids.push( result.json.id );
}

if ( args.text ) {
Expand Down
68 changes: 58 additions & 10 deletions src/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export class API {


/**
* Expected communication delay by the Mastodon server.
* Expected communication delay (in milliseconds) by the Mastodon server.
*/
public nextDelay: number;

Expand Down Expand Up @@ -101,14 +101,22 @@ export class API {


/**
* Delays an async promise by the expected amount of time, which the
* Mastodon server sends during the last communication.
* Delays an async promise by the expected amount of time (in milliseconds),
* which the Mastodon server sends during the last communication.
*
* @param forcedDelay
* Forces a certain amount of minimum delay.
*
* @return
* Promise.
*/
public async delay (): Promise<void> {
return new Promise( resolve => setTimeout( resolve, this.nextDelay ) );
public async delay (
forcedDelay?: number
): Promise<void> {
return new Promise( resolve => setTimeout(
resolve,
Math.max( ( forcedDelay || 0 ), this.nextDelay )
) );
}


Expand Down Expand Up @@ -453,13 +461,37 @@ export class API {
* @param mediaAttachmentID
* ID of the media attachment to get.
*
* @param awaitProcessing
* Set to true, to wait until server processing completed.
*
* @return
* Promise with the media attachment, if successful.
*/
public async getMediaAttachment (
mediaAttachmentID: string
mediaAttachmentID: string,
awaitProcessing?: boolean
): Promise<API.Success<JSON.MediaAttachment>> {
const result = await this.get( `media/${mediaAttachmentID}` );
const path = (
awaitProcessing ?
`../v1/media/${mediaAttachmentID}` :
`media/${mediaAttachmentID}`
);

let result = await this.get( path );

// Check status of media processing
while (
awaitProcessing &&
result.status === 206
) {
await this.delay( 10000 );

result = await this.get( path );

if ( result.error ) {
throw result;
}
}

if (
result.error ||
Expand Down Expand Up @@ -876,13 +908,29 @@ export class API {
* @param mediaAttachment
* Media attachment to post.
*
* @param awaitProcessing
* Set to true, to wait until server processing completed.
*
* @return
* Promise with the media attachment, if successful.
*/
public async postMediaAttachment (
mediaAttachment: JSON.MediaAttachmentPost
mediaAttachment: JSON.MediaAttachmentPost,
awaitProcessing?: boolean
): Promise<API.Success<JSON.MediaAttachment>> {
const result = await this.post( 'media', mediaAttachment );
const path = (
awaitProcessing ?
'../v2/media' :
'media'
);
const result = await this.post( path, mediaAttachment );

if (
awaitProcessing &&
result.status === 202
) {
return await this.getMediaAttachment( result.json.id, awaitProcessing );
}

if (
result.error ||
Expand All @@ -892,7 +940,7 @@ export class API {
) ||
!JSON.isMediaAttachment( result.json )
) {
result.error = result.error || new Error();
result.error = ( result.error || new Error() );

throw result;
}
Expand Down
30 changes: 10 additions & 20 deletions src/JSON/MediaAttachment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,18 +405,18 @@ export interface VideoAttachment {
* @since 1.5.0
*/
export interface VideoAttachmentMeta {
aspect: number;
audio_bitrate: string;
audio_channels: string;
audio_encode: string;
duration: number;
fps: number;
height: number;
length: string;
aspect?: number;
audio_bitrate?: string;
audio_channels?: string;
audio_encode?: string;
duration?: number;
fps?: number;
height?: number;
length?: string;
original: VideoAttachmentMetaOriginal;
size: string;
size?: string;
small: VideoAttachmentMetaSmall;
width: number;
width?: number;
}

/**
Expand Down Expand Up @@ -687,16 +687,6 @@ export function isVideoAttachmentMeta (
): json is VideoAttachmentMeta {
return (
typeof json === 'object' &&
typeof json.aspect === 'number' &&
typeof json.audio_bitrate === 'string' &&
typeof json.audio_channels === 'string' &&
typeof json.audio_encode === 'string' &&
typeof json.duration === 'number' &&
typeof json.fps === 'number' &&
typeof json.height === 'number' &&
typeof json.length === 'string' &&
typeof json.size === 'string' &&
typeof json.width === 'number' &&
typeof json.original === 'object' &&
typeof json.original.bitrate === 'number' &&
typeof json.original.duration === 'number' &&
Expand Down
Loading