General concept of the system

24TTL creates a web-page with rich content for a product. The web-page is converted to an e-package file which is a ZIP file containing HTML, CSS, iframe, JSON and media files along with a manifest JSON file (manifest.json). The manifest file contains the basic information on the e-package and the product described in it. The e-package is assigned a unique URL that is rendered to the CDN. The CDN creates a new equivalent link for this epackage and caches it.

24STREAM is triggered when a user visits a product page where the widget is embedded. The CDN uses the link to the manifest.json file to check if any relevant content is available.

If the content is available, the system responds with “200” and the widget, depending on the method you selected, either automatically delivers the rich content on the product page (the method findAndInsert) or provides an opportunity to handle the content manually (the method find). If there is no content, the system responds with “204”.

 

24STREAM rich content delivery tool

The 24STREAM widget is designed to broadcast rich content onto product pages of online marketplaces.

Rich content is the combination of text, images, video, animations, and inteactive media that provides more informative and engaging description of the product. Enhanced content encourages virtual interaction with the product, showcases its advantages, evokes vivid emotions, builds the brand awareness, and motivates to purchase.

The system provides for two types of rich content: minisite и AR (augmented reality).

The 24STREAM widget is embedded as a script in your online marketplace and provides methods for searching the relevant rich content and rendering it to the product page.   

Rich content is uploaded when a user of the marketplace visits the product page where the tool is embedded. The script contains the parameters that help to determine which rich product content of which brand should be rendered.

The widget neither collects nor processes any of your data.

The functions of the 24STREAM solution are as follows: building the path to the static HTML file located on the CDN; inserting the result (HTML code) into <div> block; monitoring the size of the block where the content is inserted; and, depending on the block size, changes the classes in the inserted HTML file. The content resolution is limited by width and has four types: maximum - 1200px; small pc - 1100 to 991px; tablet - 991 to 768px; and mobile - below 768 to 320px.

The page speed is not impacted because the widget does not use any external database queries, but only accesses the static content located on the CDN. Thus, regardless of the user's location, the content is always delivered from the nearest CDN server.

The content can be accessed directly without using the widget. To do this, the correct path should be built using the required attributes described in the section "Using the Widget"

Connecting the widget

The widget is adapted to operate in various environments and offers two methods of connection: asynchronous and synchronous.

To run the tool in mobile applications, it is recommended to use WebView. Rich content will be displayed in a pop-up window.

Asynchronous connection

The asynchronous connection is recommended to have the code run in your browser client.

The asynchronous-connected widget will not block loading of the main content.

If it is completely inaccessible, it will neither cause any errors nor breal the main functionality of the website.

To integrate the widget, insert the following code into the end of the document body:


<script>
	(function (w, d, s, o)  { 
		var f = d.getElementsByTagName(s)[0];
		var j = d.createElement(s);
		w.TTLStreamReady = new Promise((resolve) =>  { 
			j.async = true;
			j.src = '//content.24ttl.stream/widget.js';
			f.parentNode.insertBefore(j, f);
			j.onload = function ()  { 
				w.ttlStream = new TTLStream(o);
				resolve(w.ttlStream);
			};
		});
	})(window, document, 'script',  { });
</script>
								

The widget is ready for use!

Synchronous connection

The synchronous connection, unlike the asynchronous type, blocks the main traffic, and the scripts that follow the widget connection code will be loaded only after the widget loading.

This type of connection may be useful to those seeking to receive the data as soon as possible and to manipulate the data thereafter.

To integrate the widget, insert the following code into the end of the document body:


<script src="//content.24ttl.stream/widget.js"></script>
<script>
	window.TTLStreamReady = new Promise(resolve =>  {  window.ttlStream = new TTLStream( { }); resolve(); });
</script>
								

The widget is ready for use!

Using the widget

After you connected the widget, it is ready for use.

The widget settings differ depending on the content type (parameter).

Presently, 2 types of content are available: minisite and ar (augmented reality).

Content type 'minisite'

This type of content is a landing page with images, text and video media.

For ease of use, the widget provides two operation methods:

- findAndInsert: the method provides for finding rich content and automatically inserting it into the block;

- find: the method is similar to findAndInsert, additionally provides the ability to manipulate the content.

Example of broadcast content

Using findAndInsert for minisite

The method findAndInsert, similar to find, takes a set of parameters using which the widget finds the content and inserts it into the block.
Unlike the method find, findAndInsert does not return the data for manipulations, but inserts it into the block.

The following parameters can be sent:

Parameter Required Value type Description
el yes string

