JDownloader Community - Appwork GmbH
 

Reply
 
Thread Tools Display Modes
  #121  
Old 23.06.2017, 18:24
davlo davlo is offline
Registered / Inactive
 
Join Date: Apr 2017
Posts: 2
Default link monitoring

i don't know how to script but im hoping someone can help! is it possible to make a script that will monitor a certain link for new updates? so if a new image gets uploaded to the link being monitored it will automatically be downloaded by jdownloader.
Reply With Quote
  #122  
Old 26.06.2017, 07:25
mgpai mgpai is offline
Script Master
 
Join Date: Sep 2013
Posts: 1,591
Default

It is not possible to get just the 'new updates' using event scripter, unless it is a RSS feed (even then, it is not very simple). Each time a link is checked, all links subsequently originating from that link will be fetched. Configure JD (Advanced Settings: LinkgrabberSettings.handledupesonconfirmlatestselection) to automatically remove duplicate links.

Code:
// Add link at user defined interval
// Trigger required: 'Interval'
// Set interval to 3600000 ms (1 Hour) or more

var link = "**External links are only visible to Support Staff**; // <- Set download link

if (interval >= 3600000) {
    callAPI("linkgrabberv2", "addLinks", {
        "autostart": true,
        "deepDecrypt": false, // <- Set to 'true' to enable deep analyse
        "links": link,
    })
}
Reply With Quote
  #123  
Old 30.06.2017, 12:35
Informativ Informativ is offline
Registered / Inactive
 
Join Date: Nov 2016
Posts: 106
Default

Spoiler:
Quote:
Originally Posted by mgpai View Post
Note: This script will delete packages from download list and files/folders from disk.

Code:
// Delete 'jpg' and 'txt' files and remove download folder if empty.
// Trigger required: "A Download Stopped"

var links = link.getPackage().getDownloadLinks();

var packageMatch = links.some(function(link) {
    var ext = getPath(link.getDownloadPath()).getExtension();
    return ext == "jpg" || ext == "txt";
});

if (packageMatch) {
    var packageTried = links.every(function(link) {
        var status = link.getStatus();
        return status == "Skipped - Captcha is required" || status !== null;
    });

    if (packageTried) {
        var otherLinks = links.filter(function(link) {
            var ext = getPath(link.getDownloadPath()).getExtension();
            return !(/jpg|txt/).test(ext);
        });

        if (otherLinks.length) {
            var packageFailed = otherLinks.every(function(link) {
                var status = link.getStatus();
                return status == "Skipped - Captcha is required" || status == "File not found";
            });

            if (packageFailed) {
                package.remove();
                getPath(package.getDownloadFolder()).getChildren().forEach(function(file) {
                    var ext = getPath(file).getExtension();
                    if (ext == "txt" || ext == "jpg") {
                        file.delete();
                        file.getParent().delete();
                    }
                });
            }
        }
    }
}


hey, this script does not work for me

Spoiler:

Reply With Quote
  #124  
Old 30.06.2017, 15:31
mgpai mgpai is offline
Script Master
 
Join Date: Sep 2013
Posts: 1,591
Default

Quote:
Originally Posted by Informativ View Post
hey, this script does not work for me
I have tested the script again and it is working fine. If you have some javascript knowledge, I can help you troubleshoot it at your end. Other than that, I don't think there is anything much I can do.
Reply With Quote
  #125  
Old 09.07.2017, 07:27
kogepan kogepan is offline
Vacuum Cleaner
 
Join Date: Feb 2015
Posts: 15
Default Package Merging

Hi,

Is there any way to merge packages when "Packagizer Hook" is triggered? I tried to call "movetoNewPackage", but couldn't find a getUUID function to get linkIds in PackagizerLinkSandbox.

Code:
function setNewFolder() {
    callAPI("linkgrabberv2", "movetoNewPackage", [linkIds], [], newPkgName, downloadPath);
}
When I import a folder link of pan.baidu.com (containing multiple links), JD2 sometimes (not always) creates multiple packages, so I'd like to merge those divided links into one single package.

Thanks!

Last edited by kogepan; 09.07.2017 at 07:31. Reason: typo
Reply With Quote
  #126  
Old 09.07.2017, 09:17
raztoki's Avatar
raztoki raztoki is offline
English Supporter
 
