Express is a light-weight source provider package framework for Syncler. Unlike Kosmos, Express does not use any javascript. Instead it uses only one json file to define a set of rules to retrieve data from the internet.
There's no way to debug this package during development as of now. Developing tooling for debugging is beyond the scope of this app. If you have developed any tooling and would like to share, please send us your github repository and we will link to it here.
Full documenatation of how it works can be found after the sample package.
{
"_manifest" : {
"name": "Sample Package",
"id": "an.universally.unique.id.to.identify.your.package",
"version": 5,
"classPath": "",
"permaUrl": "http://bit.ly/...",
"cacheServer": "cache-server.example.com:443"
},
"provider1": {
"name": "Provider 1",
"enabled": true,
"languages": ["en"],
"base_url": "https://www.some-provider.com/search/",
"response_type": "text",
"movie": {
"query": "{query}/Movies/1/",
"keywords": "{title} {year}"
},
"episode": {
"query": "{query}/TV/1/",
"keywords": "{title} {episodeCode}"
},
"anime": {
"query": "{query}/Anime/1/",
"keywords": "{title} {episode}"
},
"html_parser": {
"row": "doc.querySelectorAll('tbody > tr')",
"title": "row.querySelector('a:nth-child(2)').innerHTML",
"peers": "row.querySelector('.leeches').innerHTML",
"seeds": "row.querySelector('.seeds').innerHTML",
"size": "row.querySelector('tbody > tr .size').textContent.split('B')[0] + 'B'",
"url": "'https://www.some-provider.com/'+ row.querySelector('a:nth-child(2)').getAttribute('href')"
},
"source_is_in_sub_page": true,
"title_replacement": {
"'s": "s",
"\"": ""
}
},
"provider2": {
"name": "Provider 2",
"enabled": true,
"languages": [
"en"
],
"base_url": "https://some-provider2.org/api",
"response_type": "json",
"time_to_wait_between_each_request_ms": 300,
"time_to_wait_on_too_many_request_ms": 5000,
"token": {
"query": "?get_token=get_token",
"token_validity_time_ms": 840000,
"token_format": {
"token": "token"
}
},
"movie": {
"query": "?mode=search&search_imdb={query}&category=movies&token={token}",
"keywords": "{imdbId}"
},
"episode": {
"query": "?mode=search&search_string={query}&category=tv&token={token}",
"keywords": "{title} {episodeCode}"
},
"json_format": {
"results": "torrent_results",
"url": "download",
"title": "title",
"seeds": "seeders",
"peers": "leechers",
"size": "size"
},
"title_replacement": {
"'s": "s",
"\"": ""
}
}
}
_manifest
elementOptional element to include package metadata. It is recommended you provide this.
Here we have 2 providers provider1 and provider2. The first one will parse html pages while the second will use a JSON API
For example, we want to look for the movie "Syncler Movie" aired on 2020. Here are the steps Syncler executes to search for this query:
{title} {year}
by Syncler Movie 2010
(below all available variables){query}/Movies/1/
by Syncler%20Movie%202020/Movies/1/
GET
to https://www.some-provider.com/search/Syncler%20Movie%202020/Movies/1/
If the url returns a 200 response, Syncler will parse the HTML received using html_parser property. It will:
doc.querySelectorAll('tbody > tr')
where doc
is the HTML document receivedrow.querySelector('a:nth-child(2)').innerHTML
row.querySelector('.leeches').innerHTML
row.querySelector('.seeds').innerHTML
row.querySelector('tbody > tr .size').textContent.split('B')[0] + 'B'
(If the size is a number it will be converted automatically)source_is_in_sub_page = true
it indicates url retrieved is not the torrent url but a page where the source url will be found. We get this sub page url like this from row.querySelector('a:nth-child(2)').getAttribute('href')
.The execution pattern for TV shows is the same. {episodeCode}
will look like sAAeBB where AA is the season number and BB is the episode number.
A JSON API is always preferred than parsing HTML. Here's how to search for "Syncler Movie 2020" with the IMDb id: "tt1111111"
This API needs a token for every request, first Syncler will retrieve this token because it is dynamic and it doesn't last forever.
Syncler will execute "GET" on "https://some-provider2.org/api?get_token=get_token". It expects a JSON response formatted as:
{
"token":"aToken"
}
Once the token is retrieved, it will be stored for "840000" ms, after that another token will be requested before new requests.
Here the keywords are not title+year but imdbId. Searching by imdbId will provide accurate results.
Syncler will provide all the replacements and finally call GET on "https://some-provider2.org/api?mode=search&search_imdb=tt1111111&category=movies&token=aToken"
The expected result for this request will look like:
{
"torrent_results": [
{
"url":"magnet:XXXXX",
"title":"Syncler Movie 2020 By AAA",
"seeds":100,
"peers": 90,
"size":11111
},
{
"url":"magnet:XXXXX",
"title":"Syncler Movie 2020 By BBB",
"seeds":55,
"peers": 20,
"size":99999
},
....
]
}
This is the list of all variable that can be added.