Module: X::Uploader::MediaUpload
- Extended by:
- MediaUpload
- Included in:
- MediaUpload
- Defined in:
- x-uploader/lib/x/uploader/media_upload.rb
Overview
Uploads media files to the X API
Its methods can be called on the module, or on an instance of a class that includes it, which gains its public methods alone: what they call belongs to modules of its own, or is called on the module, as upload calls await_processing, so no method the class defines, under any name, can change an upload.
Constant Summary collapse
- DEFAULT_PROCESSING_TIMEOUT =
Default number of seconds await_processing waits for processing to finish before it gives up
600- DEFAULT_CONCURRENCY =
Default number of chunks uploaded at once
4- DEFAULT_CHUNK_SIZE =
Default number of bytes in each chunk of an upload in chunks, 4 megabytes, unless the media needs larger ones to fit the segments the API numbers
Validator::DEFAULT_CHUNK
- MAX_CONCURRENCY =
Greatest number of chunks uploaded at once, each of which holds a chunk of up to 5 megabytes and a connection
Validator::MAX_CONCURRENCY
Class Method Summary collapse
-
.await_processing(media, client:, processing_timeout: DEFAULT_PROCESSING_TIMEOUT) ⇒ UploadedMedia
Wait for media processing to complete.
-
.await_processing!(media, client:, processing_timeout: DEFAULT_PROCESSING_TIMEOUT) ⇒ UploadedMedia
Wait for media processing and raise on failure.
-
.chunked_upload(media, client:, media_category: nil, media_type: nil, chunk_size: nil, concurrency: DEFAULT_CONCURRENCY, shared: nil, additional_owners: nil) ⇒ UploadedMedia
Perform a chunked upload for large files.
-
.upload(media, client:, media_category: nil, alt_text: nil, processing_timeout: DEFAULT_PROCESSING_TIMEOUT, media_type: nil, chunk_size: nil, concurrency: DEFAULT_CONCURRENCY, shared: nil, additional_owners: nil) ⇒ UploadedMedia
Upload media, in chunks when the API needs them, awaiting any processing.
Instance Method Summary collapse
-
#await_processing(media, client:, processing_timeout: DEFAULT_PROCESSING_TIMEOUT) ⇒ UploadedMedia
Wait for media processing to complete.
-
#await_processing!(media, client:, processing_timeout: DEFAULT_PROCESSING_TIMEOUT) ⇒ UploadedMedia
Wait for media processing and raise on failure.
-
#chunked_upload(media, client:, media_category: nil, media_type: nil, chunk_size: nil, concurrency: DEFAULT_CONCURRENCY, shared: nil, additional_owners: nil) ⇒ UploadedMedia
Perform a chunked upload for large files.
-
#upload(media, client:, media_category: nil, alt_text: nil, processing_timeout: DEFAULT_PROCESSING_TIMEOUT, media_type: nil, chunk_size: nil, concurrency: DEFAULT_CONCURRENCY, shared: nil, additional_owners: nil) ⇒ UploadedMedia
Upload media, in chunks when the API needs them, awaiting any processing.
Class Method Details
.await_processing(media, client:, processing_timeout: DEFAULT_PROCESSING_TIMEOUT) ⇒ UploadedMedia
Wait for media processing to complete
Media that already says its processing has ended, in success or failure, or that holds an upload response which names no processing, as that of an image does, is returned as it is, without a request, since a check would tell no more than the media holds; await_processing! raises for media that says it failed, without a request too.
Before each check it waits as long as X asked, and at least a second: media an upload returned, or the Hash of its response, which says how long to wait before the first check, is not checked until then, and media given as its identifier, or its identifier and media key alone, which say nothing of its processing, or as media in a state of processing X does not document, is checked at once.
The processing timeout is a deadline, the seconds from when it is called, measured on the monotonic clock, so that it counts the time each check takes, with any wait for a rate limit and any retry the client makes, as well as the waits between them. It gives up once the next check X asks for would come after the deadline, rather than sleep past it, or check before X asks. A check under way at the deadline is let finish, and its status returned if processing has finished, so it can return that much after the deadline.
It waits while the processing is pending or in progress alone, so a status whose processing names no state, or a state X does not document, is returned as it is, neither processing nor ready, rather than checked until the deadline, since X gives no time to check it again at; await_processing! raises for it.
322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'x-uploader/lib/x/uploader/media_upload.rb', line 322 def await_processing(media, client:, processing_timeout: DEFAULT_PROCESSING_TIMEOUT) Validator.validate_processing_timeout!(processing_timeout) uploaded = Utils.uploaded_media(media) return uploaded if uploaded.ready? || uploaded.failed? deadline, media_id, pending = processing_timeout&.then { |seconds| Utils.seconds_from_now(seconds) }, Utils.media_id(uploaded), (uploaded if uploaded.processing?) loop do Utils.wait_to_check(pending, deadline:, timeout: processing_timeout) if pending status = UploadedMedia.new(Utils.media_data(client.get("media/upload", params: {command: STATUS_COMMAND, media_id:}, **JSON_CLASSES), "of the status check")) return status unless status.processing? pending = status end end |
.await_processing!(media, client:, processing_timeout: DEFAULT_PROCESSING_TIMEOUT) ⇒ UploadedMedia
Wait for media processing and raise on failure
356 357 358 |
# File 'x-uploader/lib/x/uploader/media_upload.rb', line 356 def await_processing!(media, client:, processing_timeout: DEFAULT_PROCESSING_TIMEOUT) Utils.processed!(MediaUpload.await_processing(media, client:, processing_timeout:)) end |
.chunked_upload(media, client:, media_category: nil, media_type: nil, chunk_size: nil, concurrency: DEFAULT_CONCURRENCY, shared: nil, additional_owners: nil) ⇒ UploadedMedia
Perform a chunked upload for large files
It is the way to upload media without waiting for X to process it: #upload waits for the processing of media X processes, such as a video, and this returns once the upload is finalized, so that the caller can go on while X processes a long video, and wait for it with #await_processing or #await_processing! when it needs it. It uploads in chunks whatever the media, an image as well.
The chunks of an upload in chunks are sent by threads of their own, as many as the concurrency, so the on_response of the client runs on those threads for the response of each chunk, and a hook that reads state kept for the thread that called, such as a Rails CurrentAttributes or a logger of its own, reads that of another thread.
Each chunk is a request of its own, which a rate limit can refuse, and a chunk refused raises ChunkedUploadFailed, since the client retries a request refused for a rate limit only max_rate_limit_retries times, which is 0 by default. A large video, uploaded in many chunks, should be uploaded with a client whose max_rate_limit_retries is set, such as X::Client.new(max_rate_limit_retries: 3), so that a rate limit is waited out, up to the max_rate_limit_wait of the client, rather than fail the upload.
270 271 272 273 274 275 276 277 278 279 |
# File 'x-uploader/lib/x/uploader/media_upload.rb', line 270 def chunked_upload(media, client:, media_category: nil, media_type: nil, chunk_size: nil, concurrency: DEFAULT_CONCURRENCY, shared: nil, additional_owners: nil) source = Source.for(media) Validator.validate_sharing!(shared, additional_owners) Validator.validate_source!(source) media_category = Validator.validate_media_category!(media_category || Inference.infer_media_category(source)) Validator.validate_size!(source, media_category) Validator.validate_chunks!(chunk_size:, concurrency:) Chunks.upload(client:, source:, media_type: media_type || Inference.infer_media_type(source, media_category), media_category:, chunk_size:, concurrency:, shared:, additional_owners:) end |
.upload(media, client:, media_category: nil, alt_text: nil, processing_timeout: DEFAULT_PROCESSING_TIMEOUT, media_type: nil, chunk_size: nil, concurrency: DEFAULT_CONCURRENCY, shared: nil, additional_owners: nil) ⇒ UploadedMedia
Upload media, in chunks when the API needs them, awaiting any processing
The media is a path, or an IO open on it, which Source says how each of is read.
A video and subtitles upload in chunks, as does an animated GIF that a single request cannot take. Every argument is validated before the first request, so that no media is uploaded, and billed, for an upload that cannot finish.
An image, and a GIF that a single request takes, upload in a single request, which takes no chunks and no media type, so chunk_size, concurrency, and media_type are ignored for them: a chunk_size or a concurrency that is not valid still raises, but none is sent. Media given shared: true uploads in chunks, and uses all three. Upload with #chunked_upload to send an image in chunks too.
Media the response of the upload says is still processing is awaited as #await_processing! awaits it. Media the response says has already failed to process raises MediaProcessingFailed with that response, and media it says has finished is returned as it is, without a check of its status.
The chunks of an upload in chunks are sent by threads of their own, as many as the concurrency, so the on_response of the client runs on those threads for the response of each chunk, and a hook that reads state kept for the thread that called, such as a Rails CurrentAttributes or a logger of its own, reads that of another thread.
Each chunk is a request of its own, which a rate limit can refuse, and a chunk refused raises ChunkedUploadFailed, since the client retries a request refused for a rate limit only max_rate_limit_retries times, which is 0 by default. A large video, uploaded in many chunks, should be uploaded with a client whose max_rate_limit_retries is set, such as X::Client.new(max_rate_limit_retries: 3), so that a rate limit is waited out, up to the max_rate_limit_wait of the client, rather than fail the upload.
205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'x-uploader/lib/x/uploader/media_upload.rb', line 205 def upload(media, client:, media_category: nil, alt_text: nil, processing_timeout: DEFAULT_PROCESSING_TIMEOUT, media_type: nil, chunk_size: nil, concurrency: DEFAULT_CONCURRENCY, shared: nil, additional_owners: nil) source = Source.for(media) media_category = Validator.validate_upload!(source, media_category, alt_text:, chunk_size:, concurrency:, processing_timeout:, shared:, additional_owners:) { Inference.infer_media_category(source) } uploaded = if shared || Inference.chunked_upload?(source, media_category) Chunks.upload(client:, source:, media_type: media_type || Inference.infer_media_type(source, media_category), media_category:, chunk_size:, concurrency:, shared:, additional_owners:) else Utils.single_request(client, Inference.single_request!(source, media_category), media_category, additional_owners:) end uploaded = Utils.processed!(uploaded.processing? ? MediaProcessingCheckFailed.__send__(:keeping, uploaded) { MediaUpload.await_processing(uploaded, client:, processing_timeout:) } : uploaded) AltTextFailed.__send__(:keeping, uploaded) { Metadata.add_alt_text(uploaded, alt_text, client:) } unless alt_text.nil? uploaded end |
Instance Method Details
#await_processing(media, client:, processing_timeout: DEFAULT_PROCESSING_TIMEOUT) ⇒ UploadedMedia
Wait for media processing to complete
Media that already says its processing has ended, in success or failure, or that holds an upload response which names no processing, as that of an image does, is returned as it is, without a request, since a check would tell no more than the media holds; await_processing! raises for media that says it failed, without a request too.
Before each check it waits as long as X asked, and at least a second: media an upload returned, or the Hash of its response, which says how long to wait before the first check, is not checked until then, and media given as its identifier, or its identifier and media key alone, which say nothing of its processing, or as media in a state of processing X does not document, is checked at once.
The processing timeout is a deadline, the seconds from when it is called, measured on the monotonic clock, so that it counts the time each check takes, with any wait for a rate limit and any retry the client makes, as well as the waits between them. It gives up once the next check X asks for would come after the deadline, rather than sleep past it, or check before X asks. A check under way at the deadline is let finish, and its status returned if processing has finished, so it can return that much after the deadline.
It waits while the processing is pending or in progress alone, so a status whose processing names no state, or a state X does not document, is returned as it is, neither processing nor ready, rather than checked until the deadline, since X gives no time to check it again at; await_processing! raises for it.
322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'x-uploader/lib/x/uploader/media_upload.rb', line 322 def await_processing(media, client:, processing_timeout: DEFAULT_PROCESSING_TIMEOUT) Validator.validate_processing_timeout!(processing_timeout) uploaded = Utils.uploaded_media(media) return uploaded if uploaded.ready? || uploaded.failed? deadline, media_id, pending = processing_timeout&.then { |seconds| Utils.seconds_from_now(seconds) }, Utils.media_id(uploaded), (uploaded if uploaded.processing?) loop do Utils.wait_to_check(pending, deadline:, timeout: processing_timeout) if pending status = UploadedMedia.new(Utils.media_data(client.get("media/upload", params: {command: STATUS_COMMAND, media_id:}, **JSON_CLASSES), "of the status check")) return status unless status.processing? pending = status end end |
#await_processing!(media, client:, processing_timeout: DEFAULT_PROCESSING_TIMEOUT) ⇒ UploadedMedia
Wait for media processing and raise on failure
356 357 358 |
# File 'x-uploader/lib/x/uploader/media_upload.rb', line 356 def await_processing!(media, client:, processing_timeout: DEFAULT_PROCESSING_TIMEOUT) Utils.processed!(MediaUpload.await_processing(media, client:, processing_timeout:)) end |
#chunked_upload(media, client:, media_category: nil, media_type: nil, chunk_size: nil, concurrency: DEFAULT_CONCURRENCY, shared: nil, additional_owners: nil) ⇒ UploadedMedia
Perform a chunked upload for large files
It is the way to upload media without waiting for X to process it: #upload waits for the processing of media X processes, such as a video, and this returns once the upload is finalized, so that the caller can go on while X processes a long video, and wait for it with #await_processing or #await_processing! when it needs it. It uploads in chunks whatever the media, an image as well.
The chunks of an upload in chunks are sent by threads of their own, as many as the concurrency, so the on_response of the client runs on those threads for the response of each chunk, and a hook that reads state kept for the thread that called, such as a Rails CurrentAttributes or a logger of its own, reads that of another thread.
Each chunk is a request of its own, which a rate limit can refuse, and a chunk refused raises ChunkedUploadFailed, since the client retries a request refused for a rate limit only max_rate_limit_retries times, which is 0 by default. A large video, uploaded in many chunks, should be uploaded with a client whose max_rate_limit_retries is set, such as X::Client.new(max_rate_limit_retries: 3), so that a rate limit is waited out, up to the max_rate_limit_wait of the client, rather than fail the upload.
270 271 272 273 274 275 276 277 278 279 |
# File 'x-uploader/lib/x/uploader/media_upload.rb', line 270 def chunked_upload(media, client:, media_category: nil, media_type: nil, chunk_size: nil, concurrency: DEFAULT_CONCURRENCY, shared: nil, additional_owners: nil) source = Source.for(media) Validator.validate_sharing!(shared, additional_owners) Validator.validate_source!(source) media_category = Validator.validate_media_category!(media_category || Inference.infer_media_category(source)) Validator.validate_size!(source, media_category) Validator.validate_chunks!(chunk_size:, concurrency:) Chunks.upload(client:, source:, media_type: media_type || Inference.infer_media_type(source, media_category), media_category:, chunk_size:, concurrency:, shared:, additional_owners:) end |
#upload(media, client:, media_category: nil, alt_text: nil, processing_timeout: DEFAULT_PROCESSING_TIMEOUT, media_type: nil, chunk_size: nil, concurrency: DEFAULT_CONCURRENCY, shared: nil, additional_owners: nil) ⇒ UploadedMedia
Upload media, in chunks when the API needs them, awaiting any processing
The media is a path, or an IO open on it, which Source says how each of is read.
A video and subtitles upload in chunks, as does an animated GIF that a single request cannot take. Every argument is validated before the first request, so that no media is uploaded, and billed, for an upload that cannot finish.
An image, and a GIF that a single request takes, upload in a single request, which takes no chunks and no media type, so chunk_size, concurrency, and media_type are ignored for them: a chunk_size or a concurrency that is not valid still raises, but none is sent. Media given shared: true uploads in chunks, and uses all three. Upload with #chunked_upload to send an image in chunks too.
Media the response of the upload says is still processing is awaited as #await_processing! awaits it. Media the response says has already failed to process raises MediaProcessingFailed with that response, and media it says has finished is returned as it is, without a check of its status.
The chunks of an upload in chunks are sent by threads of their own, as many as the concurrency, so the on_response of the client runs on those threads for the response of each chunk, and a hook that reads state kept for the thread that called, such as a Rails CurrentAttributes or a logger of its own, reads that of another thread.
Each chunk is a request of its own, which a rate limit can refuse, and a chunk refused raises ChunkedUploadFailed, since the client retries a request refused for a rate limit only max_rate_limit_retries times, which is 0 by default. A large video, uploaded in many chunks, should be uploaded with a client whose max_rate_limit_retries is set, such as X::Client.new(max_rate_limit_retries: 3), so that a rate limit is waited out, up to the max_rate_limit_wait of the client, rather than fail the upload.
205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'x-uploader/lib/x/uploader/media_upload.rb', line 205 def upload(media, client:, media_category: nil, alt_text: nil, processing_timeout: DEFAULT_PROCESSING_TIMEOUT, media_type: nil, chunk_size: nil, concurrency: DEFAULT_CONCURRENCY, shared: nil, additional_owners: nil) source = Source.for(media) media_category = Validator.validate_upload!(source, media_category, alt_text:, chunk_size:, concurrency:, processing_timeout:, shared:, additional_owners:) { Inference.infer_media_category(source) } uploaded = if shared || Inference.chunked_upload?(source, media_category) Chunks.upload(client:, source:, media_type: media_type || Inference.infer_media_type(source, media_category), media_category:, chunk_size:, concurrency:, shared:, additional_owners:) else Utils.single_request(client, Inference.single_request!(source, media_category), media_category, additional_owners:) end uploaded = Utils.processed!(uploaded.processing? ? MediaProcessingCheckFailed.__send__(:keeping, uploaded) { MediaUpload.await_processing(uploaded, client:, processing_timeout:) } : uploaded) AltTextFailed.__send__(:keeping, uploaded) { Metadata.add_alt_text(uploaded, alt_text, client:) } unless alt_text.nil? uploaded end |