Join Date: Apr 2010
Location: Australia
Posts: 17,668
Default

@kogepan
usually placed in separate packages due to sub directories within the pan folder url
not sure about your query though, need to wait for mgpai or jiaz to respond
__________________
raztoki @ jDownloader reporter/developer
http://svn.jdownloader.org/users/170

Don't fight the system, use it to your advantage. :]
Reply With Quote
  #127  
Old 09.07.2017, 13:46
mgpai mgpai is offline
Script Master
 
Join Date: Sep 2013
Posts: 1,591
Default

Quote:
Originally Posted by kogepan View Post
... but couldn't find a getUUID function to get linkIds in PackagizerLinkSandbox.
UUID is not present/available for "packagizer link".

Quote:
Originally Posted by kogepan View Post
... I'd like to merge those divided links into one single package.
Although It is possible to set the package name using other properties/methods available for the "Packagizer Hook" (for e.g. Based on matching host), it could be overwritten (by plugins) when the link is added to linkgrabber. Using "Packagizer Rule" would be a better option in this case.

Other properties/methods available for the "Packagizer Hook" can be used to set the package name.
Code:
// Set Package Name based on matching host
// Trigger Required: "Packagizer Hook"

var host = "myhost.com";
var packageName = "myPackageName";

if (link.getHost() == host) link.setPackageName(packageName);

It might be simpler/easier to merge the links to a single package using a "Packagizer Rule".

Last edited by mgpai; 09.07.2017 at 15:10. Reason: Correction and adding example script.
Reply With Quote
  #128  
Old 09.07.2017, 18:44
kogepan kogepan is offline
Vacuum Cleaner
 
Join Date: Feb 2015
Posts: 15
Default

Hi,

@mgpai
Thank you very much for your instruction! I got to be able to merge my packages by using setPackageName method. I'll consider "Packagizer Rule" also for the next time as you suggested.

@raztoki
Thank you for your explanation. I got that separate packages placing is the expected behavior. It may be the plugin version or something, I've seen merged placing in the past. Anyway, now I can controll package behavior thanks to mgpai.

Thanks a lot!
Reply With Quote
  #129  
Old 19.07.2017, 13:04
mgpai mgpai is offline
Script Master
 
Join Date: Sep 2013
Posts: 1,591
Default

Related Topic: #74182
Code:
// Extract/set package name from file name
// Tigger Required: "Packagizer Hook"
// Forum Topic: https://board.jdownloader.org/showthread.php?t=74182

var fn = link.getName();
var re = /(.+).s\d{2}e\d{2}.+/i;
var re2 = /[._]/g;
var pn = re.test(fn) ? fn.match(re)[1].replace(re2, " ") : false;

if (pn) link.setPackageName(pn);
Reply With Quote
  #130  
Old 19.07.2017, 21:36
Dedi
Guest
 
Posts: n/a
Question

Hey everyone!

I browsed the hole thread, but haven't found an answer to my problem. Hope you can help.

Often, when extracting movies, Subs and Proofs are packed again into the main archive. So lets say we have the main movie archive and within we got 2 more archives for the Proof and the Subs folders. So I got three FINISHED extraction events.

Is it anyhow possible to recognize only the last extraction?
Reply With Quote
  #131  
Old 20.07.2017, 03:28
raztoki's Avatar
raztoki raztoki is offline
English Supporter
 
Join Date: Apr 2010
Location: Australia
Posts: 17,668
Default

@Dedi
sounds more like a extraction issue vs event scripter.
do you use recursive extraction ? think the setting is 'Extraction.deepextractionenabled' within advanced setting.

raztoki
__________________
raztoki @ jDownloader reporter/developer
http://svn.jdownloader.org/users/170

Don't fight the system, use it to your advantage. :]
Reply With Quote
  #132  
Old 20.07.2017, 08:52
mgpai mgpai is offline
Script Master
 
Join Date: Sep 2013
Posts: 1,591
Default

Quote:
Originally Posted by Dedi View Post
... Is it anyhow possible to recognize only the last extraction? ...
Code:
if (archive.getDownloadLinks() != null){/*your code*/};
Reply With Quote
  #133  
Old 20.07.2017, 09:10
Jiaz's Avatar
Jiaz Jiaz is offline
JD Manager
 
