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

Stay percaya diri #118

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,24 @@
# bodymovin-extension
Bodymovin UI extension panel
# Bodymovin for Telegram Stickers — After Effects extension for exporting Telegram animated stickers

Bodymovin-TG is designed to help you export your animations in the **.TGS** format supported by the Telegram Animated Stickers platform.

### Installing

1. Close After Effects if it's open
2. Install [the ZXP Installer][zxp_installer]
3. Download the latest version of [bodymovin-tg][bodymovin_tg] (*bodymovin-tg.zxp*)
4. Open the ZXP Installer and drag the bodymovin-tg extension into the ZXP Installer window
5. Open After Effects.
**Windows:** Go to Edit > Preferences > Scripting & Expressions > and check "Allow Scripts to Write Files and Access Network"
**Mac:** Go to Adobe After Effects > Preferences > Scripting & Expressions > and check "Allow Scripts to Write Files and Access Network"
6. Under the menu Window > Extensions you should see **Bodymovin for Telegram Stickers**. Now you're good to go!

For more information on creating and exporting Lottie animations, refer to [this guide][ae_guide].

For more information on Telegram Animated Stickers, see [this page][animated_stickers].

[//]: # (LINKS)
[zxp_installer]: https://zxpinstaller.com
[bodymovin_tg]: https://github.com/TelegramMessenger/bodymovin-extension/releases
[ae_guide]: http://airbnb.io/lottie/#/after-effects?id=creating-lottie-animations
[animated_stickers]: https://core.telegram.org/animated_stickers
2 changes: 1 addition & 1 deletion bundle/.debug
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<ExtensionList>
<Extension Id="com.bodymovin.bodymovin">
<Extension Id="org.telegram.bodymovin-tg">
<HostList>

<!-- Comment Host tags according to the apps you want your panel to support -->
Expand Down
12 changes: 6 additions & 6 deletions bundle/CSXS/manifest.xml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<ExtensionManifest Version="5.0" ExtensionBundleId="com.bodymovin.bodymovin" ExtensionBundleVersion="4.5.5"
<ExtensionManifest Version="5.0" ExtensionBundleId="org.telegram.bodymovin-tg" ExtensionBundleVersion="4.5.5"
ExtensionBundleName="bodymovin" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ExtensionList>
<Extension Id="com.bodymovin.bodymovin" Version="4.5.5" />
<Extension Id="org.telegram.bodymovin-tg" Version="4.5.5" />
</ExtensionList>
<ExecutionEnvironment>
<HostList>
<Host Name="AEFT" Version="[13.0,16.9]" Port="8093" />
<Host Name="AEFX" Version="[13.0,16.9]" Port="8093" />
<Host Name="AEFT" Version="[13.0,99.9]" Port="8093" />
<Host Name="AEFX" Version="[13.0,99.9]" Port="8093" />

<!-- Illustrator -->
<!-- <Host Name="ILST" Version="[18.0,18.9]" />-->
Expand All @@ -31,7 +31,7 @@
</RequiredRuntimeList>
</ExecutionEnvironment>
<DispatchInfoList>
<Extension Id="com.bodymovin.bodymovin">
<Extension Id="org.telegram.bodymovin-tg">
<DispatchInfo >
<Resources>
<MainPath>./index_dev.html</MainPath>
Expand All @@ -47,7 +47,7 @@
</Lifecycle>
<UI>
<Type>Panel</Type>
<Menu>Bodymovin</Menu>
<Menu>Bodymovin for Telegram Stickers</Menu>
<Geometry>
<Size>
<Height>500</Height>
Expand Down
2 changes: 1 addition & 1 deletion bundle/jsx/compsManager.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ $.__bodymovin.bm_compsManager = (function () {
uri = absoluteURI;
} else {
uri = Folder.desktop.absoluteURI + '/data';
uri += standalone ? '.js' : '.json';
uri += standalone ? '.js' : '.tgs';
}
var f = new File(uri);
var saveFileData = f.saveDlg();
Expand Down
267 changes: 204 additions & 63 deletions bundle/jsx/dataManager.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ $.__bodymovin.bm_dataManager = (function () {
}

function deleteExtraParams(data, settings) {
delete data.markers;
if (data.fonts.length === 0) {
delete data.fonts;
delete data.chars;
Expand Down Expand Up @@ -188,34 +189,169 @@ $.__bodymovin.bm_dataManager = (function () {
bm_eventDispatcher.sendEvent('bm:alert', {message: 'Could not create AVD file'});
_endCallback();
}

var base64Decode = function F(s)
{
var ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

F.cache || F.cache = {
RE_NON_ALPHA: new RegExp('[^' + ALPHA + ']'),
RE_BAD_EQUALS: /\=([^=]|\=\=)/
};

if( (n % 4) || F.cache.RE_NON_ALPHA.test(s) || F.cache.RE_BAD_EQUALS.test(s) )
{
throw Error("Invalid Base64 data");
}

var fChr = String.fromCharCode,
n = s.length >>> 0,
a = [],
c = 0,
i0, i1, i2, i3,
b, b0, b1, b2;

while( c < n )
{
i0 = ALPHA.indexOf(s[c++]);
i1 = ALPHA.indexOf(s[c++]);
i2 = ALPHA.indexOf(s[c++]);
i3 = ALPHA.indexOf(s[c++]);

b = (i0 << 18) + (i1 << 12) + ((i2 & 63) << 6) + (i3 & 63);
b0 = (b & (255 << 16)) >> 16;
b1 = (i2 == 64) ? -1 : (b & (255 << 8)) >> 8;
b2 = (i3 == 64) ? -1 : (b & 255);

a[a.length] = fChr(b0);
if( 0 <= b1 ) a[a.length] = fChr(b1);
if( 0 <= b2 ) a[a.length] = fChr(b2);
}

// Cleanup and return
// ---
s = a.join('');
a.length = 0;
a = fChr = null;
return s;
};

function humanFileSize(size) {
var i = Math.floor( Math.log(size) / Math.log(1024) );
return ( size / Math.pow(1024, i) ).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
}

function dataCompressed(base64Data) {
var buf = base64Decode(base64Data)
if (buf.length > 1024 * 64) {
bm_eventDispatcher.sendEvent('bm:alert', {message: 'Failed!<br />Result file size of ' + humanFileSize(buf.length) + ' exceeds the limit of 64 KB.<br />Optimize or simplify your composition to meet the criteria'});
_endCallback();
return;
}

try {
var f = new File(_destinationPath);
f.encoding = "BINARY";
f.open ("w");
f.write(buf)
f.close()

} catch (errr) {
bm_eventDispatcher.sendEvent('bm:alert', {message: 'Could not write file.<br /> Make sure you have enabled scripts to write files. <br /> Edit > Preferences > General > Allow Scripts to Write Files and Access Network '});
}

_endCallback();
}

function checkItems(items, shapes) {
var error = null;
if (items != null) {
var i, itemsLen = items.length;

for (i = 0; i < itemsLen; i += 1) {
if (items[i].ty == "rp") {
error = 'Composition should not include any Repeaters';
break;
}
if (items[i].ty == "sr") {
error = 'Composition should not include any Star Shapes';
break;
}
if (items[i].ty == "gs") {
error = 'Composition should not include any Gradient Strokes';
break;
}

if (error == null && shapes == true) {
error = checkItems(items[i].it, false)
if (error != null) {
break;
}
}
}
}
return error;
}

function checkLayers(layers) {
var error = null;
if (layers != null) {
var i, layersLen = layers.length;
loop:
for (i = 0; i < layersLen; i += 1) {
if (layers[i].ddd != null && layers[i].ddd != 0) {
error = 'Composition should not include any 3D Layers';
break;
}
if (layers[i].sr != null && layers[i].sr != 1) {
error = 'Composition should not include any Time Stretching';
break;
}
if (layers[i].tm != null) {
error = 'Composition should not include any Time Remapping';
break;
}
if (layers[i].ty === 1) {
error = 'Composition should not include any Solids';
break;
}
if (layers[i].ty === 2) {
error = 'Composition should not include any Images';
break;
}
if (layers[i].ty === 5) {
error = 'Composition should not include any Texts';
break;
}
if (layers[i].ty === 9) {
error = 'Composition should not include any Videos';
break;
}
if (layers[i].hasMask === true || layers[i].masksProperties != null) {
error = 'Composition should not include any Masks';
break;
}
if (layers[i].ao === 1) {
error = 'Composition should not include any Auto-Oriented Layers';
break;
}

error = checkItems(layers[i].shapes, true);
if (error != null) {
break;
}
}
}
return error;
}

function saveData(data, destinationPath, config, callback) {
_endCallback = callback;
_destinationPath = destinationPath;
deleteExtraParams(data, config);
separateComps(data.layers, data.comps);
var dataFile, segmentPath, s, string;
if (config.segmented) {
splitAnimation(data, config.segmentedTime);
var i, len = animationSegments.length;
var filePathName = destinationPath.substr(destinationPath.lastIndexOf('/') + 1);
filePathName = filePathName.substr(0, filePathName.lastIndexOf('.'));
for (i = 0; i < len; i += 1) {
segmentPath = destinationPath.substr(0, destinationPath.lastIndexOf('/') + 1);
segmentPath += filePathName + '_' + i + '.json';
dataFile = new File(segmentPath);
dataFile.open('w', 'TEXT', '????');
dataFile.encoding = 'UTF-8';
string = JSON.stringify(animationSegments[i]);
try {
dataFile.write(string); //DO NOT ERASE, JSON UNFORMATTED
//dataFile.write(JSON.stringify(ob.renderData.exportData, null, ' ')); //DO NOT ERASE, JSON FORMATTED
dataFile.close();
} catch (err) {
bm_eventDispatcher.sendEvent('bm:alert', {message: 'Could not write file.<br /> Make sure you have enabled scripts to write files. <br /> Edit > Preferences > General > Allow Scripts to Write Files and Access Network '});
}
}
} else if (data.comps) {

if (data.comps) {
if (data.assets) {
data.assets = data.assets.concat(data.comps);
} else {
Expand All @@ -224,59 +360,64 @@ $.__bodymovin.bm_dataManager = (function () {
data.comps = null;
delete data.comps;
}
dataFile = new File(destinationPath);
dataFile.open('w', 'TEXT', '????');
dataFile.encoding = 'UTF-8';
string = JSON.stringify(data);

var string = JSON.stringify(data);
string = string.replace(/\n/g, '');
////
if (config.demo) {
var demoStr = bm_downloadManager.getDemoData();
demoStr = demoStr.replace('"__[[ANIMATIONDATA]]__"', "" + string + "");
if(data.ddd) {
demoStr = demoStr.replace('__[[RENDERER]]__', "html");
} else {
demoStr = demoStr.replace('__[[RENDERER]]__', "svg");
}
var filePathName = destinationPath.substr(destinationPath.lastIndexOf('/') + 1);
var demoDestinationPath = destinationPath.replace(filePathName,'demo.html');
var demoFile = new File(demoDestinationPath);
demoFile.open('w', 'TEXT', '????');
demoFile.encoding = 'UTF-8';
try {
demoFile.write(demoStr); //DO NOT ERASE, JSON UNFORMATTED
//dataFile.write(JSON.stringify(ob.renderData.exportData, null, ' ')); //DO NOT ERASE, JSON FORMATTED
demoFile.close();
} catch (errr) {
bm_eventDispatcher.sendEvent('bm:alert', {message: 'Could not write file.<br /> Make sure you have enabled scripts to write files. <br /> Edit > Preferences > General > Allow Scripts to Write Files and Access Network '});

var error = null;

var frameRate = data.fr;
if (frameRate != 30.0 && frameRate != 60.0) {
error = 'Composition framerate must be exactly 30 or 60 FPS';
}

var totalFrames = data.op - data.ip;

var duration = totalFrames / frameRate;
if (error == null && duration > 3.0) {
error = 'Composition duration must be not greater than 3 seconds';
}

if (error == null && !((data.w == 100 && data.h == 100) || (data.w == 512 && data.h >= 512 && data.h <= 832 && data.h % 16 == 0) || (data.h == 512 && data.w >= 512 && data.w <= 832 && data.w % 16 == 0))) {
error = 'Composition dimensions should be exactly 512x512px (or 100x100px for sticker set thumbnail)';
}

if (error == null && data.ddd != null && data.ddd != 0) {
error = 'Composition should not include any 3D Layers';
}

var assets = data.assets;
if (error == null && assets != null) {
var i, assetsLen = assets.length;
for (i = 0; i < assetsLen; i += 1) {
var layers = assets[i].layers;
error = checkLayers(layers);
if (error != null) {
break;
}
}
}
if (config.standalone) {
var bodymovinJsStr = bm_downloadManager.getStandaloneData();
string = bodymovinJsStr.replace("\"__[ANIMATIONDATA]__\"", string );
string = string.replace("\"__[STANDALONE]__\"", 'true');

if (error == null) {
error = checkLayers(data.layers);
}

////
try {
dataFile.write(string); //DO NOT ERASE, JSON UNFORMATTED
//dataFile.write(JSON.stringify(ob.renderData.exportData, null, ' ')); //DO NOT ERASE, JSON FORMATTED
dataFile.close();
} catch (errr) {
bm_eventDispatcher.sendEvent('bm:alert', {message: 'Could not write file.<br /> Make sure you have enabled scripts to write files. <br /> Edit > Preferences > General > Allow Scripts to Write Files and Access Network '});

if (error != null) {
bm_eventDispatcher.sendEvent('bm:alert', {message: 'Render Failed!<br />' + error});
_endCallback()
return
}

bm_eventDispatcher.sendEvent('tg:compress', {path: destinationPath, message: string});

animationSegments = [];
segmentCount = 0;
if(config.avd) {
exportAVDVersion(data);
} else {
_endCallback();
}
}

ob.saveData = saveData;
ob.saveAVDData = saveAVDData;
ob.saveAVDFailed = saveAVDFailed;
ob.dataCompressed = dataCompressed;

return ob;
}());
2 changes: 1 addition & 1 deletion bundle/jsx/elements/layerElement.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ $.__bodymovin.bm_layerElement = (function () {
if (lType !== layerTypes.camera) {
bm_transformHelper.exportTransform(layerInfo, layerData, frameRate);
bm_maskHelper.exportMasks(layerInfo, layerData, frameRate);
bm_effectsHelper.exportEffects(layerInfo, layerData, frameRate, includeHiddenData);
bm_effectsHelper.exportEffects(layerInfo, layerData, frameRate);
bm_layerStylesHelper.exportStyles(layerInfo, layerData, frameRate);
bm_timeremapHelper.exportTimeremap(layerInfo, layerData, frameRate);
}
Expand Down
Loading