প্রকাশিত: ৫ জুলাই, ২০২১
প্রোগ্রেসিভ ওয়েব অ্যাপস ওয়েবে এমন অনেক বৈশিষ্ট্য নিয়ে আসে যা পূর্বে নেটিভ অ্যাপ্লিকেশনের জন্য সংরক্ষিত ছিল। PWA-এর সাথে সম্পর্কিত সবচেয়ে উল্লেখযোগ্য বৈশিষ্ট্যগুলির মধ্যে একটি হল অফলাইন অভিজ্ঞতা।
আরও ভালো হবে অফলাইন স্ট্রিমিং মিডিয়া অভিজ্ঞতা, যা এমন একটি উন্নত অভিজ্ঞতা যা আপনি আপনার ব্যবহারকারীদের কয়েকটি ভিন্ন উপায়ে অফার করতে পারেন। তবে, এটি সত্যিই একটি অনন্য সমস্যা তৈরি করে - মিডিয়া ফাইলগুলি খুব বড় হতে পারে। তাই আপনি হয়তো জিজ্ঞাসা করছেন:
- আমি কিভাবে একটি বড় ভিডিও ফাইল ডাউনলোড এবং সংরক্ষণ করব?
- এবং আমি এটি ব্যবহারকারীকে কীভাবে পরিবেশন করব?
এই প্রবন্ধে আমরা এই প্রশ্নগুলির উত্তর নিয়ে আলোচনা করব, একই সাথে আমাদের তৈরি কিনো ডেমো PWA উল্লেখ করব যা আপনাকে কোনও কার্যকরী বা উপস্থাপনামূলক কাঠামো ব্যবহার না করেই অফলাইন স্ট্রিমিং মিডিয়া অভিজ্ঞতা কীভাবে বাস্তবায়ন করতে পারে তার ব্যবহারিক উদাহরণ প্রদান করে। নিম্নলিখিত উদাহরণগুলি মূলত শিক্ষামূলক উদ্দেশ্যে, কারণ বেশিরভাগ ক্ষেত্রেই এই বৈশিষ্ট্যগুলি প্রদানের জন্য আপনার সম্ভবত বিদ্যমান মিডিয়া ফ্রেমওয়ার্কগুলির একটি ব্যবহার করা উচিত।
যদি আপনার নিজস্ব ব্যবসা তৈরির জন্য ভালো ব্যবসা না থাকে, তাহলে অফলাইন স্ট্রিমিং সহ একটি PWA তৈরির কিছু চ্যালেঞ্জ রয়েছে। এই প্রবন্ধে আপনি ব্যবহারকারীদের উচ্চমানের অফলাইন মিডিয়া অভিজ্ঞতা প্রদানের জন্য ব্যবহৃত API এবং কৌশলগুলি সম্পর্কে শিখবেন।
একটি বড় মিডিয়া ফাইল ডাউনলোড এবং সংরক্ষণ করা
প্রোগ্রেসিভ ওয়েব অ্যাপস সাধারণত অফলাইন অভিজ্ঞতা প্রদানের জন্য প্রয়োজনীয় সম্পদ: ডকুমেন্ট, স্টাইলশিট, ছবি এবং অন্যান্য ডাউনলোড এবং সংরক্ষণ করার জন্য সুবিধাজনক ক্যাশে API ব্যবহার করে।
এখানে একটি সার্ভিস ওয়ার্কারের মধ্যে ক্যাশে API ব্যবহারের একটি মৌলিক উদাহরণ দেওয়া হল:
const cacheStorageName = 'v1';
this.addEventListener('install', function(event) {
event.waitUntil(
caches.open(cacheStorageName).then(function(cache) {
return cache.addAll([
'index.html',
'style.css',
'scripts.js',
// Don't do this.
'very-large-video.mp4',
]);
})
);
});
উপরের উদাহরণটি প্রযুক্তিগতভাবে কাজ করলেও, ক্যাশে এপিআই ব্যবহারের বেশ কিছু সীমাবদ্ধতা রয়েছে যা বড় ফাইলের সাথে এর ব্যবহারকে অবাস্তব করে তোলে।
উদাহরণস্বরূপ, ক্যাশে এপিআই নিম্নলিখিত কাজ করে না:
- আপনাকে সহজেই ডাউনলোডগুলি থামাতে এবং পুনরায় শুরু করতে দেয়
- আপনাকে ডাউনলোডের অগ্রগতি ট্র্যাক করতে দেয়
- HTTP রেঞ্জের অনুরোধগুলিতে সঠিকভাবে সাড়া দেওয়ার একটি উপায় অফার করুন
এই সমস্ত সমস্যা যেকোনো ভিডিও অ্যাপ্লিকেশনের জন্য বেশ গুরুতর সীমাবদ্ধতা। আসুন আরও কিছু বিকল্প পর্যালোচনা করি যা আরও উপযুক্ত হতে পারে।
আজকাল, Fetch API হল অ্যাসিঙ্ক্রোনাসভাবে দূরবর্তী ফাইল অ্যাক্সেস করার একটি ক্রস-ব্রাউজার উপায়। আমাদের ব্যবহারের ক্ষেত্রে এটি আপনাকে স্ট্রিম হিসাবে বড় ভিডিও ফাইল অ্যাক্সেস করতে এবং HTTP রেঞ্জ অনুরোধ ব্যবহার করে ক্রমবর্ধমানভাবে খণ্ড হিসাবে সংরক্ষণ করতে দেয়।
এখন যেহেতু আপনি Fetch API ব্যবহার করে ডেটার অংশগুলি পড়তে পারেন, তাই আপনাকে সেগুলিও সংরক্ষণ করতে হবে। আপনার মিডিয়া ফাইলের সাথে অনেক মেটাডেটা যুক্ত থাকার সম্ভাবনা রয়েছে যেমন: নাম, বিবরণ, রানটাইম দৈর্ঘ্য, বিভাগ ইত্যাদি।
আপনি কেবল একটি মিডিয়া ফাইল সংরক্ষণ করছেন না, আপনি একটি কাঠামোগত বস্তু সংরক্ষণ করছেন, এবং মিডিয়া ফাইলটি এর বৈশিষ্ট্যগুলির মধ্যে একটি।
এই ক্ষেত্রে, IndexedDB API মিডিয়া ডেটা এবং মেটাডেটা উভয়ই সংরক্ষণের জন্য একটি চমৎকার সমাধান প্রদান করে। এটি সহজেই বিপুল পরিমাণে বাইনারি ডেটা ধরে রাখতে পারে এবং এটি এমন সূচকও প্রদান করে যা আপনাকে খুব দ্রুত ডেটা লুকআপ করতে দেয়।
Fetch API ব্যবহার করে মিডিয়া ফাইল ডাউনলোড করা হচ্ছে
আমরা আমাদের ডেমো PWA-তে Fetch API-এর চারপাশে কয়েকটি আকর্ষণীয় বৈশিষ্ট্য তৈরি করেছি, যার নাম আমরা Kino রেখেছি — সোর্স কোডটি সর্বজনীন, তাই এটি পর্যালোচনা করতে দ্বিধা করবেন না।
- অসম্পূর্ণ ডাউনলোডগুলি থামিয়ে পুনরায় শুরু করার ক্ষমতা।
- ডাটাবেসে ডেটার অংশ সংরক্ষণের জন্য একটি কাস্টম বাফার।
এই বৈশিষ্ট্যগুলি কীভাবে বাস্তবায়িত হয় তা দেখানোর আগে, আমরা প্রথমে ফাইল ডাউনলোড করার জন্য Fetch API কীভাবে ব্যবহার করতে পারি তার একটি সংক্ষিপ্ত বিবরণ দেব।
/**
* Downloads a single file.
*
* @param {string} url URL of the file to be downloaded.
*/
async function downloadFile(url) {
const response = await fetch(url);
const reader = response.body.getReader();
do {
const { done, dataChunk } = await reader.read();
// Store the `dataChunk` to IndexedDB.
} while (!done);
}
লক্ষ্য করুন যে await reader.read()
একটি লুপে আছে? এভাবেই আপনি নেটওয়ার্ক থেকে আসা একটি পঠনযোগ্য স্ট্রিম থেকে কিছু তথ্য পাবেন। এটি কতটা কার্যকর তা বিবেচনা করুন: নেটওয়ার্ক থেকে সমস্ত তথ্য আসার আগেই আপনি আপনার ডেটা প্রক্রিয়াকরণ শুরু করতে পারেন।
ডাউনলোডগুলি আবার শুরু করা হচ্ছে
যখন কোনও ডাউনলোড থামানো হয় বা বাধাগ্রস্ত হয়, তখন যে ডেটা খণ্ডগুলি পৌঁছেছে তা নিরাপদে একটি IndexedDB ডাটাবেসে সংরক্ষণ করা হবে। তারপরে আপনি আপনার অ্যাপ্লিকেশনে ডাউনলোড পুনরায় শুরু করার জন্য একটি বোতাম প্রদর্শন করতে পারেন। কারণ Kino ডেমো PWA সার্ভার HTTP রেঞ্জ অনুরোধগুলিকে সমর্থন করে, ডাউনলোড পুনরায় শুরু করা কিছুটা সহজ:
async downloadFile() {
// this.currentFileMeta contains data from IndexedDB.
const { bytesDownloaded, url, downloadUrl } = this.currentFileMeta;
const fetchOpts = {};
// If we already have some data downloaded,
// request everything from that position on.
if (bytesDownloaded) {
fetchOpts.headers = {
Range: `bytes=${bytesDownloaded}-`,
};
}
const response = await fetch(downloadUrl, fetchOpts);
const reader = response.body.getReader();
let dataChunk;
do {
dataChunk = await reader.read();
if (!dataChunk.done) this.buffer.add(dataChunk.value);
} while (!dataChunk.done && !this.paused);
}
IndexedDB-এর জন্য কাস্টম লেখার বাফার
কাগজে কলমে, একটি IndexedDB ডাটাবেসে dataChunk
মান লেখার প্রক্রিয়াটি সহজ। সেই মানগুলি ইতিমধ্যেই ArrayBuffer
ইনস্ট্যান্স, যা সরাসরি IndexedDB তে সংরক্ষণযোগ্য, তাই আমরা কেবল একটি উপযুক্ত আকৃতির বস্তু তৈরি করতে এবং এটি সংরক্ষণ করতে পারি।
const dataItem = {
url: fileUrl,
rangeStart: dataStartByte,
rangeEnd: dataEndByte,
data: dataChunk,
}
// Name of the store that will hold your data.
const storeName = 'fileChunksStorage'
// `db` is an instance of `IDBDatabase`.
const transaction = db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
const putRequest = store.put(data);
putRequest.onsuccess = () => { ... }
এই পদ্ধতিটি কাজ করলেও, আপনি সম্ভবত আবিষ্কার করবেন যে আপনার IndexedDB লেখা আপনার ডাউনলোডের তুলনায় উল্লেখযোগ্যভাবে ধীর। এর কারণ এই নয় যে IndexedDB লেখা ধীর, বরং কারণ আমরা নেটওয়ার্ক থেকে প্রাপ্ত প্রতিটি ডেটা অংশের জন্য একটি নতুন লেনদেন তৈরি করে প্রচুর লেনদেনের ওভারহেড যোগ করছি।
ডাউনলোড করা অংশগুলি বেশ ছোট হতে পারে এবং দ্রুত ধারাবাহিকভাবে স্ট্রিম দ্বারা নির্গত হতে পারে। আপনাকে IndexedDB লেখার হার সীমিত করতে হবে। Kino ডেমো PWA-তে আমরা একটি মধ্যস্থতাকারী লেখার বাফার প্রয়োগ করে এটি করি।
নেটওয়ার্ক থেকে ডেটা খণ্ড আসার সাথে সাথে, আমরা প্রথমে সেগুলিকে আমাদের বাফারে যুক্ত করি। যদি ইনকামিং ডেটা ফিট না হয়, তাহলে আমরা সম্পূর্ণ বাফারটি ডাটাবেসে ফ্লাশ করি এবং বাকি ডেটা যুক্ত করার আগে এটি সাফ করি। ফলস্বরূপ, আমাদের IndexedDB লেখার সংখ্যা কম হয়, যার ফলে লেখার কর্মক্ষমতা উল্লেখযোগ্যভাবে উন্নত হয়।
অফলাইন স্টোরেজ থেকে একটি মিডিয়া ফাইল পরিবেশন করা হচ্ছে
একবার আপনার মিডিয়া ফাইল ডাউনলোড হয়ে গেলে, আপনি সম্ভবত চাইবেন যে আপনার পরিষেবা কর্মী নেটওয়ার্ক থেকে ফাইলটি আনার পরিবর্তে IndexedDB থেকে এটি পরিবেশন করুক।
/**
* The main service worker fetch handler.
*
* @param {FetchEvent} event Fetch event.
*/
const fetchHandler = async (event) => {
const getResponse = async () => {
// Omitted Cache API code used to serve static assets.
const videoResponse = await getVideoResponse(event);
if (videoResponse) return videoResponse;
// Fallback to network.
return fetch(event.request);
};
event.respondWith(getResponse());
};
self.addEventListener('fetch', fetchHandler);
তাহলে getVideoResponse()
এ আপনাকে কী করতে হবে?
event.respondWith()
পদ্ধতিটি একটি প্যারামিটার হিসেবে একটিResponse
অবজেক্ট আশা করে।রেসপন্স() কনস্ট্রাক্টর আমাদের বলে যে একটি
Response
অবজেক্টকে ইনস্ট্যান্টিয়েট করার জন্য আমরা বিভিন্ন ধরণের অবজেক্ট ব্যবহার করতে পারি: একটিBlob
,BufferSource
,ReadableStream
এবং আরও অনেক কিছু।আমাদের এমন একটি অবজেক্ট দরকার যা তার সমস্ত ডেটা মেমরিতে ধরে রাখে না, তাই আমরা সম্ভবত
ReadableStream
বেছে নিতে চাইব।
এছাড়াও, যেহেতু আমরা বড় ফাইল নিয়ে কাজ করছি, এবং আমরা ব্রাউজারগুলিকে শুধুমাত্র তাদের বর্তমানে প্রয়োজনীয় ফাইলের অংশটি অনুরোধ করার অনুমতি দিতে চেয়েছিলাম, তাই HTTP রেঞ্জ অনুরোধের জন্য আমাদের কিছু মৌলিক সহায়তা বাস্তবায়নের প্রয়োজন ছিল।
/**
* Respond to a request to fetch offline video file and construct a response
* stream.
*
* Includes support for `Range` requests.
*
* @param {Request} request Request object.
* @param {Object} fileMeta File meta object.
*
* @returns {Response} Response object.
*/
const getVideoResponse = (request, fileMeta) => {
const rangeRequest = request.headers.get('range') || '';
const byteRanges = rangeRequest.match(/bytes=(?<from>[0-9]+)?-(?<to>[0-9]+)?/);
// Using the optional chaining here to access properties of
// possibly nullish objects.
const rangeFrom = Number(byteRanges?.groups?.from || 0);
const rangeTo = Number(byteRanges?.groups?.to || fileMeta.bytesTotal - 1);
// Omitting implementation for brevity.
const streamSource = {
pull(controller) {
// Read file data here and call `controller.enqueue`
// with every retrieved chunk, then `controller.close`
// once all data is read.
}
}
const stream = new ReadableStream(streamSource);
// Make sure to set proper headers when supporting range requests.
const responseOpts = {
status: rangeRequest ? 206 : 200,
statusText: rangeRequest ? 'Partial Content' : 'OK',
headers: {
'Accept-Ranges': 'bytes',
'Content-Length': rangeTo - rangeFrom + 1,
},
};
if (rangeRequest) {
responseOpts.headers['Content-Range'] = `bytes ${rangeFrom}-${rangeTo}/${fileMeta.bytesTotal}`;
}
const response = new Response(stream, responseOpts);
return response;
আমরা কীভাবে IndexedDB থেকে ফাইল ডেটা পড়ছি এবং একটি বাস্তব অ্যাপ্লিকেশনে একটি স্ট্রিম তৈরি করছি তা জানতে Kino ডেমো PWA সার্ভিস ওয়ার্কার সোর্স কোডটি নির্দ্বিধায় পরীক্ষা করে দেখুন।
অন্যান্য বিবেচ্য বিষয়
আপনার সামনে প্রধান বাধাগুলো দূর হওয়ার পর, আপনি এখন আপনার ভিডিও অ্যাপ্লিকেশনে কিছু সুবিধাজনক বৈশিষ্ট্য যোগ করতে পারেন। কিনো ডেমো PWA-তে আপনি যে বৈশিষ্ট্যগুলি পাবেন তার কয়েকটি উদাহরণ এখানে দেওয়া হল:
- মিডিয়া সেশন এপিআই ইন্টিগ্রেশন যা আপনার ব্যবহারকারীদের ডেডিকেটেড হার্ডওয়্যার মিডিয়া কী ব্যবহার করে অথবা মিডিয়া নোটিফিকেশন পপআপ থেকে মিডিয়া প্লেব্যাক নিয়ন্ত্রণ করতে দেয়।
- পুরনো ক্যাশে API ব্যবহার করে মিডিয়া ফাইলের সাথে সম্পর্কিত অন্যান্য সম্পদ যেমন সাবটাইটেল এবং পোস্টার চিত্রের ক্যাশে করা।
- অ্যাপের মধ্যে ভিডিও স্ট্রিম (DASH, HLS) ডাউনলোডের জন্য সমর্থন। যেহেতু স্ট্রিম ম্যানিফেস্ট সাধারণত বিভিন্ন বিটরেটের একাধিক উৎস ঘোষণা করে, তাই আপনাকে ম্যানিফেস্ট ফাইলটি রূপান্তর করতে হবে এবং অফলাইনে দেখার জন্য সংরক্ষণ করার আগে শুধুমাত্র একটি মিডিয়া সংস্করণ ডাউনলোড করতে হবে।
পরবর্তীতে, আপনি অডিও এবং ভিডিও প্রিলোড সহ দ্রুত প্লেব্যাক সম্পর্কে শিখবেন।