Join Date: Mar 2009
Location: Germany
Posts: 81,030
Default

Quote:
Originally Posted by Dedi View Post
Is it anyhow possible to recognize only the last extraction?
I'm sorry but no, there is none
__________________
JD-Dev & Server-Admin
Reply With Quote
  #134  
Old 20.07.2017, 09:11
Jiaz's Avatar
Jiaz Jiaz is offline
JD Manager
 
Join Date: Mar 2009
Location: Germany
Posts: 81,030
Default

Quote:
Originally Posted by mgpai View Post
Code:
if (archive.getDownloadLinks() != null){/*your code*/};
That's not complete.
Archive(Links) -> Archive(Files) can have multiples Archives(Files)..
This piece of code does only check if extracted files are from list or from files
__________________
JD-Dev & Server-Admin
Reply With Quote
  #135  
Old 20.07.2017, 12:23
Dedi
Guest
 
Posts: n/a
Thumbs up

@raztoki: Thank you for this hint. I'll try this. Cause' I normally don't need the archives within the main archive.

@mgpai/jiaz: Unfortunately the within archives are extracted after the main archive. This lead to the problem, that I can't rely on the fact that the download link of the archive is null.

So if I check the event that the main archive is extracted, the archive isn't extracted completely (extraction is still in status running), so triggering upload is not possible. When checking afterwards if the download link is null, I also don't want to trigger the upload.

So it's a bit tricky.

Spoiler:
Code:
/*
 * Executes script on any extraction event.
 *
 * @Trigger		Any Extraction Event
 * @Author		Dedi
 *
 * Possible events are:
 * - START
 * - QUEUED
 * - OPEN_ARCHIVE_SUCCESS
 * - ACTIVE_ITEM
 * - EXTRACTING
 * - FINISHED
 * - CLEANUP
 */