CSS selector of the DOM element into which the content will be inserted. You can customize the type of rich content by adding your CSS rules. Prior to calling the widget, the block where the rich content will be inserted should be assigned styles via important, for example, having added the following JS script:

 var styleElement = document.createElement('style'); styleElement.innerHTML = 'body .exampleWrapperContent #stream-container * { '+ ' font-family: \'DINPro Technopark\' !important;'+ '}'; document.getElementsByTagName("head")[0].appendChild(styleElement); 

where .exampleWrapperContent should be replaced with the class of the element into which the rich content will be inserted

brand yes string The name of the brand for whose product rich content will be delivered onto your online marketplace
productId yes string The product identifier that makes the product of the brand unique for the brand's retailer
retailerDomain yes string The domain of the retailer where the rich product content will be delivered to
templateType yes string Returned template type, can take on the values 'master_template' (html template) and 'img_template' (tempalte of images)
contentType yes string Returned content type, can take the values 'minisite' and 'ar'
resultType yes string Returned result format, can take on the values 'html' and 'iframe', a required parameter, determines the result
language no string

The country and language code for the rich content. To receive the content in a particular language for a particular country, a query should be sent in the format 'country_language'. The country code and the language code should be given in the ISO standard (the country code should be in accordance with ISO 3166-1, with the language code in ISO 639-1). If you require three pages in three languages, you should make three queries, having given in each the required parameters of the language

By default, the parameter is set to 'ru'

throwError no boolean

The value is not set or set to true: if no rich content is found, the console will show the error "404" (no content is found). You will have to resolve this error

If the value is set to false,in case no content is found, the error will not be given; therefore, there will be no necessity to resolve it

iframeMode no boolean

If the integration is via iframe, to receive the rich content you should use "iframeMode": true

Code example


<script>
	TTLStreamReady.then(() =>  { 
		ttlStream.findAndInsert( { 
			el: '.myCoolWrapper',
			brand: 'panasonic',
			productId: 'demoSKU',
			retailerDomain: 'beru.ru',
			templateType: 'master_template',
			resultType: 'html',
			contentType: 'minisite',
			language: 'ru',
		});
	});
</script>
								

The parameter resultType determines the data type which will be inserted onto the page. Below are its possible options:

html

HTML content will be inserted onto the page.

iframe

An iframe tag will be created on the page. The tag will contain rich content.

Using find for minisite

The method find takes a set of parameters using which the widget finds the content.

The following parameters can be sent:

Parameter Required Value type Description
brand  yes string The name of the brand for whose product rich content will be delivered onto your online marketplace
productId  yes string The product identifier that makes the product of the brand unique for the brand's retailer
retailerDomain  yes string The domain of the retailer where the rich product content will be delivered to
templateType  yes string Returned template type, can take on the values 'master_template' (html template) and 'img_template' (tempalte of images)
contentType  yes string Returned content type, can take on the values 'minisite' and 'ar'
resultType  yes string Returned result format, can take on the values 'html', 'iframe' and 'json', determines the result. See the example below
language no string

The country and language code for the rich content. To receive the content in a particular language for a particular country, a query should be sent in the format 'country_language'. The country code and the language code should be given in the ISO standard (the country code should be in accordance with ISO 3166-1, with the language code in ISO 639-1). If you require three pages in three languages, you should make three queries, having given in each the required parameters of the language

By default, the parameter is set to 'ru'

Code example:


<script>
	TTLStreamReady.then(() =>  { 
		ttlStream.find( { 
			brand: 'panasonic',
			productId: 'demoSKU',
			retailerDomain: 'beru.ru',
			templateType: 'master_template',
			resultType: 'html',
			contentType: 'minisite',
                       language: 'ru',
  }).then((data) => {
// The function will be called if the content is found successfully // The data variable will contain the found content }).catch((error) => { // The function will be called if the content is not found // The error variable will contain data of error reason }); }); </script>

The parameter resultType determines the data type which will be returned. Below are its possible options:

html

The variable data in the response will contain all html content of the requested page.

iframe

The variable data in the response will contain a link for inserting the widget as iframe.

json

The variable data in the response will contain raw data in json.

Content type 'ar' (augmented reality)

AR ("augmented reality") content is a visualization of a product in augmented reality that allows to show a detailed 3D copy of the product in space.

Example of broadcast AR content

Setting up AR

To set up the AR visualization, the method setArSettings is used.

The following parameters can be sent:

Parameter Description
preview A range of settings to customize the open button 
preview.html Button text

preview.style.backgroundColor

preview.style.backgroundImage

preview.style.backgroundSize

preview.style.backgroundRepeat

preview.style.backgroundPosition

Button styles. All styles are written as style object properties
viewer A number of settings to stylize the viewer
viewer.modalStyle Pop-up styles. All styles are written as style object properties
viewer.viewerStyle Viewer styles. All styles are written as style object properties
viewer.loaderColor Pre-loader bar color
viewer.closeStyle Viewer close button style. All styles are written as style object properties
qr A number of settings for QR code modal window 
qr.modal.style Modal window styles settings. All styles are written as style object properties
qr.box.style Settings of QR content block styles. All styles are written as style object properties
qr.btnOpen Settings of open button of QR code modal window
qr.btnOpen.html Buton text
qr.btnOpen.style Button styles. All styles are written as style object properties
qr.btnClose Settings of close button of QR code modal window
qr.btnClose.html Button text
qr.btnClose.style Button styles. All styles are written as style object properties
qr.title QR code title settings
qr.title.html Title text
qr.title.style Title styles. All styles are written as style object properties
qr.subtitle QR code subtitle settings
qr.subtitle.html Subtitle text
qr.subtitle.style Subtitle styles. All styles are written as style object properties

Code example


<script>
  TTLStreamReady.then(() =>  { 
      ttlStream.setArSettings( { 
          preview:  { 
              html: '',
              style:  { 
                  backgroundColor: '#231010',
                  backgroundImage: "url('https://24ar.tech/staticCDN/desktop/assets/buttons/ar.svg')",
                  backgroundSize: 'cover',
                  backgroundRepeat: 'no-repeat',
                  backgroundPosition: 'center',
              },
          },
          viewer:  { 
              modalStyle:  { },
              viewerStyle:  { },
              loaderColor: '#bd0d0d',
              closeStyle:  { },
          },
          qr:  { 
              modal:  { 
                  style:  { },
              },
              box:  { 
                  style:  { },
              },
              btnOpen:  { 
                  html: 'Hello world!',
                  style:  { },
              },
              btnClose:  { 
                  html: 'Close',
                  style:  { },
              },
              title:  { 
                  html: 'How to watch in AR',
                  style:  { },
              },
              subtitle:  { 
                  html: 'Scan the QR code with your phone's camera to see the object in real life.',
                  style:  { },
              },
          },
      });

      ttlStream.findAndInsert( { 
          el: '.myCoolWrapper',
          brand: 'panasonic',
          productId: 'demoSKU',
          retailerDomain: 'beru.ru',
          templateType: 'desktop',
          resultType: 'html',
          contentType: 'ar',
	  	});
	});
</script>
								
Using findAndInsert for AR

As applied to AR content type, the method findAndInsert is used.

The following parameters can be sent:

Parameter  Required Parameter type Description
el yes string CSS selector of the DOM element into which the content will be inserted
brand yes string The name of the brand for whose product rich content will be delivered onto your online marketplace
productId yes string The product identifier that makes the product of the brand unique for the brand's retailer
retailerDomain yes string The domain of the retailer where the rich product content will be delivered to
templateType yes string A type of the device used, can have the values 'ios', 'android', 'desktop'. If the parameter is not given, the device type will be determined automatically
contentType yes string AR
resultType yes string Returned result format, can take on the value 'html'
language no string

The country and language code for the rich content. To receive the content in a particular language for a particular country, a query should be sent in the format 'country_language'. The country code and the language code should be given in the ISO standard (the country code should be in accordance with ISO 3166-1, with the language code in ISO 639-1). If you require three pages in three languages, you should make three queries, having given in each the required parameters of the language

By default, the parameter is set to 'ru'

throwError no boolean

The value is not set or set to true: if no rich content is found, the console will show the error "404" (no content is found). You will have to resolve this error

If the value is set to false, in case no content is found, the error will not be given; therefore, there will be no necessity to resolve it

iframeMode no boolean

If the integration is via iframe, to receive the rich content you should use "iframeMode": true

Code example:


<script>
	TTLStreamReady.then(() =>  { 
		ttlStream.findAndInsert( { 
			el: '.myCoolWrapper',
			brand: 'panasonic',
			productId: 'demoSKU',
			retailerDomain: 'beru.ru',
			templateType: 'desktop',
			resultType: 'html',
			contentType: 'ar',
              }); }); </script>
Receiving your rich content list via the endpoint

24TTL provides the possibility of receiving a feed containing a complete list of products with rich content available to the requesting retailer.

The retailer should receive his API key generated by 24TTL and send the query to the endpoint. 

Query example:

curl -X POST https://panel.24ttl.stream/api/retailer/products-content-types \

-H "Accept-Charset: utf-8" \

-H "Content-Type: application/json" \

-H "Authorization: ApiKey generatedApiKey" \

-d ' {
  "domain": "retailerdomain",
  "forRichContent": true,
  "skuToLower": true,
  "mapped": true,
  "locale ": "ru",
  "updatedAt ": {
      "sign": "=",
      "value": "yyyy-mm-dd"
  }
}' \

To use the extended API with the script, use https://panel.24ttl.stream/api/retailer/products-content-types-expanded

Parameter Value type Description
domain string Required. Domain of the retailer of the page
locale string Language code for the page encoded as country_language. Country code should set according to ISO 3166-1 standard and the language code - to ISO 639-1. Defaults to ru
skuToLower boolean Converting SKU to lower_case in the response
forRichContent boolean Converting SKU and brand to the format for script filling
mapped boolean Returns only mapped SKUs
updatedAt object Filter by card update date

Important: the API key should be in the header, not in the query body.

To receive your API key and connection parameters, please turn to your retail manager.

The incorporation of the widget into the mobile application
24STREAM Flutter

A flutter plugin for creating 24STREAM Rich Page widgets.

Installation

To install add stream24 plugin to your dependencies in your pubspec.yaml

dependencies:
  ...
stream24: ^0.1.2
...

Usage

Stream24RichPage

A webview widget with rich html contents and automatic height.

Parameter Parameter type Description
brand string Required. Brand name for the page
productId string Required. Prouct ID for the page
retailerDomain string Required. Domain of the retailer of the page
templateType string Required. Template type of the page
language string Language code for the page encoded as country_language. Country code should set according to ISO 3166-1 standard and the language code - to ISO 639-1. Defaults to ru
onError Function(String) A function to call when an error is occured
contentType Stream24ContentType Content type of the page. One of .shopInShops or .minisite. Defaults to .minisite

GetHtml

Returns HTML code of the page as a String.

Parameter Parameter type Description
brand string Required. Brand name for the page
productId string Required. Prouct ID for the page
retailerDomain string Required. Domain of the retailer of the page
templateType string Required. Template type of the page
language string Language code for the page encoded as country_language. Country code should set according to ISO 3166-1 standard and the language code - to ISO 639-1. Defaults to ru
contentType Stream24ContentType Content type of the page. One of .shopInShops or .minisite. Defaults to .minisite

Example

Using Stream24RichPage

import 'package:flutter/material.dart';
import 'package:stream24/stream24.dart';
import 'package:stream24/rich_page.dart';
import 'package:webview_flutter/webview_flutter.dart';



class WebViewPage extends StatefulWidget {
  WebViewPage( { super.key});
  @override
  State<WebViewPage> createState() => _WebViewPageState();
}
class _WebViewPageState extends State<WebViewPage> {
  @override
  Widget build(BuildContext context) {
    return  Stream24RichPage(
            brand: 'Samsung',
            productId: '16651081549',
            retailerDomain: 'irshad.az',
            templateType: 'master_template',
            contentType: Stream24ContentType.shopInShops,
            onError: (errorMsg) { print(errorMsg); }
        );
    }
}

OR

Using getHtml

import 'package:flutter/material.dart';
import 'package:stream24/stream24.dart';
import 'package:webview_flutter/webview_flutter.dart';



class WebViewPage extends StatefulWidget {
  WebViewPage( { super.key});
  @override
  State<WebViewPage> createState() => _WebViewPageState();
}



class _WebViewPageState extends State<WebViewPage> {
  late WebViewController controller;
    double height = 120;



  @override
  void initState() {
    super.initState();
       controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)



      // to automatically adjust height add the following block
      ..addJavaScriptChannel("FlutterWebviewHeight",
          onMessageReceived: (JavaScriptMessage msg) {
        setState(() {
          height = double.tryParse(msg.message) ?? 0;
        });
      })
      //////////////////////////////////////////////////////////////////



      //if you want to throw errors on 404 content add the following block
      ..addJavaScriptChannel("FlutterError",
          onMessageReceived: (JavaScriptMessage msg) {
            
        throw Exception(msg.message);
      })
      //////////////////////////////////////////////////////////////////



      ..loadHtmlString(Stream24.getHtml(
          brand: 'Samsung',
          productId: '16651081549',
          retailerDomain: 'irshad.az',
          templateType: 'master_template',
          contentType: Stream24ContentType.shopInShops));
  }



  @override
  Widget build(BuildContext context) {
    return SizedBox(
        height: height,
        child: WebViewWidget(
          controller: controller,
        ),
    );
  }
}
24STREAM Kotlin

An android library for generating 24STREAM HTML widgets to be used in WebView or any other nested browser.

Installation

To install the library you need to add JitPack repository first (if you haven't already).
For older projects in the root build.gradle file add maven { url 'https://jitpack.io' } inside allprojects->repositories like following:

 allprojects  { 
repositories {
 ...
maven { url 'https://jitpack.io' }
}
 }

For newer projects you need to add it to settings.gradle. It should be in dependencyResolutionManagement->repositories like following:

dependencyResolutionManagement  { 
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

Then we need to add the dependency itself(in app module build.gradle):

dependencies  { 
    ...
    implementation 'stream.24ttl.git.pub:24stream_android:0.5.3'
}

Usage

RichPage

A webview widget with rich html contents and automatic height.

Parameter Parameter type Description
brand string Required. Brand name for the page
productId string Required. Prouct ID for the page
retailerDomain string Required. Domain of the retailer of the page
templateType string Required. Template type of the page
language string Language code for the page encoded as country_language. Country code should set according to ISO 3166-1 standard and the language code - to ISO 639-1. Defaults to ru
onError (String) -> Unit A function to call when an error is occured
contentType Stream24ContentType Content type of the page. One of .shopInShops or .minisite. Defaults to .minisite

GetHTML

Returns HTML code of the page.

Parameter Parameter type Description
brand string Required. Brand name for the page
productId string Required. Prouct ID for the page
retailerDomain string Required. Domain of the retailer of the page
templateType string Required. Template type of the page
language string Language code for the page encoded as country_language. Country code should set according to ISO 3166-1 standard and the language code - to ISO 639-1. Defaults to ru
contentType Stream24ContentType Content type of the page. One of .shopInShops or .minisite. Defaults to .minisite

Example

Using RichContent

import stream.ttl24.stream24.Stream24

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val rich = Stream24.RichContent(LocalContext.current, LocalDensity.current, "irshad.az", "Samsung", "16651081549",  "master_template", ::onError, "ru_ru",  Stream24.Stream24ContentType.shopInShops)
        setContentView(rich)
    }
}


fun onError(errorMessage:String) {
    print(errorMessage)
}

Using GetHtml

import stream.ttl24.stream24.Stream24

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val webview = WebView(this)
        val html = Stream24.GetHTML("Samsung", "16651081549", "irshad.az", "master_template", Stream24.Stream24ResultType.html, Stream24.Stream24ContentType.shopInShops)
        webview.getSettings().javaScriptEnabled = true
        webview.loadData(html,
            "text/html", "UTF-8")
        setContentView(webview)
    }
}
24STREAM Swift