require("**External links are only visible to Support Staff**);
 
function getArchive() {
    return archive;
}

function getPackage() {
	return getArchive().getDownloadLinks()[0].getPackage();
}

function getDownloadLinksFromPackage() {
	return getPackage().getDownloadLinks();
}

function getExtractionQueue() {
    return callAPI("extraction", "getQueue");
}

function getUploadFolder() {
		var pattern = /(M:\\E\\[^\\]*)(.*)/g
		var match = pattern.exec(getArchive().getExtractToFolder());
		return match[0];
}

function removeExtractedFiles() {
    extractedArchiveFiles = getArchive().getExtractedFiles();
    for (var i = 0; i < extractedArchiveFiles.length; i++) {
        getPath(extractedArchiveFiles[i]).delete();
        log("Remove File: " + extractedArchiveFiles[i]);
    }
}

log("Event: " + event);

if(_.chain(getArchive().getDownloadLinks()).size() > 0) {
	if (event == "QUEUED") {
		var queue = getExtractionQueue();
		log("Queue: " + queue.length);
		if (queue.length >= 1) {
			log("Stopping downloads...");
			stopDownloads();
			log("Downloads stopped...");
		}
	}

	if (event == "FINISHED") {
		var unfinishedLinks = _.chain(getDownloadLinksFromPackage())
			.filter(function(link) {
					return link.getArchive != null && link.getExtractionStatus() == "RUNNING";
				})
			.size();
		
		log("There are " + unfinishedLinks + "links for the package " + getPackage().getName());
		
		if(unfinishedLinks <= 0) {
			var uploadFolder = getUploadFolder();

			log("Upload Folder: " + uploadFolder);
			log(callSync("M:\\rcloneUploads.cmd", uploadFolder));

			log("Remove files...");
			removeExtractedFiles();
			log("Files removed...");

			var queue = getExtractionQueue();
			log("Queue: " + queue.length);
			if (queue.length <= 0) {
				log("Start downloads...");
				startDownloads();
				log("Downloads started...");
			}
		}
	}
} else {
	log("Archive (" + getArchive().getName() + ") has no download links!");
}


THX
Reply With Quote
  #136  
Old 20.07.2017, 13:26
Dedi
Guest
 
Posts: n/a
Default

@raztoki: Thank you! The Setting "Extraction: Deep Extraction" in the Advanced Settings did the trick

THX
Reply With Quote
  #137  
Old 02.08.2017, 11:11
Lpxatreyu
Guest
 
Posts: n/a
Default

Hey, guys, I am not good with those scripts so I need your help.
I just want to set start download between 02:00 AM- 08:00 AM.
I can do it in Scheduler but let's say my computer shut down at 07:50 then assume start at 08:20 AM it skipping my stop download setting Because at 08:00 my computer was offline. That's why I just want to make new settings in Event script. Thank you!
Reply With Quote
  #138  
Old 02.08.2017, 11:46
Jiaz's Avatar
Jiaz Jiaz is offline
JD Manager
 
Join Date: Mar 2009
Location: Germany
Posts: 81,030
Default

@Lpxatreyu: Best contact mgpai directly and ask him for little script that starts downloads if time is between xy and downloads are not running
__________________
JD-Dev & Server-Admin
Reply With Quote
  #139  
Old 02.08.2017, 13:51
Lpxatreyu
Guest
 
Posts: n/a
Default

@Jiaz Thank your answer.
And yes all need between x (02.00am) - y (08.00am) my download list start otherwise stop all download.
Can you help me for it @mgpai
Reply With Quote
  #140  
Old 03.08.2017, 14:10
mgpai mgpai is offline
Script Master
 
Join Date: Sep 2013
Posts: 1,591
Default

Quote:
Originally Posted by Lpxatreyu View Post
... need between x (02.00am) - y (08.00am) my download list start otherwise stop all download.
Code:
// Time based download control
// Trigger: "Interval" (Recommended: 300000 ms)

var t1 = 2; // <- Set starting Hour (0-23)
var t2 = 8; // <- Set ending Hour (0-23)

var now = new Date();

if (now > time(t1) && now < time(t2)) {
    if (!isDownloadControllerRunning() && downloadsPending()) startDownloads();
} else {
    if (isDownloadControllerRunning()) stopDownloads();
}

//Functions
function time(hour) {
    return new Date().setHours(hour, 0, 0, 0)
}

function downloadsPending() {
    return getAllDownloadLinks().some(function(link) {
        return link.getFinalLinkStatus() == null;
    });
}
Reply With Quote
  #141  
Old 03.08.2017, 15:17
Jiaz's Avatar
Jiaz Jiaz is offline
JD Manager
 
Join Date: Mar 2009
Location: Germany
Posts: 81,030
Default

@mgpai: very nice and clear solution
__________________
JD-Dev & Server-Admin
Reply With Quote
  #142  
Old 04.08.2017, 03:01
Lpxatreyu
Guest
 
Posts: n/a
Default

Quote:
Originally Posted by mgpai View Post
Code:
// Time based download control
// Trigger: "Interval" (Recommended: 300000 ms)

var t1 = 2; // <- Set starting Hour (0-23)
var t2 = 8; // <- Set ending Hour (0-23)

var now = new Date();

if (now > time(t1) && now < time(t2)) {
    if (!isDownloadControllerRunning() && downloadsPending()) startDownloads();
} else {
    if (isDownloadControllerRunning()) stopDownloads();
}

//Functions
function time(hour) {
    return new Date().setHours(hour, 0, 0, 0)
}

function downloadsPending() {
    return getAllDownloadLinks().some(function(link) {
        return link.getFinalLinkStatus() == null;
    });
}
Firstly I really appreciate for your fast support sir!
I forgot to mention about little change about my question.How about if i just want to change time i meant put minutes as 02:20 AM or 07:44 like this ?
Thank you for all of you !
Reply With Quote
  #143  
Old 04.08.2017, 03:03
Lpxatreyu
Guest
 
Posts: n/a
Default

Quote:
Originally Posted by Jiaz View Post
@mgpai: very nice and clear solution
Yes sir it looks working perfect ! Whenever I came your site and asking something i am getting asnwer&solution very quickly thank you for your service!
Reply With Quote
  #144  
Old 04.08.2017, 08:28
Jiaz's Avatar
Jiaz Jiaz is offline
JD Manager
 
Join Date: Mar 2009
Location: Germany
Posts: 81,030
Default

@Lpxatreyu: All thanks goes to mgpai
__________________
JD-Dev & Server-Admin
Reply With Quote
  #145  
Old 04.08.2017, 14:33
mgpai mgpai is offline
Script Master
 
Join Date: Sep 2013
Posts: 1,591
Default

Quote:
Originally Posted by Lpxatreyu View Post
... How about if i just want to change time i meant put minutes ...
Spoiler:
Code:
// Time based download control
// Trigger: "Interval" (Recommended: 300000 ms)

var t1 = [2, 20]; // <- Set starting time (hours = 0-23, minutes = 0-59)
var t2 = [7, 44]; // <- Set ending time (hours = 0-23, minutes = 0-59)

var now = new Date();

if (now > time(t1[0], t1[1]) && now < time(t2[0], t2[1])) {
    if (!isDownloadControllerRunning() && downloadsPending()) startDownloads();
} else {
    if (isDownloadControllerRunning()) stopDownloads();
}

//Functions
function time(hh, mm) {
    return new Date().setHours(hh, mm, 0, 0)
}

function downloadsPending() {
    return getAllDownloadLinks().some(function(link) {
        return link.getFinalLinkStatus() == null;
    });
}
Reply With Quote
  #146  
Old 05.08.2017, 01:10
Lpxatreyu
Guest
 
Posts: n/a
Default

Quote:
Originally Posted by mgpai View Post
Spoiler:
Code:
// Time based download control
// Trigger: "Interval" (Recommended: 300000 ms)

var t1 = [2, 20]; // <- Set starting time (hours = 0-23, minutes = 0-59)
var t2 = [7, 44]; // <- Set ending time (hours = 0-23, minutes = 0-59)

var now = new Date();

if (now > time(t1[0], t1[1]) && now < time(t2[0], t2[1])) {
    if (!isDownloadControllerRunning() && downloadsPending()) startDownloads();
} else {
    if (isDownloadControllerRunning()) stopDownloads();
}

//Functions
function time(hh, mm) {
    return new Date().setHours(hh, mm, 0, 0)
}

function downloadsPending() {
    return getAllDownloadLinks().some(function(link) {
        return link.getFinalLinkStatus() == null;
    });
}
Thank you ! Thank you so much...I will try it soon and I don't have any doubt that it won't work Thank you all of you!
Reply With Quote
  #147  
Old 10.08.2017, 21:41
CyanSlinky
Guest
 
Posts: n/a
Default

so i was referred here by Jiaz and was wondering if mgpai or someone could help me automate downloading of new youtube channel videos? maybe grab channels from a text file and check them for new videos somehow? anyways I'd really appreciate it if someone could help me with this.
Reply With Quote
  #148  
Old 12.08.2017, 13:35
mgpai mgpai is offline
Script Master
 
Join Date: Sep 2013
Posts: 1,591
Default

Quote:
Originally Posted by CyanSlinky View Post
... automate downloading of new youtube channel videos ...
Script in Post #84 can be used to fetch recent videos from RSS feeds.
Reply With Quote
  #149  
Old 15.08.2017, 17:30
facecat
Guest
 
Posts: n/a
Default

Hi,
Jdownloader is able to moove some specific size files to another folder without script ?
Like
file<2giga into C:\Users\Desktop\small
file>2giga into C:\Users\Desktop\big

If not mgpai can you make me some script if it take you not so much time plz :D
Reply With Quote
  #150  
Old 15.08.2017, 17:32
Jiaz's Avatar
Jiaz Jiaz is offline
JD Manager
 
Join Date: Mar 2009
Location: Germany
Posts: 81,030
Default

Use Packagizer and create two rules to specify different download locations. Please know that this only works for new added links
__________________
JD-Dev & Server-Admin
Reply With Quote
  #151  
Old 15.08.2017, 18:02
facecat
Guest
 
Posts: n/a
Default

Quote:
Originally Posted by Jiaz View Post
Use Packagizer and create two rules to specify different download locations. Please know that this only works for new added links
thx i forgot this one
ps : jd cant see the duration of a file before download ? for making my rule

Last edited by facecat; 15.08.2017 at 18:07.
Reply With Quote
  #152  
Old 15.08.2017, 18:08
Jiaz's Avatar
Jiaz Jiaz is offline
JD Manager
 
Join Date: Mar 2009
Location: Germany
Posts: 81,030
Default

You're welcome
__________________
JD-Dev & Server-Admin
Reply With Quote
  #153  
Old 16.08.2017, 07:36
raztoki's Avatar
raztoki raztoki is offline
English Supporter
 
Join Date: Apr 2010
Location: Australia
Posts: 17,668
Default

note: some limitations
some plugins do not give accurate filesize information
some plugins do not offer filesize information

using the eventscripter is the only effective solution, not packagiser.
__________________
raztoki @ jDownloader reporter/developer
http://svn.jdownloader.org/users/170

Don't fight the system, use it to your advantage. :]
Reply With Quote
  #154  
Old 16.08.2017, 21:21
facecat
Guest
 
Posts: n/a
Default

Quote:
Originally Posted by raztoki View Post
note: some limitations
some plugins do not give accurate filesize information
some plugins do not offer filesize information

using the eventscripter is the only effective solution, not packagiser.
so is it possible with eventscripter to get the duration of a file and move it into a specific folder ?
because finally the filesize is not accurate for what i want to do
Reply With Quote
  #155  
Old 16.08.2017, 21:30
raztoki's Avatar
raztoki raztoki is offline
English Supporter
 
Join Date: Apr 2010
Location: Australia
Posts: 17,668
Default

@facecat

original query
package customiser will work if
- plugin returns filesize && you use filesize to determine large|small
- plugin returns filename && you use filename to determine large|small
- or you can use other components in which package customiser to determine large|small

event scripter will work
- as post download event, as filesize is a known!
- you can also run it via other applications to do the analytical tasks and moving tasks.

duration? I assume you are now referring to video or audio files? You will need to use 3rd party software like ffmpeg or MediaInfo to determine that information.


raztoki
__________________
raztoki @ jDownloader reporter/developer
http://svn.jdownloader.org/users/170

Don't fight the system, use it to your advantage. :]
Reply With Quote
  #156  
Old 16.08.2017, 21:39
facecat
Guest
 
Posts: n/a
Default

yeah duration i mean lenght
sry for my english
Reply With Quote
  #157  
Old 17.08.2017, 20:40
facecat
Guest
 
Posts: n/a
Default

hi
i have mediainfo but idk other software for moving some files without lines of codes
i tried "dropit" but all software like that dont have "lenght" or "duration" rules :(
Reply With Quote
  #158  
Old 18.08.2017, 09:36
Jiaz's Avatar
Jiaz Jiaz is offline
JD Manager
 
Join Date: Mar 2009
Location: Germany
Posts: 81,030
Default

I suggest to contact mgpai and ask him for help. so after download check filesize and then move to desired location
__________________
JD-Dev & Server-Admin
Reply With Quote
  #159  
Old 19.08.2017, 07:04
mgpai mgpai is offline
Script Master
 
Join Date: Sep 2013
Posts: 1,591
Default

Quote:
Originally Posted by facecat View Post
... get the duration of a file and move it into a specific folder ...
Code:
// Move media files based on duration
// Trigger: "A Download Stopped"

if (link.isFinished()) {
    var file = getPath(link.getDownloadPath());
    var fileTypes = ["mkv", "mp4", "avi", "mp3", "m4a", "ogg", "opus"]; // <- Set media file types

    if (fileTypes.indexOf(file.getExtension()) > -1) {
        var cutoff = 60; // <- Set cutoff duration (in minutes)
        var big = "c:/downloads/big"; // <- Set folder
        var small = "c:/downloads/small"; // <- Set folder
        var ffprobe = "c:/apps/ffmpeg/bin/ffprobe.exe"; // <- Set path to ffprobe (Can also use the binary found in 'JD\tools' folder)
        var duration = JSON.parse(callSync(ffprobe, "-v", "-8", "-show_entries", "format", "-of", "json", file)).format.duration;

        if (duration < cutoff * 60) {
            file.moveTo(small);
        } else {
            file.moveTo(big)
        }
    }
}
Reply With Quote
  #160  
Old 19.08.2017, 20:34
facecat
Guest
 
Posts: n/a
Default

thanks mgpai
it works

Last edited by facecat; 20.08.2017 at 00:17.
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

All times are GMT +2. The time now is 16:15.
Provided By AppWork GmbH | Privacy | Imprint
Parts of the Design are used from Kirsch designed by Andrew & Austin
Powered by vBulletin® Version 3.8.10 Beta 1
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.