An iOS plugin for generating 24STREAM HTML widgets to be used in WKWebView or any other nested browser.

Installation

24STREAM is available through CocoaPods and Swift Package Manager.

To install it using Cocoapods, add the following line to your Podfile:

pod '24stream'

To install it using Swift Package Manager:

File -> Add packages... -> Search 'https://git.24ttl.stream/pub/24stream_swift' -> Add package

Usage

You need to import it as import _24stream. After that you can use Stream24 namespace which contains:

WebView

Parameter Parameter type Description
brand string Required. Brand name for the page
productId string Required. Prouct ID for the page
retailerDomain string Required. Domain of the retailer of the page
templateType string Required. Template type of the page
language string Language code for the page encoded as country_language. Country code should set according to ISO 3166-1 standard and the language code - to ISO 639-1. Defaults to ru
page string Page name. Defaults to index.html
onError Function((Float) -> Void) Error handling function. The function will be fired on any error thrown on the widget
contentType Stream24ContentType Content type of the page. One of .shopInShops or .minisite. Defaults to .minisite
onHeightSet Function((Float) -> Void) Action to perform once the page is loaded. scrollHeight passed as input parameter. Usually used to set dynamic height of the webview

Example

import SwiftUI
import _24stream

struct ExamplePage: View {

    //necessary for automatic height calculation
    @State var richHeight: CGFloat = 0
    func setHeight(height:Float ) {
        richHeight = CGFloat(height)
    }

    // for handling any errors
    func onError(error: String) {
        print(error)
    }
    
    var body: some View   {
        ScrollView {
            RichPage(  brand: "TestBrand",
                       retailerDomain: "24ttl.ru",
                       productId: "920-969696",
                       templateType: "master_template",
                       language: "ru",
                       page: "index.html",
                       contentType: _24StreamContentType.minisite,
                       onHeightSet: setHeight,
                       onError: onError).frame(height:richHeight)
        }
        
    }
}

struct ExamplePage_Previews: PreviewProvider {
    static var previews: some View {
        ExamplePage()
    }
}
Sandbox

For hands-on examples, we have developed the Sandbox where you can play with the parameters.