diff --git a/packages/mediacenter/kodi/config/Database/Addons19.db b/packages/mediacenter/kodi/config/Database/Addons19.db
index d21c56fcd13250e29bfcd54ba2608c69a11df81c..e5f97097ae9caf7254f8c7903c2036bd24620f6d 100644
Binary files a/packages/mediacenter/kodi/config/Database/Addons19.db and b/packages/mediacenter/kodi/config/Database/Addons19.db differ
diff --git a/packages/mediacenter/kodi/config/Database/ViewModes6.db b/packages/mediacenter/kodi/config/Database/ViewModes6.db
index af9ef4c6edb6d98c801747608f96beae04a280fe..c276b1feade1db6e5c90c71bccedce795a6e8f4e 100644
Binary files a/packages/mediacenter/kodi/config/Database/ViewModes6.db and b/packages/mediacenter/kodi/config/Database/ViewModes6.db differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.torrentrssviewer/resources/language/version.txt b/packages/mediacenter/kodi/config/base-addon_data/plugin.program.advancedsettings/XML_FILES/advancedsettings.xml
similarity index 100%
rename from packages/mediacenter/kodi/config/base-addons/plugin.video.torrentrssviewer/resources/language/version.txt
rename to packages/mediacenter/kodi/config/base-addon_data/plugin.program.advancedsettings/XML_FILES/advancedsettings.xml
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.1337x/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.1337x/settings.xml
index e4e09a24bb4f4124c9b5f19178756f0848e472c0..e008c4ebc65f7486017c5ed3d63b82f7335e746f 100644
--- a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.1337x/settings.xml
+++ b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.1337x/settings.xml
@@ -1,6 +1,7 @@
-
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.castaway/cache.db b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.castaway/cache.db
new file mode 100644
index 0000000000000000000000000000000000000000..0637825c3ef6b9dcf2d0ddc745d0a71fa6bf73dc
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.castaway/cache.db differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.genesis/library.db b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.genesis/library.db
index 24674b281d6764169c6fbb9eabe0bf1b8fe3be07..9fa79f16725dd7951965746526c36f3b9e9c0148 100644
Binary files a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.genesis/library.db and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.genesis/library.db differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/bin/linux_armv7/quasar b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/bin/linux_armv7/quasar
new file mode 100755
index 0000000000000000000000000000000000000000..db173816429c4d12911bc9c411ee0a9b8cb785c3
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/bin/linux_armv7/quasar differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.131631.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.131631.es
new file mode 100644
index 0000000000000000000000000000000000000000..d6716e18f3fc3e4d01c7877439b8c9dc7fcf6ab8
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.131631.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.131634.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.131634.es
new file mode 100644
index 0000000000000000000000000000000000000000..b38a1dee7f806fb09cb2b2cbfd53e78aa9ba6724
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.131634.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.135397.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.135397.es
new file mode 100644
index 0000000000000000000000000000000000000000..1e7136c6dc52b7030c923ce704b6671ea94ed274
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.135397.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.140300.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.140300.es
new file mode 100644
index 0000000000000000000000000000000000000000..b20cc580699c5b25501f48e7e12d2269eed0e34c
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.140300.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.140607.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.140607.es
new file mode 100644
index 0000000000000000000000000000000000000000..73ad78faf634fda958106bb75438914d2f69e5a8
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.140607.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.157336.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.157336.es
new file mode 100644
index 0000000000000000000000000000000000000000..5e459b51af11f2c6221ea9c7bf69cb7e0503ead2
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.157336.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.1771.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.1771.es
new file mode 100644
index 0000000000000000000000000000000000000000..d4169d220464fef543fdf08e64af48cf1a481000
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.1771.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.206647.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.206647.es
new file mode 100644
index 0000000000000000000000000000000000000000..215688386936347157ad0b35d892b88789073651
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.206647.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.209112.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.209112.es
new file mode 100644
index 0000000000000000000000000000000000000000..f719876e882732d280a67a181358daa39342c2e0
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.209112.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.262500.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.262500.es
new file mode 100644
index 0000000000000000000000000000000000000000..453f7d042930adaf71ad08aca7bc8df51b4d0eba
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.262500.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.262504.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.262504.es
new file mode 100644
index 0000000000000000000000000000000000000000..a2759f823bd4e5ef192b4a633c5d5958d2ef7942
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.262504.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.269149.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.269149.es
new file mode 100644
index 0000000000000000000000000000000000000000..665ef1e085ec3cc5fe9bee1b40b78fd3c58a2b7c
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.269149.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.273248.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.273248.es
new file mode 100644
index 0000000000000000000000000000000000000000..71ed673d8a489b7fd88d1867586bef6c2e21dab0
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.273248.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.281957.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.281957.es
new file mode 100644
index 0000000000000000000000000000000000000000..9a6bea3b8cc2a5bae85e78814e2815c0d4fe0353
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.281957.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.286217.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.286217.es
new file mode 100644
index 0000000000000000000000000000000000000000..56a49ebd117033f58bed938c41deeb7afca27301
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.286217.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.293660.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.293660.es
new file mode 100644
index 0000000000000000000000000000000000000000..726e8c833c1a3704d840a2c1815df792bc69b070
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.293660.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.336004.en b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.336004.en
new file mode 100644
index 0000000000000000000000000000000000000000..3af79033bcc2c02dbbd8ce314828f25a0104fd27
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.336004.en differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.336004.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.336004.es
new file mode 100644
index 0000000000000000000000000000000000000000..bf7bdb08e93152e4ab4d5fe4157474f0aaf2a750
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.336004.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.76341.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.76341.es
new file mode 100644
index 0000000000000000000000000000000000000000..258bb84e46e6860be50f5038c410d849454e63e8
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.76341.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.87101.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.87101.es
new file mode 100644
index 0000000000000000000000000000000000000000..79b57a4c9d4c5c417b04c1b1f06f03e36f01ee40
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.87101.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.99861.es b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.99861.es
new file mode 100644
index 0000000000000000000000000000000000000000..e6ad161d6d37bfd7c897c7d3dc7925a446d3de6a
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/com.tmdb.movie.99861.es differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/io.scakemyer.quasar.page.cache:8cdb4d274f1b7aceef6c4b1a538383907f442429 b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/io.scakemyer.quasar.page.cache:8cdb4d274f1b7aceef6c4b1a538383907f442429
new file mode 100644
index 0000000000000000000000000000000000000000..35c5b1adbde3114ad4fedb6c742f44b0a2bae61b
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/io.scakemyer.quasar.page.cache:8cdb4d274f1b7aceef6c4b1a538383907f442429 differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/io.scakemyer.quasar.page.cache:d84d1488447649144971a492b243250859c8e64e b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/io.scakemyer.quasar.page.cache:d84d1488447649144971a492b243250859c8e64e
new file mode 100644
index 0000000000000000000000000000000000000000..a99951b9eb213122acbb2c19520edfb624f3abfc
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/cache/io.scakemyer.quasar.page.cache:d84d1488447649144971a492b243250859c8e64e differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/settings.xml
index ded6eb7a99081ee515068435b60438555c118b76..9a6fd20b2d49a72c3a2e00aaca7093a9665052da 100644
--- a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/settings.xml
+++ b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.quasar/settings.xml
@@ -5,8 +5,8 @@
-
-
+
+
@@ -21,11 +21,11 @@
-
-
-
-
-
+
+
+
+
+
@@ -35,16 +35,16 @@
-
-
+
+
-
-
-
-
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.redbull.tv/kodion/cache.sqlite b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.redbull.tv/kodion/cache.sqlite
new file mode 100644
index 0000000000000000000000000000000000000000..86f0e92636d91aac89f8c8709a4a8986c3bc8dae
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.redbull.tv/kodion/cache.sqlite differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.redbull.tv/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.redbull.tv/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ee13546be1c2e4f588d7388f688298fc39de1f81
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.redbull.tv/settings.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.torrentrssviewer/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.torrentrssviewer/settings.xml
index 1c3d822da7eaba0c685c8c014d6a60441b26ffc9..050748f7f7b973b37fae0cd9943de82718cab6b0 100644
--- a/packages/mediacenter/kodi/config/base-addon_data/plugin.video.torrentrssviewer/settings.xml
+++ b/packages/mediacenter/kodi/config/base-addon_data/plugin.video.torrentrssviewer/settings.xml
@@ -6,14 +6,14 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -22,10 +22,10 @@
-
-
-
-
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/screensaver.digitalclock/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/screensaver.digitalclock/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d0c004c05c7bb02d26ea6f2f8c887363b0442199
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/screensaver.digitalclock/settings.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.module.metahandler/meta_cache/video_cache.db b/packages/mediacenter/kodi/config/base-addon_data/script.module.metahandler/meta_cache/video_cache.db
new file mode 100644
index 0000000000000000000000000000000000000000..930376db33dfa5d30d320a651d7cec2b21ff5d16
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.module.metahandler/meta_cache/video_cache.db differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.module.universal/watch_history.db b/packages/mediacenter/kodi/config/base-addon_data/script.module.universal/watch_history.db
new file mode 100644
index 0000000000000000000000000000000000000000..abc956fdcfd3f5c1dd0f13329e94f2f688635101
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.module.universal/watch_history.db differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/1.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/1.png
new file mode 100644
index 0000000000000000000000000000000000000000..74f1135abe53eae5109c9ccd3b6ed5a26719f83d
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/1.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/2.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/2.png
new file mode 100644
index 0000000000000000000000000000000000000000..5ef728236fa5122a3ba85559d36a2d1ee318a84d
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/2.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/3.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/3.png
new file mode 100644
index 0000000000000000000000000000000000000000..5d3eff555ecc4a5d01761787525f54bc4fca3ee8
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/3.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/4.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/4.png
new file mode 100644
index 0000000000000000000000000000000000000000..531b128c1f2e51114e7ba2e1dfffd989158d583a
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/4.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/5.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/5.png
new file mode 100644
index 0000000000000000000000000000000000000000..4c0912c48ad2fb79c1109621768aac3847926bb7
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/5.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/6.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/6.png
new file mode 100644
index 0000000000000000000000000000000000000000..db2dfe257196383ccec4e415861a1589012c51aa
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/6.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/7.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/7.png
new file mode 100644
index 0000000000000000000000000000000000000000..7f3d23683b97df1e6efffa8a1dcf3f6dda323847
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/7.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/8.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/8.png
new file mode 100644
index 0000000000000000000000000000000000000000..bec9f5aeeeb73e2db725f3e59514fc2b0734534a
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/8.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/9.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/9.png
new file mode 100644
index 0000000000000000000000000000000000000000..67a2c30b5512ed5c1f1051181cadeeb32a635ae7
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/9.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/cloudsmap-1458922525.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/cloudsmap-1458922525.png
new file mode 100644
index 0000000000000000000000000000000000000000..40d679acc00bdf8be51758563a33fa4a169da6e7
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/cloudsmap-1458922525.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/cloudsmap-1458922532.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/cloudsmap-1458922532.png
new file mode 100644
index 0000000000000000000000000000000000000000..40d679acc00bdf8be51758563a33fa4a169da6e7
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/cloudsmap/cloudsmap-1458922532.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/1.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/1.png
new file mode 100644
index 0000000000000000000000000000000000000000..23423bad60557ca0e0793447683e7a1cad376211
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/1.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/2.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/2.png
new file mode 100644
index 0000000000000000000000000000000000000000..ddb47826ceb3e8316238982ef6ed97b38b26e42e
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/2.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/3.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/3.png
new file mode 100644
index 0000000000000000000000000000000000000000..e90b9283fd2600d2af1df7977208df949b7e4786
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/3.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/4.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/4.png
new file mode 100644
index 0000000000000000000000000000000000000000..56e2dc26d1ba60f5d0528f80535dd9eb0668026a
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/4.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/5.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/5.png
new file mode 100644
index 0000000000000000000000000000000000000000..564ccb4a7f4c2e34bdc148152b8e2c4825b6a0f7
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/5.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/6.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/6.png
new file mode 100644
index 0000000000000000000000000000000000000000..e3305714829c10e4720609b35eeda7e91e83946f
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/6.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/7.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/7.png
new file mode 100644
index 0000000000000000000000000000000000000000..ece0a8057ca62657d5ca082fab5bfabce000d677
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/7.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/8.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/8.png
new file mode 100644
index 0000000000000000000000000000000000000000..d9aa64c8ac3f1c2b9b9f97f2039e75656314a7af
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/8.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/9.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/9.png
new file mode 100644
index 0000000000000000000000000000000000000000..93456875a696749650f90b615a4df607e2186361
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/9.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/precipmap-1458922525.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/precipmap-1458922525.png
new file mode 100644
index 0000000000000000000000000000000000000000..059c280f64542bfbef57ccb49589745d0fa7b858
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/precipmap-1458922525.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/precipmap-1458922532.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/precipmap-1458922532.png
new file mode 100644
index 0000000000000000000000000000000000000000..059c280f64542bfbef57ccb49589745d0fa7b858
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/precipmap/precipmap-1458922532.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/1.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/1.png
new file mode 100644
index 0000000000000000000000000000000000000000..53b88f4d522a6cf2164493123af652ecda4ac12a
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/1.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/2.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/2.png
new file mode 100644
index 0000000000000000000000000000000000000000..6966942ee7d4de1787acea82ea9b26566a2762e5
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/2.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/3.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/3.png
new file mode 100644
index 0000000000000000000000000000000000000000..1ea2c1022a7d5c9254792c417978ac4a06aab37c
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/3.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/4.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/4.png
new file mode 100644
index 0000000000000000000000000000000000000000..815caf21425df1fdc69d392708f70192775eed55
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/4.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/5.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/5.png
new file mode 100644
index 0000000000000000000000000000000000000000..f8efa40a8b1bca6d59f33d2eca816e3adde609bf
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/5.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/6.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/6.png
new file mode 100644
index 0000000000000000000000000000000000000000..85382c3b20ec9df439b29a58af616d4b9694dc88
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/6.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/7.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/7.png
new file mode 100644
index 0000000000000000000000000000000000000000..6bf64eacb9a85243b52bfc7dac870967ecb9948a
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/7.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/8.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/8.png
new file mode 100644
index 0000000000000000000000000000000000000000..be135fbfecad01c5e4d9dbea9687dca9e26d620e
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/8.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/9.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/9.png
new file mode 100644
index 0000000000000000000000000000000000000000..6364a8719c980670e4811784bdd49383f8ebc355
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/9.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/pressuremap-1458922525.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/pressuremap-1458922525.png
new file mode 100644
index 0000000000000000000000000000000000000000..991a222a5e5e7a2b1b7446e12a9839bfb9416cec
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/pressuremap-1458922525.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/pressuremap-1458922532.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/pressuremap-1458922532.png
new file mode 100644
index 0000000000000000000000000000000000000000..991a222a5e5e7a2b1b7446e12a9839bfb9416cec
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/pressuremap/pressuremap-1458922532.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/1.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/1.png
new file mode 100644
index 0000000000000000000000000000000000000000..dc7091a8366b7ac53585e49986bef451af84da67
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/1.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/2.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/2.png
new file mode 100644
index 0000000000000000000000000000000000000000..2a606531eae509a661c52dd2d861db34a4ab7e73
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/2.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/3.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/3.png
new file mode 100644
index 0000000000000000000000000000000000000000..e1b464edeeab949d5399092d06e1722bc0cadb65
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/3.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/4.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/4.png
new file mode 100644
index 0000000000000000000000000000000000000000..911e43a1bdb389d2ae01cfacbe4f9db4a1ed138c
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/4.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/5.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/5.png
new file mode 100644
index 0000000000000000000000000000000000000000..e8122d5ae1588daee1b3f6f6db24d212ee72eb29
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/5.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/6.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/6.png
new file mode 100644
index 0000000000000000000000000000000000000000..1198b6cec3cc6cfec6d18be1b5c59cdafc6f0872
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/6.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/7.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/7.png
new file mode 100644
index 0000000000000000000000000000000000000000..bf51ca0d96c5bf4d8e3864741ef56c3d2ceb553a
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/7.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/8.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/8.png
new file mode 100644
index 0000000000000000000000000000000000000000..ca681107bff57a780f84d4393f2255a982c397e4
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/8.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/9.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/9.png
new file mode 100644
index 0000000000000000000000000000000000000000..b9e7b61798ac40509c0396ce0b254cb3939f4916
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/9.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/streetmap.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/streetmap.png
new file mode 100644
index 0000000000000000000000000000000000000000..3e2eeb823f8394097aeaeb7b9fd6136f1150c250
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/streetmap-54aedda4261a481c54cac913db93c184/streetmap.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/1.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/1.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c4d8d493f45c98935435e8183fefb92bb8fb06f
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/1.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/2.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/2.png
new file mode 100644
index 0000000000000000000000000000000000000000..a1cbdd6c228c878e5c50d24ce22de40b1556fc03
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/2.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/3.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/3.png
new file mode 100644
index 0000000000000000000000000000000000000000..52e8d1bdc427b1d0072fdd3dd48b030ab79a32e8
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/3.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/4.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/4.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c4d8d493f45c98935435e8183fefb92bb8fb06f
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/4.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/5.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/5.png
new file mode 100644
index 0000000000000000000000000000000000000000..4607aacc9c3949563bc40d3dce69059597fce6aa
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/5.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/6.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/6.png
new file mode 100644
index 0000000000000000000000000000000000000000..361b5e5bb396d71a00d1fad5db0165ba56945b3f
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/6.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/7.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/7.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c4d8d493f45c98935435e8183fefb92bb8fb06f
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/7.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/8.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/8.png
new file mode 100644
index 0000000000000000000000000000000000000000..3487ff0a8aae8a6f77b55f080912578ec2ec8ac3
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/8.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/9.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/9.png
new file mode 100644
index 0000000000000000000000000000000000000000..2af3c8709fce4670144b9449663ab0479ee88e89
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/9.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/tempmap-1458922525.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/tempmap-1458922525.png
new file mode 100644
index 0000000000000000000000000000000000000000..930eeae141407f44300d73e704ca3cef5d8a8453
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/tempmap-1458922525.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/tempmap-1458922532.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/tempmap-1458922532.png
new file mode 100644
index 0000000000000000000000000000000000000000..930eeae141407f44300d73e704ca3cef5d8a8453
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/tempmap/tempmap-1458922532.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/1.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/1.png
new file mode 100644
index 0000000000000000000000000000000000000000..298304f764375d8c5500c41686d90c9268ac5598
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/1.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/2.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/2.png
new file mode 100644
index 0000000000000000000000000000000000000000..28793003b31770ef7448f5ab7abdf6cd04101a8e
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/2.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/3.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/3.png
new file mode 100644
index 0000000000000000000000000000000000000000..2fe08bda7ee1a9f2cdc6cc7ae931f010defb8181
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/3.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/4.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/4.png
new file mode 100644
index 0000000000000000000000000000000000000000..3f89dfc6691e8b466dbf77537fb750d929ec7fa4
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/4.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/5.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/5.png
new file mode 100644
index 0000000000000000000000000000000000000000..7b9e9a9896be318b6cdbf94d24e35c32fac4191e
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/5.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/6.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/6.png
new file mode 100644
index 0000000000000000000000000000000000000000..9896b74d87af97a2d87d57124274a8c00d54fe35
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/6.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/7.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/7.png
new file mode 100644
index 0000000000000000000000000000000000000000..85af09c3a225fed6853b8cf4008ea9b7bae91562
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/7.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/8.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/8.png
new file mode 100644
index 0000000000000000000000000000000000000000..4e79a2ebb4fe7e776ef2bbedcfaea005840dc7ae
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/8.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/9.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/9.png
new file mode 100644
index 0000000000000000000000000000000000000000..21af0dacd61ad70da9d79189971a50bfcd0f7748
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/9.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/windmap-1458922525.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/windmap-1458922525.png
new file mode 100644
index 0000000000000000000000000000000000000000..a1a8425d25aad89e29d88de90a7f0003cc9b3489
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/windmap-1458922525.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/windmap-1458922532.png b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/windmap-1458922532.png
new file mode 100644
index 0000000000000000000000000000000000000000..a1a8425d25aad89e29d88de90a7f0003cc9b3489
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addon_data/script.openweathermap.maps/maps/windmap/windmap-1458922532.png differ
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.provider.manager/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.provider.manager/settings.xml
deleted file mode 100644
index f0f3c073effb2b145db0bbde2b136cf2929a1e5d..0000000000000000000000000000000000000000
--- a/packages/mediacenter/kodi/config/base-addon_data/script.provider.manager/settings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.provider.quasar.manager/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.provider.quasar.manager/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..750b0d8cd4a00db5d82ec406452bc9bee47c27e0
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.provider.quasar.manager/settings.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.divxtotal-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.divxtotal-mc/settings.xml
deleted file mode 100644
index 594ac80c6e8db9194863900e90c3ddc76941f50a..0000000000000000000000000000000000000000
--- a/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.divxtotal-mc/settings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.elitetorrent-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.elitetorrent-mc/settings.xml
deleted file mode 100644
index eb15d26d2ab925184c8b0dbd0d98760628aa769b..0000000000000000000000000000000000000000
--- a/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.elitetorrent-mc/settings.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.kickass-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.kickass-mc/settings.xml
deleted file mode 100644
index 78ac0094697438a9b07fa31714814fcfee386709..0000000000000000000000000000000000000000
--- a/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.kickass-mc/settings.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.thepiratebay-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.thepiratebay-mc/settings.xml
deleted file mode 100644
index 0c555e9e72061ba270d39a1f790c123c05c4ca50..0000000000000000000000000000000000000000
--- a/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.thepiratebay-mc/settings.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.torrentz-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.torrentz-mc/settings.xml
deleted file mode 100644
index 01ace501eb944e0173c276aa037178270028b5dc..0000000000000000000000000000000000000000
--- a/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.torrentz-mc/settings.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.yify-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.yify-mc/settings.xml
deleted file mode 100644
index 6c36412bd05b78afa42ffc632b833d3f59a14f81..0000000000000000000000000000000000000000
--- a/packages/mediacenter/kodi/config/base-addon_data/script.pulsar.yify-mc/settings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.quasar.1333x-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.1333x-mc/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e2a49a36871558ce2d4ce1f4ad2bc483f7f3fe75
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.1333x-mc/settings.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.quasar.bitsnoop-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.bitsnoop-mc/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d29d2a8f9c84f8adaef000d5ccddc9a059a5f230
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.bitsnoop-mc/settings.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.quasar.btjunkie-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.btjunkie-mc/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..05f02d02b5d418a8fcc547861600c4bc6704c702
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.btjunkie-mc/settings.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.quasar.demonoid-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.demonoid-mc/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..05855ffd1b0d2db88df3526fef6e748045c7f02c
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.demonoid-mc/settings.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.quasar.divxatope-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.divxatope-mc/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bbce72738dd694a38cdb66f402b2672ece371871
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.divxatope-mc/settings.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.quasar.extratorrent-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.extratorrent-mc/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e2251f71d55adb5008c305cb90abe707fde49008
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.extratorrent-mc/settings.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.quasar.magnetdl-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.magnetdl-mc/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7cf00fc26b0480a67aad60221dfb722c153a3071
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.magnetdl-mc/settings.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.quasar.mtor-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.mtor-mc/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9921b5077c373cc8db085e4071edc9a991488ee9
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.mtor-mc/settings.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.quasar.newpct1-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.newpct1-mc/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..28051d169817fee0dcc2e9936ae99d5ee7503045
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.newpct1-mc/settings.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.quasar.rarbg-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.rarbg-mc/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..41d20741669fb615bb16d44b035a39d14fa521ab
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.rarbg-mc/settings.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.quasar.torlock-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.torlock-mc/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ebf4d23939d9cc795b7638c2e63b74b1482589d7
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.torlock-mc/settings.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.quasar.torrentz-mc/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.torrentz-mc/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2b543410410adb924bdc1f8ccde4a93c97d75923
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.quasar.torrentz-mc/settings.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.subscription.quasar.favourites/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.subscription.quasar.favourites/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bd5408e1059b9a1b7c6c38b4ecf7c790629bc81b
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.subscription.quasar.favourites/settings.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.subscription.quasar.imdb/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.subscription.quasar.imdb/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1226726bf05441c92a98664af76e1a28dda59f99
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.subscription.quasar.imdb/settings.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.subscription.quasar.trakt/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.subscription.quasar.trakt/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c4ee5fb05c7ff999b3820d8ef68e06451db12b1c
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.subscription.quasar.trakt/settings.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.videoextras/overlay_image_used.txt b/packages/mediacenter/kodi/config/base-addon_data/script.videoextras/overlay_image_used.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b03497f1ad6f69770ae04596e35b1c5cef2f4db0
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.videoextras/overlay_image_used.txt
@@ -0,0 +1 @@
+/usr/share/kodi/addons/script.videoextras/resources/skins/icons/overlay1.png
\ No newline at end of file
diff --git a/packages/mediacenter/kodi/config/base-addon_data/script.videoextras/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/script.videoextras/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f83e91c1f01c1cadff24b3c877628e30034c023a
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/script.videoextras/settings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/service.nextup.notification/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/service.nextup.notification/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..086a7907720a5676b2fee714c1e1fe5b3f3fb1b6
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/service.nextup.notification/settings.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/service.subtitles.opensubtitles/temp/190628b9-7777-4184-ac58-08501f6007b1.srt b/packages/mediacenter/kodi/config/base-addon_data/service.subtitles.opensubtitles/temp/190628b9-7777-4184-ac58-08501f6007b1.srt
new file mode 100644
index 0000000000000000000000000000000000000000..a0e92efe2272f51d0cf065448ff23e3362da8063
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/service.subtitles.opensubtitles/temp/190628b9-7777-4184-ac58-08501f6007b1.srt
@@ -0,0 +1,4965 @@
+ļ»æ1
+00:00:06,001 --> 00:00:11,919
+āŖ āŖ
+
+2
+00:00:13,000 --> 00:00:19,074
+Support us and become VIP member
+to remove all ads from www.OpenSubtitles.org
+
+3
+00:00:50,392 --> 00:00:54,685
+Sync and corrections by btsix
+www.addic7ed.com
+
+4
+00:00:57,349 --> 00:00:59,585
+The day that changed my life.
+
+5
+00:01:01,409 --> 00:01:06,750
+The day that changed my life
+wasn't a day but a night.
+
+6
+00:01:07,259 --> 00:01:10,308
+It was Halloween night.
+
+7
+00:01:10,309 --> 00:01:12,408
+I was Buffalo Bob,
+
+8
+00:01:12,411 --> 00:01:15,610
+and I had the real cowhide vest.
+
+9
+00:01:15,611 --> 00:01:18,510
+It was 1960,
+
+10
+00:01:18,511 --> 00:01:21,950
+and I lived in Holden, Kentucky.
+
+11
+00:01:23,441 --> 00:01:26,470
+I loved Zagnut bars,
+
+12
+00:01:26,471 --> 00:01:31,590
+and my sister Ellen loved Baby Ruth bars.
+
+13
+00:01:31,591 --> 00:01:36,620
+My brother Tuggah loved everything.
+
+14
+00:01:36,621 --> 00:01:40,199
+I don't love Halloween anymore.
+
+15
+00:01:46,461 --> 00:01:49,420
+It was the night my mom, my brother,
+
+16
+00:01:49,421 --> 00:01:52,490
+and my sister was murdered...
+
+17
+00:01:53,551 --> 00:01:54,869
+With a hammer.
+
+18
+00:01:56,966 --> 00:02:01,480
+I remember the blood smelled like pennies.
+
+19
+00:02:05,229 --> 00:02:10,320
+My father killed everyone
+
+20
+00:02:10,322 --> 00:02:12,402
+but me.
+
+21
+00:02:14,212 --> 00:02:17,784
+I don't like to remember it,
+
+22
+00:02:17,786 --> 00:02:20,896
+but I can't forget it.
+
+23
+00:02:22,402 --> 00:02:24,382
+The end.
+
+24
+00:02:26,979 --> 00:02:31,939
+I know it isn't very... long.
+
+25
+00:02:36,889 --> 00:02:38,069
+Harry, that...
+
+26
+00:02:38,070 --> 00:02:40,623
+that's a powerful story.
+
+27
+00:02:42,939 --> 00:02:45,700
+Thank you for sharing that with us.
+
+28
+00:02:47,869 --> 00:02:49,275
+Now, why was this good?
+
+29
+00:02:50,909 --> 00:02:54,620
+Because when we heard it, we knew him.
+
+30
+00:02:54,664 --> 00:02:56,000
+It was honest.
+
+31
+00:02:56,566 --> 00:02:58,908
+What Harry wrote was very painful.
+
+32
+00:02:58,909 --> 00:03:02,088
+The truths probably made us all
+feel a little uncomfortable.
+
+33
+00:03:02,089 --> 00:03:05,888
+Because of that, it was...
+It was very powerful.
+
+34
+00:03:05,889 --> 00:03:06,939
+Let me see that.
+
+35
+00:03:17,069 --> 00:03:19,028
+Boom.
+
+36
+00:03:19,029 --> 00:03:20,089
+Good job.
+
+37
+00:03:20,090 --> 00:03:25,008
+Good job.
+
+38
+00:03:25,009 --> 00:03:27,058
+Thanks.
+
+39
+00:03:27,059 --> 00:03:28,768
+Bye.
+
+40
+00:03:28,770 --> 00:03:31,968
+See you.
+
+41
+00:03:31,969 --> 00:03:33,884
+Harry, hey,
+
+42
+00:03:34,909 --> 00:03:37,833
+that was... That was something.
+
+43
+00:03:37,835 --> 00:03:40,918
+I never got an A+ before.
+
+44
+00:03:40,919 --> 00:03:42,918
+Well, congratulations.
+
+45
+00:03:42,919 --> 00:03:47,566
+Can I ask you a question?
+
+46
+00:03:47,568 --> 00:03:48,801
+Yeah.
+
+47
+00:03:48,803 --> 00:03:53,008
+I'm going for a promotion.
+
+48
+00:03:53,009 --> 00:03:55,928
+Can you write a letter
+
+49
+00:03:55,929 --> 00:03:59,078
+to the principal?
+
+50
+00:03:59,556 --> 00:04:01,871
+Only if you promise to give me a raise
+
+51
+00:04:01,873 --> 00:04:03,998
+as soon as you're running this place.
+
+52
+00:04:03,999 --> 00:04:07,909
+- Yeah.
+- All right, see you.
+
+53
+00:04:15,039 --> 00:04:17,978
+āŖ Bet you got it all planned right āŖ
+
+54
+00:04:17,979 --> 00:04:20,888
+āŖ Bet you never worry āŖ
+
+55
+00:04:20,889 --> 00:04:22,908
+āŖ Never even feel a fright āŖ
+
+56
+00:04:22,909 --> 00:04:26,028
+āŖ Bet you got it all planned right āŖ
+
+57
+00:04:26,029 --> 00:04:29,028
+āŖ Never fit to worry āŖ
+
+58
+00:04:29,029 --> 00:04:31,315
+āŖ Never even feel a fright. āŖ
+
+59
+00:04:31,362 --> 00:04:33,968
+āŖ Single sleeps alone āŖ
+
+60
+00:04:38,979 --> 00:04:42,574
+āŖ āŖ
+
+61
+00:04:45,125 --> 00:04:47,064
+There's not a Pilates class in the world
+
+62
+00:04:47,066 --> 00:04:48,868
+is going to help you if you
+keep eating like that.
+
+63
+00:04:48,869 --> 00:04:51,029
+Thanks for worrying about my ass.
+
+64
+00:04:52,085 --> 00:04:53,780
+Why don't you put a diet
+burger on the menu?
+
+65
+00:04:53,782 --> 00:04:55,881
+I do make a diet burger.
+
+66
+00:04:55,883 --> 00:04:57,422
+It's called a salad.
+
+67
+00:04:57,454 --> 00:04:58,524
+Right.
+
+68
+00:05:00,581 --> 00:05:03,570
+"His eyes were like two round, blue circles
+
+69
+00:05:03,571 --> 00:05:07,550
+with another black circle in the middle"?
+
+70
+00:05:07,551 --> 00:05:09,430
+Well, you know, every once in a while,
+
+71
+00:05:09,431 --> 00:05:11,520
+you get a young Ernest
+Hemingway, don't you?
+
+72
+00:05:11,521 --> 00:05:16,320
+Every once in a while,
+someone gives me a $10 tip.
+
+73
+00:05:16,322 --> 00:05:17,439
+Hmm.
+
+74
+00:05:17,441 --> 00:05:20,450
+We both have a lot to look forward to.
+
+75
+00:05:20,451 --> 00:05:22,480
+- Hey, Christy.
+- Hi.
+
+76
+00:05:22,481 --> 00:05:24,470
+How are you?
+
+77
+00:05:24,471 --> 00:05:25,531
+You look good.
+
+78
+00:05:25,532 --> 00:05:27,471
+Thank you, Al.
+
+79
+00:05:34,381 --> 00:05:36,561
+- Want a bite?
+- It's $1.25.
+
+80
+00:05:36,562 --> 00:05:38,540
+That can't be real beef.
+
+81
+00:05:38,541 --> 00:05:40,561
+You're missing out.
+
+82
+00:05:44,511 --> 00:05:48,490
+I wrote to Helene when
+I heard about your dad.
+
+83
+00:05:48,491 --> 00:05:50,510
+Did you get a chance to talk to him before?
+
+84
+00:05:51,057 --> 00:05:53,017
+He died when I was on the plane to see him.
+
+85
+00:05:54,381 --> 00:05:55,561
+I'm sorry.
+
+86
+00:06:04,491 --> 00:06:07,490
+There's three sets to sign.
+
+87
+00:06:07,491 --> 00:06:10,420
+One for you, one for me,
+and one for the lawyer.
+
+88
+00:06:10,421 --> 00:06:12,450
+Need a pen?
+
+89
+00:06:12,451 --> 00:06:14,361
+I signed already.
+
+90
+00:06:14,501 --> 00:06:17,640
+Right. Always prepared.
+
+91
+00:06:21,381 --> 00:06:23,083
+You doing any writing these days?
+
+92
+00:06:23,085 --> 00:06:24,455
+Nope.
+
+93
+00:06:25,501 --> 00:06:27,498
+I always loved your writing.
+
+94
+00:06:30,421 --> 00:06:31,521
+There you go.
+
+95
+00:06:37,399 --> 00:06:40,958
+Believe it or not, I wish you all the best.
+
+96
+00:06:45,245 --> 00:06:46,661
+Christy.
+
+97
+00:06:55,123 --> 00:06:56,707
+I wish you the best too.
+
+98
+00:07:08,089 --> 00:07:09,099
+Al?
+
+99
+00:07:09,101 --> 00:07:11,360
+Get out of here.
+
+100
+00:07:11,574 --> 00:07:12,613
+Al, you all right?
+
+101
+00:07:13,024 --> 00:07:14,154
+Just go home.
+
+102
+00:07:14,156 --> 00:07:17,144
+Are you okay?
+
+103
+00:07:19,205 --> 00:07:21,692
+Jesus Christ. What happened to you?
+
+104
+00:07:23,701 --> 00:07:25,660
+I'm fine.
+
+105
+00:07:25,661 --> 00:07:27,640
+You're not fine. You look like crap.
+
+106
+00:07:27,641 --> 00:07:28,831
+Go on.
+
+107
+00:07:28,832 --> 00:07:30,986
+What the hell's going on?
+
+108
+00:07:30,987 --> 00:07:32,097
+What happened to you?
+
+109
+00:07:32,099 --> 00:07:34,026
+Ah, shit.
+
+110
+00:07:34,027 --> 00:07:35,117
+Al, Al, Al!
+
+111
+00:07:43,078 --> 00:07:44,977
+Maybe you should take one of those pills
+
+112
+00:07:44,978 --> 00:07:47,997
+the doctor gave you so
+you can get some sleep.
+
+113
+00:07:47,998 --> 00:07:52,017
+I'll start calling the numbers
+on that list of in-home nurses.
+
+114
+00:07:52,461 --> 00:07:53,611
+Just forget it.
+
+115
+00:07:53,612 --> 00:07:55,580
+I don't need a nurse.
+
+116
+00:07:59,421 --> 00:08:00,601
+I don't get it.
+
+117
+00:08:00,602 --> 00:08:02,570
+One minute you're okay;
+the next minute you're not.
+
+118
+00:08:02,571 --> 00:08:05,104
+I... Why didn't you tell me?
+
+119
+00:08:05,169 --> 00:08:08,308
+Just leave everything. I'm fine.
+
+120
+00:08:13,229 --> 00:08:14,821
+You sound fine.
+
+121
+00:08:16,189 --> 00:08:18,308
+Now, you want to tell me
+what the fuck is going on?
+
+122
+00:08:18,309 --> 00:08:20,339
+You heard what's going on.
+
+123
+00:08:21,189 --> 00:08:22,713
+It's cancer.
+
+124
+00:08:24,389 --> 00:08:27,556
+Al, you got... You got
+cancer in five minutes?
+
+125
+00:08:28,209 --> 00:08:30,217
+Oh, I'm tired.
+
+126
+00:08:31,209 --> 00:08:33,600
+Do me a favor.
+
+127
+00:08:34,349 --> 00:08:37,388
+Close the door on your way out.
+
+128
+00:08:43,289 --> 00:08:47,248
+Come over tomorrow,
+
+129
+00:08:47,249 --> 00:08:49,298
+and I'll explain.
+
+130
+00:08:49,299 --> 00:08:51,388
+I'll explain everything.
+
+131
+00:08:56,269 --> 00:08:58,002
+Go home.
+
+132
+00:09:02,169 --> 00:09:03,279
+Male Narrator: In the 1930s,
+
+133
+00:09:03,280 --> 00:09:05,358
+researchers began
+experimenting with a number
+
+134
+00:09:05,359 --> 00:09:08,268
+of so-called shock therapies
+
+135
+00:09:08,269 --> 00:09:10,429
+meant to reroute the circuitry
+of the diseased brain...
+
+136
+00:09:12,259 --> 00:09:14,092
+Narrator: Patients were brought
+to the breach of death...
+
+137
+00:09:14,094 --> 00:09:15,328
+Cody. What the fuck?
+
+138
+00:09:15,329 --> 00:09:18,358
+Narrator: Through an intentional
+overdose of the hormone insulin.
+
+139
+00:09:18,359 --> 00:09:19,389
+Others were given...
+
+140
+00:09:19,390 --> 00:09:23,228
+All right, real quick.
+
+141
+00:09:23,229 --> 00:09:26,178
+Why do we think the filmmakers
+
+142
+00:09:26,179 --> 00:09:28,208
+chose to document these conditions?
+
+143
+00:09:28,209 --> 00:09:31,208
+Narrator: If you look at the
+patient writings after this...
+
+144
+00:09:31,209 --> 00:09:33,348
+People tend to think the
+important stories are wars,
+
+145
+00:09:33,349 --> 00:09:36,515
+elections, political movements,
+
+146
+00:09:37,259 --> 00:09:39,338
+but these people matter.
+
+147
+00:09:39,339 --> 00:09:41,248
+Little things matter.
+
+148
+00:09:44,249 --> 00:09:45,299
+All right, I'll see you tomorrow.
+
+149
+00:09:45,300 --> 00:09:47,208
+Bye, Carrie. Bye.
+
+150
+00:09:47,209 --> 00:09:48,399
+- Cody?
+- Yeah?
+
+151
+00:09:49,339 --> 00:09:51,328
+What's holding your attention
+on this little screen?
+
+152
+00:09:51,329 --> 00:09:53,398
+Uh, here, look.
+
+153
+00:09:53,399 --> 00:09:58,188
+āŖ I don't care, I love it āŖ
+
+154
+00:09:58,189 --> 00:10:00,318
+āŖ I don't care āŖ
+
+155
+00:10:00,319 --> 00:10:02,338
+I could send you the clip if you want it.
+
+156
+00:10:02,339 --> 00:10:04,278
+Yeah, you do that.
+
+157
+00:10:04,279 --> 00:10:06,398
+āŖ On the summer day when you were gone āŖ
+
+158
+00:10:06,399 --> 00:10:08,199
+āŖ I crashed my car into... āŖ
+
+159
+00:10:11,349 --> 00:10:13,238
+All right, here we are.
+
+160
+00:10:16,299 --> 00:10:19,208
+How long have we known each other?
+
+161
+00:10:19,209 --> 00:10:21,188
+10, 12 years.
+
+162
+00:10:21,189 --> 00:10:23,198
+Yeah, yeah, yeah.
+
+163
+00:10:28,219 --> 00:10:30,248
+You trust me?
+
+164
+00:10:30,249 --> 00:10:33,248
+Not really.
+
+165
+00:10:33,249 --> 00:10:36,368
+Well, I'm gonna tell you something
+
+166
+00:10:36,369 --> 00:10:38,348
+that's gonna seem crazy.
+
+167
+00:10:38,349 --> 00:10:40,278
+Okay.
+
+168
+00:10:40,279 --> 00:10:45,168
+But before I do,
+
+169
+00:10:45,169 --> 00:10:48,258
+I need you to go in this closet.
+
+170
+00:10:48,259 --> 00:10:50,328
+Just go in there
+
+171
+00:10:50,329 --> 00:10:54,178
+and take a look around.
+
+172
+00:10:54,179 --> 00:10:57,298
+Stay as long as you need,
+and then come back.
+
+173
+00:10:57,299 --> 00:11:00,238
+Then I'll tell you everything.
+
+174
+00:11:00,239 --> 00:11:03,188
+You want me to go in the
+closet, look around,
+
+175
+00:11:03,189 --> 00:11:04,279
+spend as much time as I need?
+
+176
+00:11:04,280 --> 00:11:06,358
+- I know I sound batshit, okay?
+- Yeah.
+
+177
+00:11:06,359 --> 00:11:10,308
+But I thought about this,
+and it'll just be simpler
+
+178
+00:11:10,309 --> 00:11:12,208
+if you go in, like I'm asking.
+
+179
+00:11:12,209 --> 00:11:13,208
+Go in the closet?
+
+180
+00:11:13,209 --> 00:11:14,379
+Can you do me a goddamn favor
+
+181
+00:11:14,380 --> 00:11:17,278
+and just walk into the fucking closet?
+
+182
+00:11:17,279 --> 00:11:18,349
+Fine.
+
+183
+00:11:18,350 --> 00:11:20,359
+I'm going, okay? Relax.
+
+184
+00:11:24,289 --> 00:11:28,328
+All right, going in the closet.
+
+185
+00:11:28,329 --> 00:11:31,258
+Better not be any spiders in here.
+
+186
+00:11:31,259 --> 00:11:35,248
+Take a few steps forward.
+
+187
+00:11:35,249 --> 00:11:37,288
+Yeah.
+
+188
+00:11:37,289 --> 00:11:40,178
+If I feel any cobwebs on my face...
+
+189
+00:11:40,179 --> 00:11:41,249
+Just keep on.
+
+190
+00:11:41,250 --> 00:11:43,198
+I can't see anything.
+
+191
+00:11:43,199 --> 00:11:46,198
+Just go a few more steps.
+
+192
+00:11:46,199 --> 00:11:48,298
+You want me to keep going?
+
+193
+00:11:48,299 --> 00:11:50,188
+Yep.
+
+194
+00:11:50,189 --> 00:11:53,218
+Whoa!
+
+195
+00:11:59,249 --> 00:12:01,318
+I got it!
+
+196
+00:12:01,319 --> 00:12:04,268
+Man!
+
+197
+00:12:16,309 --> 00:12:18,398
+Oh, for the love of Mike.
+
+198
+00:12:22,189 --> 00:12:25,318
+āŖ Just a little bit longer āŖ
+
+199
+00:12:29,379 --> 00:12:31,389
+You shouldn't be here.
+
+200
+00:12:35,289 --> 00:12:37,348
+You shouldn't be here.
+
+201
+00:12:37,349 --> 00:12:40,218
+Do you hear me? Huh?
+
+202
+00:12:40,219 --> 00:12:42,289
+You shouldn't be here!
+
+203
+00:12:45,329 --> 00:12:48,388
+What the fuck was that?
+
+204
+00:12:48,389 --> 00:12:53,258
+That was October 21, 1960.
+
+205
+00:12:53,259 --> 00:12:55,228
+No, it wasn't.
+
+206
+00:12:55,229 --> 00:12:59,189
+Now you understand why I
+needed you to go in there.
+
+207
+00:13:01,289 --> 00:13:03,198
+What are you saying? What are
+you saying, this is a...
+
+208
+00:13:03,199 --> 00:13:05,388
+Hard to find the right words, I know.
+
+209
+00:13:05,389 --> 00:13:10,338
+But you go through there, and it's 1960.
+
+210
+00:13:10,339 --> 00:13:13,198
+You're saying this is a time portal?
+
+211
+00:13:13,199 --> 00:13:15,238
+I call it a rabbit hole.
+
+212
+00:13:15,239 --> 00:13:16,329
+How long has it been there?
+
+213
+00:13:16,330 --> 00:13:18,238
+Since I had the diner.
+
+214
+00:13:18,239 --> 00:13:20,168
+Who else knows about it?
+
+215
+00:13:20,169 --> 00:13:22,178
+Just you.
+
+216
+00:13:22,179 --> 00:13:26,228
+If I told anyone else about it,
+they might try to stop me.
+
+217
+00:13:26,229 --> 00:13:27,249
+They'd try to stop you?
+
+218
+00:13:27,250 --> 00:13:29,238
+What are you talking about?
+
+219
+00:13:29,239 --> 00:13:31,168
+Al, why did you show this to me?
+
+220
+00:13:31,169 --> 00:13:34,348
+I need you to do what I couldn't.
+
+221
+00:13:34,349 --> 00:13:37,208
+I need you to go back there
+
+222
+00:13:37,209 --> 00:13:41,349
+to prevent the assassination
+of John F. Kennedy.
+
+223
+00:13:48,299 --> 00:13:51,398
+You heard about the butterfly effect?
+
+224
+00:13:51,399 --> 00:13:53,328
+- Yeah.
+- All right.
+
+225
+00:13:53,329 --> 00:13:58,228
+Do you think that if JFK lived,
+
+226
+00:13:58,229 --> 00:14:02,318
+Robert Kennedy would've run
+for president, seriously?
+
+227
+00:14:02,319 --> 00:14:04,258
+It's doubtful.
+
+228
+00:14:04,259 --> 00:14:10,178
+So, if Bobby doesn't run,
+that means no Sirhan Sirhan
+
+229
+00:14:10,179 --> 00:14:13,378
+at the Ambassador Hotel in 1968.
+
+230
+00:14:13,379 --> 00:14:17,208
+Save JFK, save his brother.
+
+231
+00:14:17,209 --> 00:14:20,249
+And that's what I mean about
+the butterfly effect.
+
+232
+00:14:25,309 --> 00:14:27,388
+Then there's Vietnam.
+
+233
+00:14:27,389 --> 00:14:32,238
+Uh, okay, so if you save JFK,
+then there's no Vietnam?
+
+234
+00:14:32,239 --> 00:14:36,378
+Johnson was the one who
+escalated everything in Nam.
+
+235
+00:14:36,379 --> 00:14:41,259
+If Kennedy had survived, no way
+does that escalation continue.
+
+236
+00:14:43,179 --> 00:14:46,198
+Those boys...
+
+237
+00:14:46,199 --> 00:14:49,398
+would've lived.
+
+238
+00:14:49,399 --> 00:14:52,368
+Al, I get it,
+
+239
+00:14:52,369 --> 00:14:57,218
+but changing the past to make
+it how you think it should be
+
+240
+00:14:57,219 --> 00:14:58,399
+just seems...
+
+241
+00:14:58,400 --> 00:15:01,368
+You think Vietnam unfolded
+exactly as it should have,
+
+242
+00:15:01,369 --> 00:15:05,238
+that recent American history
+was just hunky-dory?
+
+243
+00:15:05,239 --> 00:15:07,388
+Saving JFK is a theory.
+
+244
+00:15:07,389 --> 00:15:09,368
+You don't know what it's gonna change!
+
+245
+00:15:09,369 --> 00:15:11,288
+You know what I know?
+
+246
+00:15:11,289 --> 00:15:13,178
+You save Kennedy's life,
+
+247
+00:15:13,179 --> 00:15:15,378
+you make the world a better place.
+
+248
+00:15:15,379 --> 00:15:16,379
+God damn it!
+
+249
+00:15:16,380 --> 00:15:18,328
+Al.
+
+250
+00:15:18,329 --> 00:15:20,388
+Don't you want to do any
+fucking thing that matters?
+
+251
+00:15:20,389 --> 00:15:22,348
+I'm just saying you don't have any proof.
+
+252
+00:15:22,349 --> 00:15:24,388
+You don't know that what you do in the past
+
+253
+00:15:24,389 --> 00:15:26,329
+is gonna change anything here.
+
+254
+00:15:34,299 --> 00:15:36,168
+Go see.
+
+255
+00:15:36,169 --> 00:15:37,389
+You check for yourself.
+
+256
+00:15:37,390 --> 00:15:39,218
+What do you want...
+
+257
+00:15:39,219 --> 00:15:40,329
+Y-you want me to go stab someone?
+
+258
+00:15:40,330 --> 00:15:42,248
+No, Jake.
+
+259
+00:15:42,249 --> 00:15:44,198
+There's a tree out there.
+
+260
+00:15:44,199 --> 00:15:47,199
+Carve something in it. Jesus Christ.
+
+261
+00:15:50,209 --> 00:15:52,279
+Oh, God damn.
+
+262
+00:15:55,169 --> 00:15:58,388
+Fucking... Goddamn...
+
+263
+00:16:02,219 --> 00:16:05,278
+Stupid thing.
+
+264
+00:16:05,279 --> 00:16:07,388
+I hate the fucking dark.
+
+265
+00:16:11,329 --> 00:16:14,338
+Hey, batter, batter!
+
+266
+00:16:24,309 --> 00:16:26,168
+Oh, for the love of Mike.
+
+267
+00:16:34,209 --> 00:16:35,399
+You shouldn't be here.
+
+268
+00:16:41,249 --> 00:16:47,129
+āŖ āŖ
+
+269
+00:17:01,339 --> 00:17:05,238
+- I want to tell you something.
+- Back off, okay?
+
+270
+00:17:07,369 --> 00:17:11,359
+āŖ āŖ
+
+271
+00:17:32,219 --> 00:17:35,328
+Next time you go through,
+
+272
+00:17:35,329 --> 00:17:37,398
+that carving won't be there.
+
+273
+00:17:37,399 --> 00:17:39,348
+Why?
+
+274
+00:17:39,349 --> 00:17:43,328
+Each time you go in, everything resets.
+
+275
+00:17:43,329 --> 00:17:46,328
+It's always 11:58
+
+276
+00:17:46,329 --> 00:17:51,248
+on October 21, 1960.
+
+277
+00:17:51,249 --> 00:17:54,258
+You saw how everything
+looked exactly the same?
+
+278
+00:17:54,259 --> 00:17:55,279
+Yeah.
+
+279
+00:17:55,280 --> 00:17:57,358
+No matter how long you stay...
+
+280
+00:17:57,359 --> 00:18:00,338
+Three weeks, three years...
+
+281
+00:18:00,339 --> 00:18:02,178
+When you come back,
+
+282
+00:18:02,179 --> 00:18:04,298
+only two minutes will have passed here.
+
+283
+00:18:04,299 --> 00:18:06,398
+Two minutes? Who makes up these rules?
+
+284
+00:18:06,399 --> 00:18:08,248
+I'm just explaining that,
+
+285
+00:18:08,249 --> 00:18:11,298
+if you want to change something forever,
+
+286
+00:18:11,299 --> 00:18:13,358
+you can't ever go back.
+
+287
+00:18:13,359 --> 00:18:17,198
+Going back erases what you did before.
+
+288
+00:18:17,199 --> 00:18:21,208
+All right, who's the guy with
+the hat and the yellow card?
+
+289
+00:18:21,209 --> 00:18:22,218
+He's some bum.
+
+290
+00:18:22,219 --> 00:18:23,399
+Well, he got up in my face.
+
+291
+00:18:23,400 --> 00:18:26,298
+I mean, he seemed to be the only
+one that noticed me come and go.
+
+292
+00:18:26,299 --> 00:18:28,318
+Just forget the yellow card man.
+
+293
+00:18:28,319 --> 00:18:30,278
+He's not important.
+
+294
+00:18:30,279 --> 00:18:34,348
+What's important is that
+we have a chance here.
+
+295
+00:18:34,349 --> 00:18:37,328
+A chance to change people's lives.
+
+296
+00:18:37,329 --> 00:18:40,348
+I saw you sitting there with Christy,
+
+297
+00:18:40,349 --> 00:18:44,358
+and I knew you could do this.
+
+298
+00:18:44,359 --> 00:18:47,328
+What are you talking about?
+
+299
+00:18:47,329 --> 00:18:50,258
+That's the last time I went through.
+
+300
+00:18:50,259 --> 00:18:53,308
+Stayed about... About two years.
+
+301
+00:18:53,309 --> 00:18:54,369
+Two years?
+
+302
+00:18:54,370 --> 00:18:57,188
+While I signed my divorce papers?
+
+303
+00:18:57,189 --> 00:18:59,288
+- That was...
+- Two minutes, yeah.
+
+304
+00:18:59,289 --> 00:19:01,228
+It doesn't make any sense.
+
+305
+00:19:01,229 --> 00:19:02,389
+It doesn't make any sense.
+
+306
+00:19:02,390 --> 00:19:06,379
+Jake, the way the world is
+now doesn't make any sense.
+
+307
+00:19:08,229 --> 00:19:12,318
+Just think about it, eh?
+
+308
+00:19:12,319 --> 00:19:15,278
+You know how to think, don't you?
+
+309
+00:19:17,209 --> 00:19:18,379
+Marisol Breslin.
+
+310
+00:19:23,249 --> 00:19:27,168
+Congratulations, Marisol.
+
+311
+00:19:27,169 --> 00:19:29,298
+Hector de la Fuente.
+
+312
+00:19:31,289 --> 00:19:34,248
+That idiot always schedules
+these goddamn things
+
+313
+00:19:34,249 --> 00:19:35,248
+on a Friday.
+
+314
+00:19:35,249 --> 00:19:36,339
+Maureen Kelly.
+
+315
+00:19:36,340 --> 00:19:40,278
+We can't send diplomas in the mail?
+
+316
+00:19:42,369 --> 00:19:44,348
+How long have you worked here, Alice?
+
+317
+00:19:44,349 --> 00:19:47,188
+I went here.
+
+318
+00:19:47,189 --> 00:19:48,229
+Wow.
+
+319
+00:19:48,230 --> 00:19:50,348
+Harry Dunning!
+
+320
+00:19:50,349 --> 00:19:52,299
+Harry!
+
+321
+00:19:58,399 --> 00:20:03,189
+All right. Harry!
+
+322
+00:20:04,359 --> 00:20:07,298
+He's a great writer.
+
+323
+00:20:07,299 --> 00:20:11,308
+I'll look for his work in "The New Yorker."
+
+324
+00:20:11,309 --> 00:20:13,258
+Did you get my recommendation?
+
+325
+00:20:13,259 --> 00:20:15,208
+Listen, you love Harry.
+
+326
+00:20:15,209 --> 00:20:17,178
+I love Harry. We all love Harry.
+
+327
+00:20:17,179 --> 00:20:19,188
+He's great at pushing a broom,
+
+328
+00:20:19,189 --> 00:20:21,268
+but at management, making a schedule,
+
+329
+00:20:21,269 --> 00:20:24,318
+signing time sheets, no way.
+
+330
+00:20:24,319 --> 00:20:27,358
+All these graduates are starting new lives!
+
+331
+00:20:27,359 --> 00:20:29,218
+Yeah!
+
+332
+00:20:29,219 --> 00:20:31,168
+That's it. I'm off the clock.
+
+333
+00:20:31,169 --> 00:20:34,278
+Let's hear it for all our
+adult education graduates
+
+334
+00:20:34,279 --> 00:20:37,349
+for all their hard work.
+
+335
+00:20:51,349 --> 00:20:53,378
+All right. Tell me more.
+
+336
+00:20:53,379 --> 00:20:57,189
+Come here.
+
+337
+00:21:11,359 --> 00:21:14,229
+Holy shit.
+
+338
+00:21:15,379 --> 00:21:19,388
+I thought you didn't have any hobbies.
+
+339
+00:21:19,389 --> 00:21:23,178
+Who do you think killed Kennedy?
+
+340
+00:21:23,179 --> 00:21:25,348
+People seem to think it
+was Lee Harvey Oswald.
+
+341
+00:21:25,349 --> 00:21:27,258
+Yeah.
+
+342
+00:21:27,259 --> 00:21:30,218
+You know, in a conspiracy
+with the mafia, CIA,
+
+343
+00:21:30,219 --> 00:21:32,368
+FBI, the Russians,
+
+344
+00:21:32,369 --> 00:21:35,328
+babushka lady on the grassy knoll.
+
+345
+00:21:35,329 --> 00:21:39,228
+Lot of books written about it.
+
+346
+00:21:39,229 --> 00:21:43,329
+Most of them written
+about Lee Harvey Oswald.
+
+347
+00:21:44,399 --> 00:21:47,338
+Guy doesn't make sense.
+
+348
+00:21:47,339 --> 00:21:53,308
+U.S. Marine, defected to Russia, came back,
+
+349
+00:21:53,309 --> 00:21:56,318
+but everyone who wrote about Oswald
+
+350
+00:21:56,319 --> 00:22:00,309
+studied him after the assassination.
+
+351
+00:22:02,279 --> 00:22:07,248
+I saw him before.
+
+352
+00:22:07,249 --> 00:22:11,198
+I saw him when he came back from Russia.
+
+353
+00:22:11,199 --> 00:22:14,239
+In 1962.
+
+354
+00:22:19,239 --> 00:22:20,329
+What the hell are you staring at?
+
+355
+00:22:20,330 --> 00:22:22,279
+- Nothing.
+- Yeah, well, mind your own.
+
+356
+00:22:26,299 --> 00:22:28,288
+Why didn't you shoot him right there?
+
+357
+00:22:28,289 --> 00:22:31,378
+I don't know whether Oswald
+was the man who did it,
+
+358
+00:22:31,379 --> 00:22:33,228
+and I still don't know.
+
+359
+00:22:33,229 --> 00:22:38,278
+Not gonna shoot someone because of maybe.
+
+360
+00:22:38,279 --> 00:22:40,298
+There are hundreds of questions
+
+361
+00:22:40,299 --> 00:22:46,258
+that haven't been answered
+concerning Oswald and that day.
+
+362
+00:22:46,259 --> 00:22:49,238
+This is a... this is a big one.
+
+363
+00:22:49,239 --> 00:22:51,258
+Who's this?
+
+364
+00:22:51,259 --> 00:22:53,198
+Walker.
+
+365
+00:22:53,199 --> 00:22:55,278
+Ran for Texas governor.
+
+366
+00:22:55,279 --> 00:22:58,278
+You know that in 1963,
+
+367
+00:22:58,279 --> 00:23:00,348
+six months before Kennedy was shot,
+
+368
+00:23:00,349 --> 00:23:03,248
+there was an assassination
+attempt on this man?
+
+369
+00:23:03,249 --> 00:23:04,339
+Okay.
+
+370
+00:23:04,340 --> 00:23:09,378
+Bullet came from a 6.5
+caliber Carcano rifle.
+
+371
+00:23:09,379 --> 00:23:13,268
+Same exact make of gun that killed Kennedy.
+
+372
+00:23:13,269 --> 00:23:18,238
+Well, did you go back and
+see Oswald shoot at Walker?
+
+373
+00:23:18,239 --> 00:23:21,188
+- That's what I want you to do.
+- How?
+
+374
+00:23:21,189 --> 00:23:24,338
+I want you to make it to April 10th, 1963.
+
+375
+00:23:24,339 --> 00:23:28,288
+And if Oswald fires that gun
+alone, you got to take him out.
+
+376
+00:23:28,289 --> 00:23:30,388
+Hold on, what are we
+even talking about here?
+
+377
+00:23:30,389 --> 00:23:34,358
+We're talking about seeing if
+Oswald took a shot at Walker.
+
+378
+00:23:34,359 --> 00:23:37,348
+I go back through the rabbit
+hole, I come out in 1960.
+
+379
+00:23:37,349 --> 00:23:39,318
+You want me to hang out for three years?
+
+380
+00:23:39,319 --> 00:23:42,198
+- Two minutes here.
+- What am I gonna do in 1960?
+
+381
+00:23:42,199 --> 00:23:43,258
+I'm nothing there.
+
+382
+00:23:43,259 --> 00:23:44,379
+How am I gonna... How am I gonna live?
+
+383
+00:23:44,380 --> 00:23:47,199
+I'm glad you asked.
+
+384
+00:23:49,289 --> 00:23:50,359
+What's this?
+
+385
+00:23:52,359 --> 00:23:55,238
+Treasure chest?
+
+386
+00:23:55,239 --> 00:23:56,389
+James Amberson?
+
+387
+00:23:56,390 --> 00:24:01,368
+See, you can still answer
+to Jake if you want.
+
+388
+00:24:01,369 --> 00:24:03,228
+This is for me?
+
+389
+00:24:03,229 --> 00:24:05,188
+You're welcome.
+
+390
+00:24:05,189 --> 00:24:08,348
+Social security card, birth
+certificate, charge cards.
+
+391
+00:24:08,349 --> 00:24:13,229
+Everything you need to
+hold a job, blend in.
+
+392
+00:24:14,369 --> 00:24:19,288
+Al, when did you do this?
+
+393
+00:24:19,289 --> 00:24:23,309
+I got diagnosed in 1962.
+
+394
+00:24:25,239 --> 00:24:27,318
+You made all this when you were back there?
+
+395
+00:24:27,319 --> 00:24:31,199
+Here's the most important thing.
+
+396
+00:24:37,379 --> 00:24:39,288
+Sports stats?
+
+397
+00:24:39,289 --> 00:24:42,288
+Yeah, baseball and boxing mostly.
+
+398
+00:24:42,289 --> 00:24:44,288
+This is how you survived?
+
+399
+00:24:44,289 --> 00:24:46,258
+You spent all your time
+back there gambling?
+
+400
+00:24:46,259 --> 00:24:52,238
+That, and there was a butcher
+I liked on Main Street.
+
+401
+00:24:52,239 --> 00:24:54,348
+How do you think I keep
+my prices here so low?
+
+402
+00:24:54,349 --> 00:24:57,228
+That's what you've been feeding me?
+
+403
+00:24:57,229 --> 00:25:00,358
+I've been eating hamburgers from 1960?
+
+404
+00:25:00,359 --> 00:25:02,208
+Yeah.
+
+405
+00:25:08,369 --> 00:25:12,248
+Al...
+
+406
+00:25:12,249 --> 00:25:16,248
+I don't know who you think I am.
+
+407
+00:25:16,249 --> 00:25:18,369
+I just don't think I'm
+the right guy for this.
+
+408
+00:25:26,209 --> 00:25:27,379
+I know you are.
+
+409
+00:25:32,259 --> 00:25:34,248
+That's about all I know.
+
+410
+00:25:34,249 --> 00:25:37,328
+You'll figure out the
+rest when you get there.
+
+411
+00:25:37,329 --> 00:25:39,258
+Let's... let's go.
+
+412
+00:25:39,259 --> 00:25:40,369
+Go where?
+
+413
+00:25:40,370 --> 00:25:42,388
+To the diner. I'll watch you go through.
+
+414
+00:25:42,389 --> 00:25:44,338
+I can wait two minutes.
+
+415
+00:25:44,339 --> 00:25:47,178
+Two minutes to get a better world.
+
+416
+00:25:53,289 --> 00:25:56,339
+I have to think about all this.
+
+417
+00:25:59,209 --> 00:26:00,389
+What's there to think about?
+
+418
+00:26:08,349 --> 00:26:10,178
+Okay?
+
+419
+00:26:10,179 --> 00:26:12,259
+Hm. Yeah.
+
+420
+00:26:13,359 --> 00:26:15,339
+Oh, yeah.
+
+421
+00:26:21,219 --> 00:26:24,228
+And what a waste of fucking time.
+
+422
+00:26:24,229 --> 00:26:26,238
+What?
+
+423
+00:26:26,239 --> 00:26:31,218
+I can see how this is gonna go.
+
+424
+00:26:31,219 --> 00:26:33,338
+"Oh, my car needs an oil change."
+
+425
+00:26:33,339 --> 00:26:37,278
+"Got to give a... Give a big test."
+
+426
+00:26:37,279 --> 00:26:39,328
+"Got to go take a nap."
+
+427
+00:26:39,329 --> 00:26:42,358
+Anything to avoid doing
+something real in your life.
+
+428
+00:26:42,359 --> 00:26:43,389
+Al, this is... I've been up all night
+
+429
+00:26:43,390 --> 00:26:45,248
+listening to fucking crazy...
+
+430
+00:26:45,249 --> 00:26:46,319
+I thought maybe for once...
+
+431
+00:26:46,320 --> 00:26:48,208
+This is fucking crazy!
+
+432
+00:26:48,209 --> 00:26:51,178
+You might do something,
+not just talk about it.
+
+433
+00:26:51,179 --> 00:26:53,248
+Just because you wasted your life on this,
+
+434
+00:26:53,249 --> 00:26:55,289
+it doesn't mean that I have to.
+
+435
+00:26:59,299 --> 00:27:02,279
+You little shit.
+
+436
+00:27:03,319 --> 00:27:06,169
+Get the fuck out of my house.
+
+437
+00:27:09,229 --> 00:27:11,299
+Get out.
+
+438
+00:27:20,369 --> 00:27:26,356
+āŖ āŖ
+
+439
+00:27:36,239 --> 00:27:42,226
+āŖ āŖ
+
+440
+00:27:48,259 --> 00:27:51,239
+Al?
+
+441
+00:27:58,229 --> 00:28:00,268
+Al?
+
+442
+00:28:00,269 --> 00:28:02,269
+Sorry about last night.
+
+443
+00:28:05,359 --> 00:28:07,179
+Couldn't sleep.
+
+444
+00:28:11,279 --> 00:28:12,289
+Al?
+
+445
+00:28:23,179 --> 00:28:24,399
+Shit.
+
+446
+00:28:58,339 --> 00:29:04,261
+āŖ āŖ
+
+447
+00:29:10,239 --> 00:29:13,349
+Okay, buddy.
+
+448
+00:29:15,399 --> 00:29:18,258
+I'll see you in two minutes.
+
+449
+00:29:21,259 --> 00:29:26,278
+āŖ āŖ
+
+450
+00:29:39,199 --> 00:29:45,176
+āŖ āŖ
+
+451
+00:29:45,177 --> 00:29:47,359
+Here I go.
+
+452
+00:30:01,219 --> 00:30:02,358
+Careful.
+
+453
+00:30:06,309 --> 00:30:09,318
+Shouldn't be here.
+
+454
+00:30:09,319 --> 00:30:14,348
+Keep telling you, you shouldn't be here.
+
+455
+00:30:14,349 --> 00:30:17,268
+Well, I'm here.
+
+456
+00:30:17,269 --> 00:30:20,358
+āŖ Stay with me āŖ
+
+457
+00:30:20,359 --> 00:30:22,378
+āŖ Whoa-oh-oh, yeah āŖ
+
+458
+00:30:22,379 --> 00:30:26,169
+āŖ Just a little bit longer āŖ
+
+459
+00:30:27,389 --> 00:30:29,238
+āŖ Please! āŖ
+
+460
+00:30:29,239 --> 00:30:31,218
+āŖ Please, please, please, please āŖ
+
+461
+00:30:31,219 --> 00:30:34,318
+āŖ Tell me you're going to āŖ
+
+462
+00:30:34,319 --> 00:30:36,278
+āŖ Come on, come on, come on, and āŖ
+
+463
+00:30:36,279 --> 00:30:38,368
+- āŖ Stay! āŖ
+- āŖ Yeah, yeah, yeah āŖ
+
+464
+00:30:38,369 --> 00:30:41,358
+āŖ Come on, come on, come on, stay...
+whoop! āŖ
+
+465
+00:30:41,359 --> 00:30:45,179
+āŖ La-de-dah, come on, come
+on, come on and stay āŖ
+
+466
+00:30:52,359 --> 00:30:58,332
+āŖ āŖ
+
+467
+00:31:02,339 --> 00:31:03,389
+āŖ La la la la āŖ
+
+468
+00:31:03,390 --> 00:31:05,238
+āŖ La la la la la āŖ
+
+469
+00:31:05,239 --> 00:31:08,328
+āŖ La la la la la la la la la la āŖ
+
+470
+00:31:08,329 --> 00:31:09,389
+āŖ La la la la āŖ
+
+471
+00:31:09,390 --> 00:31:11,268
+āŖ La la la la la āŖ
+
+472
+00:31:11,269 --> 00:31:14,328
+āŖ La la la la la la la āŖ
+
+473
+00:31:14,329 --> 00:31:16,328
+āŖ When I saw her on the corner āŖ
+
+474
+00:31:16,329 --> 00:31:18,258
+You'll need to blend in,
+
+475
+00:31:18,259 --> 00:31:22,208
+so get your hair cut and get
+rid of the stuff on your face.
+
+476
+00:31:22,209 --> 00:31:24,349
+It's not gonna work.
+
+477
+00:31:26,319 --> 00:31:28,338
+Looks like you're overdue.
+
+478
+00:31:28,339 --> 00:31:31,168
+Yeah, I guess so.
+
+479
+00:31:31,169 --> 00:31:33,228
+From out of town?
+
+480
+00:31:33,229 --> 00:31:36,258
+Uh, yes. James Amberson.
+
+481
+00:31:36,259 --> 00:31:37,389
+- Amberson?
+- Mm-hmm.
+
+482
+00:31:37,390 --> 00:31:40,338
+Are those the Ambersons
+over in Castle Rock?
+
+483
+00:31:40,340 --> 00:31:41,308
+No.
+
+484
+00:31:41,309 --> 00:31:43,288
+You know, my wife's cousin
+married an Amberson,
+
+485
+00:31:43,289 --> 00:31:45,248
+but he was from New Hampshire.
+
+486
+00:31:45,249 --> 00:31:46,359
+Those are your people?
+
+487
+00:31:46,360 --> 00:31:49,228
+Actually, I was adopted.
+
+488
+00:31:49,229 --> 00:31:50,269
+Huh.
+
+489
+00:31:55,319 --> 00:31:57,188
+āŖ La la la la āŖ
+
+490
+00:31:57,189 --> 00:31:58,319
+āŖ La la la la la āŖ
+
+491
+00:31:58,320 --> 00:32:00,208
+āŖ La la la la āŖ
+
+492
+00:32:00,209 --> 00:32:02,198
+Get the right clothes.
+
+493
+00:32:02,199 --> 00:32:06,178
+Back then grown men wore suits and hats.
+
+494
+00:32:06,179 --> 00:32:08,338
+When I vote, it comes down to character.
+
+495
+00:32:08,339 --> 00:32:11,268
+- Mm-hmm?
+- Richard Nixon has it.
+
+496
+00:32:11,269 --> 00:32:14,378
+You look at him, you think
+"that's a good man."
+
+497
+00:32:14,379 --> 00:32:16,368
+Yeah.
+
+498
+00:32:16,369 --> 00:32:19,218
+Perfect. Cash or credit, sir?
+
+499
+00:32:19,219 --> 00:32:22,189
+Oh, uh, cash.
+
+500
+00:32:25,259 --> 00:32:28,178
+Can I have a hat too, please?
+
+501
+00:32:28,179 --> 00:32:29,179
+Certainly.
+
+502
+00:32:29,180 --> 00:32:31,328
+Thanks.
+
+503
+00:32:31,329 --> 00:32:32,399
+āŖ La la la la āŖ
+
+504
+00:32:32,400 --> 00:32:34,248
+āŖ La la la la la āŖ
+
+505
+00:32:34,249 --> 00:32:37,328
+āŖ La la la la la la la la la la āŖ
+
+506
+00:32:37,329 --> 00:32:38,399
+āŖ La la la la āŖ
+
+507
+00:32:38,400 --> 00:32:40,268
+āŖ La la la la la āŖ
+
+508
+00:32:40,269 --> 00:32:43,268
+āŖ La la la la la la la āŖ
+
+509
+00:32:43,269 --> 00:32:45,248
+Hey, food tastes better.
+
+510
+00:32:45,249 --> 00:32:47,219
+Man, everything tastes better.
+
+511
+00:32:49,339 --> 00:32:50,389
+Mmm.
+
+512
+00:32:54,330 --> 00:32:56,318
+I guess you liked it.
+
+513
+00:32:56,319 --> 00:32:58,218
+That is the best pie that I've ever had.
+
+514
+00:32:59,370 --> 00:33:01,208
+It's insane.
+
+515
+00:33:01,209 --> 00:33:04,168
+Um... I hope that's a good thing.
+
+516
+00:33:04,169 --> 00:33:07,268
+Yeah, yeah, insanely good.
+
+517
+00:33:07,269 --> 00:33:09,198
+That'll be $0.60.
+
+518
+00:33:09,199 --> 00:33:13,278
+$0.60? Okay, of course.
+
+519
+00:33:13,279 --> 00:33:15,218
+Hey, keep the change.
+
+520
+00:33:15,219 --> 00:33:18,208
+Oh, thank you.
+
+521
+00:33:18,209 --> 00:33:20,328
+I'll tell my mother you
+liked her pie, mister...
+
+522
+00:33:20,329 --> 00:33:25,188
+Mr. Amberson. Yeah, thank you, uh...
+
+523
+00:33:25,189 --> 00:33:26,325
+Alice.
+
+524
+00:33:30,169 --> 00:33:32,188
+You're Alice. Oh, my God.
+
+525
+00:33:32,189 --> 00:33:33,339
+Something the matter?
+
+526
+00:33:33,340 --> 00:33:36,338
+Do you go to South Lisbon High?
+
+527
+00:33:36,339 --> 00:33:38,348
+Yeah, I just graduated.
+
+528
+00:33:38,349 --> 00:33:40,208
+And you're gonna teach there, right?
+
+529
+00:33:40,209 --> 00:33:41,329
+Heck no.
+
+530
+00:33:41,330 --> 00:33:43,248
+After this summer I'm moving to Bangor.
+
+531
+00:33:43,249 --> 00:33:45,298
+- Oh.
+- And then after that?
+
+532
+00:33:45,299 --> 00:33:47,188
+- New York.
+- Hmm.
+
+533
+00:33:47,189 --> 00:33:49,248
+My mother doesn't know yet.
+
+534
+00:33:49,249 --> 00:33:53,178
+Right, well, good luck with that.
+
+535
+00:33:53,179 --> 00:33:54,249
+Thank you.
+
+536
+00:33:54,250 --> 00:33:58,248
+Do me a favor, just try to stay sweet.
+
+537
+00:33:58,249 --> 00:33:59,268
+Okay.
+
+538
+00:33:59,269 --> 00:34:00,339
+Maybe I'll see you around.
+
+539
+00:34:02,219 --> 00:34:03,329
+āŖ I can laugh āŖ
+
+540
+00:34:03,330 --> 00:34:06,248
+āŖ When things ain't funny āŖ
+
+541
+00:34:06,249 --> 00:34:10,358
+āŖ Ha-ha-ha-happy go lucky me āŖ
+
+542
+00:34:10,359 --> 00:34:13,208
+āŖ Yeah, I can smile āŖ
+
+543
+00:34:13,209 --> 00:34:16,258
+āŖ When I ain't got no money āŖ
+
+544
+00:34:16,259 --> 00:34:19,218
+āŖ Ha-ha-ha-happy go lucky me āŖ
+
+545
+00:34:19,219 --> 00:34:24,188
+Get a neutral, reliable, boring car.
+
+546
+00:34:24,189 --> 00:34:26,258
+There's a lot on Maple Street.
+
+547
+00:34:26,259 --> 00:34:30,178
+I got a deal on a '54 Chevy.
+
+548
+00:34:30,179 --> 00:34:31,199
+Need to make your money last.
+
+549
+00:34:31,200 --> 00:34:33,228
+āŖ Me and my filly āŖ
+
+550
+00:34:33,229 --> 00:34:34,319
+āŖ Well, we both share āŖ
+
+551
+00:34:36,239 --> 00:34:40,268
+āŖ A slappy-go-happy, happy go lucky love āŖ
+
+552
+00:34:40,269 --> 00:34:44,268
+āŖ Well, life is sweet, whoa-oh-oh āŖ
+
+553
+00:34:44,269 --> 00:34:48,288
+You a buyer or a looker?
+
+554
+00:34:48,289 --> 00:34:50,208
+Looker.
+
+555
+00:34:50,209 --> 00:34:51,349
+Honey of a car.
+
+556
+00:34:51,350 --> 00:34:53,288
+Oh, yeah.
+
+557
+00:34:53,289 --> 00:34:56,248
+You want to know, this thing
+drives like a horse afire.
+
+558
+00:34:56,249 --> 00:34:57,369
+Oh, I bet.
+
+559
+00:34:57,370 --> 00:35:00,208
+Bought it from Arlene Keagan over in Durham
+
+560
+00:35:00,209 --> 00:35:03,268
+after her husband died.
+
+561
+00:35:03,269 --> 00:35:05,308
+- From out of town, am I right?
+- Yeah.
+
+562
+00:35:05,309 --> 00:35:06,399
+Andy Carson.
+
+563
+00:35:06,400 --> 00:35:09,178
+Jake Amberson.
+
+564
+00:35:09,179 --> 00:35:12,328
+- So, how much?
+- $750.
+
+565
+00:35:12,329 --> 00:35:16,358
+$750?
+
+566
+00:35:16,359 --> 00:35:22,278
+What would you say if I said $650... cash?
+
+567
+00:35:22,279 --> 00:35:25,308
+Mr. Amberson, make it $700
+you got yourself a deal.
+
+568
+00:35:25,309 --> 00:35:27,238
+Deal.
+
+569
+00:35:27,239 --> 00:35:29,258
+Young lady in the office will
+write the sale up for you.
+
+570
+00:35:29,259 --> 00:35:32,238
+I'll put on a sticker and a 14-day plate.
+
+571
+00:35:32,239 --> 00:35:35,198
+Sticker'll be another $5.
+
+572
+00:35:35,199 --> 00:35:36,329
+All right, you got me.
+
+573
+00:35:36,330 --> 00:35:38,229
+Yeah.
+
+574
+00:35:41,229 --> 00:35:45,319
+Uh, hey, Andy?
+
+575
+00:35:47,289 --> 00:35:49,358
+Kind of cleans me out.
+
+576
+00:35:49,359 --> 00:35:51,348
+You happen to know any place around here
+
+577
+00:35:51,349 --> 00:35:53,278
+a guy might place a bet?
+
+578
+00:35:53,279 --> 00:35:55,278
+A little more than a friendly wager?
+
+579
+00:36:15,299 --> 00:36:17,278
+āŖ It may sound silly āŖ
+
+580
+00:36:17,279 --> 00:36:20,268
+āŖ But I don't care āŖ
+
+581
+00:36:20,269 --> 00:36:22,388
+āŖ I got the moonlight, I got the sun āŖ
+
+582
+00:36:22,389 --> 00:36:25,358
+āŖ I've got the stars above āŖ
+
+583
+00:36:25,359 --> 00:36:27,368
+āŖ Me and my filly āŖ
+
+584
+00:36:27,369 --> 00:36:30,258
+āŖ Well, we both share āŖ
+
+585
+00:36:30,259 --> 00:36:34,348
+āŖ A slappy-go-happy, happy go lucky love āŖ
+
+586
+00:36:34,349 --> 00:36:38,348
+āŖ Well, life is sweet, whoa-oh-oh, yeah āŖ
+
+587
+00:36:38,349 --> 00:36:41,198
+āŖ Sweet as honey āŖ
+
+588
+00:36:41,199 --> 00:36:44,178
+āŖ Ha-ha-ha-happy go lucky me āŖ
+
+589
+00:36:46,209 --> 00:36:49,239
+āŖ Ha-ha-ha-happy go lucky me āŖ
+
+590
+00:36:56,359 --> 00:37:00,189
+There you go, chicken. It's on the house.
+
+591
+00:37:04,229 --> 00:37:06,398
+Hey, guy down at the car lot
+
+592
+00:37:06,399 --> 00:37:10,168
+said this was a place somebody could
+
+593
+00:37:10,169 --> 00:37:13,238
+make a wager.
+
+594
+00:37:13,239 --> 00:37:15,238
+Not from around here, are you?
+
+595
+00:37:15,239 --> 00:37:19,208
+No, uh... James Amberson.
+
+596
+00:37:19,209 --> 00:37:20,339
+I'm in town on a little business.
+
+597
+00:37:20,340 --> 00:37:23,278
+I'm gonna be here for a while.
+
+598
+00:37:23,279 --> 00:37:25,348
+What are you looking to make a bet on?
+
+599
+00:37:25,349 --> 00:37:28,358
+Fight of the week, Sanchez versus Jofre.
+
+600
+00:37:28,359 --> 00:37:29,359
+Jofre to win.
+
+601
+00:37:29,360 --> 00:37:31,178
+3 to 1 odds.
+
+602
+00:37:31,179 --> 00:37:33,278
+3 to 1? That's it?
+
+603
+00:37:33,279 --> 00:37:35,268
+Yeah, that's it.
+
+604
+00:37:35,269 --> 00:37:37,278
+Want to make it interesting?
+
+605
+00:37:37,279 --> 00:37:39,258
+Name the round.
+
+606
+00:37:39,259 --> 00:37:41,198
+Okay.
+
+607
+00:37:41,199 --> 00:37:45,368
+What if I said Jofre'll knock him out in
+
+608
+00:37:45,369 --> 00:37:48,208
+sixth round?
+
+609
+00:37:48,209 --> 00:37:51,328
+Ah, I'd say 35 to 1.
+
+610
+00:37:51,329 --> 00:37:55,368
+Wow. 35 to 1?
+
+611
+00:37:55,369 --> 00:37:58,349
+Hell, I'll make that bet.
+
+612
+00:38:07,269 --> 00:38:10,318
+Hey.
+
+613
+00:38:10,319 --> 00:38:12,328
+You want to take 35 to 1?
+
+614
+00:38:12,329 --> 00:38:13,369
+Do it, Sam.
+
+615
+00:38:13,370 --> 00:38:16,228
+Okay.
+
+616
+00:38:16,229 --> 00:38:17,259
+You got a bet.
+
+617
+00:38:17,260 --> 00:38:19,238
+- Oh, yeah?
+- Yeah.
+
+618
+00:38:19,239 --> 00:38:21,398
+Well, great. Uh...
+
+619
+00:38:21,399 --> 00:38:26,178
+How about $100?
+
+620
+00:38:26,179 --> 00:38:29,308
+$100?
+
+621
+00:38:29,309 --> 00:38:31,398
+Or...
+
+622
+00:38:31,399 --> 00:38:36,208
+you know, whatever.
+
+623
+00:38:36,209 --> 00:38:37,349
+I'll cover that bet.
+
+624
+00:38:37,350 --> 00:38:39,218
+You will?
+
+625
+00:38:39,219 --> 00:38:40,359
+What's the name?
+
+626
+00:38:40,360 --> 00:38:42,388
+James Amberson.
+
+627
+00:38:42,389 --> 00:38:45,178
+My friends call me Little Eddie.
+
+628
+00:38:45,179 --> 00:38:46,218
+Little Eddie?
+
+629
+00:38:46,219 --> 00:38:47,229
+Let me buy you a drink.
+
+630
+00:38:47,230 --> 00:38:49,169
+We can listen together.
+
+631
+00:38:53,239 --> 00:38:55,228
+Male announcer: Buster
+Henderson yelling instructions
+
+632
+00:38:55,229 --> 00:38:56,309
+from Jofre's camp,
+
+633
+00:38:56,310 --> 00:38:58,278
+but Jofre can't seem to get up.
+
+634
+00:38:58,279 --> 00:39:00,378
+- 30 seconds to go...
+- You got him, you got him.
+
+635
+00:39:00,379 --> 00:39:02,288
+- Come on, buddy.
+- Come on.
+
+636
+00:39:02,289 --> 00:39:03,309
+Announcer: Grazing shot,
+Jofre's still walking,
+
+637
+00:39:03,310 --> 00:39:05,218
+stalking forward.
+
+638
+00:39:05,219 --> 00:39:08,228
+Jofre still game, still
+carries the big bomb,
+
+639
+00:39:08,229 --> 00:39:10,298
+still can unleash that heavy leather,
+
+640
+00:39:10,299 --> 00:39:12,278
+but Sanchez is tossing...
+
+641
+00:39:12,279 --> 00:39:14,238
+Doesn't look too good for your man Jofre.
+
+642
+00:39:14,239 --> 00:39:16,208
+- Yeah, I can hear.
+- Announcer: Ladies and gentlemen,
+
+643
+00:39:16,209 --> 00:39:18,388
+we are seeing Jofre turn it around.
+
+644
+00:39:18,389 --> 00:39:20,378
+- A flurry of uppercuts.
+- What?
+
+645
+00:39:20,379 --> 00:39:22,328
+Beautiful shots all the way.
+
+646
+00:39:22,329 --> 00:39:23,399
+- What are you doing?
+- Here he comes.
+
+647
+00:39:23,400 --> 00:39:25,198
+Come on, buddy.
+
+648
+00:39:25,199 --> 00:39:27,178
+Come on, Jofre!
+
+649
+00:39:27,179 --> 00:39:28,319
+- Sanchez is stunned!
+- Get up!
+
+650
+00:39:28,320 --> 00:39:30,328
+- Get up!
+- Get up off the canvas.
+
+651
+00:39:30,329 --> 00:39:32,388
+Sanchez, flat on his back.
+Wait, he's getting up...
+
+652
+00:39:32,389 --> 00:39:34,258
+- Oh!
+- Announcer: No!
+
+653
+00:39:34,259 --> 00:39:36,248
+- Sanchez is down!
+- Get up!
+
+654
+00:39:36,249 --> 00:39:38,288
+- Get up!
+- Five, six, seven, eight...
+
+655
+00:39:38,289 --> 00:39:40,318
+- Yeah!
+- Nine...
+
+656
+00:39:40,319 --> 00:39:42,308
+- All right!
+- The fight is over!
+
+657
+00:39:42,309 --> 00:39:45,178
+The fight is over!
+
+658
+00:39:45,179 --> 00:39:47,278
+Jofre has knocked out Sanchez!
+
+659
+00:39:47,279 --> 00:39:49,318
+Nobody saw this coming!
+
+660
+00:39:49,319 --> 00:39:53,338
+Was that the sixth round?
+
+661
+00:39:57,239 --> 00:39:59,338
+You won.
+
+662
+00:39:59,339 --> 00:40:01,279
+Yeah.
+
+663
+00:40:06,229 --> 00:40:09,358
+Did you just win a 35 to 1 bet?
+
+664
+00:40:09,359 --> 00:40:14,248
+Yeah. Guess I'm lucky.
+
+665
+00:40:14,249 --> 00:40:16,399
+Guess so.
+
+666
+00:40:19,179 --> 00:40:22,218
+All right, thank you, Little Eddie.
+
+667
+00:40:22,219 --> 00:40:23,268
+Count it.
+
+668
+00:40:23,269 --> 00:40:24,329
+No, it's okay. I trust you.
+
+669
+00:40:24,330 --> 00:40:26,248
+Count it.
+
+670
+00:40:26,249 --> 00:40:28,299
+Make sure it's all there.
+
+671
+00:40:31,239 --> 00:40:33,179
+Okay.
+
+672
+00:40:40,379 --> 00:40:45,318
+That looks like it's all there. $3,600.
+
+673
+00:40:45,319 --> 00:40:47,178
+Okay, well, I should get going,
+
+674
+00:40:47,179 --> 00:40:49,308
+but I really appreciate the bet, so...
+
+675
+00:40:49,309 --> 00:40:53,179
+He appreciates the bet.
+
+676
+00:40:54,369 --> 00:40:57,278
+Enjoy your time in Lisbon.
+
+677
+00:40:57,279 --> 00:41:01,188
+Maybe I'll see you guys around.
+
+678
+00:41:18,189 --> 00:41:21,368
+Hey, nice wheels.
+
+679
+00:41:21,369 --> 00:41:23,308
+Thanks.
+
+680
+00:41:25,389 --> 00:41:29,189
+See ya.
+
+681
+00:41:36,199 --> 00:41:42,121
+āŖ āŖ
+
+682
+00:42:06,319 --> 00:42:08,238
+Welcome to the Tamarack Motor Court.
+
+683
+00:42:08,239 --> 00:42:10,178
+I need a room, do you have one?
+
+684
+00:42:10,179 --> 00:42:11,278
+Okeydoke.
+
+685
+00:42:11,279 --> 00:42:12,279
+Okay.
+
+686
+00:42:15,319 --> 00:42:19,258
+$5.
+
+687
+00:42:19,259 --> 00:42:21,248
+All right.
+
+688
+00:42:21,249 --> 00:42:22,329
+Room eight's ready for you.
+
+689
+00:42:22,330 --> 00:42:25,219
+Thank you.
+
+690
+00:42:53,179 --> 00:42:54,359
+You're being paranoid.
+
+691
+00:42:54,360 --> 00:42:56,298
+Okay, people make bets all the time.
+
+692
+00:42:56,299 --> 00:42:59,228
+Those guys, they win and lose every night.
+
+693
+00:42:59,229 --> 00:43:00,549
+All right? You're being paranoid.
+
+694
+00:43:09,189 --> 00:43:11,328
+Oh, no. Oh, no.
+
+695
+00:43:11,329 --> 00:43:13,379
+Oh, no.
+
+696
+00:43:15,379 --> 00:43:18,178
+Shit.
+
+697
+00:43:22,349 --> 00:43:28,239
+āŖ āŖ
+
+698
+00:43:53,299 --> 00:43:54,389
+āŖ I crashed my car into the bridge āŖ
+
+699
+00:43:54,390 --> 00:43:56,388
+āŖ I watched I let it burn āŖ
+
+700
+00:43:56,389 --> 00:43:58,348
+āŖ I threw your shit into a bag āŖ
+
+701
+00:43:58,349 --> 00:44:00,348
+āŖ And pushed it down the stairs āŖ
+
+702
+00:44:00,349 --> 00:44:03,238
+āŖ I crashed my car into the bridge āŖ
+
+703
+00:44:03,239 --> 00:44:06,238
+āŖ I don't care, I love it āŖ
+
+704
+00:44:14,299 --> 00:44:20,286
+āŖ āŖ
+
+705
+00:44:28,299 --> 00:44:29,309
+Jesus Christ!
+
+706
+00:45:25,239 --> 00:45:27,368
+āŖ I'm a road runner, honey āŖ
+
+707
+00:45:27,369 --> 00:45:30,348
+āŖ And you can't keep up with me āŖ
+
+708
+00:45:30,349 --> 00:45:32,268
+āŖ Beep, beep āŖ
+
+709
+00:45:32,269 --> 00:45:35,178
+āŖ I'm a road runner, honey āŖ
+
+710
+00:45:35,179 --> 00:45:37,368
+āŖ And you can't keep up with me āŖ
+
+711
+00:45:37,369 --> 00:45:40,218
+āŖ Beep, beep āŖ
+
+712
+00:45:40,219 --> 00:45:42,278
+āŖ Come on, let's race āŖ
+
+713
+00:45:42,279 --> 00:45:46,358
+āŖ Baby, baby, you will see āŖ
+
+714
+00:45:46,359 --> 00:45:50,308
+āŖ Here I come āŖ
+
+715
+00:45:50,309 --> 00:45:53,188
+āŖ Beep, beep āŖ
+
+716
+00:45:53,189 --> 00:45:54,209
+Mm.
+
+717
+00:45:54,210 --> 00:45:56,378
+āŖ Move over, honey āŖ
+
+718
+00:45:56,379 --> 00:46:00,168
+āŖ Let me by āŖ
+
+719
+00:46:02,209 --> 00:46:04,348
+āŖ Move over, baby āŖ
+
+720
+00:46:04,349 --> 00:46:06,328
+āŖ Let this man by āŖ
+
+721
+00:46:06,329 --> 00:46:09,168
+Hi!
+
+722
+00:46:09,169 --> 00:46:10,198
+āŖ I'm gonna show you, baby āŖ
+
+723
+00:46:10,199 --> 00:46:11,299
+āŖ Look out your head āŖ
+
+724
+00:46:11,300 --> 00:46:15,338
+āŖ Gonna put some dirt in your eye āŖ
+
+725
+00:46:15,339 --> 00:46:17,348
+āŖ Here I go āŖ
+
+726
+00:46:28,369 --> 00:46:31,198
+āŖ Oh, yeah, how am I doing? āŖ
+
+727
+00:46:31,199 --> 00:46:34,208
+You's going the wrong way, sir.
+
+728
+00:46:34,209 --> 00:46:35,209
+āŖ Beep beep āŖ
+
+729
+00:46:37,399 --> 00:46:41,238
+āŖ Take my hand, baby āŖ
+
+730
+00:46:41,239 --> 00:46:44,268
+āŖ I'm gonna prove to you that I'm a āŖ
+
+731
+00:46:44,269 --> 00:46:49,288
+āŖ A road running man āŖ
+
+732
+00:46:49,289 --> 00:46:52,228
+āŖ I wanna show you something āŖ
+
+733
+00:46:52,229 --> 00:46:56,338
+āŖ That I'm the fastest in the land āŖ
+
+734
+00:46:56,339 --> 00:46:59,348
+āŖ Now, let me by āŖ
+
+735
+00:46:59,349 --> 00:47:02,258
+āŖ Beep beep āŖ
+
+736
+00:47:02,259 --> 00:47:05,198
+āŖ Oh, yeah āŖ
+
+737
+00:47:05,199 --> 00:47:09,208
+āŖ You said you fast āŖ
+
+738
+00:47:09,209 --> 00:47:11,238
+āŖ Ha, ha, ha, ha, ha āŖ
+
+739
+00:47:11,239 --> 00:47:15,328
+āŖ But it don't look like you gonna last āŖ
+
+740
+00:47:15,329 --> 00:47:19,248
+āŖ Good-bye āŖ
+
+741
+00:47:19,249 --> 00:47:21,349
+āŖ I've got to put you down āŖ
+
+742
+00:47:52,279 --> 00:47:54,288
+Oh, Jesus Christ.
+
+743
+00:47:54,289 --> 00:47:55,318
+You okay? You all right?
+
+744
+00:47:55,319 --> 00:47:56,339
+Excuse me.
+
+745
+00:47:56,340 --> 00:47:58,208
+- Sorry, sorry.
+- Oh.
+
+746
+00:47:58,209 --> 00:48:00,398
+- Greta, you're okay?
+- Yes.
+
+747
+00:48:00,399 --> 00:48:02,278
+Okay, say a prayer, let's move along.
+
+748
+00:48:02,279 --> 00:48:04,258
+Move along, girls.
+
+749
+00:48:04,259 --> 00:48:06,298
+Move along.
+
+750
+00:48:06,299 --> 00:48:07,339
+Sorry.
+
+751
+00:48:11,259 --> 00:48:13,258
+You were clearly outnumbered.
+
+752
+00:48:13,259 --> 00:48:15,348
+Exactly, that's why I
+had to stand my ground.
+
+753
+00:48:15,349 --> 00:48:18,338
+Well, good for you.
+
+754
+00:48:18,339 --> 00:48:21,198
+You got something against Catholics?
+
+755
+00:48:24,279 --> 00:48:26,288
+Uh, Miss?
+
+756
+00:48:26,289 --> 00:48:30,208
+Miss, you left this.
+
+757
+00:48:30,209 --> 00:48:32,368
+Thank you.
+
+758
+00:48:32,369 --> 00:48:34,338
+I forget everything when I'm reading.
+
+759
+00:48:34,339 --> 00:48:36,258
+Me, too.
+
+760
+00:48:36,259 --> 00:48:38,178
+One time I was 12 and
+
+761
+00:48:38,179 --> 00:48:40,288
+I was reading "Of Mice and Men" on the bus
+
+762
+00:48:40,289 --> 00:48:42,198
+and missed every stop.
+
+763
+00:48:42,199 --> 00:48:44,208
+Admit it, you cried.
+
+764
+00:48:44,209 --> 00:48:46,168
+"Of Mice and Men"?
+
+765
+00:48:46,169 --> 00:48:48,248
+Like a baby.
+
+766
+00:48:48,249 --> 00:48:52,178
+Well, last month, I was
+reading a Chandler novel
+
+767
+00:48:52,179 --> 00:48:54,278
+and I lost my other handbag, so thank you.
+
+768
+00:48:54,279 --> 00:48:57,358
+Got to stop doing that.
+
+769
+00:48:57,359 --> 00:48:59,298
+"From Here to Eternity."
+
+770
+00:48:59,299 --> 00:49:01,338
+Which do you like better,
+the book or the movie?
+
+771
+00:49:01,339 --> 00:49:03,348
+Please. The book's always better.
+
+772
+00:49:03,349 --> 00:49:05,268
+Everybody knows that.
+
+773
+00:49:05,269 --> 00:49:08,278
+Yeah... how 'bout "The
+Manchurian Candidate"?
+
+774
+00:49:08,279 --> 00:49:11,348
+Is that a film? I know the book.
+
+775
+00:49:11,349 --> 00:49:16,238
+No, it's not, but maybe
+they'll make one someday.
+
+776
+00:49:16,239 --> 00:49:17,389
+Then maybe I'll change my mind.
+
+777
+00:49:17,390 --> 00:49:19,188
+Yeah.
+
+778
+00:49:19,189 --> 00:49:20,229
+Sadie Clayton.
+
+779
+00:49:20,230 --> 00:49:22,228
+Hi, Jake, uh, Amberson.
+
+780
+00:49:28,279 --> 00:49:30,318
+Well, thank you for coming
+to the rescue, Mr. Amberson.
+
+781
+00:49:30,319 --> 00:49:32,248
+Yeah.
+
+782
+00:49:32,249 --> 00:49:35,248
+My husband would kill me
+if I lost another handbag.
+
+783
+00:49:35,249 --> 00:49:37,189
+Right.
+
+784
+00:49:42,169 --> 00:49:48,055
+āŖ āŖ
+
+785
+00:49:52,379 --> 00:49:55,298
+No ladies in your room after 7:00,
+
+786
+00:49:55,299 --> 00:49:58,208
+and this'll be the common room.
+
+787
+00:49:58,209 --> 00:50:00,248
+You may enjoy it any time you like.
+
+788
+00:50:00,249 --> 00:50:04,328
+Breakfast and supper are
+included in the weekly rate.
+
+789
+00:50:04,329 --> 00:50:06,278
+This is my son Henry.
+
+790
+00:50:06,279 --> 00:50:08,308
+- Hello.
+- Sir.
+
+791
+00:50:08,309 --> 00:50:12,188
+Doing your homework. Good boy.
+
+792
+00:50:12,189 --> 00:50:13,199
+Now, how did you find us?
+
+793
+00:50:13,200 --> 00:50:15,228
+Was it the sign outside?
+
+794
+00:50:15,229 --> 00:50:17,338
+No, actually a friend
+told me about this place.
+
+795
+00:50:17,339 --> 00:50:18,389
+Al Templeton?
+
+796
+00:50:18,390 --> 00:50:21,248
+He stayed here.
+
+797
+00:50:21,249 --> 00:50:23,238
+I don't recall anybody by that name.
+
+798
+00:50:23,239 --> 00:50:26,338
+Oh, my mistake.
+
+799
+00:50:26,339 --> 00:50:29,288
+What brings you to Dallas, Mr. Amberson?
+
+800
+00:50:29,289 --> 00:50:32,338
+Doing research for a book that I'm writing.
+
+801
+00:50:32,339 --> 00:50:36,248
+Oh, a writer?
+
+802
+00:50:36,249 --> 00:50:38,258
+It's perfect.
+
+803
+00:50:38,259 --> 00:50:39,389
+Supper's at 6:00.
+
+804
+00:50:39,390 --> 00:50:41,259
+Thanks.
+
+805
+00:50:50,199 --> 00:50:52,299
+All right.
+
+806
+00:51:08,379 --> 00:51:11,358
+Do your homework.
+
+807
+00:51:11,359 --> 00:51:14,299
+You need to be prepared for what's to come.
+
+808
+00:51:20,289 --> 00:51:24,379
+I've put everything I
+know into those files.
+
+809
+00:51:39,189 --> 00:51:43,389
+You're gonna feel apart from other people.
+
+810
+00:51:46,369 --> 00:51:50,239
+That doesn't go away.
+
+811
+00:51:52,349 --> 00:51:54,318
+But tread lightly.
+
+812
+00:51:54,319 --> 00:51:58,178
+Don't get too close to anyone.
+
+813
+00:51:58,179 --> 00:52:00,378
+It never ends well.
+
+814
+00:52:00,379 --> 00:52:05,368
+You see, the past doesn't
+want to be changed.
+
+815
+00:52:05,369 --> 00:52:10,178
+There are times when you
+feel it push back, you know?
+
+816
+00:52:10,179 --> 00:52:12,178
+You feel it.
+
+817
+00:52:12,179 --> 00:52:14,218
+When you're close to changing something,
+
+818
+00:52:14,219 --> 00:52:20,208
+it's hard to describe, but you'll know.
+
+819
+00:52:20,209 --> 00:52:25,358
+If you do something that
+really fucks with the past,
+
+820
+00:52:25,359 --> 00:52:28,219
+the past fucks with you.
+
+821
+00:53:11,219 --> 00:53:12,389
+Operator.
+
+822
+00:53:12,390 --> 00:53:17,258
+Hi, can you give me the number
+for a Christopher Epping
+
+823
+00:53:17,259 --> 00:53:19,188
+in Chicago, Illinois?
+
+824
+00:53:19,189 --> 00:53:21,269
+$0.35.
+
+825
+00:53:25,169 --> 00:53:27,228
+Connecting to Christopher Epping...
+
+826
+00:53:32,179 --> 00:53:33,188
+Hello?
+
+827
+00:53:33,189 --> 00:53:34,339
+Hello?
+
+828
+00:53:34,340 --> 00:53:36,298
+- What?
+- Hello, hello?
+
+829
+00:53:36,299 --> 00:53:38,318
+- Dad?
+- Can you hear me?
+
+830
+00:53:38,319 --> 00:53:40,338
+Dad?
+
+831
+00:53:40,339 --> 00:53:43,178
+Dad?
+
+832
+00:53:43,179 --> 00:53:45,198
+Are you there?
+
+833
+00:54:10,369 --> 00:54:16,358
+āŖ āŖ
+
+834
+00:54:16,359 --> 00:54:21,349
+Oh, my God.
+
+835
+00:54:27,289 --> 00:54:30,169
+You shouldn't be here.
+
+836
+00:54:33,239 --> 00:54:36,358
+He argued with Mr. Khrushchev
+in the kitchen, it is true,
+
+837
+00:54:36,359 --> 00:54:40,308
+pointing out that while we
+may be behind in space,
+
+838
+00:54:40,309 --> 00:54:43,328
+we were ahead in color television.
+
+839
+00:54:43,329 --> 00:54:46,178
+Freedom is not communism.
+
+840
+00:54:49,179 --> 00:54:55,110
+āŖ āŖ
+
+841
+00:54:56,390 --> 00:54:58,258
+- Mr. Amberson?
+- Yeah?
+
+842
+00:54:58,259 --> 00:55:01,188
+- Are you all right?
+- Yeah, I'm fine.
+
+843
+00:55:01,189 --> 00:55:02,259
+Can I get you anything?
+
+844
+00:55:02,260 --> 00:55:04,209
+No, it's okay.
+
+845
+00:55:06,249 --> 00:55:09,219
+I'm... I'm okay.
+
+846
+00:55:15,399 --> 00:55:21,364
+āŖ āŖ
+
+847
+00:55:21,365 --> 00:55:26,188
+Oh, Mr. Amberson,
+
+848
+00:55:26,189 --> 00:55:29,208
+you had me worried last night.
+
+849
+00:55:29,209 --> 00:55:30,269
+I'm sorry about that.
+
+850
+00:55:30,270 --> 00:55:32,368
+Oh, don't be sorry.
+
+851
+00:55:32,369 --> 00:55:34,268
+I'm glad you're feeling better.
+
+852
+00:55:34,269 --> 00:55:36,298
+Let me make you some fresh coffee.
+
+853
+00:55:36,299 --> 00:55:39,219
+Please.
+
+854
+00:55:41,289 --> 00:55:43,298
+Good morning, sir.
+
+855
+00:55:43,299 --> 00:55:45,239
+Good morning.
+
+856
+00:55:49,279 --> 00:55:51,218
+How old are you?
+
+857
+00:55:51,219 --> 00:55:53,238
+I'm 14, sir.
+
+858
+00:55:53,239 --> 00:55:55,338
+You headed to the Army?
+
+859
+00:55:55,339 --> 00:55:57,238
+Soon as I turn 18.
+
+860
+00:55:57,239 --> 00:56:00,248
+Mind if I ask why?
+
+861
+00:56:00,249 --> 00:56:03,279
+Because I want to serve my country, sir.
+
+862
+00:56:04,329 --> 00:56:06,229
+Okay.
+
+863
+00:56:16,289 --> 00:56:20,309
+Henry, don't eat with your fingers.
+
+864
+00:56:21,339 --> 00:56:23,198
+Here you go.
+
+865
+00:56:23,199 --> 00:56:25,259
+- Thank you.
+- You're welcome.
+
+866
+00:56:37,209 --> 00:56:41,398
+Oswald doesn't get back
+from Russia for two years.
+
+867
+00:56:41,399 --> 00:56:47,388
+And in 1963, somebody
+shoots at General Walker.
+
+868
+00:56:47,389 --> 00:56:52,399
+In the meantime, there are
+plenty of other leads to follow.
+
+869
+00:56:56,229 --> 00:56:59,308
+On October 26th, follow George.
+
+870
+00:56:59,309 --> 00:57:03,248
+Just go where he goes,
+then you'll understand.
+
+871
+00:57:03,249 --> 00:57:05,199
+Who the hell is George?
+
+872
+00:57:09,199 --> 00:57:11,328
+That tastes like snot.
+
+873
+00:57:11,329 --> 00:57:12,369
+Yeah.
+
+874
+00:57:15,169 --> 00:57:19,178
+George de Mohrenschildt, Russian expat.
+
+875
+00:57:19,179 --> 00:57:23,328
+He's educated, charming, rich,
+
+876
+00:57:23,329 --> 00:57:27,318
+belongs to the Dallas Petroleum Club,
+
+877
+00:57:27,319 --> 00:57:33,228
+and in 1962, he becomes Lee's best friend.
+
+878
+00:57:33,229 --> 00:57:38,228
+We heard there was a couple,
+moved here from... Russia,
+
+879
+00:57:38,229 --> 00:57:41,298
+and they were having a very bad time,
+
+880
+00:57:41,299 --> 00:57:42,319
+financially, and so I went to see them.
+
+881
+00:57:42,320 --> 00:57:44,278
+Tell me, Jake,
+
+882
+00:57:44,279 --> 00:57:48,338
+why is this guy hanging out
+in Lee's shitty apartment?
+
+883
+00:57:48,339 --> 00:57:49,339
+They're swingers?
+
+884
+00:57:51,289 --> 00:57:53,218
+Lee had taken the cigarette from her
+
+885
+00:57:53,219 --> 00:57:55,268
+and had put it out on her shoulder.
+
+886
+00:57:55,269 --> 00:57:58,248
+If Oswald had a handler, it was him.
+
+887
+00:57:58,249 --> 00:58:00,168
+All right, hold on. You lost me.
+
+888
+00:58:00,169 --> 00:58:01,329
+A handler? What... what does that mean?
+
+889
+00:58:01,330 --> 00:58:03,328
+George told Oswald to shoot Kennedy?
+
+890
+00:58:03,329 --> 00:58:06,178
+If Lee was set up, then
+George was the only guy
+
+891
+00:58:06,179 --> 00:58:07,259
+who could've done it.
+
+892
+00:58:07,260 --> 00:58:10,228
+You need to cross off that possibility.
+
+893
+00:58:10,229 --> 00:58:13,278
+All right, and if I do that, then what?
+
+894
+00:58:13,279 --> 00:58:17,259
+Then you kill Oswald.
+
+895
+00:58:28,219 --> 00:58:34,216
+āŖ āŖ
+
+896
+00:59:02,169 --> 00:59:04,248
+āŖ āŖ
+
+897
+00:59:04,249 --> 00:59:08,199
+Where the hell are you
+going, George de Morenfuck?
+
+898
+00:59:53,259 --> 00:59:57,218
+Lyndon Johnson and I do not
+promise a life of ease.
+
+899
+00:59:57,219 --> 01:00:00,178
+We cannot promise a
+solution to the problems
+
+900
+01:00:00,179 --> 01:00:01,359
+which disturb our lives.
+
+901
+01:00:01,360 --> 01:00:05,208
+But we can promise that
+if we are successful,
+
+902
+01:00:05,209 --> 01:00:09,308
+we shall move with vigor and
+vitality on the problems
+
+903
+01:00:09,309 --> 01:00:12,328
+which disturb us here and around the world.
+
+904
+01:00:12,329 --> 01:00:17,278
+Thomas Paine...
+
+905
+01:00:17,279 --> 01:00:21,208
+Thomas Paine, in the revolution of 1776...
+
+906
+01:00:21,209 --> 01:00:22,389
+- Isn't he wonderful? Yes.
+- Yeah.
+
+907
+01:00:22,390 --> 01:00:27,238
+Said that the cause of America
+is the cause of all mankind.
+
+908
+01:00:27,239 --> 01:00:30,238
+I think in the revolution of 1960
+
+909
+01:00:30,239 --> 01:00:35,198
+that the cause of all mankind
+is the cause of America.
+
+910
+01:00:35,199 --> 01:00:40,218
+And as we move ahead, we think
+not only of the City of Dallas
+
+911
+01:00:40,219 --> 01:00:42,208
+and the City of Boston,
+
+912
+01:00:42,209 --> 01:00:44,398
+the State of Massachusetts
+and the State of Texas,
+
+913
+01:00:44,399 --> 01:00:46,348
+the United States.
+
+914
+01:00:46,349 --> 01:00:50,168
+We think of all those who wish to join us
+
+915
+01:00:50,169 --> 01:00:52,168
+in a great effort around the world
+
+916
+01:00:52,169 --> 01:00:55,398
+to maintain their freedom
+and maintain the peace.
+
+917
+01:00:55,399 --> 01:00:58,388
+We ask your help in this campaign.
+
+918
+01:00:58,389 --> 01:01:00,258
+Give us your voice.
+
+919
+01:01:00,259 --> 01:01:02,198
+Give us your help.
+
+920
+01:01:02,199 --> 01:01:06,238
+Join with us in this effort
+to move this country ahead.
+
+921
+01:01:32,299 --> 01:01:36,188
+āŖ Everyone is voting for Jack āŖ
+
+922
+01:01:36,189 --> 01:01:38,318
+āŖ 'Cause he's got what all the rest lack āŖ
+
+923
+01:01:38,319 --> 01:01:40,178
+Just checking in.
+
+924
+01:01:40,179 --> 01:01:43,258
+āŖ Everyone wants to back Jack āŖ
+
+925
+01:01:43,259 --> 01:01:45,208
+āŖ Jack is on the right track āŖ
+
+926
+01:01:45,209 --> 01:01:47,368
+āŖ 'Cause he's got high hopes āŖ
+
+927
+01:01:47,369 --> 01:01:50,218
+Uh, Al Templeton.
+
+928
+01:01:50,219 --> 01:01:51,379
+I don't see that name here.
+
+929
+01:01:51,380 --> 01:01:55,238
+Oh, I'm a guest of George de Mohrenschildt.
+
+930
+01:01:55,239 --> 01:01:58,398
+He doesn't like my name on any lists.
+
+931
+01:01:58,399 --> 01:02:00,358
+I assure you he'll be very upset
+
+932
+01:02:00,359 --> 01:02:02,228
+if I don't make this appointment.
+
+933
+01:02:02,229 --> 01:02:05,298
+āŖ Vote for Kennedy āŖ
+
+934
+01:02:05,299 --> 01:02:07,168
+Thank you.
+
+935
+01:02:07,169 --> 01:02:10,268
+āŖ Kennedy, he just keeps rolling a... āŖ
+
+936
+01:02:10,269 --> 01:02:13,348
+āŖ Kenney, he just keeps rolling a... āŖ
+
+937
+01:02:13,349 --> 01:02:16,358
+āŖ Kennedy, he just keeps rolling along āŖ
+
+938
+01:02:16,359 --> 01:02:18,268
+Yes, a little bit. I'll take one of those.
+
+939
+01:02:18,269 --> 01:02:20,369
+āŖ Vote for Kennedy! āŖ
+
+940
+01:02:25,229 --> 01:02:26,339
+Your suit collar looked lonely.
+
+941
+01:02:26,340 --> 01:02:30,169
+Oh, then you should give me two.
+
+942
+01:02:36,279 --> 01:02:38,278
+You like working for the campaign?
+
+943
+01:02:38,279 --> 01:02:41,398
+I think Senator Kennedy is
+the one person who's really,
+
+944
+01:02:41,399 --> 01:02:43,198
+really gonna make a difference.
+
+945
+01:02:43,199 --> 01:02:45,178
+Yeah.
+
+946
+01:02:45,179 --> 01:02:46,349
+Oh, could I get one?
+
+947
+01:02:46,350 --> 01:02:48,398
+Of course.
+
+948
+01:02:48,399 --> 01:02:51,368
+Oh, thank you.
+
+949
+01:02:51,369 --> 01:02:54,278
+- Oh, look, Jackie's here.
+- There she is.
+
+950
+01:02:54,279 --> 01:02:55,379
+- Mrs. Kennedy!
+- Mrs. Kennedy, a photo, please?
+
+951
+01:02:55,380 --> 01:02:58,258
+One more.
+
+952
+01:02:58,259 --> 01:03:00,368
+- Mrs. Kennedy?
+- Mrs. Kennedy!
+
+953
+01:03:00,369 --> 01:03:02,368
+Jackie, Jackie.
+
+954
+01:03:04,219 --> 01:03:06,208
+Hey, you shouldn't be here.
+
+955
+01:03:06,209 --> 01:03:07,379
+Oh, no, I'm just waiting for George.
+
+956
+01:03:07,380 --> 01:03:09,238
+Yeah, just come with me, okay?
+
+957
+01:03:09,239 --> 01:03:11,218
+Oh, no it's okay.
+
+958
+01:03:11,219 --> 01:03:12,259
+Oh!
+
+959
+01:03:12,260 --> 01:03:15,338
+Let's go.
+
+960
+01:03:15,339 --> 01:03:17,338
+Stop him!
+
+961
+01:03:20,339 --> 01:03:23,338
+āŖ āŖ
+
+962
+01:03:23,339 --> 01:03:26,328
+- Over there!
+- Hey, hey, hey, stop!
+
+963
+01:03:30,179 --> 01:03:33,308
+Going that way!
+
+964
+01:03:40,359 --> 01:03:45,168
+āŖ āŖ
+
+965
+01:03:48,169 --> 01:03:54,129
+āŖ āŖ
+
+966
+01:04:11,249 --> 01:04:14,208
+I don't know, I don't see him!
+
+967
+01:04:14,209 --> 01:04:16,308
+He's got to be in here!
+
+968
+01:04:23,249 --> 01:04:29,200
+āŖ āŖ
+
+969
+01:04:29,201 --> 01:04:34,208
+Aah! Aah!
+
+970
+01:04:37,329 --> 01:04:38,339
+Aah!
+
+971
+01:04:45,379 --> 01:04:49,169
+Wakey, wakey, sunshine. Let's go.
+
+972
+01:04:51,340 --> 01:04:53,358
+James Amberson from Maine.
+
+973
+01:04:53,359 --> 01:04:56,258
+- Yeah.
+- Pretty far from home.
+
+974
+01:04:56,259 --> 01:05:00,188
+Yeah, I just came to see the president.
+
+975
+01:05:00,189 --> 01:05:02,178
+I mean, senator Kennedy.
+
+976
+01:05:02,179 --> 01:05:05,358
+Well, you scammed your way
+into the senator's VIP room.
+
+977
+01:05:05,359 --> 01:05:09,168
+There are people out there who
+don't like the Kennedy campaign.
+
+978
+01:05:09,169 --> 01:05:10,249
+Well, that's not me.
+
+979
+01:05:10,250 --> 01:05:12,178
+I love the guy.
+
+980
+01:05:12,179 --> 01:05:13,349
+Well, why did you give a fake ID?
+
+981
+01:05:13,350 --> 01:05:15,248
+And then run away?
+
+982
+01:05:15,249 --> 01:05:17,208
+That looks bad.
+
+983
+01:05:17,209 --> 01:05:19,238
+I'm not interested in spending
+the rest of the night
+
+984
+01:05:19,239 --> 01:05:20,339
+in this basement.
+
+985
+01:05:20,340 --> 01:05:22,178
+Who are you?
+
+986
+01:05:22,179 --> 01:05:24,258
+Why were you in the VIP room?
+
+987
+01:05:24,259 --> 01:05:27,298
+Why are you here?
+
+988
+01:05:27,299 --> 01:05:29,378
+- Is he still here?
+- Who?
+
+989
+01:05:29,379 --> 01:05:32,258
+JFK, uh...
+
+990
+01:05:32,259 --> 01:05:35,338
+Jack... is on the right track.
+
+991
+01:05:35,339 --> 01:05:37,338
+I'm gonna be honest with you guys.
+
+992
+01:05:37,339 --> 01:05:40,188
+I came out here to shake the man's hand.
+
+993
+01:05:40,189 --> 01:05:42,368
+I just think he is the
+best that this country
+
+994
+01:05:42,369 --> 01:05:44,308
+has to offer, all right?
+
+995
+01:05:44,309 --> 01:05:46,238
+Get ready for greatness.
+
+996
+01:05:46,239 --> 01:05:49,188
+And that speech? Did you hear?
+You heard it, right?
+
+997
+01:05:49,189 --> 01:05:50,389
+I mean, you heard that speech.
+
+998
+01:05:50,390 --> 01:05:52,238
+I-I just wanted to tell him face-to-face
+
+999
+01:05:52,239 --> 01:05:53,318
+that I am a believer.
+
+1000
+01:05:53,319 --> 01:05:54,339
+Do you know what I'm talking about?
+
+1001
+01:05:54,340 --> 01:05:56,188
+Shut up, for the love of God.
+
+1002
+01:05:56,189 --> 01:05:57,198
+Do you know what I'm talking about?
+
+1003
+01:05:57,199 --> 01:05:58,369
+Just shut up, shut up.
+
+1004
+01:05:58,370 --> 01:06:00,268
+I mean, can you blame me for
+wanting to tell the guy
+
+1005
+01:06:00,269 --> 01:06:01,399
+I'm gonna vote for that I'm a believer?
+
+1006
+01:06:01,400 --> 01:06:03,218
+You can't arrest me for enthusiasm, right?
+
+1007
+01:06:03,219 --> 01:06:04,299
+There's no law against that.
+
+1008
+01:06:04,300 --> 01:06:06,258
+Actually, you're wrong about that.
+
+1009
+01:06:06,259 --> 01:06:08,188
+And if I catch your enthusiastic
+ass near Kennedy's campaign
+
+1010
+01:06:08,189 --> 01:06:10,318
+again, we'll bring you up on charges.
+
+1011
+01:06:10,319 --> 01:06:12,299
+Don't go places you're not invited.
+
+1012
+01:06:14,199 --> 01:06:18,298
+Okay, right. Um...
+
+1013
+01:06:18,299 --> 01:06:21,288
+Will you just do one thing for me?
+
+1014
+01:06:21,289 --> 01:06:25,248
+Just tell him that I'm his number one fan.
+
+1015
+01:06:25,249 --> 01:06:26,258
+Will you do that?
+
+1016
+01:06:26,259 --> 01:06:27,319
+Sure.
+
+1017
+01:06:27,320 --> 01:06:30,298
+Thank you, all right, thank you.
+
+1018
+01:06:30,299 --> 01:06:33,189
+Okay, see you later.
+
+1019
+01:06:37,269 --> 01:06:41,278
+So George de Mohrenschildt
+
+1020
+01:06:41,279 --> 01:06:43,278
+might be an informant,
+
+1021
+01:06:43,279 --> 01:06:46,298
+might work for Russia.
+
+1022
+01:06:46,299 --> 01:06:48,258
+He's got interests in Haiti,
+
+1023
+01:06:48,259 --> 01:06:53,258
+pals around with Duvalier;
+who knows what else.
+
+1024
+01:06:53,259 --> 01:06:56,398
+He told a reporter in 1977
+
+1025
+01:06:56,399 --> 01:06:58,298
+that it was the CIA
+
+1026
+01:06:58,299 --> 01:07:01,289
+who gave him Oswald's address.
+
+1027
+01:07:04,169 --> 01:07:06,228
+And then he was asked
+to testify in front of
+
+1028
+01:07:06,229 --> 01:07:09,219
+the House Committee on Assassinations.
+
+1029
+01:07:11,349 --> 01:07:13,298
+Next day?
+
+1030
+01:07:13,299 --> 01:07:15,278
+He committed suicide.
+
+1031
+01:07:15,279 --> 01:07:18,398
+Supposedly.
+
+1032
+01:07:18,399 --> 01:07:23,269
+I followed him while I could in 1960.
+
+1033
+01:07:24,329 --> 01:07:27,309
+And there was one night...
+
+1034
+01:07:29,389 --> 01:07:34,369
+One night I realized was
+actually an important night.
+
+1035
+01:07:36,309 --> 01:07:41,179
+Because I felt the past push back.
+
+1036
+01:07:50,319 --> 01:07:55,199
+He took his wife Jean out
+to dinner at El Conejo.
+
+1037
+01:07:59,209 --> 01:08:03,298
+All the movers and shakers
+in Dallas ate there.
+
+1038
+01:08:03,299 --> 01:08:06,389
+Best Tex-Mex place in town.
+
+1039
+01:08:09,239 --> 01:08:11,248
+First thing I felt
+
+1040
+01:08:11,249 --> 01:08:15,208
+was an argument outside the
+restaurant got out of hand.
+
+1041
+01:08:15,209 --> 01:08:18,188
+Don't get drawn in.
+
+1042
+01:08:18,189 --> 01:08:22,229
+You need to get the hell out of the way.
+
+1043
+01:08:34,399 --> 01:08:37,228
+His wife goes off to the bar
+
+1044
+01:08:37,229 --> 01:08:40,259
+and he heads into the restaurant alone.
+
+1045
+01:08:44,249 --> 01:08:49,218
+- May I take your coat, sir?
+- No, it's fine, thank you.
+
+1046
+01:09:01,339 --> 01:09:05,278
+The fella out front is gonna
+say, "No tables available."
+
+1047
+01:09:05,279 --> 01:09:07,168
+Sorry, sir,
+
+1048
+01:09:07,169 --> 01:09:09,298
+without reservation we won't be able to...
+
+1049
+01:09:09,299 --> 01:09:12,308
+So have cash.
+
+1050
+01:09:12,309 --> 01:09:15,378
+Oh, I think I can help you.
+
+1051
+01:09:15,379 --> 01:09:17,268
+Can I take your coat, please?
+
+1052
+01:09:17,269 --> 01:09:19,338
+No, it's fine, but may I have that table
+
+1053
+01:09:19,339 --> 01:09:22,348
+right there so I can be close to the band?
+
+1054
+01:09:22,349 --> 01:09:24,268
+Of course.
+
+1055
+01:09:28,389 --> 01:09:30,248
+- Thank you very much.
+- You're very welcome.
+
+1056
+01:09:30,249 --> 01:09:32,268
+This is where you want to be careful,
+
+1057
+01:09:32,269 --> 01:09:35,188
+because one minute, I was
+walking down the stairs,
+
+1058
+01:09:35,189 --> 01:09:39,318
+and the next, I was on fire.
+
+1059
+01:09:39,319 --> 01:09:41,318
+Sent me to the hospital,
+
+1060
+01:09:41,319 --> 01:09:43,288
+and it kept me from
+following de Mohrenschildt
+
+1061
+01:09:43,289 --> 01:09:45,368
+all the way into the restaurant.
+
+1062
+01:09:45,369 --> 01:09:49,178
+I never got to see who he met with.
+
+1063
+01:09:49,179 --> 01:09:50,259
+Oh!
+
+1064
+01:09:50,260 --> 01:09:53,248
+Be careful.
+
+1065
+01:09:53,249 --> 01:09:55,328
+Are you okay? I'm so sorry, seƱor.
+
+1066
+01:09:55,329 --> 01:09:56,399
+Yeah, I'm fine.
+
+1067
+01:09:56,400 --> 01:09:59,278
+- Your coat.
+- Yeah, just leave it.
+
+1068
+01:09:59,279 --> 01:10:01,258
+- Are you sure?
+- Yeah, it's okay.
+
+1069
+01:10:01,259 --> 01:10:05,258
+Once you're past the fire,
+I can't help you anymore.
+
+1070
+01:10:05,259 --> 01:10:08,368
+You're on your own.
+
+1071
+01:10:12,339 --> 01:10:14,208
+Oh, my God.
+
+1072
+01:10:17,309 --> 01:10:19,308
+I'm so sorry. Are you okay?
+
+1073
+01:10:19,309 --> 01:10:21,288
+- Are you okay?
+- I don't know what's going on
+
+1074
+01:10:21,289 --> 01:10:23,288
+tonight. I'm so sorry.
+
+1075
+01:10:25,329 --> 01:10:27,238
+We'll clean that up right away.
+
+1076
+01:10:27,239 --> 01:10:29,168
+Maestro, musica.
+
+1077
+01:10:30,240 --> 01:10:33,388
+Your table.
+
+1078
+01:10:33,389 --> 01:10:35,278
+I'm so sorry.
+
+1079
+01:10:35,279 --> 01:10:36,319
+Could I get you a cocktail?
+
+1080
+01:10:36,320 --> 01:10:38,188
+It's on the house.
+
+1081
+01:10:38,189 --> 01:10:42,179
+- Oh, uh... margarita.
+- Right away.
+
+1082
+01:10:46,229 --> 01:10:50,188
+Of course, I've always
+admired Allen Dulles.
+
+1083
+01:10:50,189 --> 01:10:53,308
+None of this, you understand,
+is completely on the books.
+
+1084
+01:11:03,189 --> 01:11:08,188
+A man like me wants his life to matter.
+
+1085
+01:11:08,189 --> 01:11:11,218
+SeƱor, here's your margarita,
+
+1086
+01:11:11,219 --> 01:11:13,308
+and are you ready to place
+your order for your first...
+
+1087
+01:11:13,309 --> 01:11:15,258
+I'll order later.
+
+1088
+01:11:15,259 --> 01:11:18,288
+Of course, seƱor, may I make
+a suggestion of the special?
+
+1089
+01:11:18,289 --> 01:11:23,198
+- Late, later, not hungry now.
+- Of course, seƱor, of course.
+
+1090
+01:11:23,199 --> 01:11:25,278
+Take your time. It's a short list.
+
+1091
+01:11:25,279 --> 01:11:29,238
+Just a couple guys we'd like to
+know a little bit more about.
+
+1092
+01:11:29,239 --> 01:11:31,188
+We've been instructed
+by Langley to meet with
+
+1093
+01:11:31,189 --> 01:11:33,178
+prospective assets such as yourself.
+
+1094
+01:11:45,309 --> 01:11:49,288
+He's still angry about
+Stalin seizing his estates.
+
+1095
+01:11:49,289 --> 01:11:51,238
+Antonov, no.
+
+1096
+01:11:51,239 --> 01:11:55,238
+Belinsky sometimes complains about the U.S.
+
+1097
+01:11:55,239 --> 01:11:57,278
+but knows he has it good.
+
+1098
+01:11:57,279 --> 01:12:00,378
+Lee Harvey Oswald?
+
+1099
+01:12:02,179 --> 01:12:03,279
+He's a nobody.
+
+1100
+01:12:06,279 --> 01:12:08,398
+āŖ āŖ
+
+1101
+01:12:08,399 --> 01:12:11,358
+I'll let you know what I learn.
+
+1102
+01:12:11,359 --> 01:12:13,239
+We'll be in contact.
+
+1103
+01:12:25,229 --> 01:12:31,168
+No one knows when George
+first met with the CIA.
+
+1104
+01:12:31,169 --> 01:12:34,318
+But the CIA recruited him. This we know.
+
+1105
+01:12:37,289 --> 01:12:39,338
+āŖ āŖ
+
+1106
+01:12:39,339 --> 01:12:43,368
+What we don't know is if
+George recruited Oswald
+
+1107
+01:12:43,369 --> 01:12:45,349
+to kill Kennedy.
+
+1108
+01:13:11,239 --> 01:13:12,248
+- Oh!
+- Oh!
+
+1109
+01:13:12,249 --> 01:13:13,399
+I'm sorry.
+
+1110
+01:13:13,400 --> 01:13:16,258
+I-I'm sorry.
+
+1111
+01:13:19,299 --> 01:13:24,388
+āŖ āŖ
+
+1112
+01:13:24,389 --> 01:13:28,268
+Holy shit, that was the CIA.
+
+1113
+01:13:28,269 --> 01:13:30,278
+That was the Goddamn CIA.
+
+1114
+01:13:30,279 --> 01:13:32,329
+You were right, Al.
+
+1115
+01:13:39,339 --> 01:13:42,208
+Oh, my God. Oh, my God.
+
+1116
+01:13:55,209 --> 01:13:57,168
+Aah!
+
+1117
+01:13:57,169 --> 01:13:58,348
+- No, stop.
+- No, all my stuff is in there.
+
+1118
+01:13:58,349 --> 01:13:59,399
+Stop, just stop.
+
+1119
+01:13:59,400 --> 01:14:04,298
+No, no, no! Help him! Henry!
+
+1120
+01:14:04,299 --> 01:14:08,248
+Oh, why? Oh, how can...
+
+1121
+01:14:08,249 --> 01:14:11,288
+My son!
+
+1122
+01:14:16,289 --> 01:14:22,211
+āŖ āŖ
+
+1123
+01:14:45,369 --> 01:14:51,259
+āŖ āŖ
+
+1124
+01:15:03,309 --> 01:15:07,268
+Oh, shit.
+
+1125
+01:15:07,269 --> 01:15:10,329
+Shit, shit, shit.
+
+1126
+01:15:23,299 --> 01:15:26,378
+Sir, you shouldn't be here.
+
+1127
+01:15:26,379 --> 01:15:31,388
+Yeah, know what, you're right.
+
+1128
+01:15:31,389 --> 01:15:33,228
+I shouldn't be here.
+
+1129
+01:15:33,229 --> 01:15:35,398
+I can't do this.
+
+1130
+01:15:35,399 --> 01:15:38,308
+There are beds available
+down at the shelter.
+
+1131
+01:15:38,309 --> 01:15:40,298
+No, I'm done.
+
+1132
+01:15:40,299 --> 01:15:43,239
+I'm going back to Maine.
+
+1133
+01:15:44,379 --> 01:15:47,258
+āŖ Everyone wants to back Jack āŖ
+
+1134
+01:15:47,259 --> 01:15:49,258
+āŖ Jack is on the right track āŖ
+
+1135
+01:15:49,259 --> 01:15:53,188
+āŖ 'Cause he's got high hopes āŖ
+
+1136
+01:15:53,189 --> 01:15:56,278
+āŖ He's got high hopes āŖ
+
+1137
+01:15:56,279 --> 01:16:00,178
+āŖ 1960's the year āŖ
+
+1138
+01:16:00,179 --> 01:16:03,239
+āŖ For his high hopes āŖ
+
+1139
+01:17:07,319 --> 01:17:13,168
+The day that changed my life
+wasn't a day, but a night.
+
+1140
+01:17:13,169 --> 01:17:17,178
+It was Halloween night.
+
+1141
+01:17:17,179 --> 01:17:22,338
+It was the night my father murdered my mom
+
+1142
+01:17:22,339 --> 01:17:24,368
+and my brother
+
+1143
+01:17:24,369 --> 01:17:29,198
+and my sister with a hammer
+
+1144
+01:17:29,199 --> 01:17:33,268
+and hurt me bad.
+
+1145
+01:17:33,269 --> 01:17:37,198
+It was 1960.
+
+1146
+01:17:37,199 --> 01:17:40,388
+I lived in Holden, Kentucky.
+
+1147
+01:17:40,389 --> 01:17:43,238
+Hey, mister.
+
+1148
+01:17:43,239 --> 01:17:47,258
+You lost?
+
+1149
+01:17:47,259 --> 01:17:50,278
+How far is Holden?
+
+1150
+01:17:50,279 --> 01:17:51,379
+Yeah, turn around.
+
+1151
+01:17:51,380 --> 01:17:56,298
+Head south down Route
+13 for about 30 miles.
+
+1152
+01:17:56,299 --> 01:17:58,198
+Not much of a town, though.
+
+1153
+01:17:58,199 --> 01:18:01,188
+Why'd you want to go there?
+
+1154
+01:18:01,189 --> 01:18:06,398
+'Cause I might be able to do one thing.
+
+1155
+01:18:06,399 --> 01:18:08,329
+Thank you very much.
+
+1156
+01:18:29,239 --> 01:18:32,318
+Bang, got you, you're dead.
+
+1157
+01:18:32,319 --> 01:18:34,348
+Come on, Ellen, now you're dead.
+
+1158
+01:18:34,349 --> 01:18:38,288
+Aah!
+
+1159
+01:18:38,289 --> 01:18:41,388
+Ellen, for goodness sakes,
+turn down the drama.
+
+1160
+01:18:41,389 --> 01:18:44,208
+The neighbors are gonna think
+someone's dying over here.
+
+1161
+01:18:46,299 --> 01:18:48,268
+Daddy!
+
+1162
+01:18:48,269 --> 01:18:52,208
+Hey, don't suppose any of you
+kids want any ice cream, do you?
+
+1163
+01:18:52,209 --> 01:18:54,168
+- Both: I do!
+- You do? Come here!
+
+1164
+01:18:55,309 --> 01:18:56,389
+Come here, Buster.
+
+1165
+01:18:56,390 --> 01:18:58,248
+All right, let's get in there.
+
+1166
+01:18:58,249 --> 01:19:02,278
+You guys in the back.
+
+1167
+01:19:02,279 --> 01:19:04,168
+You're looking beautiful, Doris.
+
+1168
+01:19:04,169 --> 01:19:05,258
+Have them home by 6:00.
+
+1169
+01:19:05,259 --> 01:19:06,399
+Come on, Harry.
+
+1170
+01:19:06,400 --> 01:19:09,168
+What, you don't like ice cream?
+
+1171
+01:19:09,169 --> 01:19:11,258
+Come here, you. Come here, you.
+
+1172
+01:19:13,219 --> 01:19:15,178
+You're going in the front.
+You're going in the front.
+
+1173
+01:19:15,179 --> 01:19:17,339
+You're going in the window.
+Get in there, get in there.
+
+1174
+01:19:31,369 --> 01:19:37,353
+www.addic7ed.com
+
+1175
+01:19:41,249 --> 01:19:47,242
+āŖ āŖ
+
+1176
+01:19:47,243 --> 01:19:52,318
+āŖ Well, you stay out late at night āŖ
+
+1177
+01:19:52,319 --> 01:19:55,178
+āŖ You don't treat your baby right āŖ
+
+1178
+01:19:55,179 --> 01:19:59,198
+āŖ There's strange things
+happening every day āŖ
+
+1179
+01:19:59,199 --> 01:20:00,389
+āŖ Every day āŖ
+
+1180
+01:20:00,390 --> 01:20:02,248
+āŖ Every day āŖ
+
+1181
+01:20:02,249 --> 01:20:03,299
+āŖ Every day āŖ
+
+1182
+01:20:03,300 --> 01:20:05,208
+āŖ Every day āŖ
+
+1183
+01:20:05,209 --> 01:20:10,248
+āŖ Strange things are happening every day āŖ
+
+1184
+01:20:10,249 --> 01:20:12,228
+āŖ Found me someone new āŖ
+
+1185
+01:20:12,229 --> 01:20:15,238
+āŖ And I'm really through with you āŖ
+
+1186
+01:20:15,239 --> 01:20:20,168
+āŖ Strange things are happening every day āŖ
+
+1187
+01:20:20,169 --> 01:20:23,388
+āŖ Yeah, strange things are happening āŖ
+
+1188
+01:20:23,389 --> 01:20:29,341
+āŖ Happening every day āŖ
+
+1188
+01:20:30,305 --> 01:20:36,479
+Please rate this subtitle at www.osdb.link/78y7q
+Help other users to choose the best subtitles
diff --git a/packages/mediacenter/kodi/config/base-addon_data/visualization.spectrum/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/visualization.spectrum/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..61bb41acb34b6bf1b5af107368ae7148179f6355
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/visualization.spectrum/settings.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/weather.openweathermap/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/weather.openweathermap/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3f6c020c982b0581a67c9628be6f6a2b7f7fb390
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addon_data/weather.openweathermap/settings.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addon_data/weather.yahoo/settings.xml b/packages/mediacenter/kodi/config/base-addon_data/weather.yahoo/settings.xml
deleted file mode 100644
index ead32a35df71d7b9aadde9d2c1ba6b887403dcfe..0000000000000000000000000000000000000000
--- a/packages/mediacenter/kodi/config/base-addon_data/weather.yahoo/settings.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.advancedsettings/helpers/__init__.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.advancedsettings/helpers/__init__.pyo
deleted file mode 100644
index f8b99eee4e82384eac8b82d8c079d122a79c3c6d..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.advancedsettings/helpers/__init__.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.advancedsettings/helpers/stringhelpers.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.advancedsettings/helpers/stringhelpers.pyo
deleted file mode 100644
index 55fc8d690a16c41403e7e0cb35b46250efb02c71..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.advancedsettings/helpers/stringhelpers.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.advancedsettings/settings.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.advancedsettings/settings.pyo
deleted file mode 100644
index 212de86c387ca27a81ab69fd36054a559053cf5e..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.advancedsettings/settings.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.logviewer/settings.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.logviewer/settings.pyo
deleted file mode 100644
index 2e8c5d1c02c715c7e5bc3d334e2434a3c920949a..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.logviewer/settings.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.sickbeard/resources/__init__.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.sickbeard/resources/__init__.pyo
deleted file mode 100644
index 85a3f77227941314bee830f7ec2a644d2b524022..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.sickbeard/resources/__init__.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.sickbeard/resources/lib/__init__.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.sickbeard/resources/lib/__init__.pyo
deleted file mode 100644
index 7c8181af90e34f8f189d576bfe1eeb28bc4e0f78..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.sickbeard/resources/lib/__init__.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.sickbeard/resources/lib/settings.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.sickbeard/resources/lib/settings.pyo
deleted file mode 100644
index f78e4012b9eab9ebe8984f751f15012a79ee50dc..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.sickbeard/resources/lib/settings.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.sickbeard/resources/lib/shows.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.sickbeard/resources/lib/shows.pyo
deleted file mode 100644
index 1d9db3edc95504818eac3ff49c414a1db0bb8ff2..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.sickbeard/resources/lib/shows.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.sickbeard/resources/lib/sickbeard.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.sickbeard/resources/lib/sickbeard.pyo
deleted file mode 100644
index 804ba275157304c6201f0c2aa8a6ee36fc44b42d..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.sickbeard/resources/lib/sickbeard.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/cache.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/cache.pyo
deleted file mode 100644
index ad359a5e4ca93bac513b728962a79f4e5318ffd8..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/cache.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/favourite.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/favourite.pyo
deleted file mode 100644
index 2a277b3319e690e8e1dc27722021794b82e00edd..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/favourite.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/history.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/history.pyo
deleted file mode 100644
index a6f8821091d8edb45e13bbf6387de06841605266..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/history.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/menus.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/menus.pyo
deleted file mode 100644
index 75ec45fc340c93f1613844642d292a6904806ed3..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/menus.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/player.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/player.pyo
deleted file mode 100644
index de802e09af85207220dfa8e5a8ebe71c8943bc24..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/player.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/playlist.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/playlist.pyo
deleted file mode 100644
index 75ef20177245a92902eb4f4f877e616d7e5d1883..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/playlist.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/quicknet.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/quicknet.pyo
deleted file mode 100644
index 47bb929a5c0c42390de6b95865f483aa7c1c85b3..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/quicknet.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/search.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/search.pyo
deleted file mode 100644
index 6fe397fb7b98d116e1f5044f39b91e8bd34bd6bb..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/search.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/sfile.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/sfile.pyo
deleted file mode 100644
index 89846b075d49a9fe1973982f9526768a6c69a440..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/sfile.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/utils.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/utils.pyo
deleted file mode 100644
index 7a1f2293bf0fc57187b30fbee9cdcf7938faa8d1..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.program.super.favourites/utils.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.1337x/main.pyo b/packages/mediacenter/kodi/config/base-addons/plugin.video.1337x/main.pyo
deleted file mode 100644
index d73fa22ff5cb3eeff68f96ac158add106786c9ee..0000000000000000000000000000000000000000
Binary files a/packages/mediacenter/kodi/config/base-addons/plugin.video.1337x/main.pyo and /dev/null differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/.gitignore b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..a76e8852a6cf48985ba3661384e942cca07b4c40
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/.gitignore
@@ -0,0 +1,9 @@
+*.lwp
+*.pyc
+*.pyo
+.settings
+pysrc/
+cache/*
+.project
+.pydevproject
+*xbmcstubs*
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/LICENSE.txt b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4f8e8eb30cc7571be180a7061524b5b3d7a85beb
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/LICENSE.txt
@@ -0,0 +1,282 @@
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+-------------------------------------------------------------------------
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/addon.xml b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/addon.xml
new file mode 100644
index 0000000000000000000000000000000000000000..eef2ffebcb9695b5a6273b25122b9d9d2e914586
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/addon.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+ video
+
+
+ Play videos of different sports streaming sites
+ Play videos of different sports streaming sites
+ GNU GENERAL PUBLIC LICENSE. Version 2, June 1991
+ all
+
+
\ No newline at end of file
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/changelog.txt b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/changelog.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7e188c3e9e6200dfb6780d8d49857ce3d86400e3
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/changelog.txt
@@ -0,0 +1,167 @@
+v 1.8.8.0 (17-Oct-14)
+------------------------------
+- Fix: iLive (thanks to L2501)
+- New: StreamHD in Sports TV
+- New: HitSports in Sports TV
+- Some minor fixes and catcher updates (thanks to L2501)
+
+------------------------------
+v 1.8.7.9 (29-Sep-14)
+------------------------------
+- New: Post data support (thanks to L2501)
+- Updated RojaDirecta (new url)
+- Some catcher updates (thanks to L2501 and Enen)
+
+------------------------------
+v 1.8.7.8 (20-Sep-14)
+------------------------------
+- Updated CoolSports (new url)
+- Fixed FirstRowSports.eu
+- Some catcher updates (thanks to L2501)
+
+------------------------------
+v 1.8.7.7 (24-Aug-14)
+------------------------------
+- Added SoccerEmbed.com to Sports TV
+- Added U-Peak.me to Sports TV
+- Catchers update
+
+------------------------------
+v 1.8.7.6 (21-Aug-14)
+------------------------------
+- Updated VipBox.tv
+- Updated CoolSports.tv
+- Some catcher updates (thanks to L2501)
+
+------------------------------
+v 1.8.7.5 (20-Jul-14)
+------------------------------
+- Added p.a.c.k.e.r function (thanks to shani_08)
+- Added javascript demystification function can now be called from cfg files (thanks to shani_08)
+- Added Streamago.tv to Live TV
+- Added iPhone.TV to Live TV
+- Updated p2p links (thanks to sattekop)
+- Updated Cricfree.tv new url
+- Fixed AtdheNet.tv
+- Fixed Strmh24.com
+- Fixed SportsHDFree.com
+- Some minor catcher updates
+
+------------------------------
+v 1.8.7.4 (10-Jul-14)
+------------------------------
+- Some minor catcher updates
+
+------------------------------
+v 1.8.7.3 (10-Jul-14)
+------------------------------
+- Updated Cricket in Sports TV
+- Updated BlackTvLive.com in Live TV (Shini's zemtv addon is needed to play some links)
+- Some minor catcher updates
+
+------------------------------
+v 1.8.7.2 (9-Jul-14)
+------------------------------
+- New js functions work now(thanks to shani_08)
+- Some minor catcher updates
+
+------------------------------
+v 1.8.7.1 (6-Jul-14)
+------------------------------
+- Added javascript demystification functions(thanks to shani_08)
+- Updated VipBox.tv new url
+- Updated FirstRowSports.eu new url
+- Fixed FirstRowSports.ge
+- Fixed LiveTV.ru
+- Fixed Strmh24.com
+- Some minor catcher updates
+
+------------------------------
+v 1.8.7 (27-Jun-14)
+------------------------------
+- Fixed division function (thanks to Enen)
+- Fixed StopStream.com categories
+- Fixed rojadirecta.me not all available links were captured
+- Fixed Strmh24.com
+- Added DittoTV.com (Indian TV)
+- Added BlackTvLive.com (Indian TV)
+- Some minor catcher updates
+
+------------------------------
+v 1.8.6.9 (10-Jun-14)
+------------------------------
+- Updated Ace links(thanks to sattekop)
+- Fixed HDfoots
+- Added ZengaTV.com (Indian TV)
+- Some minor catcher updates
+
+------------------------------
+v 1.8.6.8 (28-May-14)
+------------------------------
+- Fixed favourites (thanks to enen)
+- Added livefootball.ws to Live Sports (thanks to enen)
+- Fixed rojadirecta.me in Live Sports (thanks to enen)
+- Added fifaembed.com and strmh24.com in Sports TV
+- Fixed cricfire.com, mycrickethighlights.com and goalsarena in Highlights
+- Lots of minor fixes
+
+------------------------------
+v 1.8.6.7 (18-May-14)
+------------------------------
+- Catchers update
+
+------------------------------
+v 1.8.6.6 (13-Apr-14)
+------------------------------
+- Updated Wiziwig.tv not all available links were captured
+- Added TvToast.com to Live TV
+- Some minor catcher updates
+
+------------------------------
+v 1.8.6.5 (31-Mar-14)
+------------------------------
+- MilhouseVH has improved regex performance which will improve loading time
+- Updated VipBox.tv
+- Updated LiveFootballVideo.com
+- Fixed CricFire.com in Highlights
+- Added VaughnLive.tv to Live TV (thanks to pmolson for help with url resolution)
+- Some minor catcher updates
+
+------------------------------
+v 1.8.6.4 (15-Mar-14)
+------------------------------
+- Added bundesliga-streams.net to Live Sports
+- Updated Ace links(thanks to sattekop)
+- Fixed hdfooty.tv and iGole.tv in Sports TV
+- SopCast links are now streamed with P2P Streams plugin
+- Some minor catcher updates
+
+------------------------------
+v 1.8.6.3 (26-Feb-14)
+------------------------------
+- Fixed Highlights section (added new url resolvers: vk.com, facebook, playwire and dailymotion links are now streamed using dailymotion addon.
+- Added Movies category in iLive module
+- Some minor catcher fixes
+
+------------------------------
+v 1.8.6.2 (16-Feb-14)
+------------------------------
+- iLive fixed
+- Ace links updated (thanks to sattekop)
+
+------------------------------
+v 1.8.6.1 (15-Feb-14)
+------------------------------
+- AceStreams can now be streamed with external player or P2P Streams plugin
+- Lots of minor catcher fixes.
+
+------------------------------
+v 1.8.6 (15-Feb-14)
+------------------------------
+- Initial new repo release
+
+
+
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/default.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/default.py
new file mode 100644
index 0000000000000000000000000000000000000000..19c0cb3f0023fc43f1da0b6dd910b995dfa97645
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/default.py
@@ -0,0 +1,32 @@
+import sys, traceback
+
+
+# REMOTE DEBUGGING
+REMOTE_DBG = False
+
+# append pydev remote debugger
+if REMOTE_DBG:
+ # Make pydev debugger works for auto reload.
+ # Note pydevd module need to be copied in XBMC\system\python\Lib\pysrc
+ try:
+ import pydevd
+ # stdoutToServer and stderrToServer redirect stdout and stderr to eclipse console
+ pydevd.settrace('localhost', stdoutToServer=True, stderrToServer=True, suspend=False)
+ except ImportError:
+ sys.stderr.write("Error: " +
+ "You must add org.python.pydev.debug.pysrc to your PYTHONPATH.")
+ sys.exit(1)
+ except:
+ sys.stderr.write('Remote Debugger is not started')
+
+
+
+
+# ACTUAL ADDON
+from lib import main
+
+try:
+ myAddon = main.Main()
+ myAddon.run(sys.argv)
+except:
+ traceback.print_exc(file = sys.stdout)
\ No newline at end of file
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/fanart.jpg b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/fanart.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..5e8c0a4850c1de21d49743ee016f0ea5c3f92e16
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/fanart.jpg differ
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/icon.png b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..144b6373c0eea81116151caec3d3ccc4124820c1
Binary files /dev/null and b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/icon.png differ
diff --git a/packages/mediacenter/kodi/config/base-addons/script.subscription.pulsar.favourites/resources/language/version.txt b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/__init__.py
similarity index 100%
rename from packages/mediacenter/kodi/config/base-addons/script.subscription.pulsar.favourites/resources/language/version.txt
rename to packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/__init__.py
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/addonInstaller.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/addonInstaller.py
new file mode 100644
index 0000000000000000000000000000000000000000..f47124aac12550b05aea167025786beddafcd9f5
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/addonInstaller.py
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+import os
+import xbmc, xbmcaddon
+import common
+import urllib
+import zipfile
+
+from traceback import print_exc
+from dialogs.dialogProgress import DialogProgress
+from utils.fileUtils import getFileContent, clearDirectory
+from utils.regexUtils import findall
+
+PACKAGE_DIR = "special://home/addons/packages/"
+INSTALL_DIR = "special://home/addons/"
+
+
+DICT = {
+ 'veetle': 'https://github.com/sissbruecker/xbmc-veetle-plugin/archive/master.zip',
+ 'jtv': 'https://divingmules-repo.googlecode.com/files/plugin.video.jtv.archives-0.3.6.zip',
+ 'youtube': 'http://ftp.hosteurope.de/mirror/xbmc.org/addons/frodo/plugin.video.youtube/plugin.video.youtube-4.4.4.zip'
+ }
+
+def install(key):
+ entry = DICT[key]
+ return _install_addon(entry)
+
+def _install_addon(url):
+ ri = AddonInstaller()
+ compressed = ri.download(url)
+ if compressed:
+ addonId = ri.install(compressed)
+ if addonId:
+ xbmc.sleep(100)
+ xbmc.executebuiltin('UpdateLocalAddons')
+ xbmc.sleep(100)
+ try:
+ _N_ = xbmcaddon.Addon(id=addonId)
+ common.showNotification(_N_.getAddonInfo("name"), 'Addon installed', 2000, _N_.getAddonInfo("icon"))
+ return True
+ except:
+ pass
+ return False
+
+def isInstalled(addonId):
+ try:
+ _N_ = xbmcaddon.Addon(id=addonId)
+ return True
+ except:
+ return False
+
+
+class AddonInstaller:
+
+ def download(self, url, destination=PACKAGE_DIR):
+ try:
+ dlg = DialogProgress()
+ dlg.create('SportsDevil - Installing external addon')
+ destination = xbmc.translatePath(destination) + os.path.basename(url)
+ def _report_hook(count, blocksize, totalsize):
+ percent = int(float(count * blocksize * 100) / totalsize)
+ dlg.update(percent, url, destination)
+ fp, _ = urllib.urlretrieve(url, destination, _report_hook)
+ return fp
+ except:
+ print_exc()
+ dlg.close()
+ return ""
+
+ def extract(self, fileOrPath, directory):
+ try:
+ if not directory.endswith(':') and not os.path.exists(directory):
+ os.mkdir(directory)
+ zf = zipfile.ZipFile(fileOrPath)
+ for _, name in enumerate(zf.namelist()):
+ if name.endswith('/'):
+ path = os.path.join(directory, name)
+ if os.path.exists(path):
+ clearDirectory(path)
+ else:
+ os.makedirs(path, 0777)
+ else:
+ outfile = open(os.path.join(directory, name), 'wb')
+ outfile.write(zf.read(name))
+ outfile.flush()
+ outfile.close()
+ return zf.filelist
+
+ except:
+ print_exc()
+
+ return None
+
+ def install(self, filename):
+ destination = xbmc.translatePath(INSTALL_DIR)
+ files = self.extract(filename, destination)
+ if files:
+ addonXml = filter(lambda x: x.filename.endswith('addon.xml'), files)
+ if addonXml:
+ path = os.path.join(destination, addonXml[0].filename)
+ content = getFileContent(path)
+ addonId = findall(content, ' 0:
+ index = ''
+ words = []
+ newword = ''
+ for m in m_reg:
+ if not m.startswith(' '):
+ index = m
+ del words[:]
+ else:
+ replWord = m.strip()
+ words.append(replWord)
+ if word.find(' ') != -1:
+ newword = word.replace(replWord,index)
+
+ if (word in words) or (word == index):
+ return index
+
+ if newword != '' and newword != word:
+ return newword
+ except:
+ common.log('Skipped Replacement: ' + word)
+
+ return word
+
+
+def select(params,src):
+ paramArr = __parseParams(params)
+ title = paramArr[0]
+ params = paramArr[1]
+ menuItems = params.split("|")
+ return select(title, menuItems)
+
+
+def convDate(params, src):
+ language = common.language
+
+ if params.find("','") != -1:
+ paramArr = __parseParams(params)
+ oldfrmt = paramArr[0]
+ newfrmt = paramArr[1]
+ offsetStr = ''
+ if len(paramArr) > 2:
+ offsetStr = paramArr[2]
+ return dt.convDate(language, src, str(oldfrmt), str(newfrmt), offsetStr)
+ else:
+ params = params.strip("'")
+ return dt.convDate(language, src,params)
+
+
+def convTimestamp(params, src):
+ if params.find("','") != -1:
+ paramArr = __parseParams(params)
+ newfrmt = paramArr[0]
+ offsetStr = paramArr[1]
+ return dt.convTimestamp(src, str(newfrmt), offsetStr)
+ else:
+ newfrmt = params.strip("'")
+ return dt.convTimestamp(src, str(newfrmt))
+
+
+def offset(params, src):
+ paramArr = __parseParams(params)
+ t = paramArr[0].replace('%s', src)
+ o = paramArr[1].replace('%s', src)
+
+ hours = int(t.split(':')[0])
+ minutes = int(t.split(':')[1])
+ ti = datetime.datetime(2000, 1, 1, hours, minutes)
+
+ offset = dt.datetimeoffset(ti, o)
+
+ return offset.strftime('%H:%M')
+
+
+def getSource(params, src):
+ paramPage = ''
+ paramReferer = ''
+ if params.find('\',\'') > -1:
+ paramPage, paramReferer = __parseParams(params)
+ else:
+ paramPage = params.strip('\',\'')
+
+ paramPage = paramPage.replace('%s', src)
+ return common.getHTML(paramPage, None, paramReferer)
+
+
+def parseText(item, params, src):
+ paramArr = __parseParams(params)
+
+ text = paramArr[0].replace('%s',src)
+ if text.startswith('@') and text.endswith('@'):
+ text = item.getInfo(text.strip('@'))
+
+ regex = paramArr[1].replace('%s', src)
+ if regex.startswith('@') and regex.endswith('@'):
+ regex = item.getInfo(regex.strip('@'))
+
+ variables = []
+ if len(paramArr) > 2:
+ variables = paramArr[2].split('|')
+ return reg.parseText(text, regex, variables)
+
+
+def getInfo(item, params, src, xml=False, mobile=False):
+ paramArr = __parseParams(params)
+ paramPage = paramArr[0].replace('%s', src)
+
+ if paramPage.startswith('@') and paramPage.endswith('@'):
+ paramPage = item.getInfo(paramPage.strip('@'))
+
+
+ paramRegex = paramArr[1].replace('%s', src)
+ if paramRegex.startswith('@') and paramRegex.endswith('@'):
+ paramRegex = item.getInfo(paramRegex.strip('@'))
+
+ referer = ''
+ form_data = ''
+ variables=[]
+ if len(paramArr) > 2:
+ referer = paramArr[2]
+ referer = referer.replace('%s', src)
+ if referer.startswith('@') and referer.endswith('@'):
+ referer = item.getInfo(referer.strip('@'))
+ if len(paramArr) > 3:
+ variables = paramArr[3].strip("'").split('|')
+
+ parsed_link = urlparse.urlsplit(referer)
+ parsed_link = parsed_link._replace(netloc=parsed_link.netloc.encode('idna'),path=urllib.quote(parsed_link.path.encode('utf-8')))
+ referer = parsed_link.geturl().encode('utf-8')
+
+ try:
+ parts = (paramPage.split('|', 1) + [None] * 2)[:2]
+ paramPage, form_data = parts
+ form_data = urlparse.parse_qsl(form_data)
+ except:
+ pass
+
+ common.log('Get Info from: "'+ paramPage + '" from "' + referer + '"')
+ data = common.getHTML(paramPage, form_data, referer, xml, mobile, ignoreCache=False,demystify=True)
+ return reg.parseText(data, paramRegex, variables)
+
+
+def decodeBase64(src):
+ from base64 import b64decode
+ return b64decode(src)
+
+def decodeRawUnicode(src):
+ try:
+ return src
+ except:
+ return src
+
+def resolve(src):
+ try:
+ parsed_link = urlparse.urlsplit(src)
+ tmp_host = parsed_link.netloc.split(':')
+ if tmp_host[0] == 'watch4.streamlive.to':
+ servers = ['80.82.78.4',
+ '94.102.63.55',
+ '95.211.210.69',
+ '95.211.196.5',
+ #+'94.102.63.56',
+ '184.173.85.91']
+ import random
+ tmp_host[0] = random.choice(servers)
+ else:
+ tmp_host[0] = socket.gethostbyname(tmp_host[0])
+ tmp_host = ':'.join(tmp_host)
+ parsed_link = parsed_link._replace(netloc=tmp_host)
+ return parsed_link.geturl()
+ except:
+ return src
+
+
+def replace(params, src):
+ paramArr = __parseParams(params)
+ paramstr = paramArr[0].replace('%s', src)
+ paramSrch = paramArr[1]
+ paramRepl = paramArr[2]
+ return paramstr.replace(paramSrch,paramRepl)
+
+
+def replaceRegex(params, src):
+ paramArr = __parseParams(params)
+ paramStr = paramArr[0].replace('%s', src)
+ paramSrch = paramArr[1]
+ paramRepl = paramArr[2]
+
+ r = re.compile(paramSrch, re.DOTALL + re.IGNORECASE)
+ ms = r.findall(paramStr)
+ if ms:
+ for m in ms:
+ paramStr = paramStr.replace(m, paramRepl,1)
+ return paramStr
+ return src
+
+
+def resolveVariable(item, param):
+ if param.startswith('@') and param.endswith('@'):
+ return item.getInfo(param.strip('@'))
+ return param
+
+
+def ifEmpty(item, params, src):
+ paramArr = __parseParams(params)
+
+ paramSource = resolveVariable(item, paramArr[0].replace('%s', src))
+ paramTrue = resolveVariable(item, paramArr[1].replace('%s', src))
+ paramFalse = resolveVariable(item, paramArr[2].replace('%s', src))
+
+ if paramSource == '':
+ return paramTrue
+ else:
+ return paramFalse
+
+
+def isEqual(item, params, src):
+ paramArr = __parseParams(params)
+ paramSource = resolveVariable(item, paramArr[0].replace('%s', src))
+ paramComp = resolveVariable(item, paramArr[1].replace('%s', src))
+ paramTrue = resolveVariable(item, paramArr[2].replace('%s', src))
+ paramFalse = resolveVariable(item, paramArr[3].replace('%s', src))
+
+ if (paramSource == paramComp):
+ return paramTrue
+ else:
+ return paramFalse
+
+
+def ifFileExists(item, params, src):
+ paramArr = __parseParams(params)
+ paramSource = resolveVariable(item, paramArr[0].replace('%s', src))
+ paramTrue = resolveVariable(item, paramArr[1].replace('%s', src))
+ paramFalse = resolveVariable(item, paramArr[2].replace('%s', src))
+
+ if fileExists(paramSource):
+ return paramTrue
+ else:
+ return paramFalse
+
+
+def ifExists(item, params, src):
+ paramArr = __parseParams(params)
+ paramSource = resolveVariable(item, paramArr[0].replace('%s', src))
+ paramTrue = resolveVariable(item, paramArr[1].replace('%s', src))
+ paramFalse = resolveVariable(item, paramArr[2].replace('%s', src))
+
+ return paramTrue
+
+
+
+def urlMerge(params, src):
+ paramArr = __parseParams(params)
+ paramTrunk = paramArr[0].replace('%s', src).replace("\t","")
+ paramFile= paramArr[1].replace('%s', src).replace("\t","")
+
+ if not paramFile.startswith('http'):
+ up = urlparse.urlparse(urllib.unquote(paramTrunk))
+ if paramFile.startswith('/'):
+ return urllib.basejoin(up[0] + '://' + up[1], paramFile)
+ else:
+ return urllib.basejoin(up[0] + '://' + up[1] + '/' + up[2],paramFile)
+ return src
+
+def decodeXppod(src):
+ return xp.decode(src)
+
+def decodeXppod_hls(src):
+ return xp.decode_hls(src)
+
+def getCookies(cookieName, url):
+ domain = urlparse.urlsplit(url).netloc
+ return common.getCookies(cookieName, domain)
+
+
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/customModulesManager.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/customModulesManager.py
new file mode 100644
index 0000000000000000000000000000000000000000..c27a8113ab411b63eb15af15adfa2c6707573fc1
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/customModulesManager.py
@@ -0,0 +1,156 @@
+# -*- coding: utf-8 -*-
+
+import urllib
+import os
+import zipfile
+
+from utils import xbmcUtils, fileUtils
+
+
+class CustomModulesManager:
+
+ def __init__(self, customModulesFolder, customModulesRepo):
+ self._customModulesFolder = customModulesFolder
+
+ if not os.path.exists(self._customModulesFolder):
+ os.makedirs(self._customModulesFolder, 0777)
+
+ self._customModulesRepo = customModulesRepo
+ self._customModulesFile = os.path.join(self._customModulesFolder, 'custom.cfg')
+ self.modules = []
+
+
+ def getCustomModules(self):
+ head = [\
+ '########################################################',
+ '# Custom Modules #',
+ '########################################################',
+ ''
+ ]
+
+ txt = '\n'.join(head)
+
+ self.modules = []
+ for root, _, files in os.walk(self._customModulesFolder , topdown = False):
+ for name in files:
+ if name.endswith('.module'):
+ self.modules.append(name)
+ txt += fileUtils.getFileContent(os.path.join(root, name)) + '\n'
+ fileUtils.setFileContent(self._customModulesFile, txt)
+
+
+
+ def downloadCustomModules(self):
+
+ def get_dir_listing(url):
+
+
+ f = urllib.urlopen(url)
+ response = f.read()
+ f.close()
+
+ text = response.split("\n")
+ urls = []
+ httptag = "http://"
+ tag=' href="'
+ for line in text:
+ if tag in line.lower():
+ for i, _ in enumerate(line):
+ if tag == line[i:i+len(tag)].lower():
+ textline = line[i+len(tag):]
+ end = textline.find('"')
+ u = textline[:end]
+ if not httptag in u and not ".." in u and not "mailto:" in u and not "mailto:" in u:
+ if url[-1] != "/":
+ u = url+"/"+u
+ else:
+ u = url+u
+ if not "/." in u:
+ urls.append(u)
+
+ return urls
+
+
+ def downloadFile(url, file_path):
+ urllib.urlretrieve(url, file_path)
+ return os.path.isfile(file_path)
+
+
+ def extract(fileOrPath, directory):
+ if not directory.endswith(':') and not os.path.exists(directory):
+ os.mkdir(directory)
+
+ zf = zipfile.ZipFile(fileOrPath)
+
+ for _, name in enumerate(zf.namelist()):
+ if name.endswith('/'):
+ os.makedirs(os.path.join(directory, name), 0777)
+ else:
+ outfile = open(os.path.join(directory, name), 'wb')
+ outfile.write(zf.read(name))
+ outfile.flush()
+ outfile.close()
+
+
+
+ repo_url = self._customModulesRepo
+
+ xbmcUtils.showBusyAnimation()
+ files = get_dir_listing(repo_url)
+ menuItems = map(lambda x: x.replace(repo_url,'').replace('.zip',''), files)
+ xbmcUtils.hideBusyAnimation()
+
+ select = xbmcUtils.select('Select module', menuItems)
+ if select:
+ target = os.path.join(self._customModulesFolder, select + '.zip')
+
+ xbmcUtils.showBusyAnimation()
+ index = menuItems.index(select)
+ success = downloadFile(files[index], target)
+ xbmcUtils.hideBusyAnimation()
+
+ if success:
+ extract(target, self._customModulesFolder)
+ os.remove(target)
+ return True
+ else:
+ return False
+
+ return None
+
+
+ def removeCustomModule(self, moduleName):
+ try:
+ customCfg = self._customModulesFile
+ content = fileUtils.getFileContent(customCfg)
+ lines = content.splitlines()
+
+ startIdx = -1
+ cfgUrl = ''
+ for i in range(0, len(lines)):
+ if lines[i].startswith("title=%s" % moduleName):
+ startIdx = i
+
+ elif startIdx > -1 and lines[i].startswith("url="):
+ tmp = lines[i][4:]
+ cfgUrl = os.path.join(self._customModulesFolder, tmp)
+ break
+
+ if os.path.isfile(cfgUrl):
+ os.remove(cfgUrl)
+ os.remove(cfgUrl.replace(".cfg", ".module"))
+
+ # remove all folder that start with cfg name and a dot
+ baseDir = os.path.dirname(cfgUrl)
+ prefix = os.path.basename(cfgUrl).replace(".cfg", ".")
+ dirs = fileUtils.get_immediate_subdirectories(baseDir)
+ for d in dirs:
+ if d.startswith(prefix):
+ fileUtils.clearDirectory(os.path.join(baseDir, d))
+ os.removedirs(os.path.join(baseDir, d))
+
+ return True
+ except:
+ pass
+
+ return False
\ No newline at end of file
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/customReplacements.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/customReplacements.py
new file mode 100644
index 0000000000000000000000000000000000000000..0335b03992eee5e338eada54b23e70a0fbcf8f2e
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/customReplacements.py
@@ -0,0 +1,161 @@
+# -*- coding: utf-8 -*-
+import os.path
+import re
+from string import lower
+
+
+import common
+import utils.fileUtils as fu
+from utils.regexUtils import findall
+
+
+
+class CustomReplacements(object):
+
+ def __init__(self):
+
+ self.simpleScheme = {'(@PLATFORM@)': os.environ.get('OS'),
+ '(@CURRENT_URL@)': fu.getFileContent(os.path.join(common.Paths.cacheDir, 'lasturl')),
+ '(@LANGUAGE@)': self.languageShortName(common.language)
+ }
+
+ self.complexScheme = { 'import': '(#*@IMPORT=([^@]+)@)',
+ 'find': '(#*@FIND\(.*?\)@)',
+ 'catch': '(#*@CATCH\([^\)]+\)@)'
+ }
+
+
+ def languageShortName(self, longName):
+ if str(longName).lower() == 'german':
+ return 'de'
+ else:
+ return 'en'
+
+ def regex(self, item):
+ return self.complexScheme.get(item)
+
+ def __replaceImports(self, pathToImports, data):
+ while True:
+ m_reg = findall(data, self.regex('import'))
+ if len(m_reg) > 0:
+ for idat in m_reg:
+ if idat[0].startswith('#'):
+ data = data.replace(idat[0],'')
+ continue
+ filename = idat[1]
+ pathImp = os.path.join(common.Paths.modulesDir, filename)
+ if not os.path.exists(pathImp):
+ pathImp = os.path.join(pathToImports, filename)
+ if not (os.path.exists(pathImp)):
+ common.log('Skipped Import: ' + filename)
+ continue
+ dataImp = fu.getFileContent(pathImp)
+ dataImp = dataImp.replace('\r\n','\n')
+ data = data.replace(idat[0], dataImp)
+ else:
+ break
+ return data
+
+ def __replaceParameters(self, data, params=[]):
+ i=1
+ for par in params:
+ matches = findall(data,'(@PARAM' + str(i) + '@)')
+ if matches:
+ for m in matches:
+ ptemp = str(par).strip()
+ data = data.replace(m, ptemp)
+ i = i + 1
+ return data
+
+ def __replaceFinders(self, data):
+ m_reg = findall(data, self.regex('find'))
+ if len(m_reg) > 0:
+ for idat in m_reg:
+ if idat.startswith('#'):
+ continue
+ ps = idat[6:-2].strip().split(',')
+ method = ps[0].strip("'")
+ param1 = ps[1].strip("'")
+ param2 = ps[2].strip("'")
+ param3 = ps[3].strip("'")
+
+ if method == 'JS1':
+ jsName = param1
+ idName = param2
+ varName = param3
+ regex = "(?:java)?scr(?:'\+')?ipt[^<]+" + idName + "\s*=\s*[\"']([^\"']+)[\"'][^<]*[^<]* 0:
+ for m in m_reg:
+ new_reg=p_reg.match(m[0])
+ condStr = new_reg.group(3)
+ hidePassage=False
+ if condStr.find('==') != -1:
+ condArr=condStr.split('==')
+ hidePassage = condArr[0].strip().lower() != condArr[1].strip().lower()
+ elif condStr.find('!=') != -1:
+ condArr=condStr.split('!=')
+ hidePassage = condArr[0].strip().lower() == condArr[1].strip().lower()
+
+ if hidePassage:
+ data = data.replace(str(new_reg.group(1)),'')
+ else:
+ tmpdata = str(new_reg.group(1))
+ tmpdata = tmpdata.replace(str(new_reg.group(2)),'',1)
+ tmpdata = tmpdata[:-len(str(new_reg.group(4)))]
+ data = data.replace(str(new_reg.group(1)),tmpdata)
+ return data
+
+
+
+
+ def replace(self, pathToImports, data, lItem, params=[]):
+ data = self.__replaceParameters(data, params)
+ data = self.__replaceConditions(data)
+ data = self.__replaceImports(pathToImports, data)
+ data = self.__replaceParameters(data, params)
+ data = self.__replaceFinders(data)
+ data = self.__replaceCatchers(data)
+ data = self.__replaceSimpleVars(data)
+ data = self.__replaceConditions(data)
+ return data
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/__init__.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/dialogBrowser.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/dialogBrowser.py
new file mode 100644
index 0000000000000000000000000000000000000000..41d8fa448d73f2e5172ba8fa08dda5c0620d21bf
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/dialogBrowser.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+
+import xbmcgui
+
+class DialogBrowser:
+
+ def __init__(self):
+ self.dlg = xbmcgui.Dialog()
+
+ def browseFolders(self, head):
+ return self.dlg.browse(0, head,'files', '', False, False)
+
+ def close(self):
+ self.dlg.close()
\ No newline at end of file
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/dialogError.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/dialogError.py
new file mode 100644
index 0000000000000000000000000000000000000000..afa31bf475a2f33b4fcb62bf72784e8a4bd0303c
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/dialogError.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+
+import xbmcgui
+
+class DialogError:
+
+ def __init__(self):
+ self.dlg = xbmcgui.Dialog()
+ self.head = 'SportsDevil Error'
+
+ def show(self, message):
+ self.dlg.ok(self.head, message)
+
+ def close(self):
+ self.dlg.close()
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/dialogInfo.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/dialogInfo.py
new file mode 100644
index 0000000000000000000000000000000000000000..cf50f679282375063917c9840519d37b2602257b
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/dialogInfo.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+
+import xbmcgui
+
+class DialogInfo:
+
+ def __init__(self):
+ self.dlg = xbmcgui.Dialog()
+ self.head = 'SportsDevil Info'
+
+ def show(self, message):
+ self.dlg.ok(self.head, message)
\ No newline at end of file
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/dialogProgress.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/dialogProgress.py
new file mode 100644
index 0000000000000000000000000000000000000000..4e4b385ebb874ffc51cb3af951c49e948dbf2c97
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/dialogProgress.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+
+import xbmcgui
+
+class DialogProgress:
+
+ def __init__(self):
+ self.dlg = xbmcgui.DialogProgress()
+ self.__reset__()
+
+ def __reset__(self):
+ self.head = ''
+ self.firstline = ''
+ self.secondline = None
+ self.thirdline = None
+ self.percent = 0
+
+ def isCanceled(self):
+ return self.dlg.iscanceled()
+
+ def update(self, percent=None, firstline=None, secondline=None, thirdline=None):
+ if firstline:
+ self.firstline = firstline
+ if secondline:
+ self.secondline = secondline
+ if thirdline:
+ self.thirdline = thirdline
+ if percent:
+ self.percent = percent
+
+ if self.secondline and self.thirdline:
+ self.dlg.update(self.percent, self.firstline, self.secondline, self.thirdline)
+ elif self.secondline:
+ self.dlg.update(self.percent, self.firstline, self.secondline)
+ else:
+ self.dlg.update(self.percent, self.firstline)
+
+
+
+ def create(self, head, firstline = None, secondline=None, thirdline=None):
+ if firstline:
+ self.firstline = firstline
+ if secondline:
+ self.secondline = secondline
+ if thirdline:
+ self.thirdline = thirdline
+
+ if self.secondline and self.thirdline:
+ self.dlg.create(head, self.firstline, self.secondline, self.thirdline)
+ elif self.secondline:
+ self.dlg.create(head, self.firstline, self.secondline)
+ else:
+ self.dlg.create(head, self.firstline)
+
+
+ def close(self):
+ self.dlg.close()
+ self.__reset__()
\ No newline at end of file
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/dialogQuestion.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/dialogQuestion.py
new file mode 100644
index 0000000000000000000000000000000000000000..c087bf5c33311c5ddf8d3e9c4cf0be89d42dfc0a
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/dialogs/dialogQuestion.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+
+import xbmcgui
+
+class DialogQuestion:
+
+ def __init__(self):
+ self.dlg = xbmcgui.Dialog()
+ self.head = 'SportsDevil Question'
+
+ def ask(self, question):
+ return self.dlg.yesno(self.head, question)
+
+ def close(self):
+ self.dlg.close()
\ No newline at end of file
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/downloader.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/downloader.py
new file mode 100644
index 0000000000000000000000000000000000000000..430e9ffbbe7157e275b664ddd95081f1d2e9ec0f
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/downloader.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+
+import common
+
+import urllib
+import os.path
+import xbmc, xbmcgui
+
+class Downloader(object):
+ def __init__(self):
+ self.pDialog = None
+
+ def downloadWithJDownloader(self, url, title):
+ common.runPlugin('plugin://plugin.program.jdownloader/?action=addlink&url=' + url)
+ common.showNotification('Sent to JDownloader:')
+
+ def downloadMovie(self, url, path, title, extension):
+ if not os.path.exists(path):
+ common.log('Path does not exist')
+ return None
+ if title == '':
+ common.log('No title given')
+ return None
+
+ file_path = xbmc.makeLegalFilename(os.path.join(path, title + extension))
+ file_path = urllib.unquote_plus(file_path)
+ # Overwrite existing file?
+ if os.path.isfile(file_path):
+ self.pDialog = xbmcgui.Dialog()
+
+ if not common.ask('File already exists. Overwrite?\n' + os.path.basename(file_path)):
+ title = common.showOSK(urllib.unquote_plus(title), common.translate(30102))
+ if not title:
+ return None
+ file_path = xbmc.makeLegalFilename(os.path.join(path, title + extension))
+ file_path = urllib.unquote_plus(file_path)
+
+ success = self.__download(url, file_path)
+ if success:
+ return file_path
+ else:
+ return None
+
+
+ def __download(self, url, file_path):
+ try:
+ # Setup progress dialog and download
+ self.pDialog = xbmcgui.DialogProgress()
+ self.pDialog.create('SportsDevil', common.translate(30050), common.translate(30051))
+ urllib.urlretrieve(url, file_path, self.video_report_hook)
+ self.pDialog.close()
+ return True
+ except IOError:
+ self.pDialog.close()
+ common.showError(common.translate(30053))
+ except KeyboardInterrupt:
+ self.pDialog.close()
+ return False
+
+
+ def video_report_hook(self, count, blocksize, totalsize):
+ percent = int(float(count * blocksize * 100) / totalsize)
+ self.pDialog.update(percent, common.translate(30050), common.translate(30051))
+ if self.pDialog.iscanceled():
+ raise KeyboardInterrupt
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/entities/CItemInfo.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/entities/CItemInfo.py
new file mode 100644
index 0000000000000000000000000000000000000000..c9f4ab53cd42025899c2dddcd0f7cdc05ccaac7f
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/entities/CItemInfo.py
@@ -0,0 +1,10 @@
+
+class CItemInfo(object):
+
+ def __init__(self):
+ self.name = ''
+ self.src = 'url'
+ self.rule = ''
+ self.default = ''
+ self.build = ''
+ self.convert = []
\ No newline at end of file
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/entities/CList.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/entities/CList.py
new file mode 100644
index 0000000000000000000000000000000000000000..8b67b39b56f2a540d0ccb12e3f7328d883918af8
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/entities/CList.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+
+class CList(object):
+
+ def __init__(self):
+ self.start = ''
+ self.section = ''
+ self.sort = ''
+ self.cfg = ''
+ self.skill = ''
+ self.catcher = ''
+ self.items = []
+ self.rules = []
+
+ def getVideos(self):
+ return filter(lambda x: x['type'] == 'video', self.items)
\ No newline at end of file
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/entities/CListItem.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/entities/CListItem.py
new file mode 100644
index 0000000000000000000000000000000000000000..75e4cb5f9c78e9f355c49256fc25308f5f0c6711
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/entities/CListItem.py
@@ -0,0 +1,42 @@
+
+import string
+
+
+class CListItem(object):
+
+ def __init__(self):
+ self.infos = {}
+
+ def __getitem__(self, key):
+ return self.getInfo(key)
+
+ def __setitem__(self, key, value):
+ self.setInfo(key, value)
+
+ def getInfo(self, key):
+ if self.infos.has_key(key):
+ return self.infos[key]
+ return None
+
+ def setInfo(self, key, value):
+ self.infos[key] = value
+
+
+ def merge(self, item):
+ for key in item.infos.keys():
+ if not self[key]:
+ self[key] = item[key]
+
+ def __str__(self):
+ txt = ''
+ for key in self.infos.keys():
+ txt += str(string.ljust(key, 15)) + ':\t' + str(self[key]) + '\n'
+ return txt
+
+
+
+
+# STATIC FUNCTIONS
+
+def create():
+ return CListItem()
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/entities/CRuleItem.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/entities/CRuleItem.py
new file mode 100644
index 0000000000000000000000000000000000000000..607794f2e5c022a04b90afd1ba035a7991c16f11
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/entities/CRuleItem.py
@@ -0,0 +1,10 @@
+
+class CRuleItem(object):
+
+ def __init__(self):
+ self.infos = ''
+ self.order = ''
+ self.skill = ''
+ self.curr = ''
+ self.info_list = []
+ self.url_build = ''
\ No newline at end of file
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/entities/__init__.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/entities/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/favouritesManager.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/favouritesManager.py
new file mode 100644
index 0000000000000000000000000000000000000000..1e7c8fa175335890b8d3646c165855f4126af9c0
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/favouritesManager.py
@@ -0,0 +1,434 @@
+# -*- coding: utf-8 -*-
+
+import os.path
+import re
+import xbmcgui
+import urllib
+import common
+from string import lower
+
+from entities.CListItem import CListItem
+from xml.dom.minidom import parse as parseXml
+
+
+from utils import fileUtils as fu
+
+from utils.xbmcUtils import getKeyboard, getImage
+from utils import regexUtils
+
+
+
+class FavouritesManager:
+
+ def __init__(self, favouritesFolder):
+ self.cfgBuilder = CfgBuilder()
+
+ self._favouritesFolder = favouritesFolder
+ if not os.path.exists(self._favouritesFolder):
+ os.makedirs(self._favouritesFolder, 0777)
+
+ self._favouritesFile = os.path.join(self._favouritesFolder, 'favourites.cfg')
+ if not os.path.exists(self._favouritesFile):
+ self._createVirtualFolder('Favourites', self._favouritesFile)
+
+ self._favouritesFoldersFolder = os.path.join(self._favouritesFolder, 'favfolders')
+ if not os.path.exists(self._favouritesFoldersFolder):
+ os.mkdir(self._favouritesFoldersFolder)
+
+
+
+
+# ----------------------------------------------------------
+# Helper functions
+# ----------------------------------------------------------
+
+ def _getFullPath(self, path):
+ if path.startswith('favfolders'):
+ path = os.path.normpath(os.path.join(self._favouritesFolder, path))
+ return path
+
+ def _getShortPath(self, path):
+ if not path.startswith('favfolders'):
+ path = os.path.normpath(path).replace(self._favouritesFolder, '').strip(os.path.sep)
+ return path
+
+ def _parseXbmcFavourites(self):
+ favItems = None
+ xbmcFavsFile = common.Paths.xbmcFavouritesFile
+ if os.path.exists(xbmcFavsFile):
+ doc = parseXml(xbmcFavsFile)
+ xbmcFavs = doc.documentElement.getElementsByTagName('favourite')
+ favItems = []
+ for node in xbmcFavs:
+ favItem = XbmcFavouriteItem.fromXmlNode(node)
+ favItems.append(favItem)
+ return favItems
+
+ def _createItem(self, title, m_type, icon, fanart, cfg, url, catcher):
+ data = self.cfgBuilder.buildItem(title, m_type, url, icon, fanart, cfg, catcher)
+ return data
+
+ def _createFavourite(self, item):
+ title = item.getInfo('title')
+ m_type = item.getInfo('type')
+ icon = item.getInfo('icon')
+ fanart = item.getInfo('fanart')
+ cfg = item.getInfo('cfg')
+ url = item.getInfo('url')
+ catcher = item.getInfo('catcher')
+ return self._createItem(title, m_type, icon, fanart, cfg, url, catcher)
+
+# ----------------------------------------------------------
+# Virtual folders
+# ----------------------------------------------------------
+
+ def _virtualFolderSelection(self, name=None, path=None):
+ if not name:
+ name = 'Favourites'
+ if not path:
+ path = self._favouritesFile
+
+ fullpath = self._getFullPath(path)
+ items = self._parseVirtualFolder(fullpath)
+ virtualFolders = filter(lambda x: self._isVirtualFolder(x), items)
+ if len(virtualFolders) > 0:
+ menuItems = ['root(' + name + ')']
+ menuItems += map(lambda x: x['title'], virtualFolders)
+ select = xbmcgui.Dialog().select('Select destination', menuItems)
+ if select == -1:
+ return None
+ elif select == 0:
+ return fullpath
+ else:
+ selItem = virtualFolders[select-1]
+ return self._virtualFolderSelection(selItem['title'], selItem['url'])
+ else:
+ return fullpath
+
+ def _isVirtualFolder(self, item):
+ url = item.getInfo('url')
+ return url and (url.startswith("favfolders/") or url.startswith("favfolders\\"))
+
+ def _getVirtualFoldersList(self):
+ virtualFolders = os.listdir(self._favouritesFoldersFolder)
+ return virtualFolders
+
+ def _createVirtualFolder(self, name, path):
+ fullPath = self._getFullPath(path)
+ data = self.cfgBuilder.buildHeader(name)
+ fu.setFileContent(fullPath, data)
+
+ def _removeVirtualFolder(self, path, removeSubfolders=False):
+ fullPath = self._getFullPath(path)
+ if removeSubfolders:
+ items = self._parseVirtualFolder(fullPath)
+ subFolders = filter(lambda x: self._isVirtualFolder(x), items)
+ for s in subFolders:
+ self._removeVirtualFolder(s['url'], True)
+ if os.path.exists(fullPath) and os.path.isfile(fullPath):
+ os.remove(fullPath)
+
+ def _parseVirtualFolder(self, path):
+ fullpath = self._getFullPath(path)
+ data = fu.getFileContent(fullpath)
+ data = data.replace('\r\n', '\n').split('\n')
+ items = []
+ for m in data:
+ if m and m[0] != '#':
+ index = m.find('=')
+ if index != -1:
+ key = lower(m[:index]).strip()
+ value = m[index+1:]
+
+ index = value.find('|')
+ if value[:index] == 'sports.devil.locale':
+ value = common.translate(int(value[index+1:]))
+ elif value[:index] == 'sports.devil.image':
+ value = os.path.join(common.Paths.imgDir, value[index+1:])
+
+ if key == 'title':
+ tmp = CListItem()
+ tmp['title'] = value
+ elif key == 'url':
+ tmp['url'] = value
+ items.append(tmp)
+ tmp = None
+ elif tmp != None:
+ tmp[key] = value
+ return items
+
+
+
+
+# ----------------------------------------------------------
+# Add item
+# ----------------------------------------------------------
+ def add(self, rootFolder=None):
+ menuItems = ["Add folder", "Add SportsDevil item", "Add xbmc favourite"]
+ select = xbmcgui.Dialog().select('Choose', menuItems)
+ if select == 0:
+ name = getKeyboard(default = '', heading = 'Set name')
+ if name and len(name) > 0:
+ return self._addFolder(name, rootFolder)
+ elif select == 1:
+ common.showInfo('Please browse through SportsDevil and use \ncontext menu entry "Add to SportsDevil favourites"')
+ elif select == 2:
+ return self._addXbmcFavourite(rootFolder)
+ return False
+
+
+ def _addXbmcFavourite(self, root):
+ xbmcFavs = self._parseXbmcFavourites()
+ if xbmcFavs is None:
+ common.showInfo('Favourites file not found')
+ elif len(xbmcFavs) == 0:
+ common.showInfo('No favourites found')
+ else:
+ select = xbmcgui.Dialog().select('Choose' , map(lambda x: x.title, xbmcFavs))
+ if select == -1:
+ return False
+ else:
+ item = xbmcFavs[select].convertToCListItem()
+ self.addItem(item, root)
+ return True
+ return False
+
+ def _addFolder(self, name, rootFolder=None):
+ # create cfg
+ filename = urllib.quote_plus(fu.cleanFilename(name))
+ virtualFolderFile = filename + '.cfg'
+ physicalFolder = os.path.normpath(self._favouritesFoldersFolder)
+ virtualFolderPath = os.path.join(physicalFolder, virtualFolderFile)
+ if os.path.exists(virtualFolderPath):
+ prefix = filename + '-'
+ suffix = '.cfg'
+ virtualFolderFile = fu.randomFilename(directory=physicalFolder, prefix=prefix, suffix=suffix)
+ virtualFolderPath = os.path.join(physicalFolder, virtualFolderFile)
+ self._createVirtualFolder(name, virtualFolderPath)
+
+ # create link
+ linkToFolder = self._createItem(name, 'rss', '', '', None, 'favfolders/' + virtualFolderFile)
+ if not rootFolder or os.path.normpath(rootFolder) == self._favouritesFile:
+ rootFolder = self._favouritesFile
+ fu.appendFileContent(rootFolder, linkToFolder)
+ return True
+
+
+ def addItem(self, item, root=None):
+ target = root
+ if not target:
+ # if virtual folders exist
+ virtualFolder = self._virtualFolderSelection()
+ if virtualFolder:
+ target = virtualFolder
+
+ if target and os.path.exists(target):
+ fav = self._createFavourite(item)
+ fu.appendFileContent(target, fav)
+
+
+
+# ----------------------------------------------------------
+# Change or remove item
+# ----------------------------------------------------------
+
+ def editItem(self, item):
+ menuItems = ["Change label", "Change icon", "Change fanart"]
+ virtualFolders = self._getVirtualFoldersList()
+ if len(virtualFolders) > 0 and not item.getInfo('url').startswith('favfolders/'):
+ menuItems.append("Move to folder")
+ select = xbmcgui.Dialog().select('Choose' , menuItems)
+ if select == -1:
+ return False
+ cfgFile = self._favouritesFile
+ definedIn = item.getInfo('definedIn')
+ if definedIn and definedIn.startswith('favfolders/'):
+ cfgFile = os.path.join(self._favouritesFoldersFolder, definedIn.split('/')[1])
+ if select == 0:
+ newLabel = getKeyboard(default = item.getInfo('title'), heading = 'Change label')
+ if not newLabel or newLabel == "":
+ return False
+ self.changeLabel(item, newLabel)
+ elif select == 1:
+ newIcon = getImage('Change icon')
+ if not newIcon:
+ return False
+ self.changeIcon(item, newIcon)
+ elif select == 2:
+ newFanart = getImage('Change fanart')
+ if not newFanart:
+ return False
+ self.changeFanart(item, newFanart)
+ elif select == 3:
+ newCfgFile = self._virtualFolderSelection()
+ if not newCfgFile or cfgFile == newCfgFile:
+ return False
+ self.moveToFolder(cfgFile, item, newCfgFile)
+ return True
+
+
+ def _findItem(self, item):
+ cfgFile = self._favouritesFile
+ definedIn = item.getInfo('definedIn')
+ if definedIn and definedIn.startswith('favfolders/'):
+ cfgFile = os.path.join(self._favouritesFolder, definedIn)
+ if os.path.exists(cfgFile):
+ data = fu.getFileContent(cfgFile)
+ regex = self.cfgBuilder.buildItem(re.escape(item.getInfo('title')), "[^#]*", re.escape(item.getInfo('url')))
+ matches = regexUtils.findall(data, regex)
+ if matches:
+ return (cfgFile, data, matches[0])
+ return None
+
+ def changeLabel(self, item, newLabel):
+ found = self._findItem(item)
+ if found:
+ item['title'] = newLabel
+ [cfgFile, data, fav] = found
+ # if it's a virtual folder, rename file, rename header, update link
+ if self._isVirtualFolder(item):
+ url = item.getInfo('url')
+ oldFile = self._getFullPath(url)
+ newFilename = urllib.quote_plus(fu.cleanFilename(newLabel))
+ virtualFolderFile = newFilename + '.cfg'
+ physicalFolder = os.path.normpath(self._favouritesFoldersFolder)
+ virtualFolderPath = os.path.join(physicalFolder, virtualFolderFile)
+ # check if new target is valid
+ if os.path.exists(virtualFolderPath):
+ prefix = newFilename + '-'
+ suffix = '.cfg'
+ virtualFolderFile = fu.randomFilename(directory=physicalFolder, prefix=prefix, suffix=suffix)
+ virtualFolderPath = os.path.join(physicalFolder, virtualFolderFile)
+ # update header
+ content = fu.getFileContent(oldFile)
+ oldHeader = self.cfgBuilder.buildHeader(item['title'])
+ newHeader = self.cfgBuilder.buildHeader(newLabel)
+ content = content.replace(oldHeader, newHeader)
+ # rename file
+ self._removeVirtualFolder(oldFile, False)
+ fu.setFileContent(virtualFolderPath, content)
+ # update link
+ item['url'] = self._getShortPath(virtualFolderPath)
+ newfav = self._createFavourite(item)
+ new = data.replace(fav, newfav.encode('utf-8'))
+ fu.setFileContent(cfgFile, new)
+
+
+ def changeIcon(self, item, newIcon):
+ found = self._findItem(item)
+ if found:
+ [cfgFile, data, fav] = found
+ newfav = self._createFavourite(item, icon=newIcon)
+ new = data.replace(fav, newfav.encode('utf-8'))
+ fu.setFileContent(cfgFile, new)
+
+ def changeFanart(self, item, newFanart):
+ found = self._findItem(item)
+ if found:
+ [cfgFile, data, fav] = found
+ newfav = self._createFavourite(item, fanart=newFanart)
+ new = data.replace(fav, newfav.encode('utf-8'))
+ fu.setFileContent(cfgFile, new)
+
+ def moveToFolder(self, cfgFile, item, newCfgFile):
+ found = self._findItem(item)
+ if found:
+ [cfgFile, data, fav] = found
+ if os.path.exists(newCfgFile):
+ new = data.replace(fav,'')
+ fu.setFileContent(cfgFile, new)
+ fu.appendFileContent(newCfgFile, fav)
+
+ def removeItem(self, item):
+ found = self._findItem(item)
+ if found:
+ try:
+ # delete virtual folder
+ if self._isVirtualFolder(item):
+ self._removeVirtualFolder(item['url'], True)
+ # delete link
+ [cfgFile, data, fav] = found
+ new = data.replace(fav,'')
+ fu.setFileContent(cfgFile, new)
+ return True
+ except:
+ return False
+ return False
+
+
+
+
+
+
+
+
+class XbmcFavouriteItem:
+ def __init__(self, title, icon, url):
+ self.title = title
+ self.icon = icon
+ self.url = url
+
+
+ @classmethod
+ def fromXmlNode(cls, node):
+ try:
+ title = node.attributes['name'].nodeValue
+ except:
+ title = ''
+ try:
+ icon = node.attributes['thumb'].nodeValue
+ except:
+ icon = ''
+ try:
+ url = node.childNodes[0].nodeValue
+ except:
+ url = ''
+ return cls(title, icon, url)
+
+
+ def convertToCListItem(self):
+ item = CListItem()
+ item.setInfo('title', self.title)
+ item.setInfo('type', 'command')
+ item.setInfo('icon', self.icon)
+ item.setInfo('url', self.url)
+ return item
+
+
+class CfgBuilder:
+
+ def __init__(self):
+ self.minWidth = 52
+ pass
+
+ def buildSeperator(self, title):
+ titleLength = len(title)
+ width = max(titleLength, self.minWidth) + 4 # '# ' and ' #' = 4 chars
+ sepLine = '#' * width
+ return sepLine
+
+ def buildHeader(self, title):
+ titleLength = len(title)
+ sepLine = self.buildSeperator(title)
+ space = len(sepLine) - titleLength - 4
+ titleLine = '# ' + title.upper() + ' ' * space + ' #'
+ data = [sepLine, titleLine, sepLine]
+ return '\n'.join(data)
+
+ def buildItem(self, title, m_type, url, icon=None, fanart=None, cfg=None, catcher=None):
+ sepLine = self.buildSeperator(title)
+ data = [
+ '\n' + sepLine,
+ 'title=' + title,
+ 'type=' + m_type
+ ]
+ if icon:
+ data.append('icon=' + icon)
+ if fanart:
+ data.append('fanart=' + fanart)
+ if cfg:
+ data.append('cfg=' + cfg)
+ if catcher:
+ data.append('catcher=' + catcher)
+ data.append('url=' + url)
+ return '\n'.join(data)
\ No newline at end of file
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/main.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..b30f73693f64422708ed11018648e27c793ac3a6
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/main.py
@@ -0,0 +1,754 @@
+# -*- coding: utf-8 -*-
+
+# Merge xSopcast and SportsDevil
+#http://forum.xbmc.org/showthread.php?tid=100031&pid=1101338#pid1101338
+
+#Freedocast fix
+#http://forum.xbmc.org/showthread.php?tid=100597&pid=1094333#pid1094333
+
+import os
+import xbmcplugin
+import xbmc, xbmcgui
+import urllib
+
+import common
+
+from utils import fileUtils as fu
+
+from utils.regexUtils import parseText
+from utils.xbmcUtils import getKeyboard, setSortMethodsForCurrentXBMCList
+from dialogs.dialogProgress import DialogProgress
+
+from parser import Parser, ParsingResult
+from downloader import Downloader
+from favouritesManager import FavouritesManager
+
+import entities.CListItem as ListItem
+
+from utils import xbmcUtils
+
+from dialogs.dialogQuestion import DialogQuestion
+
+from customModulesManager import CustomModulesManager
+
+from addonInstaller import install
+
+from utils.beta.t0mm0.common.addon import Addon
+
+#from cacheManager import CacheManager
+
+
+class Mode:
+ UPDATE = 0
+ VIEW = 1
+ PLAY = 2
+ QUEUE = 3
+ DOWNLOAD = 4
+ EXECUTE = 5
+ ADDTOFAVOURITES = 6
+ REMOVEFROMFAVOURITES = 7
+ EDITITEM = 8
+ ADDITEM = 9
+ DOWNLOADCUSTOMMODULE = 10
+ REMOVEFROMCUSTOMMODULES = 11
+ INSTALLADDON = 12
+ CHROME = 13
+ WEBDRIVER = 14
+
+
+class Main:
+
+ MAIN_MENU_FILE = 'mainMenu.cfg'
+
+
+ def __init__(self):
+
+ if not os.path.exists(common.Paths.pluginDataDir):
+ os.makedirs(common.Paths.pluginDataDir, 0777)
+
+ self.favouritesManager = FavouritesManager(common.Paths.favouritesFolder)
+ self.customModulesManager = CustomModulesManager(common.Paths.customModulesDir, common.Paths.customModulesRepo)
+
+ if not os.path.exists(common.Paths.customModulesDir):
+ os.makedirs(common.Paths.customModulesDir, 0777)
+
+ self.parser = Parser()
+ self.currentlist = None
+
+ self.addon = None
+
+ common.log('SportsDevil initialized')
+
+
+ # removed in 17 http://forum.kodi.tv/showthread.php?tid=250936&pid=2198464#pid2198464
+ def getPlayerType(self):
+ sPlayerType = common.getSetting('playerType')
+
+ if (sPlayerType == '0'):
+ return xbmc.PLAYER_CORE_AUTO
+ elif (sPlayerType == '1'):
+ return xbmc.PLAYER_CORE_MPLAYER
+ elif (sPlayerType == '2'):
+ return xbmc.PLAYER_CORE_DVDPLAYER
+ # PLAYER_CORE_AMLPLAYER
+ elif (sPlayerType == '3'):
+ return 5
+
+ return xbmc.PLAYER_CORE_AUTO
+
+
+ def playVideo(self, videoItem, isAutoplay = False):
+ if not videoItem:
+ return
+
+ listitem = self.createXBMCListItem(videoItem)
+
+ title = videoItem['videoTitle']
+ if title:
+ listitem.setInfo('video', {'title': title})
+
+ if not isAutoplay:
+ xbmcplugin.setResolvedUrl(self.handle, True, listitem)
+ else:
+ url = urllib.unquote_plus(videoItem['url'])
+ xbmc.Player().play(url, listitem)
+
+ def launchChrome(self, url, title):
+ action = 'RunPlugin(%s)' % ('plugin://plugin.program.chrome.launcher/?kiosk=yes&mode=showSite&stopPlayback=yes&url=' + url)
+ common.log('chrome test:' + str(action))
+ xbmc.executebuiltin(action)
+
+ def playWebDriver(self, url, title):
+ try:
+ import liveremote
+ video = liveremote.resolve(url)
+ liz = xbmcgui.ListItem(title)
+ liz.setPath(video)
+ liz.setProperty('IsPlayable','true')
+ xbmc.Player().play(video, liz)
+ except:
+ import sys,traceback
+ traceback.print_exc(file = sys.stdout)
+ common.showInfo('This is not the option you are looking for.')
+
+
+ def downloadVideo(self, url, title):
+ common.log('Trying to download video ' + str(url))
+
+ # check url
+ if url.startswith('plugin'):
+ common.log('Video is not downloadable')
+ return None
+
+ path = common.getSetting('download_path')
+ if not path:
+ path = common.browseFolders(common.translate(30017))
+ common.setSetting('download_path', path)
+
+ title = getKeyboard(default = fu.cleanFilename(title),heading='SportsDevil')
+ if title == None or title == '':
+ return None
+
+ downloader = Downloader()
+ downloaded_file = downloader.downloadMovie(url, path, fu.cleanFilename(title), '.flv')
+
+ if downloaded_file == None:
+ common.log ('Download cancelled')
+ else:
+ common.log('Video ' + url + " downloaded to '" + downloaded_file + "'")
+
+ return downloaded_file
+
+
+ def getVideos(self, lItem, dia = None, percent = 0, percentSpan = 100):
+ allitems = []
+
+ if dia and dia.isCanceled():
+ return allitems
+
+ currentName = lItem['title']
+
+ if lItem['type'].find('video') != -1:
+ if dia:
+ dia.update(percent + percentSpan, thirdline=currentName)
+ allitems.append(lItem)
+ else:
+ tmpList = self.parser.parse(lItem).list
+ if tmpList and len(tmpList.items) > 0:
+
+ if dia:
+ dia.update(percent, secondline=currentName, thirdline=' ')
+
+ inc = percentSpan/len(tmpList.items)
+ for item in tmpList.items:
+
+ if dia and dia.isCanceled():
+ break
+
+ children = self.getVideos(item, dia, percent, inc)
+ if children:
+ allitems.extend(children)
+ percent += inc
+
+ return allitems
+
+
+ def getSearchPhrase(self):
+ searchCache = os.path.join(common.Paths.cacheDir, 'search')
+ default_phrase = fu.getFileContent(searchCache)
+ if not default_phrase:
+ default_phrase = ''
+
+ search_phrase = common.showOSK(default_phrase, common.translate(30102))
+ if search_phrase == '':
+ return None
+ xbmc.sleep(10)
+ fu.setFileContent(searchCache, search_phrase)
+
+ return search_phrase
+
+
+ def parseView(self, lItem):
+
+ def endOfDirectory(succeeded=True):
+ xbmcplugin.endOfDirectory(handle=self.handle, succeeded=succeeded, cacheToDisc=True)
+
+ if not lItem:
+ endOfDirectory(False)
+ return None
+
+ if lItem['type'] == 'search':
+ search_phrase = self.getSearchPhrase()
+ if not search_phrase:
+ common.log("search canceled")
+ endOfDirectory(False)
+ return None
+ else:
+ lItem['type'] = 'rss'
+ lItem['url'] = lItem['url'] % (urllib.quote_plus(search_phrase))
+
+ url = lItem['url']
+
+ if url == common.Paths.customModulesFile:
+ self.customModulesManager.getCustomModules()
+
+ tmpList = None
+ result = self.parser.parse(lItem)
+ if result.code == ParsingResult.Code.SUCCESS:
+ tmpList = result.list
+ elif result.code == ParsingResult.Code.CFGFILE_NOT_FOUND:
+ common.showError("Cfg file not found")
+ endOfDirectory(False)
+ return None
+ elif result.code == ParsingResult.Code.CFGSYNTAX_INVALID:
+ common.showError("Cfg syntax invalid")
+ endOfDirectory(False)
+ return None
+ elif result.code == ParsingResult.Code.WEBREQUEST_FAILED:
+ common.showError("Web request failed")
+ if len(result.list.items) > 0:
+ tmpList = result.list
+ else:
+ endOfDirectory(False)
+ return None
+
+ # if it's the main menu, add folder 'Favourites' and 'Custom Modules
+ if url == self.MAIN_MENU_FILE:
+ tmp = ListItem.create()
+ tmp['title'] = 'Favourites'
+ tmp['type'] = 'rss'
+ tmp['icon'] = os.path.join(common.Paths.imgDir, 'bookmark.png')
+ tmp['url'] = str(common.Paths.favouritesFile)
+ tmpList.items.insert(0, tmp)
+
+ #tmp = ListItem.create()
+ #tmp['title'] = '[COLOR red]Custom Modules[/COLOR]'
+ #tmp['type'] = 'rss'
+ #tmp['url'] = os.path.join(common.Paths.customModulesDir, 'custom.cfg')
+ #tmpList.items.insert(0, tmp)
+
+ # if it's the favourites menu, add item 'Add item'
+ elif url == common.Paths.favouritesFile or url.startswith('favfolders'):
+
+ if url.startswith('favfolders'):
+ url = os.path.normpath(os.path.join(common.Paths.favouritesFolder, url))
+
+ tmp = ListItem.create()
+ tmp['title'] = 'Add item...'
+ tmp['type'] = 'command'
+ tmp['icon'] = os.path.join(common.Paths.imgDir, 'bookmark_add.png')
+ action = 'RunPlugin(%s)' % (self.base + '?mode=' + str(Mode.ADDITEM) + '&url=' + url)
+ tmp['url'] = action
+ tmpList.items.append(tmp)
+
+ # if it's the custom modules menu, add item 'more...'
+ elif url == common.Paths.customModulesFile:
+ tmp = ListItem.create()
+ tmp['title'] = 'more...'
+ tmp['type'] = 'command'
+ #tmp['icon'] = os.path.join(common.Paths.imgDir, 'bookmark_add.png')
+ action = 'RunPlugin(%s)' % (self.base + '?mode=' + str(Mode.DOWNLOADCUSTOMMODULE) + '&url=')
+ tmp['url'] = action
+ tmpList.items.append(tmp)
+
+
+ # Create menu or play, if it's a single video and autoplay is enabled
+ proceed = False
+
+ count = len(tmpList.items)
+ if count == 0:
+ if url.startswith('favfolders'):
+ proceed = True
+ else:
+ common.showInfo('No stream available')
+ elif count > 0 and not (common.getSetting('autoplay') == 'true' and count == 1 and len(tmpList.getVideos()) == 1):
+ # sort methods
+ sortKeys = tmpList.sort.split('|')
+ setSortMethodsForCurrentXBMCList(self.handle, sortKeys)
+
+ # Add items to XBMC list
+ for m in tmpList.items:
+ self.addListItem(m, len(tmpList.items))
+
+ proceed = True
+
+ endOfDirectory(proceed)
+ common.log('End of directory')
+ return tmpList
+
+
+ def downloadCustomModule(self):
+ success = self.customModulesManager.downloadCustomModules()
+ if success == True:
+ # refresh container if SportsDevil is active
+ currContainer = xbmcUtils.getContainerFolderPath()
+ common.showNotification('SportsDevil', 'Download successful', 1000)
+ if currContainer.startswith(self.base):
+ xbmc.executebuiltin('Container.Refresh()')
+ return True
+ elif success == False:
+ common.showNotification('SportsDevil', 'Download failed', 1000)
+ return False
+
+
+ def removeCustomModule(self, item):
+ name = urllib.unquote(item["title"])
+ success = self.customModulesManager.removeCustomModule(name)
+ if success:
+ xbmc.executebuiltin('Container.Refresh()')
+
+
+ def createXBMCListItem(self, item):
+ title = item['title']
+
+ m_type = item['type']
+
+ icon = item['icon']
+
+ if icon and not icon.startswith('http'):
+ try:
+ if not fu.fileExists(icon):
+ tryFile = os.path.join(common.Paths.modulesDir, icon)
+ if not fu.fileExists(tryFile):
+ tryFile = os.path.join(common.Paths.customModulesDir, icon)
+ if fu.fileExists(tryFile):
+ icon = tryFile
+ except:
+ pass
+
+ if not icon:
+ if m_type == 'video':
+ icon = common.Paths.defaultVideoIcon
+ else:
+ icon = common.Paths.defaultCategoryIcon
+
+ fanart = item['fanart']
+ if not fanart:
+ fanart = common.Paths.pluginFanart
+
+ liz = xbmcgui.ListItem(title)
+ try:
+ liz.setArt({'thumb': icon, 'fanart': fanart})
+ except:
+ liz.setProperty('fanart_image', fanart)
+ liz.setThumbnailImage(icon)
+ common.log('main.py:374: setThumbnailImage is deprecated')
+
+ """
+ General Values that apply to all types:
+ count : integer (12) - can be used to store an id for later, or for sorting purposes
+ size : long (1024) - size in bytes
+ date : string (%d.%m.%Y / 01.01.2009) - file date
+
+ Video Values:
+ genre : string (Comedy)
+ year : integer (2009)
+ episode : integer (4)
+ season : integer (1)
+ top250 : integer (192)
+ tracknumber : integer (3)
+ rating : float (6.4) - range is 0..10
+ watched : depreciated - use playcount instead
+ playcount : integer (2) - number of times this item has been played
+ overlay : integer (2) - range is 0..8. See GUIListItem.h for values
+ cast : list (Michal C. Hall)
+ castandrole : list (Michael C. Hall|Dexter)
+ director : string (Dagur Kari)
+ mpaa : string (PG-13)
+ plot : string (Long Description)
+ plotoutline : string (Short Description)
+ title : string (Big Fan)
+ originaltitle : string (Big Fan)
+ duration : string (3:18)
+ studio : string (Warner Bros.)
+ tagline : string (An awesome movie) - short description of movie
+ writer : string (Robert D. Siegel)
+ tvshowtitle : string (Heroes)
+ premiered : string (2005-03-04)
+ status : string (Continuing) - status of a TVshow
+ code : string (tt0110293) - IMDb code
+ aired : string (2008-12-07)
+ credits : string (Andy Kaufman) - writing credits
+ lastplayed : string (%Y-%m-%d %h:%m:%s = 2009-04-05 23:16:04)
+ album : string (The Joshua Tree)
+ votes : string (12345 votes)
+ trailer : string (/home/user/trailer.avi)
+ """
+
+ infoLabels = {}
+ for video_info_name in item.infos.keys():
+ infoLabels[video_info_name] = item[video_info_name]
+ infoLabels['title'] = title
+
+ liz.setInfo('video', infoLabels)
+
+ url = urllib.unquote_plus(item['url'])
+ liz.setPath(url)
+
+ if m_type == 'video':
+ liz.setProperty('IsPlayable','true')
+
+ if title.startswith('p2pcast'):
+ try:
+ liz.setMimeType('application/vnd.apple.mpegurl')
+ #liz.setContentLookup(False)
+ except:
+ common.showError('Update Kodi to 16+ to view this stream')
+ return None
+
+ return liz
+
+
+ def addListItem(self, lItem, totalItems):
+ def createContextMenuItem(label, mode, codedItem):
+ action = 'XBMC.RunPlugin(%s)' % (self.addon.build_plugin_url({'mode': str(mode), 'item': codedItem}))
+ return (label, action)
+
+ def encoded_dict(in_dict):
+ out_dict = {}
+ for k, v in in_dict.iteritems():
+ if isinstance(v, unicode):
+ v = v.encode('utf8')
+ elif isinstance(v, str):
+ # Must be encoded in UTF-8
+ v.decode('utf8')
+ out_dict[k] = v
+ return urllib.urlencode(out_dict)
+
+ contextMenuItems = []
+ definedIn = lItem['definedIn']
+
+ codedItem = urllib.quote(encoded_dict(lItem.infos))
+
+ if definedIn:
+ # Queue
+ contextMenuItem = createContextMenuItem('Queue', Mode.QUEUE, codedItem)
+ contextMenuItems.append(contextMenuItem)
+
+ # Favourite
+ if definedIn.endswith('favourites.cfg') or definedIn.startswith("favfolders/"):
+ # Remove from favourites
+ contextMenuItem = createContextMenuItem('Remove', Mode.REMOVEFROMFAVOURITES, codedItem)
+ contextMenuItems.append(contextMenuItem)
+
+ # Edit label
+ contextMenuItem = createContextMenuItem('Edit', Mode.EDITITEM, codedItem)
+ contextMenuItems.append(contextMenuItem)
+
+ else:
+ # Custom module
+ if definedIn.endswith('custom.cfg'):
+ # Remove from custom modules
+ contextMenuItem = createContextMenuItem('Remove module', Mode.REMOVEFROMCUSTOMMODULES, codedItem)
+ contextMenuItems.append(contextMenuItem)
+
+ if lItem['title'] != "Favourites":
+ # Add to favourites
+ contextMenuItem = createContextMenuItem('Add to SportsDevil favourites', Mode.ADDTOFAVOURITES, codedItem)
+ contextMenuItems.append(contextMenuItem)
+ contextMenuItem = createContextMenuItem('Open with Chrome launcher', Mode.CHROME, codedItem)
+ contextMenuItems.append(contextMenuItem)
+ #contextMenuItem = createContextMenuItem('Open with WebDriver', Mode.WEBDRIVER, codedItem)
+ #contextMenuItems.append(contextMenuItem)
+
+ liz = self.createXBMCListItem(lItem)
+
+ m_type = lItem['type']
+ if not m_type:
+ m_type = 'rss'
+
+ if m_type == 'video':
+ u = self.base + '?mode=' + str(Mode.PLAY) + '&item=' + codedItem
+ if lItem['IsDownloadable']:
+ contextMenuItem = createContextMenuItem('Download', Mode.DOWNLOAD, codedItem)
+ contextMenuItems.append(contextMenuItem)
+ isFolder = False
+ elif m_type.find('command') > -1:
+ u = self.base + '?mode=' + str(Mode.EXECUTE) + '&item=' + codedItem
+ isFolder = False
+ else:
+ u = self.base + '?mode=' + str(Mode.VIEW) + '&item=' + codedItem
+ isFolder = True
+
+ liz.addContextMenuItems(contextMenuItems)
+ xbmcplugin.addDirectoryItem(handle = self.handle, url = u, listitem = liz, isFolder = isFolder, totalItems = totalItems)
+
+
+ def clearCache(self):
+ cacheDir = common.Paths.cacheDir
+ if not os.path.exists(cacheDir):
+ os.mkdir(cacheDir, 0777)
+ common.log('Cache directory created' + str(cacheDir))
+ else:
+ fu.clearDirectory(cacheDir)
+ common.log('Cache directory purged')
+
+
+ def update(self):
+
+ def checkForUpdates():
+ return None
+
+ def doUpdates(typeName, updates):
+ count = len(updates)
+
+ head = "SportsDevil Updates - %s" % typeName
+
+ msg = common.translate(30277)
+ if count == 1:
+ msg = common.translate(30276)
+
+ question = ("%s %s: " % (count, msg)) + ', '.join(map(lambda u: u.split('/')[-1], updates.keys())) + '\n'
+ question += common.translate(30278)
+
+ updates = updates.values()
+
+ countFailed = 0
+
+ dlg = DialogQuestion()
+ dlg.head = head
+ if dlg.ask(question):
+ dlg = DialogProgress()
+ firstline = common.translate(30279)
+ dlg.create(head, firstline, " ")
+
+ for i in range(0, count):
+ update = updates[i]
+ percent = int((i+1.0)*100/count)
+ dlg.update(percent, firstline, update.name)
+ if not update.do():
+ countFailed += 1
+
+ msg = " "
+ if countFailed > 0:
+ msg = "%s %s" % (countFailed, common.translate(30280))
+
+ dlg.update(100, msg, " ")
+ xbmc.sleep(500)
+ dlg.close()
+
+ allupdates = checkForUpdates()
+ count = len(allupdates)
+ if count == 0:
+ common.showNotification('SportsDevil', common.translate(30273))
+ return
+ else:
+ for key, value in allupdates.items():
+ doUpdates(key, value)
+
+
+ def queueAllVideos(self, item):
+ dia = DialogProgress()
+ dia.create('SportsDevil', 'Get videos...' + item['title'])
+ dia.update(0)
+
+ items = self.getVideos(item, dia)
+ if items:
+ for it in items:
+ item = self.createXBMCListItem(it)
+ queries = {'mode': str(Mode.PLAY), 'url': self.addon.build_plugin_url(it.infos)}
+ uc = self.addon.build_plugin_url(queries)
+ xbmc.PlayList(xbmc.PLAYLIST_VIDEO).add(uc, item)
+ resultLen = len(items)
+ msg = 'Queued ' + str(resultLen) + ' video'
+ if resultLen > 1:
+ msg += 's'
+ dia.update(100, msg)
+ xbmc.sleep(500)
+ dia.update(100, msg,' ',' ')
+ else:
+ dia.update(0, 'No items found',' ')
+
+ xbmc.sleep(700)
+ dia.close()
+
+
+ def executeItem(self, item):
+ url = item['url']
+ if '(' in url:
+ xbmcCommand = parseText(url,'([^\(]*).*')
+ if xbmcCommand.lower() in ['activatewindow', 'runscript', 'runplugin', 'playmedia']:
+ if xbmcCommand.lower() == 'activatewindow':
+ params = parseText(url, '.*\(\s*(.+?)\s*\).*').split(',')
+ for i in range(len(params)-1,-1,-1):
+ p = params[i]
+ if p == 'return':
+ params.remove(p)
+ path = params[len(params)-1]
+ xbmc.executebuiltin('Container.Update(' + path + ')')
+ return
+ xbmc.executebuiltin(url)
+
+
+ def _parseParameters(self):
+ mode = int(self.addon.queries['mode'])
+ queryString = self.addon.queries['item']
+ item = ListItem.create()
+ if mode in [Mode.CHROME, Mode.ADDTOFAVOURITES, Mode.REMOVEFROMFAVOURITES, Mode.EDITITEM, Mode.WEBDRIVER]:
+ item.infos = self.addon.parse_query(urllib.unquote(queryString),{})
+ else:
+ item.infos = self.addon.parse_query(queryString,{})
+ item.infos = dict((k.decode('utf8'), v.decode('utf8')) for k, v in item.infos.items())
+ return [mode, item]
+
+
+ def run(self, argv=None):
+
+ self.addon = Addon('plugin.video.SportsDevil', argv)
+
+ common.log('SportsDevil running')
+
+ base = argv[0]
+ handle = int(argv[1])
+ parameter = argv[2]
+ self.base = base
+ self.handle = handle
+
+ paramstring = urllib.unquote_plus(parameter)
+ common.log(paramstring)
+
+ try:
+
+ # if addon is started
+ listItemPath = xbmcUtils.getListItemPath()
+ if not listItemPath.startswith(self.base):
+ if not('mode=' in paramstring and not 'mode=1&' in paramstring):
+ xbmcplugin.setPluginFanart(self.handle, common.Paths.pluginFanart)
+ self.clearCache()
+
+ #if common.getSetting('autoupdate') == 'true':
+ # self.update()
+
+
+ # Main Menu
+ if len(paramstring) <= 2:
+ mainMenu = ListItem.create()
+ mainMenu['url'] = self.MAIN_MENU_FILE
+ tmpList = self.parseView(mainMenu)
+ if tmpList:
+ self.currentlist = tmpList
+
+ else:
+ [mode, item] = self._parseParameters()
+
+ # switch(mode)
+ if mode == Mode.VIEW:
+ tmpList = self.parseView(item)
+ if tmpList:
+ self.currentlist = tmpList
+ count = len(self.currentlist.items)
+ if count == 1:
+ # Autoplay single video
+ autoplayEnabled = common.getSetting('autoplay') == 'true'
+ if autoplayEnabled:
+ videos = self.currentlist.getVideos()
+ if len(videos) == 1:
+ self.playVideo(videos[0], True)
+
+
+ elif mode == Mode.ADDITEM:
+ tmp = os.path.normpath(paramstring.split('url=')[1])
+ if tmp:
+ suffix = tmp.split(os.path.sep)[-1]
+ tmp = tmp.replace(suffix,'') + urllib.quote_plus(suffix)
+ if self.favouritesManager.add(tmp):
+ xbmc.executebuiltin('Container.Refresh()')
+
+
+ elif mode in [Mode.ADDTOFAVOURITES, Mode.REMOVEFROMFAVOURITES, Mode.EDITITEM]:
+
+ if mode == Mode.ADDTOFAVOURITES:
+ self.favouritesManager.addItem(item)
+ elif mode == Mode.REMOVEFROMFAVOURITES:
+ self.favouritesManager.removeItem(item)
+ xbmc.executebuiltin('Container.Refresh()')
+ elif mode == Mode.EDITITEM:
+ if self.favouritesManager.editItem(item):
+ xbmc.executebuiltin('Container.Refresh()')
+
+
+ elif mode == Mode.EXECUTE:
+ self.executeItem(item)
+
+ elif mode == Mode.PLAY:
+ self.playVideo(item)
+
+ elif mode == Mode.WEBDRIVER:
+ url = urllib.quote(item['url'])
+ title = item['title']
+ self.playWebDriver(url, title)
+
+ elif mode == Mode.QUEUE:
+ self.queueAllVideos(item)
+
+ elif mode == Mode.DOWNLOAD:
+ url = urllib.unquote(item['url'])
+ title = item['title']
+ self.downloadVideo(url, title)
+
+ elif mode == Mode.CHROME:
+ url = urllib.quote(item['url'])
+ title = item['title']
+ self.launchChrome(url, title)
+
+ elif mode == Mode.REMOVEFROMCUSTOMMODULES:
+ self.removeCustomModule(item)
+
+ #elif mode == Mode.UPDATE:
+ # self.update()
+
+ elif mode == Mode.DOWNLOADCUSTOMMODULE:
+ self.downloadCustomModule()
+
+ elif mode == Mode.INSTALLADDON:
+ success = install(item['url'])
+ if success:
+ xbmc.sleep(100)
+ if xbmcUtils.getCurrentWindowXmlFile() == 'DialogAddonSettings.xml':
+ # workaround to update settings dialog
+ common.setSetting('', '')
+
+
+ except Exception, e:
+ common.showError('Error running SportsDevil')
+ common.log('Error running SportsDevil. Reason:' + str(e))
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/parser.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/parser.py
new file mode 100644
index 0000000000000000000000000000000000000000..c6d853d851a00d77b6b12e6439a11ca831486621
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/parser.py
@@ -0,0 +1,731 @@
+# -*- coding: utf-8 -*-
+import common
+import sys, os, traceback
+import time
+import random
+import re
+import urllib
+import string
+from string import lower
+
+from entities.CList import CList
+from entities.CItemInfo import CItemInfo
+from entities.CListItem import CListItem
+from entities.CRuleItem import CRuleItem
+
+
+import customReplacements as cr
+import customConversions as cc
+
+from utils import decryptionUtils as crypt
+from utils import datetimeUtils as dt
+from utils import rowbalance as rb
+
+from utils.fileUtils import findInSubdirectory, getFileContent, getFileExtension
+from utils.scrapingUtils import findVideoFrameLink, findContentRefreshLink, findRTMP, findJS, findPHP, getHostName, findEmbedPHPLink
+from common import getHTML
+
+
+class ParsingResult(object):
+ class Code:
+ SUCCESS = 0
+ CFGFILE_NOT_FOUND = 1
+ CFGSYNTAX_INVALID = 2
+ WEBREQUEST_FAILED = 3
+
+ def __init__(self, code, itemsList):
+ self.code = code
+ self.list = itemsList
+ self.message = None
+
+class Parser(object):
+
+ """
+ returns a list of items
+ """
+ def parse(self, lItem):
+ url = lItem['url']
+ cfg = lItem['cfg']
+ ext = getFileExtension(url)
+
+ successfullyScraped = True
+
+ tmpList = None
+ if lItem['catcher']:
+ catcher = lItem['catcher']
+ cfg = os.path.join(common.Paths.catchersDir, '__' + catcher + '.cfg')
+ tmpList = self.__loadLocal(cfg, lItem)
+ if tmpList and len(tmpList.rules) > 0:
+ successfullyScraped = self.__loadRemote(tmpList, lItem)
+ else:
+ if ext == 'cfg':
+ tmpList = self.__loadLocal(url, lItem)
+ if tmpList and tmpList.start != '' and len(tmpList.rules) > 0:
+ lItem['url'] = tmpList.start
+ successfullyScraped = self.__loadRemote(tmpList, lItem)
+ elif cfg:
+ tmpList = self.__loadLocal(cfg, lItem)
+ if tmpList and len(tmpList.rules) > 0:
+ successfullyScraped = self.__loadRemote(tmpList, lItem)
+
+ # autoselect
+ if tmpList and tmpList.skill.find('autoselect') != -1 and len(tmpList.items) == 1:
+ m = tmpList.items[0]
+ m_type = m['type']
+
+ if m_type == 'rss':
+ common.log('Autoselect - ' + m['title'])
+ lItem = m
+ tmpList = self.parse(lItem).list
+
+ if not tmpList:
+ return ParsingResult(ParsingResult.Code.CFGSYNTAX_INVALID, None)
+ if tmpList and successfullyScraped == False:
+ return ParsingResult(ParsingResult.Code.WEBREQUEST_FAILED, tmpList)
+
+ # Remove duplicates
+ if tmpList.skill.find('allowDuplicates') == -1:
+ urls = []
+ for i in range(len(tmpList.items)-1,-1,-1):
+ item = tmpList.items[i]
+ tmpUrl = item['url']
+ tmpCfg = item['cfg']
+ if not tmpCfg:
+ tmpCfg = ''
+ if not urls.__contains__(tmpUrl + '|' + tmpCfg):
+ urls.append(tmpUrl + '|' + tmpCfg)
+ else:
+ tmpList.items.remove(item)
+
+ return ParsingResult(ParsingResult.Code.SUCCESS, tmpList)
+
+
+ """
+ loads cfg, creates list and sets up rules for scraping
+ """
+ def __loadLocal(self, filename, lItem = None):
+ params = []
+
+ #get Parameters
+ if filename.find('@') != -1:
+ params = filename.split('@')
+ filename = params.pop(0)
+
+ # get cfg file
+ cfg = filename
+ if not os.path.exists(cfg):
+ cfg = os.path.join(common.Paths.modulesDir, filename)
+ if not os.path.exists(cfg):
+ tmpPath = os.path.dirname(os.path.join(common.Paths.modulesDir, lItem["definedIn"]))
+ cfg = os.path.join(tmpPath ,filename)
+ if not os.path.exists(cfg):
+ srchFilename = filename
+ if filename.find('/') > -1:
+ srchFilename = srchFilename.split('/')[1]
+ try:
+ cfg = findInSubdirectory(srchFilename, common.Paths.modulesDir)
+ except:
+ try:
+ cfg = findInSubdirectory(srchFilename, common.Paths.favouritesFolder)
+ except:
+ try:
+ cfg = findInSubdirectory(srchFilename, common.Paths.customModulesDir)
+ except:
+ common.log('File not found: ' + srchFilename)
+ return None
+
+ #load file and apply parameters
+ data = getFileContent(cfg)
+ data = cr.CustomReplacements().replace(os.path.dirname(cfg), data, lItem, params)
+
+ #log
+ msg = 'Local file ' + filename + ' opened'
+ if len(params) > 0:
+ msg += ' with Parameter(s): '
+ msg += ",".join(params)
+ common.log(msg)
+
+ outputList = self.__parseCfg(filename, data, lItem)
+
+ return outputList
+
+
+ """
+ scrape items according to rules and add them to the list
+ """
+ def __loadRemote(self, inputList, lItem):
+
+ try:
+ inputList.curr_url = lItem['url']
+
+ count = 0
+ i = 1
+ maxits = 2 # 1 optimistic + 1 demystified
+ ignoreCache = False
+ demystify = False
+ back = ''
+ startUrl = inputList.curr_url
+ while count == 0 and i <= maxits:
+ if i > 1:
+ ignoreCache = True
+ demystify = True
+
+ # Trivial: url is from known streamer
+ if back:
+ lItem['referer'] = back
+ items = self.__parseHtml(inputList.curr_url, '"' + inputList.curr_url + '"', inputList.rules, inputList.skill, inputList.cfg, lItem)
+ count = len(items)
+
+
+ # try to find items in html source code
+ if count == 0:
+ referer = ''
+ if lItem['referer']:
+ referer = lItem['referer']
+ data = common.getHTML(inputList.curr_url, None, referer, False, False, ignoreCache, demystify)
+ if data == '':
+ return False
+
+ msg = 'Remote URL ' + inputList.curr_url + ' opened'
+ if demystify:
+ msg += ' (demystified)'
+ common.log(msg)
+
+
+ if inputList.section != '':
+ section = inputList.section
+ data = self.__getSection(data, section)
+
+ if lItem['section']:
+ section = lItem['section']
+ data = self.__getSection(data, section)
+
+
+ items = self.__parseHtml(inputList.curr_url, data, inputList.rules, inputList.skill, inputList.cfg, lItem)
+ count = len(items)
+ common.log(' -> ' + str(count) + ' item(s) found')
+
+ # find rtmp stream
+ #common.log('Find rtmp stream')
+ if count == 0:
+ item = self.__findRTMP(data, startUrl, lItem)
+ if item:
+ items = []
+ items.append(item)
+ count = 1
+
+ # find embedding javascripts
+ #common.log('Find embedding javascripts')
+ if count == 0:
+ item = findJS(data)
+ if item:
+ firstJS = item[0]
+ streamId = firstJS[0]
+ jsUrl = firstJS[1]
+ if not jsUrl.startswith('http://'):
+ jsUrl = urllib.basejoin(startUrl,jsUrl)
+ streamerName = getHostName(jsUrl)
+ jsSource = getHTML(jsUrl, None, startUrl)
+ phpUrl = findPHP(jsSource, streamId)
+ if phpUrl:
+ data = getHTML(phpUrl, None, startUrl)
+ item = self.__findRTMP(data, phpUrl, lItem)
+ if item:
+
+ if streamerName:
+ item['title'] = item['title'].replace('RTMP', streamerName)
+
+ items = []
+ items.append(item)
+ count = 1
+ else:
+ red = phpUrl
+ common.log(' -> Redirect: ' + red)
+ if back == red:
+ break
+ back = inputList.curr_url
+ inputList.curr_url = red
+ common.log(str(len(inputList.items)) + ' items ' + inputList.cfg + ' -> ' + red)
+ startUrl = red
+ continue
+
+
+ # find redirects
+ #common.log('find redirects')
+ if count == 0:
+ red = self.__findRedirect(startUrl, inputList.curr_url)
+ if startUrl == red:
+ common.log(' -> No redirect found')
+ else:
+ common.log(' -> Redirect: ' + red)
+ if back == red:
+ break
+ back = inputList.curr_url
+ inputList.curr_url = red
+ common.log(str(len(inputList.items)) + ' items ' + inputList.cfg + ' -> ' + red)
+ startUrl = red
+ i = 0
+
+ i += 1
+
+
+ if count != 0:
+ inputList.items = inputList.items + items
+
+
+ except:
+ traceback.print_exc(file = sys.stdout)
+ return False
+ return True
+
+
+ def __findRTMP(self, data, pageUrl, lItem):
+ rtmp = findRTMP(pageUrl, data)
+ if rtmp:
+ item = CListItem()
+ item['title'] = 'RTMP* - ' + rtmp[1]
+ item['type'] = 'video'
+ item['url'] = rtmp[0] + ' playPath=' + rtmp[1] + ' swfUrl=' + rtmp[2] +' swfVfy=1 live=true pageUrl=' + pageUrl
+ item.merge(lItem)
+ return item
+
+ return None
+
+
+ def __getSection(self, data, section):
+ p = re.compile(section, re.IGNORECASE + re.DOTALL + re.UNICODE)
+ m = p.search(data)
+ if m:
+ return m.group(0)
+ else:
+ common.log(' -> Section could not be found:' + section)
+ return data
+
+
+ def __findRedirect(self, page, referer='', demystify=False):
+ data = common.getHTML(page, None, referer=referer, xml=False, mobile=False, demystify=demystify)
+
+ if findContentRefreshLink(page, data):
+ return findContentRefreshLink(page, data)
+ elif findVideoFrameLink(page, data):
+ return findVideoFrameLink(page, data)
+ elif findEmbedPHPLink(data):
+ return findEmbedPHPLink(data)
+
+ if not demystify:
+ return self.__findRedirect(page, referer, True)
+
+ return page
+
+
+ def __parseCfg(self, cfgFile, data, lItem):
+ tmpList = CList()
+
+ data = data.replace('\r\n', '\n').split('\n')
+
+ items = []
+ tmp = None
+ hasOwnCfg = False
+
+ for m in data:
+ if m and m[0] != '#':
+ index = m.find('=')
+ if index != -1:
+ key = lower(m[:index]).strip()
+ value = m[index+1:]
+
+ index = value.find('|')
+ if value[:index] == 'sports.devil.locale':
+ value = common.translate(int(value[index+1:]))
+ elif value[:index] == 'sports.devil.image':
+ value = os.path.join(common.Paths.imgDir, value[index+1:])
+
+ if key == 'start':
+ tmpList.start = value
+ elif key == 'section':
+ tmpList.section = value
+ elif key == 'sort':
+ tmpList.sort = value
+ elif key == 'skill':
+ tmpList.skill = value
+ elif key == 'catcher':
+ tmpList.catcher = value
+
+ elif key == 'item_infos':
+ rule_tmp = CRuleItem()
+ hasOwnCfg = False
+ rule_tmp.infos = value
+ elif key == 'item_order':
+ rule_tmp.order = value
+ elif key == 'item_skill':
+ rule_tmp.skill = value
+ elif key == 'item_curr':
+ rule_tmp.curr = value
+ elif key == 'item_precheck':
+ rule_tmp.precheck = value
+
+ elif key.startswith('item_info'):
+ tmpkey = key[len('item_info'):]
+ if tmpkey == '_name':
+ info_tmp = CItemInfo()
+ info_tmp.name = value
+ if value == 'cfg':
+ hasOwnCfg = True
+ elif tmpkey == '_from':
+ info_tmp.src = value
+ elif tmpkey == '':
+ info_tmp.rule = value
+ elif tmpkey == '_default':
+ info_tmp.default = value
+ elif tmpkey == '_convert':
+ info_tmp.convert.append(value)
+ elif tmpkey == '_build':
+ info_tmp.build = value
+ rule_tmp.info_list.append(info_tmp)
+
+ elif key == 'item_url_build':
+ rule_tmp.url_build = value
+
+ if tmpList.catcher != '':
+
+ refInf = CItemInfo()
+ refInf.name = 'referer'
+ refInf.build = value
+
+ rule_tmp.info_list.append(refInf)
+
+ if not hasOwnCfg:
+ refInf = CItemInfo()
+ refInf.name = 'catcher'
+ refInf.build = tmpList.catcher
+
+ rule_tmp.info_list.append(refInf)
+
+ tmpList.rules.append(rule_tmp)
+
+
+ # static menu items (without regex)
+ elif key == 'title':
+ tmp = CListItem()
+ tmp['title'] = value
+ if tmpList.skill.find('videoTitle') > -1:
+ tmp['videoTitle'] = value
+ elif key == 'url':
+ tmp['url'] = value
+ if lItem:
+ tmp.merge(lItem)
+
+ if tmpList.catcher != '':
+ tmp['referer'] = value
+ if not hasOwnCfg:
+ tmp['catcher'] = tmpList.catcher
+
+ tmp['definedIn'] = cfgFile
+ items.append(tmp)
+ tmp = None
+ elif tmp != None:
+ if key == 'cfg':
+ hasOwnCfg = True
+ tmp[key] = value
+
+
+ tmpList.items = items
+ tmpList.cfg = cfgFile
+ return tmpList
+
+
+ def __parseHtml(self, url, data, rules, skills, definedIn, lItem):
+
+ #common.log('_parseHtml called' + url)
+ items = []
+
+ for item_rule in rules:
+ #common.log('rule: ' + item_rule.infos)
+
+ if not hasattr(item_rule, 'precheck') or (item_rule.precheck in data):
+
+ revid = re.compile(item_rule.infos, re.IGNORECASE + re.DOTALL + re.MULTILINE + re.UNICODE)
+ for reinfos in revid.findall(data):
+ tmp = CListItem()
+
+ if lItem['referer']:
+ tmp['referer'] = lItem['referer']
+
+ if item_rule.order.find('|') != -1:
+ infos_names = item_rule.order.split('|')
+ infos_values = list(reinfos)
+ i = 0
+ for name in infos_names:
+ tmp[name] = infos_values[i]
+ i = i+1
+ else:
+ tmp[item_rule.order] = reinfos
+
+ for info in item_rule.info_list:
+ info_value = tmp[info.name]
+ if info_value:
+ if info.build.find('%s') != -1:
+ tmpVal = info.build % info_value
+ tmp[info.name] = tmpVal
+ continue
+
+ if info.build.find('%s') != -1:
+ if info.src.__contains__('+'):
+ tmpArr = info.src.split('+')
+ src = ''
+ for t in tmpArr:
+ t = t.strip()
+ if t.find('\'') != -1:
+ src = src + t.strip('\'')
+ else:
+ src = src + (tmp[t] or '')
+ elif info.src.__contains__('||'):
+ variables = info.src.split('||')
+ src = firstNonEmpty(tmp, variables)
+ else:
+ src = tmp[info.src]
+
+ if src and info.convert != []:
+ tmp['referer'] = url
+ src = self.__parseCommands(tmp, src, info.convert)
+ if isinstance(src, dict):
+ for dKey in src:
+ tmp[dKey] = src[dKey]
+ src = src.values()[0]
+
+ info_value = info.build % (src)
+ else:
+ info_value = info.build
+
+ tmp[info.name] = info_value
+
+ if tmp['url']:
+ tmp['url'] = item_rule.url_build % (tmp['url'])
+ else:
+ tmp['url'] = url
+
+ tmp.merge(lItem)
+ if item_rule.skill.find('append') != -1:
+ tmp['url'] = url + tmp['url']
+
+ if item_rule.skill.find('space') != -1:
+ tmp['title'] = ' %s ' % tmp['title'].strip()
+
+ if skills.find('videoTitle') > -1:
+ tmp['videoTitle'] = tmp['title']
+
+ tmp['definedIn'] = definedIn
+ items.append(tmp)
+
+ return items
+
+
+ def __parseCommands(self, item, src, convCommands):
+ common.log('_parseCommands called')
+ # helping function
+ def parseCommand(txt):
+ command = {"command": txt, "params": ""}
+ if txt.find("(") > -1:
+ command["command"] = txt[0:txt.find("(")]
+ command["params"] = txt[len(command["command"]) + 1:-1]
+ return command
+
+ for convCommand in convCommands:
+ pComm = parseCommand(convCommand)
+ command = pComm["command"]
+ params = pComm["params"]
+
+ if params.find('@REFERER@'):
+ referer = item['referer']
+ if not referer:
+ referer = ''
+ params = params.replace('@REFERER@', referer)
+
+ if command == 'convDate':
+ src = cc.convDate(params, src)
+
+ elif command == 'convTimestamp':
+ src = cc.convTimestamp(params, src)
+
+ elif command == 'select':
+ src = cc.select(params, src)
+ if not src:
+ continue
+
+ elif command == 'unicode_escape':
+ src = src.decode('unicode-escape')
+
+ elif command == 'replaceFromDict':
+ dictName = str(params.strip('\''))
+ path = os.path.join(common.Paths.dictsDir, dictName + '.txt')
+ if not (os.path.exists(path)):
+ common.log('Dictionary file not found: ' + path)
+ continue
+ src = cc.replaceFromDict(path, src)
+
+ elif command == 'time':
+ src = time.time()
+
+ elif command == 'timediff':
+ src = dt.timediff(src,params.strip('\''))
+
+ elif command == 'offset':
+ src = cc.offset(params, src)
+
+ elif command == 'getSource':
+ src = cc.getSource(params, src)
+
+ elif command == 'quote':
+ try:
+ src = urllib.quote(params.strip("'").replace('%s', src),'')
+ except:
+ cleanParams = params.strip("'")
+ cleanParams = cleanParams.replace("%s",src)
+ src = urllib.quote(cleanParams.encode('utf-8'),'')
+
+ elif command == 'unquote':
+ src = urllib.unquote(params.strip("'").replace('%s', src))
+
+ elif command == 'parseText':
+ src = cc.parseText(item, params, src)
+
+ elif command == 'getInfo':
+ src = cc.getInfo(item, params, src)
+
+ elif command == 'getXML':
+ src = cc.getInfo(item, params, src, xml=True)
+
+ elif command == 'getMobile':
+ src = cc.getInfo(item, params, src, mobile=True)
+
+ elif command == 'decodeBase64':
+ src = cc.decodeBase64(src)
+
+ elif command == 'decodeRawUnicode':
+ src = cc.decodeRawUnicode(src)
+
+ elif command == 'resolve':
+ src = cc.resolve(src)
+
+ elif command == 'decodeXppod':
+ src = cc.decodeXppod(src)
+
+ elif command == 'decodeXppodHLS':
+ if 'stkey' in item.infos:
+ src = src.replace(item.infos['stkey'],'')
+ src = cc.decodeXppod_hls(src)
+
+ elif command == 'replace':
+ src = cc.replace(params, src)
+
+ elif command == 'replaceRegex':
+ src = cc.replaceRegex(params, src)
+
+ elif command == 'ifEmpty':
+ src = cc.ifEmpty(item, params, src)
+
+ elif command == 'isEqual':
+ src = cc.isEqual(item, params, src)
+
+ elif command == 'ifFileExists':
+ src = cc.ifFileExists(item, params, src)
+
+ elif command == 'ifExists':
+ src = cc.ifExists(item, params, src)
+
+ elif command == 'encryptJimey':
+ src = crypt.encryptJimey(params.strip("'").replace('%s', src))
+
+ elif command == 'gAesDec':
+ src = crypt.gAesDec(src,item.infos[params])
+
+ elif command == 'cjsAesDec':
+ src = crypt.cjsAesDec(src,item.infos[params])
+
+ elif command == 'aesDec':
+ src = crypt.aesDec(src,item.infos[params])
+
+ elif command == 'getCookies':
+ src = cc.getCookies(params, src)
+
+ elif command == 'destreamer':
+ src = crypt.destreamer(params.strip("'").replace('%s', src))
+
+ elif command == 'unixTimestamp':
+ src = dt.getUnixTimestamp()
+
+ elif command == 'rowbalance':
+ src = rb.get()
+
+ elif command == 'urlMerge':
+ src = cc.urlMerge(params, src)
+
+ elif command == 'translate':
+ try:
+ src = common.translate(int(src))
+ except:
+ pass
+
+ elif command == 'camelcase':
+ src = string.capwords(string.capwords(src, '-'))
+
+ elif command == 'lowercase':
+ src = string.lower(src)
+
+ elif command == 'reverse':
+ src = src[::-1]
+
+ elif command == 'demystify':
+ src = crypt.doDemystify(src)
+
+ elif command == 'random':
+ paramArr = params.split(',')
+ minimum = int(paramArr[0])
+ maximum = int(paramArr[1])
+ src = str(random.randrange(minimum,maximum))
+
+ elif command == 'debug':
+ common.log('Debug from cfg file: ' + src)
+
+ elif command == 'divide':
+ paramArr = params.split(',')
+ a = paramArr[0].strip().strip("'").replace('%s', src)
+ a = resolveVariable(a, item)
+ b = paramArr[1].strip().strip("'").replace('%s', src)
+ b = resolveVariable(b, item)
+
+ if not a or not b:
+ continue
+
+ a = int(a)
+ b = int(b)
+ try:
+ src = str(a/b)
+ except:
+ pass
+
+ return src
+
+
+
+
+def resolveVariable(varStr, item):
+ if varStr.startswith('@') and varStr.endswith('@'):
+ return item.getInfo(varStr.strip('@'))
+ return varStr
+
+
+def firstNonEmpty(tmp, variables):
+ for v in variables:
+ vClean = v.strip()
+ if vClean.find("'") != -1:
+ vClean = vClean.strip("'")
+ else:
+ vClean = tmp.getInfo(vClean)
+
+ if vClean != '':
+ return vClean
+
+ return ''
+
+
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/__init__.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/beta/__init__.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/beta/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/beta/t0mm0/__init__.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/beta/t0mm0/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/beta/t0mm0/common/__init__.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/beta/t0mm0/common/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..72e020642e9fbb7274e8637fad6cc9f12a9e0033
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/beta/t0mm0/common/__init__.py
@@ -0,0 +1,19 @@
+'''
+ common XBMC Module
+ Copyright (C) 2011 t0mm0
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+'''
+
+__ALL__ = ['addon', 'net']
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/beta/t0mm0/common/addon.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/beta/t0mm0/common/addon.py
new file mode 100644
index 0000000000000000000000000000000000000000..298a4a5c53d4b9103f8c10b0a1218303920594aa
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/beta/t0mm0/common/addon.py
@@ -0,0 +1,790 @@
+'''
+ common XBMC Module
+ Copyright (C) 2011 t0mm0
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+'''
+
+import cgi
+import re
+import os
+try:
+ import cPickle as pickle
+except:
+ import pickle
+
+import urllib
+import xbmc
+import xbmcaddon
+import xbmcgui
+import xbmcplugin
+
+class Addon:
+ '''
+ This class provides a lot of code that is used across many XBMC addons
+ in the hope that it will simplify some of the common tasks an addon needs
+ to perform.
+
+ Mostly this is achieved by providing a wrapper around commonly used parts
+ of :mod:`xbmc`, :mod:`xbmcaddon`, :mod:`xbmcgui` and :mod:`xbmcplugin`.
+
+ You probably want to have exactly one instance of this class in your addon
+ which you can call from anywhere in your code.
+
+ Example::
+
+ import sys
+ from t0mm0.common.addon import Addon
+ addon = Addon('my.plugin.id', argv=sys.argv)
+ '''
+
+
+ def __init__(self, addon_id, argv=None):
+ '''
+ Args:
+ addon_id (str): Your addon's id (eg. 'plugin.video.t0mm0.test').
+
+ Kwargs:
+ argv (list): List of arguments passed to your addon if applicable
+ (eg. sys.argv).
+ '''
+ self.addon = xbmcaddon.Addon(id=addon_id)
+ if argv:
+ self.url = argv[0]
+ self.handle = int(argv[1])
+ self.queries = self.parse_query(argv[2][1:])
+
+
+ def get_author(self):
+ '''Returns the addon author as defined in ``addon.xml``.'''
+ return self.addon.getAddonInfo('author')
+
+
+ def get_changelog(self):
+ '''Returns the addon changelog.'''
+ return self.addon.getAddonInfo('changelog')
+
+
+ def get_description(self):
+ '''Returns the addon description as defined in ``addon.xml``.'''
+ return self.addon.getAddonInfo('description')
+
+
+ def get_disclaimer(self):
+ '''Returns the addon disclaimer as defined in ``addon.xml``.'''
+ return self.addon.getAddonInfo('disclaimer')
+
+
+ def get_fanart(self):
+ '''Returns the full path to the addon fanart.'''
+ return self.addon.getAddonInfo('fanart')
+
+
+ def get_icon(self):
+ '''Returns the full path to the addon icon.'''
+ return self.addon.getAddonInfo('icon')
+
+
+ def get_id(self):
+ '''Returns the addon id as defined in ``addon.xml``.'''
+ return self.addon.getAddonInfo('id')
+
+
+ def get_name(self):
+ '''Returns the addon name as defined in ``addon.xml``.'''
+ return self.addon.getAddonInfo('name')
+
+
+ def get_path(self):
+ '''Returns the full path to the addon directory.'''
+ return self.addon.getAddonInfo('path')
+
+
+ def get_profile(self):
+ '''
+ Returns the full path to the addon profile directory
+ (useful for storing files needed by the addon such as cookies).
+ '''
+ return xbmc.translatePath(self.addon.getAddonInfo('profile'))
+
+
+ def get_stars(self):
+ '''Returns the number of stars for this addon.'''
+ return self.addon.getAddonInfo('stars')
+
+
+ def get_summary(self):
+ '''Returns the addon summary as defined in ``addon.xml``.'''
+ return self.addon.getAddonInfo('summary')
+
+
+ def get_type(self):
+ '''
+ Returns the addon summary as defined in ``addon.xml``
+ (eg. xbmc.python.pluginsource).
+ '''
+ return self.addon.getAddonInfo('type')
+
+
+ def get_version(self):
+ '''Returns the addon version as defined in ``addon.xml``.'''
+ return self.addon.getAddonInfo('version')
+
+
+ def get_setting(self, setting):
+ '''
+ Returns an addon setting. Settings must be defined in your addon's
+ ``resources/settings.xml`` file.
+
+ Args:
+ setting (str): Name of the setting to be retrieved.
+
+ Returns:
+ str containing the requested setting.
+ '''
+ return self.addon.getSetting(setting)
+
+
+ def get_string(self, string_id):
+ '''
+ Returns a localized string. Strings must be defined in your addon's
+ ``resources/language/[lang_name]/strings.xml`` file.
+
+ Args:
+ string_id (int): id of the translated string to retrieve.
+
+ Returns:
+ str containing the localized requested string.
+ '''
+ return self.addon.getLocalizedString(string_id)
+
+
+ def parse_query(self, query, defaults={'mode': 'main'}):
+ '''
+ Parse a query string as used in a URL or passed to your addon by XBMC.
+
+ Example:
+
+ >>> addon.parse_query('name=test&type=basic')
+ {'mode': 'main', 'name': 'test', 'type': 'basic'}
+
+ Args:
+ query (str): A query string.
+
+ Kwargs:
+ defaults (dict): A dictionary containing key/value pairs parsed
+ from the query string. If a key is repeated in the query string
+ its value will be a list containing all of that keys values.
+ '''
+ queries = cgi.parse_qs(query)
+ q = defaults
+ for key, value in queries.items():
+ if len(value) == 1:
+ q[key] = value[0]
+ else:
+ q[key] = value
+ return q
+
+
+ def build_plugin_url(self, queries):
+ '''
+ Returns a ``plugin://`` URL which can be used to call the addon with
+ the specified queries.
+
+ Example:
+
+ >>> addon.build_plugin_url({'name': 'test', 'type': 'basic'})
+ 'plugin://your.plugin.id/?name=test&type=basic'
+
+
+ Args:
+ queries (dict): A dctionary of keys/values to be added to the
+ ``plugin://`` URL.
+
+ Retuns:
+ A string containing a fully formed ``plugin://`` URL.
+ '''
+ out_dict = {}
+ for k, v in queries.iteritems():
+ if isinstance(v, unicode):
+ v = v.encode('utf8')
+ elif isinstance(v, str):
+ # Must be encoded in UTF-8
+ v.decode('utf8')
+ out_dict[k] = v
+ return self.url + '?' + urllib.urlencode(out_dict)
+
+
+ def log(self, msg, level=xbmc.LOGDEBUG):
+ '''
+ Writes a string to the XBMC log file. The addon name is inserted into
+ the beginning of the message automatically to help you find relevent
+ messages in the log file.
+
+ The available log levels are defined in the :mod:`xbmc` module and are
+ currently as follows::
+
+ xbmc.LOGDEBUG = 0
+ xbmc.LOGERROR = 4
+ xbmc.LOGFATAL = 6
+ xbmc.LOGINFO = 1
+ xbmc.LOGNONE = 7
+ xbmc.LOGNOTICE = 2
+ xbmc.LOGSEVERE = 5
+ xbmc.LOGWARNING = 3
+
+ Args:
+ msg (str or unicode): The message to be written to the log file.
+
+ Kwargs:
+ level (int): The XBMC log level to write at.
+ '''
+ #msg = unicodedata.normalize('NFKD', unicode(msg)).encode('ascii',
+ # 'ignore')
+ xbmc.log('%s: %s' % (self.get_name(), msg), level)
+
+
+ def log_error(self, msg):
+ '''
+ Convenience method to write to the XBMC log file at the
+ ``xbmc.LOGERROR`` error level. Use when something has gone wrong in
+ your addon code. This will show up in the log prefixed with 'ERROR:'
+ whether you have debugging switched on or not.
+ '''
+ self.log(msg, xbmc.LOGERROR)
+
+
+ def log_debug(self, msg):
+ '''
+ Convenience method to write to the XBMC log file at the
+ ``xbmc.LOGDEBUG`` error level. Use this when you want to print out lots
+ of detailed information that is only usefull for debugging. This will
+ show up in the log only when debugging is enabled in the XBMC settings,
+ and will be prefixed with 'DEBUG:'.
+ '''
+ self.log(msg, xbmc.LOGDEBUG)
+
+
+ def log_notice(self, msg):
+ '''
+ Convenience method to write to the XBMC log file at the
+ ``xbmc.LOGNOTICE`` error level. Use for general log messages. This will
+ show up in the log prefixed with 'NOTICE:' whether you have debugging
+ switched on or not.
+ '''
+ self.log(msg, xbmc.LOGNOTICE)
+
+
+ def show_ok_dialog(self, msg, title=None, is_error=False):
+ '''
+ Display an XBMC dialog with a message and a single 'OK' button. The
+ message is also written to the XBMC log file at the appropriate log
+ level.
+
+ .. warning::
+
+ Don't forget that `msg` must be a list of strings and not just a
+ string even if you only want to display a single line!
+
+ Example::
+
+ addon.show_ok_dialog(['My message'], 'My Addon')
+
+ Args:
+ msg (list of strings): The message to be displayed in the dialog.
+ Only the first 3 list items will be displayed.
+
+ Kwargs:
+ title (str): String to be displayed as the title of the dialog box.
+ Defaults to the addon name.
+
+ is_error (bool): If ``True``, the log message will be written at
+ the ERROR log level, otherwise NOTICE will be used.
+ '''
+ if not title:
+ title = self.get_name()
+ log_msg = ' '.join(msg)
+
+ while len(msg) < 3:
+ msg.append('')
+
+ if is_error:
+ self.log_error(log_msg)
+ else:
+ self.log_notice(log_msg)
+
+ xbmcgui.Dialog().ok(title, msg[0], msg[1], msg[2])
+
+
+ def show_error_dialog(self, msg):
+ '''
+ Convenience method to show an XBMC dialog box with a single OK button
+ and also write the message to the log file at the ERROR log level.
+
+ The title of the dialog will be the addon's name with the prefix
+ 'Error: '.
+
+ .. warning::
+
+ Don't forget that `msg` must be a list of strings and not just a
+ string even if you only want to display a single line!
+
+ Args:
+ msg (list of strings): The message to be displayed in the dialog.
+ Only the first 3 list items will be displayed.
+ '''
+ self.show_ok_dialog(msg, 'Error: %s' % self.get_name(), True)
+
+
+ def show_small_popup(self, title='', msg='', delay=5000, image=''):
+ '''
+ Displays a small popup box in the lower right corner. The default delay
+ is 5 seconds.
+
+ Code inspired by anarchintosh and daledude's Icefilms addon.
+
+ Example::
+
+ import os
+ logo = os.path.join(addon.get_path(), 'art','logo.jpg')
+ addon.show_small_popup('MyAddonName','Is now loaded enjoy', 5000, logo)
+
+ Kwargs:
+ title (str): title to be displayed at the top of the box
+
+ msg (str): Main message body
+
+ delay (int): delay in milliseconds until it disapears
+
+ image (str): Path to the image you want to display
+ '''
+ xbmc.executebuiltin('XBMC.Notification("%s","%s",%d,"%s")' %
+ (title, msg, delay, image))
+
+
+ def show_countdown(self, time_to_wait, title='', text=''):
+ '''
+ Show a countdown dialog with a progress bar for XBMC while delaying
+ execution. Necessary for some filehosters eg. megaupload
+
+ The original version of this code came from Anarchintosh.
+
+ Args:
+ time_to_wait (int): number of seconds to pause for.
+
+ Kwargs:
+ title (str): Displayed in the title of the countdown dialog. Default
+ is blank.
+
+ text (str): A line of text to be displayed in the dialog. Default
+ is blank.
+
+ Returns:
+ ``True`` if countdown is allowed to complete, ``False`` if the
+ user cancelled the countdown.
+ '''
+
+ dialog = xbmcgui.DialogProgress()
+ dialog.create(title)
+
+ self.log_notice('waiting %d secs' % time_to_wait)
+
+ secs = 0
+ increment = 100 / time_to_wait
+
+ cancelled = False
+ while secs <= time_to_wait:
+
+ if (dialog.iscanceled()):
+ cancelled = True
+ break
+
+ if secs != 0:
+ xbmc.sleep(1000)
+
+ secs_left = time_to_wait - secs
+ if secs_left == 0:
+ percent = 100
+ else:
+ percent = increment * secs
+
+ remaining_display = ('Wait %d seconds for the ' +
+ 'video stream to activate...') % secs_left
+ dialog.update(percent, text, remaining_display)
+
+ secs += 1
+
+ if cancelled == True:
+ self.log_notice('countdown cancelled')
+ return False
+ else:
+ self.log_debug('countdown finished waiting')
+ return True
+
+
+ def show_settings(self):
+ '''Shows the settings dialog for this addon.'''
+ self.addon.openSettings()
+
+
+ def resolve_url(self, stream_url):
+ '''
+ Tell XBMC that you have resolved a URL (or not!).
+
+ This method should be called as follows:
+
+ #. The user selects a list item that has previously had ``isPlayable``
+ set (this is true for items added with :meth:`add_item`,
+ :meth:`add_music_item` or :meth:`add_music_item`)
+ #. Your code resolves the item requested by the user to a media URL
+ #. Your addon calls this method with the resolved URL
+
+ Args:
+ stream_url (str or ``False``): If a string, tell XBMC that the
+ media URL ha been successfully resolved to stream_url. If ``False``
+ or an empty string tell XBMC the resolving failed and pop up an
+ error messsage.
+ '''
+ if stream_url:
+ self.log_debug('resolved to: %s' % stream_url)
+ xbmcplugin.setResolvedUrl(self.handle, True,
+ xbmcgui.ListItem(path=stream_url))
+ else:
+ self.show_error_dialog(['sorry, failed to resolve URL :('])
+ xbmcplugin.setResolvedUrl(self.handle, False, xbmcgui.ListItem())
+
+
+ def get_playlist(self, pl_type, new=False):
+ '''
+ Return a :class:`xbmc.Playlist` object of the specified type.
+
+ The available playlist types are defined in the :mod:`xbmc` module and
+ are currently as follows::
+
+ xbmc.PLAYLIST_MUSIC = 0
+ xbmc.PLAYLIST_VIDEO = 1
+
+ .. seealso::
+
+ :meth:`get_music_playlist`, :meth:`get_video_playlist`
+
+ Args:
+ pl_type (int): The type of playlist to get.
+
+ new (bool): If ``False`` (default), get the current
+ :class:`xbmc.Playlist` object of the type specified. If ``True``
+ then return a new blank :class:`xbmc.Playlist`.
+
+ Returns:
+ A :class:`xbmc.Playlist` object.
+ '''
+ pl = xbmc.PlayList(pl_type)
+ if new:
+ pl.clear()
+ return pl
+
+
+ def get_music_playlist(self, new=False):
+ '''
+ Convenience method to return a music :class:`xbmc.Playlist` object.
+
+ .. seealso::
+
+ :meth:`get_playlist`
+
+ Kwargs:
+ new (bool): If ``False`` (default), get the current music
+ :class:`xbmc.Playlist` object. If ``True`` then return a new blank
+ music :class:`xbmc.Playlist`.
+ Returns:
+ A :class:`xbmc.Playlist` object.
+ '''
+ self.get_playlist(xbmc.PLAYLIST_MUSIC, new)
+
+
+ def get_video_playlist(self, new=False):
+ '''
+ Convenience method to return a video :class:`xbmc.Playlist` object.
+
+ .. seealso::
+
+ :meth:`get_playlist`
+
+ Kwargs:
+ new (bool): If ``False`` (default), get the current video
+ :class:`xbmc.Playlist` object. If ``True`` then return a new blank
+ video :class:`xbmc.Playlist`.
+
+ Returns:
+ A :class:`xbmc.Playlist` object.
+ '''
+ self.get_playlist(xbmc.PLAYLIST_VIDEO, new)
+
+
+ def add_item(self, queries, infolabels, contextmenu_items='', context_replace=False, img='',
+ fanart='', resolved=False, total_items=0, playlist=False, item_type='video',
+ is_folder=False):
+ '''
+ Adds an item to the list of entries to be displayed in XBMC or to a
+ playlist.
+
+ Use this method when you want users to be able to select this item to
+ start playback of a media file. ``queries`` is a dict that will be sent
+ back to the addon when this item is selected::
+
+ add_item({'host': 'youtube.com', 'media_id': 'ABC123XYZ'},
+ {'title': 'A youtube vid'})
+
+ will add a link to::
+
+ plugin://your.plugin.id/?host=youtube.com&media_id=ABC123XYZ
+
+ .. seealso::
+
+ :meth:`add_music_item`, :meth:`add_video_item`,
+ :meth:`add_directory`
+
+ Args:
+ queries (dict): A set of keys/values to be sent to the addon when
+ the user selects this item.
+
+ infolabels (dict): A dictionary of information about this media
+ (see the `XBMC Wiki InfoLabels entry
+ `_).
+
+ Kwargs:
+
+ contextmenu_items (list): A list of contextmenu items
+
+ context_replace (bool): To replace the xbmc default contextmenu items
+
+ img (str): A URL to an image file to be used as an icon for this
+ entry.
+
+ fanart (str): A URL to a fanart image for this entry.
+
+ resolved (str): If not empty, ``queries`` will be ignored and
+ instead the added item will be the exact contentes of ``resolved``.
+
+ total_items (int): Total number of items to be added in this list.
+ If supplied it enables XBMC to show a progress bar as the list of
+ items is being built.
+
+ playlist (playlist object): If ``False`` (default), the item will
+ be added to the list of entries to be displayed in this directory.
+ If a playlist object is passed (see :meth:`get_playlist`) then
+ the item will be added to the playlist instead
+
+ item_type (str): The type of item to add (eg. 'music', 'video' or
+ 'pictures')
+ '''
+ infolabels = self.unescape_dict(infolabels)
+ if not resolved:
+ if not is_folder:
+ queries['play'] = 'True'
+ play = self.build_plugin_url(queries)
+ else:
+ play = resolved
+ listitem = xbmcgui.ListItem(infolabels['title'])
+ listitem.setInfo(item_type, infolabels)
+ listitem.setProperty('IsPlayable', 'true')
+ listitem.setProperty('fanart_image', fanart)
+ try:
+ listitem.setArt({'thumb': img})
+ except:
+ listitem.setThumbnailImage(img)
+ self.log_debug('t0mm0-addon.py: setThumbnailImage is deprecated')
+
+ if contextmenu_items:
+ listitem.addContextMenuItems(contextmenu_items, replaceItems=context_replace)
+ if playlist is not False:
+ self.log_debug('adding item: %s - %s to playlist' % \
+ (infolabels['title'], play))
+ playlist.add(play, listitem)
+ else:
+ self.log_debug('adding item: %s - %s' % (infolabels['title'], play))
+ xbmcplugin.addDirectoryItem(self.handle, play, listitem,
+ isFolder=is_folder,
+ totalItems=total_items)
+
+
+ def add_video_item(self, queries, infolabels, contextmenu_items='', context_replace=False,
+ img='', fanart='', resolved=False, total_items=0, playlist=False):
+ '''
+ Convenience method to add a video item to the directory list or a
+ playlist.
+
+ See :meth:`add_item` for full infomation
+ '''
+ self.add_item(queries, infolabels, contextmenu_items, context_replace, img, fanart,
+ resolved, total_items, playlist, item_type='video')
+
+
+ def add_music_item(self, queries, infolabels, contextmenu_items='', context_replace=False,
+ img='', fanart='', resolved=False, total_items=0, playlist=False):
+ '''
+ Convenience method to add a music item to the directory list or a
+ playlist.
+
+ See :meth:`add_item` for full infomation
+ '''
+ self.add_item(queries, infolabels, contextmenu_items, img, context_replace, fanart,
+ resolved, total_items, playlist, item_type='music')
+
+
+ def add_directory(self, queries, infolabels, contextmenu_items='', context_replace=False,
+ img='', fanart='', total_items=0, is_folder=True):
+ '''
+ Convenience method to add a directory to the display list or a
+ playlist.
+
+ See :meth:`add_item` for full infomation
+ '''
+ self.add_item(queries, infolabels, contextmenu_items, context_replace, img, fanart,
+ total_items=total_items, resolved=self.build_plugin_url(queries),
+ is_folder=is_folder)
+
+ def end_of_directory(self):
+ '''Tell XBMC that we have finished adding items to this directory.'''
+ xbmcplugin.endOfDirectory(self.handle)
+
+
+ def _decode_callback(self, matches):
+ '''Callback method used by :meth:`decode`.'''
+ _id = matches.group(1)
+ try:
+ return unichr(int(_id))
+ except:
+ return _id
+
+
+ def decode(self, data):
+ '''
+ Regular expression to convert entities such as ``,`` to the correct
+ characters. It is called by :meth:`unescape` and so it is not required
+ to call it directly.
+
+ This method was found `on the web `_
+
+ Args:
+ data (str): String to be cleaned.
+
+ Returns:
+ Cleaned string.
+ '''
+ return re.sub("(\d+)(;|(?=\s))", self._decode_callback, data).strip()
+
+
+ def unescape(self, text):
+ '''
+ Decodes HTML entities in a string.
+
+ You can add more entities to the ``rep`` dictionary.
+
+ Args:
+ text (str): String to be cleaned.
+
+ Returns:
+ Cleaned string.
+ '''
+ try:
+ text = self.decode(text)
+ rep = {'<': '<',
+ '>': '>',
+ '"': '"',
+ '’': '\'',
+ '´': '\'',
+ }
+ for s, r in rep.items():
+ text = text.replace(s, r)
+ # this has to be last:
+ text = text.replace("&", "&")
+
+ #we don't want to fiddle with non-string types
+ except TypeError:
+ pass
+
+ return text
+
+
+ def unescape_dict(self, d):
+ '''
+ Calls :meth:`unescape` on all values in a dictionary.
+
+ Args:
+ d (dict): A dictionary containing string values
+
+ Returns:
+ A dictionary with HTML entities removed from the values.
+ '''
+ out = {}
+ for key, value in d.items():
+ out[key] = self.unescape(value)
+ return out
+
+ def save_data(self, filename, data):
+ '''
+ Saves the data structure using pickle. If the addon data path does
+ not exist it will be automatically created. This save function has
+ the same restrictions as the pickle module.
+
+ Args:
+ filename (string): name of the file you want to save data to. This
+ file will be saved in your addon's profile directory.
+
+ data (data object/string): you want to save.
+
+ Returns:
+ True on success
+ False on failure
+ '''
+ profile_path = self.get_profile()
+ try:
+ os.makedirs(profile_path)
+ except:
+ pass
+ save_path = os.path.join(profile_path, filename)
+ try:
+ pickle.dump(data, open(save_path, 'wb'))
+ return True
+ except pickle.PickleError:
+ return False
+
+ def load_data(self,filename):
+ '''
+ Load the data that was saved with save_data() and returns the
+ data structure.
+
+ Args:
+ filename (string): Name of the file you want to load data from. This
+ file will be loaded from your addons profile directory.
+
+ Returns:
+ Data stucture on success
+ False on failure
+ '''
+ profile_path = self.get_profile()
+ load_path = os.path.join(profile_path, filename)
+ print profile_path
+ if not os.path.isfile(load_path):
+ self.log_debug('%s does not exist' % load_path)
+ return False
+ try:
+ data = pickle.load(open(load_path))
+ except:
+ return False
+ return data
+
+
+
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/beta/t0mm0/common/net.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/beta/t0mm0/common/net.py
new file mode 100644
index 0000000000000000000000000000000000000000..ef634d99a398aded421584b85b21cc9cf8cfb92a
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/beta/t0mm0/common/net.py
@@ -0,0 +1,336 @@
+'''
+ common XBMC Module
+ Copyright (C) 2011 t0mm0
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+'''
+
+import cookielib
+import gzip
+import re
+import StringIO
+import urllib
+import urllib2
+import socket
+
+#Set Global timeout - Useful for slow connections and Putlocker.
+socket.setdefaulttimeout(30)
+
+#use ipv4 only
+origGetAddrInfo = socket.getaddrinfo
+
+def getAddrInfoWrapper(host, port, family=0, socktype=0, proto=0, flags=0):
+ return origGetAddrInfo(host, port, socket.AF_INET, socktype, proto, flags)
+
+# replace the original socket.getaddrinfo by our version
+socket.getaddrinfo = getAddrInfoWrapper
+
+class HeadRequest(urllib2.Request):
+ '''A Request class that sends HEAD requests'''
+ def get_method(self):
+ return 'HEAD'
+
+class Net:
+ '''
+ This class wraps :mod:`urllib2` and provides an easy way to make http
+ requests while taking care of cookies, proxies, gzip compression and
+ character encoding.
+
+ Example::
+
+ from t0mm0.common.net import Net
+ net = Net()
+ response = net.http_GET('http://xbmc.org')
+ print response.content
+ '''
+
+ _cj = cookielib.LWPCookieJar()
+ _proxy = None
+ _user_agent = 'Mozilla/5.0 (Windows NT 6.3) AppleWebKit/537.36 ' + \
+ '(KHTML, like Gecko) Chrome/44.0.2403.107 Safari/537.36'
+ _http_debug = False
+
+
+ def __init__(self, cookie_file='', proxy='', user_agent='',
+ http_debug=False):
+ '''
+ Kwargs:
+ cookie_file (str): Full path to a file to be used to load and save
+ cookies to.
+
+ proxy (str): Proxy setting (eg.
+ ``'http://user:pass@example.com:1234'``)
+
+ user_agent (str): String to use as the User Agent header. If not
+ supplied the class will use a default user agent (chrome)
+
+ http_debug (bool): Set ``True`` to have HTTP header info written to
+ the XBMC log for all requests.
+ '''
+ if cookie_file:
+ self.set_cookies(cookie_file)
+ if proxy:
+ self.set_proxy(proxy)
+ if user_agent:
+ self.set_user_agent(user_agent)
+ self._http_debug = http_debug
+ self._update_opener()
+
+
+ def set_cookies(self, cookie_file):
+ '''
+ Set the cookie file and try to load cookies from it if it exists.
+
+ Args:
+ cookie_file (str): Full path to a file to be used to load and save
+ cookies to.
+ '''
+ try:
+ self._cj.load(cookie_file, ignore_discard=True)
+ self._update_opener()
+ return True
+ except:
+ return False
+
+
+ def get_cookies(self):
+ '''Returns A dictionary containing all cookie information by domain.'''
+ return self._cj._cookies
+
+
+ def save_cookies(self, cookie_file):
+ '''
+ Saves cookies to a file.
+
+ Args:
+ cookie_file (str): Full path to a file to save cookies to.
+ '''
+ self._cj.save(cookie_file, ignore_discard=True)
+
+
+ def set_proxy(self, proxy):
+ '''
+ Args:
+ proxy (str): Proxy setting (eg.
+ ``'http://user:pass@example.com:1234'``)
+ '''
+ self._proxy = proxy
+ self._update_opener()
+
+
+ def get_proxy(self):
+ '''Returns string containing proxy details.'''
+ return self._proxy
+
+
+ def set_user_agent(self, user_agent):
+ '''
+ Args:
+ user_agent (str): String to use as the User Agent header.
+ '''
+ self._user_agent = user_agent
+
+
+ def get_user_agent(self):
+ '''Returns user agent string.'''
+ return self._user_agent
+
+
+ def _update_opener(self):
+ '''
+ Builds and installs a new opener to be used by all future calls to
+ :func:`urllib2.urlopen`.
+ '''
+ if self._http_debug:
+ http = urllib2.HTTPHandler(debuglevel=1)
+ else:
+ http = urllib2.HTTPHandler()
+
+ if self._proxy:
+ opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self._cj),
+ urllib2.ProxyHandler({'http':
+ self._proxy}),
+ urllib2.HTTPBasicAuthHandler(),
+ http)
+
+ else:
+ opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self._cj),
+ urllib2.HTTPBasicAuthHandler(),
+ http)
+ urllib2.install_opener(opener)
+
+
+ def http_GET(self, url, headers={}, compression=True):
+ '''
+ Perform an HTTP GET request.
+
+ Args:
+ url (str): The URL to GET.
+
+ Kwargs:
+ headers (dict): A dictionary describing any headers you would like
+ to add to the request. (eg. ``{'X-Test': 'testing'}``)
+
+ compression (bool): If ``True`` (default), try to use gzip
+ compression.
+
+ Returns:
+ An :class:`HttpResponse` object containing headers and other
+ meta-information about the page and the page content.
+ '''
+ return self._fetch(url, headers=headers, compression=compression)
+
+
+ def http_POST(self, url, form_data, headers={}, compression=True):
+ '''
+ Perform an HTTP POST request.
+
+ Args:
+ url (str): The URL to POST.
+
+ form_data (dict): A dictionary of form data to POST.
+
+ Kwargs:
+ headers (dict): A dictionary describing any headers you would like
+ to add to the request. (eg. ``{'X-Test': 'testing'}``)
+
+ compression (bool): If ``True`` (default), try to use gzip
+ compression.
+
+ Returns:
+ An :class:`HttpResponse` object containing headers and other
+ meta-information about the page and the page content.
+ '''
+ return self._fetch(url, form_data, headers=headers,
+ compression=compression)
+
+
+ def http_HEAD(self, url, headers={}):
+ '''
+ Perform an HTTP HEAD request.
+
+ Args:
+ url (str): The URL to GET.
+
+ Kwargs:
+ headers (dict): A dictionary describing any headers you would like
+ to add to the request. (eg. ``{'X-Test': 'testing'}``)
+
+ Returns:
+ An :class:`HttpResponse` object containing headers and other
+ meta-information about the page.
+ '''
+ req = HeadRequest(url)
+ req.add_header('User-Agent', self._user_agent)
+ for k, v in headers.items():
+ req.add_header(k, v)
+ response = urllib2.urlopen(req)
+ return HttpResponse(response)
+
+
+ def _fetch(self, url, form_data={}, headers={}, compression=True):
+ '''
+ Perform an HTTP GET or POST request.
+
+ Args:
+ url (str): The URL to GET or POST.
+
+ form_data (dict): A dictionary of form data to POST. If empty, the
+ request will be a GET, if it contains form data it will be a POST.
+
+ Kwargs:
+ headers (dict): A dictionary describing any headers you would like
+ to add to the request. (eg. ``{'X-Test': 'testing'}``)
+
+ compression (bool): If ``True`` (default), try to use gzip
+ compression.
+
+ Returns:
+ An :class:`HttpResponse` object containing headers and other
+ meta-information about the page and the page content.
+ '''
+ req = urllib2.Request(url)
+ if form_data:
+ form_data = urllib.urlencode(form_data)
+ req = urllib2.Request(url, form_data)
+ req.add_header('User-Agent', self._user_agent)
+ for k, v in headers.items():
+ req.add_header(k, v)
+ if compression:
+ req.add_header('Accept-Encoding', 'gzip')
+ response = urllib2.urlopen(req)
+ return HttpResponse(response)
+
+
+
+class HttpResponse:
+ '''
+ This class represents a resoponse from an HTTP request.
+
+ The content is examined and every attempt is made to properly encode it to
+ Unicode.
+
+ .. seealso::
+ :meth:`Net.http_GET`, :meth:`Net.http_HEAD` and :meth:`Net.http_POST`
+ '''
+
+ content = ''
+ '''Unicode encoded string containing the body of the reposne.'''
+
+
+ def __init__(self, response):
+ '''
+ Args:
+ response (:class:`mimetools.Message`): The object returned by a call
+ to :func:`urllib2.urlopen`.
+ '''
+ self._response = response
+ html = response.read()
+ try:
+ if response.headers['content-encoding'].lower() == 'gzip':
+ html = gzip.GzipFile(fileobj=StringIO.StringIO(html)).read()
+ except:
+ pass
+
+ try:
+ content_type = response.headers['content-type']
+ if 'charset=' in content_type:
+ encoding = content_type.split('charset=')[-1]
+ except:
+ pass
+
+ r = re.search('= len(codec):
+ ring = 0
+ return ''.join(res)
+
+def encryptJimey(data):
+ result = encryptDES_ECB(data,"PASSWORD").encode('base64').replace('/','').strip()
+ return result
+
+# used by 24cast
+def destreamer(s):
+ #remove all but[0-9A-Z]
+ string = re.sub("[^0-9A-Z]", "", s.upper())
+ result = ""
+ nextchar = ""
+ for i in range(0,len(string)-1):
+ nextchar += string[i]
+ if len(nextchar) == 2:
+ result += ntos(int(nextchar,16))
+ nextchar = ""
+ return result
+
+def ntos(n):
+ n = hex(n)[2:]
+ if len(n) == 1:
+ n = "0" + n
+ n = "%" + n
+ return urllib.unquote(n)
+
+def doDemystify(data):
+ escape_again=False
+
+ #init jsFunctions and jsUnpacker
+ jsF = JsFunctions()
+ jsU = JsUnpacker()
+ jsU2 = JsUnpackerV2()
+ jsUW = JsUnwiser()
+ jsUI = JsUnIonCube()
+ jsUF = JsUnFunc()
+ jsUP = JsUnPP()
+ jsU95 = JsUnpacker95High()
+ JsPush = JsUnPush()
+
+ # replace NUL
+ #data = data.replace('\0','')
+
+
+ # unescape
+ r = re.compile('a1=["\'](%3C(?=[^\'"]*%\w\w)[^\'"]+)["\']')
+ while r.findall(data):
+ for g in r.findall(data):
+ quoted=g
+ data = data.replace(quoted, urllib.unquote_plus(quoted))
+
+
+ r = re.compile('unescape\(\s*["\']((?=[^\'"]*%\w\w)[^\'"]+)["\']')
+ while r.findall(data):
+ for g in r.findall(data):
+ quoted=g
+ data = data.replace(quoted, urllib.unquote_plus(quoted))
+
+ r = re.compile('unescape\(\s*["\']((?=[^\'"]*\\u00)[^\'"]+)["\']')
+ while r.findall(data):
+ for g in r.findall(data):
+ quoted=g
+ data = data.replace(quoted, quoted.decode('unicode-escape'))
+
+ r = re.compile('(\'\+dec\("\w+"\)\+\')')
+ while r.findall(data):
+ for g in r.findall(data):
+ r2 = re.compile('dec\("(\w+)"\)')
+ for dec_data in r2.findall(g):
+ res = ''
+ for i in dec_data:
+ res = res + chr(ord(i) ^ 123)
+ data = data.replace(g, res)
+
+ r = re.compile('(eval\(decodeURIComponent\(atob\([\'"][^\'"]+[\'"]\)\)\);)')
+ while r.findall(data):
+ for g in r.findall(data):
+ r2 = re.compile('eval\(decodeURIComponent\(atob\([\'"]([^\'"]+)[\'"]\)\)\);')
+ for base64_data in r2.findall(g):
+ data = data.replace(g, urllib.unquote(base64_data.decode('base-64')))
+
+ r = re.compile('(", re.IGNORECASE + re.DOTALL)
+ gs = r.findall(data)
+ if gs != None and gs != []:
+ for g in gs:
+ data = data.replace(g[0], jsF.RrRrRrRr(g[1].replace('\\','')))
+
+ # hp_d01
+ if 'function hp_d01(' in data:
+ r = re.compile("hp_d01\(unescape\(\"(.+?)\"\)\);//-->")
+ gs = r.findall(data)
+ if gs:
+ for g in gs:
+ data = data.replace(g, jsF.hp_d01(g))
+
+ # ew_dc
+ if 'function ew_dc(' in data:
+ r = re.compile("ew_dc\(unescape\(\"(.+?)\"\)\);")
+ gs = r.findall(data)
+ if gs:
+ for g in gs:
+ data = data.replace(g, jsF.ew_dc(g))
+
+ # pbbfa0
+ if 'function pbbfa0(' in data:
+ r = re.compile("pbbfa0\(''\).*?'(.+?)'.\+.unescape")
+ gs = r.findall(data)
+ if gs:
+ for g in gs:
+ data = data.replace(g, jsF.pbbfa0(g))
+
+
+ # util.de
+ if 'Util.de' in data:
+ r = re.compile("Util.de\(unescape\(['\"](.+?)['\"]\)\)")
+ gs = r.findall(data)
+ if gs:
+ for g in gs:
+ data = data.replace(g,g.decode('base64'))
+
+ # 24cast
+ if 'destreamer(' in data:
+ r = re.compile("destreamer\(\"(.+?)\"\)")
+ gs = r.findall(data)
+ if gs:
+ for g in gs:
+ data = data.replace(g, destreamer(g))
+
+ # JS P,A,C,K,E,D
+ if jsU95.containsPacked(data):
+ data = jsU95.unpackAll(data)
+ escape_again=True
+
+ if jsU2.containsPacked(data):
+ data = jsU2.unpackAll(data)
+ escape_again=True
+
+ if jsU.containsPacked(data):
+ data = jsU.unpackAll(data)
+ escape_again=True
+
+ # JS W,I,S,E
+ if jsUW.containsWise(data):
+ data = jsUW.unwiseAll(data)
+ escape_again=True
+
+ # JS IonCube
+ if jsUI.containsIon(data):
+ data = jsUI.unIonALL(data)
+ escape_again=True
+
+ # Js unFunc
+ if jsUF.cointainUnFunc(data):
+ data = jsUF.unFuncALL(data)
+ escape_again=True
+
+ if jsUP.containUnPP(data):
+ data = jsUP.UnPPAll(data)
+ escape_again=True
+
+ if JsPush.containUnPush(data):
+ data = JsPush.UnPush(data)
+
+ # unescape again
+ if escape_again:
+ data = doDemystify(data)
+ return data
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/fileUtils.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/fileUtils.py
new file mode 100644
index 0000000000000000000000000000000000000000..32a87116fc2cb826f39a81af13676cfc6b21942f
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/fileUtils.py
@@ -0,0 +1,172 @@
+# -*- coding: utf-8 -*-
+
+import os
+import datetime, time
+import random
+import hashlib
+import codecs
+
+#######################################
+# File Helpers
+#######################################
+def fileExists(filename):
+ return os.path.isfile(filename)
+
+
+def getFileExtension(filename):
+ ext_pos = filename.rfind('.')
+ if ext_pos != -1:
+ return filename[ext_pos+1:]
+ else:
+ return ''
+
+def get_immediate_subdirectories(directory):
+ return [name for name in os.listdir(directory)
+ if os.path.isdir(os.path.join(directory, name))]
+
+def findInSubdirectory(filename, subdirectory=''):
+ if subdirectory:
+ path = subdirectory
+ else:
+ path = os.getcwd()
+ for root, _, names in os.walk(path):
+ if filename in names:
+ return os.path.join(root, filename)
+ raise 'File not found'
+
+
+def cleanFilename(s):
+ if not s:
+ return ''
+ badchars = '\\/:*?\"<>|'
+ for c in badchars:
+ s = s.replace(c, '')
+ return s;
+
+
+def randomFilename(directory, chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', length = 8, prefix = '', suffix = '', attempts = 10000):
+ for _ in range(attempts):
+ filename = ''.join([random.choice(chars) for _ in range(length)])
+ filename = prefix + filename + suffix
+ if not os.path.exists(os.path.join(directory, filename)):
+ return filename
+ return None
+
+
+def getFileContent(filename):
+ try:
+ f = codecs.open(filename,'r','utf-8')
+ txt = f.read()
+ f.close()
+ return txt
+ except:
+ return ''
+
+def setFileContent(filename, txt, createFolders=False):
+ try:
+ if createFolders:
+ folderPath = os.path.dirname(filename)
+ if not os.path.exists(folderPath):
+ os.makedirs(folderPath, 0777)
+
+ f = codecs.open(filename, 'w','utf-8')
+ f.write(txt)
+ f.close()
+ return True
+ except:
+ return False
+
+def appendFileContent(filename, txt):
+ try:
+ f = codecs.open(filename, 'a','utf-8')
+ f.write(txt)
+ f.close()
+ return True
+ except:
+ return False
+
+def md5(fileName, excludeLine="", includeLine=""):
+ """Compute md5 hash of the specified file"""
+ m = hashlib.md5()
+ try:
+ fd = codecs.open(fileName,"rb",'utf-8')
+ except IOError:
+ #print "Unable to open the file in readmode:", fileName
+ return
+ content = fd.readlines()
+ fd.close()
+ for eachLine in content:
+ if excludeLine and eachLine.startswith(excludeLine):
+ continue
+ m.update(eachLine)
+ m.update(includeLine)
+ return m.hexdigest()
+
+def lastModifiedAt(path):
+ return datetime.datetime.utcfromtimestamp(os.path.getmtime(path))
+
+def setLastModifiedAt(path, date):
+ try:
+ stinfo = os.stat(path)
+ atime = stinfo.st_atime
+ mtime = int(time.mktime(date.timetuple()))
+ os.utime(path, (atime, mtime))
+ return True
+ except:
+ pass
+
+ return False
+
+def clearDirectory(path):
+ try:
+ for root, _, files in os.walk(path , topdown = False):
+ for name in files:
+ os.remove(os.path.join(root, name))
+ except:
+ return False
+
+ return True
+
+
+# http://akiscode.com/articles/sha-1directoryhash.shtml
+# Copyright (c) 2009 Stephen Akiki
+# MIT License (Means you can do whatever you want with this)
+# See http://www.opensource.org/licenses/mit-license.php
+# Error Codes:
+# -1 -> Directory does not exist
+# -2 -> General error (see stack traceback)
+
+def GetHashofDirs(directory, verbose=0):
+
+ SHAhash = hashlib.sha1()
+ if not os.path.exists (directory):
+ return -1
+
+ try:
+ for root, _, files in os.walk(directory):
+ for names in files:
+ #if verbose == 1:
+ #print 'Hashing', names
+ filepath = os.path.join(root,names)
+ try:
+ f1 = codecs.open(filepath, 'rb','utf-8')
+ except:
+ # You can't open the file for some reason
+ f1.close()
+ continue
+
+ while 1:
+ # Read file in as little chunks
+ buf = f1.read(4096)
+ if not buf:
+ break
+ SHAhash.update(hashlib.sha1(buf).hexdigest())
+ f1.close()
+
+ except:
+ import traceback
+ # Print the stack traceback
+ traceback.print_exc()
+ return -2
+
+ return SHAhash.hexdigest()
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/javascriptUtils.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/javascriptUtils.py
new file mode 100644
index 0000000000000000000000000000000000000000..db291adab674c1ce4654a152b0bb77db2c41aca9
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/javascriptUtils.py
@@ -0,0 +1,372 @@
+# -*- coding: utf-8 -*-
+
+import re
+import urllib
+import base64
+import unpackstd
+import unpack95High
+from string import join
+import traceback, sys
+
+class JsFunctions:
+
+ def hp_d01(self, s):
+ ar=[]
+ os=""
+ for i in range(0,len(s)-1):
+ c = ord(s[i])
+ if c < 128:
+ c = c^2
+ os += chr(c)
+ if len(os) > 80:
+ ar.append(os)
+ os = ""
+ o = join(ar,'') + os
+ return o
+
+ def o61a2a8f(self, s):
+ r = "";
+ tmp = s.split("18267506");
+ s = urllib.unquote(tmp[0]);
+ k = urllib.unquote(tmp[1] + "511382");
+ for i in range(0,len(s)-1):
+ r += chr((int(k[i%len(k)])^ord(s[i]))+1);
+ return r;
+
+ def n98c4d2c(self, s):
+ txtArr = s.split('18234663')
+ s = urllib.unquote(txtArr[0])
+ t = urllib.unquote(txtArr[1] + '549351')
+ tmp=''
+ for i in range(0,len(s)-1):
+ tmp += chr((int(t[i%len(t)])^ord(s[i]))+-6)
+ return urllib.unquote(tmp)
+
+ def RrRrRrRr(self, teaabb):
+ tttmmm=""
+ l=len(teaabb)
+ www = hhhhffff = int(round(l/2))
+ if l<2*www:
+ hhhhffff -= 1
+ for i in range(0,hhhhffff-1):
+ tttmmm = tttmmm + teaabb[i] + teaabb[i+hhhhffff]
+ if l<2*www :
+ tttmmm = tttmmm + teaabb[l-1]
+ return tttmmm
+
+ def ew_dc(self, s):
+ d=''
+ a=[]
+ for i in range(0, len(s)-1):
+ c = ord(s[i])
+ if (c<128):
+ c = c^5
+ d += chr(c)
+ if (i+1) % 99 == 0:
+ a.append(d)
+ d=''
+ r = join(a,'') + d
+ return r
+
+ def pbbfa0(self, s):
+ r = ""
+ tmp = s.split("17753326")
+ s = urllib.unquote(tmp[0])
+ k = urllib.unquote(tmp[1] + "527117")
+ for i in range(0,len(s)):
+ r += chr((int(k[i%len(k)])^ord(s[i]))+7)
+ return r
+
+class JsUnpacker:
+ def unpackAll(self, data):
+ try:
+ in_data=data
+ sPattern = '(eval\\(function\\(p,a,c,k,e,d.*)'
+ enc_data=re.compile(sPattern).findall(in_data)
+ if len(enc_data)==0:
+ sPattern = '(eval\\(function\\(p,a,c,k,e,r.*)'
+ enc_data=re.compile(sPattern).findall(in_data)
+
+
+ for enc_val in enc_data:
+ unpack_val=unpackstd.unpack(enc_val)
+ in_data=in_data.replace(enc_val,unpack_val)
+ in_data=in_data.replace('\\\'','\'')
+ return in_data
+ except:
+ traceback.print_exc(file=sys.stdout)
+ return data.replace(enc_val,'')
+
+ def containsPacked(self, data):
+ return 'p,a,c,k,e,d' in data or 'p,a,c,k,e,r' in data
+
+
+class JsUnpackerV2:
+
+ def unpackAll(self, data):
+ try:
+ in_data=data
+ sPattern = '(eval\\(function\\(p,a,c,k,e,d.*)'
+ enc_data=re.compile(sPattern).findall(in_data)
+ #print 'enc_data',enc_data, len(enc_data)
+ if len(enc_data)==0:
+ sPattern = '(eval\\(function\\(p,a,c,k,e,r.*)'
+ enc_data=re.compile(sPattern).findall(in_data)
+ #print 'enc_data packer...',enc_data
+
+ for enc_val in enc_data:
+ unpack_val=self.unpack(enc_val)
+ in_data=in_data.replace(enc_val,unpack_val)
+ return in_data
+ except:
+ traceback.print_exc(file=sys.stdout)
+ return data
+
+
+ def containsPacked(self, data):
+ return 'String.fromCharCode(c+29)' in data
+
+ def unpack(self,sJavascript,iteration=1, totaliterations=1 ):
+
+ aSplit = sJavascript.split("rn p}('")
+
+ p1,a1,c1,k1=('','0','0','')
+ ss="p1,a1,c1,k1=(\'"+aSplit[1].split(".spli")[0]+')'
+ exec(ss)
+
+ k1=k1.split('|')
+ aSplit = aSplit[1].split("))'")
+ e = ''
+ d = ''#32823
+ sUnpacked1 = str(self.__unpack(p1, a1, c1, k1, e, d,iteration))
+ if iteration>=totaliterations:
+ return sUnpacked1
+ else:
+ return self.unpack(sUnpacked1,iteration+1)
+
+ def __unpack(self,p, a, c, k, e, d, iteration,v=1):
+ while (c >= 1):
+ c = c -1
+ if (k[c]):
+ aa=str(self.__itoaNew(c, a))
+ p=re.sub('\\b' + aa +'\\b', k[c], p)# THIS IS Bloody slow!
+ return p
+
+ def __itoa(self,num, radix):
+
+ result = ""
+ if num==0: return '0'
+ while num > 0:
+ result = "0123456789abcdefghijklmnopqrstuvwxyz"[num % radix] + result
+ num /= radix
+ return result
+
+ def __itoaNew(self,cc, a):
+ aa="" if cc < a else self.__itoaNew(int(cc / a),a)
+ cc = (cc % a)
+ bb=chr(cc + 29) if cc> 35 else str(self.__itoa(cc,36))
+ return aa+bb
+
+class JsUnpacker95High:
+ def unpackAll(self, data):
+ try:
+ in_data=data
+ sPattern = '(eval\\(function\\(p,a,c,k,e,d.*)'
+ enc_data=re.compile(sPattern).findall(in_data)
+ if len(enc_data)==0:
+ sPattern = '(eval\\(function\\(p,a,c,k,e,r.*)'
+ enc_data=re.compile(sPattern).findall(in_data)
+
+
+ for enc_val in enc_data:
+ unpack_val=unpack95High.unpack(enc_val)
+ in_data=in_data.replace(enc_val,unpack_val)
+ in_data=in_data.replace('\\\'','\'')
+ return in_data
+ except:
+ traceback.print_exc(file=sys.stdout)
+ return data.replace(enc_val,'')
+
+ def containsPacked(self, data):
+ return r'[\xa1-\xff]' in data
+
+
+class JsUnIonCube:
+ def ionX(self, x, arrayX):
+ r = []
+ s = 0
+ w = 0
+
+ for d in x:
+ w |= int(arrayX[ord(d)-48]) << s
+ if (s):
+ r.append(chr(165 ^ w & 255))
+ w >>= 8
+ s -= 2
+ else:
+ s = 6
+
+ r = ''.join(r)
+ return r
+
+ def unIonALL(self,data):
+ in_data=data
+ sPattern = 'c="(.*?)";eval\\(unescape\\(".*"\\)\\);x\\("(.*?)"\\)'
+ undc_data=re.compile(sPattern).findall(in_data)
+ c = undc_data[0][0]
+ x = undc_data[0][1]
+
+ l = list(c)
+ for i in range(0, len(c), 3):
+ l[i]='%'
+
+ c = ''.join(l)
+ c = urllib.unquote_plus(c)
+
+ arrayPattern = 't=Array\\((.*?)\\)'
+ arrayData = re.compile(arrayPattern).findall(c)
+ ionArray = arrayData[0].split(',')
+ data=self.ionX(x,ionArray)
+
+ return data
+
+ def containsIon(self,data):
+ return 'eval(unescape("d="";' in data
+
+class JsUnwiser:
+ def unwiseAll(self, data):
+ try:
+ in_data=data
+ sPattern = 'eval\\(function\\(w,i,s,e\\).*?}\\((.*?)\\)'
+ wise_data=re.compile(sPattern).findall(in_data)
+ for wise_val in wise_data:
+ unpack_val=self.unwise(wise_val)
+ #print '\nunpack_val',unpack_val
+ in_data=in_data.replace(wise_val,unpack_val)
+ return re.sub("eval\(function\(w,i,s,e\).*?join\(''\);}", "", in_data, count=1, flags=re.DOTALL)
+ except:
+ traceback.print_exc(file=sys.stdout)
+ return data
+
+ def containsWise(self, data):
+ return 'w,i,s,e' in data
+
+ def unwise(self, sJavascript):
+ #print 'sJavascript',sJavascript
+ page_value=""
+ try:
+ ss="w,i,s,e=("+sJavascript+')'
+ exec (ss)
+ page_value=self.__unpack(w,i,s,e)
+ except: traceback.print_exc(file=sys.stdout)
+ return page_value
+
+ def __unpack( self,w, i, s, e):
+ lIll = 0;
+ ll1I = 0;
+ Il1l = 0;
+ ll1l = [];
+ l1lI = [];
+ while True:
+ if (lIll < 5):
+ l1lI.append(w[lIll])
+ elif (lIll < len(w)):
+ ll1l.append(w[lIll]);
+ lIll+=1;
+ if (ll1I < 5):
+ l1lI.append(i[ll1I])
+ elif (ll1I < len(i)):
+ ll1l.append(i[ll1I])
+ ll1I+=1;
+ if (Il1l < 5):
+ l1lI.append(s[Il1l])
+ elif (Il1l < len(s)):
+ ll1l.append(s[Il1l]);
+ Il1l+=1;
+ if (len(w) + len(i) + len(s) + len(e) == len(ll1l) + len(l1lI) + len(e)):
+ break;
+
+ lI1l = ''.join(ll1l)#.join('');
+ I1lI = ''.join(l1lI)#.join('');
+ ll1I = 0;
+ l1ll = [];
+ for lIll in range(0,len(ll1l),2):
+ #print 'array i',lIll,len(ll1l)
+ ll11 = -1;
+ if ( ord(I1lI[ll1I]) % 2):
+ ll11 = 1;
+ #print 'val is ', lI1l[lIll: lIll+2]
+ l1ll.append(chr( int(lI1l[lIll: lIll+2], 36) - ll11));
+ ll1I+=1;
+ if (ll1I >= len(l1lI)):
+ ll1I = 0;
+ ret=''.join(l1ll)
+ if 'eval(function(w,i,s,e)' in ret:
+ ret=re.compile('eval\(function\(w,i,s,e\).*}\((.*?)\)').findall(ret)[0]
+ return self.unwise(ret)
+ else:
+ return ret
+
+class JsUnFunc:
+ def unFuncALL(self,data):
+ in_data = data
+ dec_data = ''
+ sPattern = r"var\s*tmp\s*=\s*s.split\(\"([^\"]+)\"\)"
+ kPattern = r"unescape\(tmp\[1\]\s*\+\s*\"([^\"]+)\"\)"
+ dataPattern = r"document.write\(\w+\(\'\'\)\s*\+\s*\'([^\']+)"
+ modPattern = r"charCodeAt\(i\)\)\+\s*([^\)]+)\)"
+
+ s_data = re.compile(sPattern).findall(in_data)
+ k_data = re.compile(kPattern).findall(in_data)
+ undc_data = re.compile(dataPattern).findall(in_data)
+ mod_data = re.compile(modPattern).findall(in_data)
+
+ sDelimiter = s_data[0]
+ s = undc_data[0]
+ tmp = urllib.unquote(s).split(sDelimiter)
+ k = tmp[1] + k_data[0]
+ mod = int(mod_data[0])
+ encData = tmp[0]
+
+ for i,d in enumerate(encData):
+ dec_data += chr((int(k[i % len(k)]) ^ ord(d)) + mod)
+
+ data = re.sub("eval\(unescape\('function.*?unescape\(''\)\);'\)\);", dec_data, in_data, count=1, flags=re.DOTALL)
+ return data
+
+ def cointainUnFunc(self,data):
+ return 'String.fromCharCode((parseInt' in data
+
+class JsUnPP:
+ def UnPPAll(self,data):
+ def removeNonAscii(s): return "".join(i for i in s if ord(i)<128)
+
+ in_data = data
+ tPattern = r"var\s*t=['\"](\w+)['\"]\s*;\s*for"
+
+ t_data = re.compile(tPattern).findall(in_data)
+
+ for i in t_data:
+ out_data = removeNonAscii(str(base64.b16decode(i.upper())))
+ data = re.sub(r"var\s*t=\"[^}]+}", out_data, data, count=1)
+
+ return data
+ def containUnPP(self,data):
+ return 'parseInt(t.substr' in data
+
+class JsUnPush:
+ def UnPush(self,data):
+ in_data = data
+ varPattern = '(var\s*\w+\s*=\s*new.*?\.push\(\'\'\);)'
+ var_data = re.compile(varPattern).findall(in_data)
+
+ charPattern = '\(\'([%0-9a-fA-F])\'\)'
+
+ chars = re.compile(charPattern).findall(var_data[0])
+ res = urllib.unquote(''.join(chars))
+ out_data=in_data.replace(var_data[0],res)
+ return out_data
+
+ def containUnPush(self,data):
+ return '.push(\'%\')' in data
+
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/mycrypt.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/mycrypt.py
new file mode 100644
index 0000000000000000000000000000000000000000..8828bb28a312b38f7292a9ae763bf45f5bd7e3c1
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/mycrypt.py
@@ -0,0 +1,504 @@
+#!/usr/bin/env python2
+#!python
+'''
+Implement openssl compatible AES-256 CBC mode encryption/decryption.
+
+This module provides encrypt() and decrypt() functions that are compatible
+with the openssl algorithms.
+
+This is basically a python encoding of my C++ work on the Cipher class
+using the Crypto.Cipher.AES class.
+
+URL: http://projects.joelinoff.com/cipher-1.1/doxydocs/html/
+'''
+
+# LICENSE
+#
+# Copyright (c) 2014 Joe Linoff
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+import argparse
+import base64
+import os
+import re
+import hashlib
+import sys
+from getpass import getpass
+try:
+ from Crypto.Cipher import AES
+except ImportError:
+ import pyaes as AES
+
+
+VERSION='1.1'
+
+
+# ================================================================
+# get_key_and_iv
+# ================================================================
+def get_key_and_iv(password, salt, klen=32, ilen=16, msgdgst='md5'):
+ '''
+ Derive the key and the IV from the given password and salt.
+
+ This is a niftier implementation than my direct transliteration of
+ the C++ code although I modified to support different digests.
+
+ CITATION: http://stackoverflow.com/questions/13907841/implement-openssl-aes-encryption-in-python
+
+ @param password The password to use as the seed.
+ @param salt The salt.
+ @param klen The key length.
+ @param ilen The initialization vector length.
+ @param msgdgst The message digest algorithm to use.
+ '''
+ # equivalent to:
+ # from hashlib import as mdf
+ # from hashlib import md5 as mdf
+ # from hashlib import sha512 as mdf
+ mdf = getattr(__import__('hashlib', fromlist=[msgdgst]), msgdgst)
+ password = password.encode('ascii','ignore') # convert to ASCII
+
+ try:
+ maxlen = klen + ilen
+ keyiv = mdf(password + salt).digest()
+ tmp = [keyiv]
+ while len(tmp) < maxlen:
+ tmp.append( mdf(tmp[-1] + password + salt).digest() )
+ keyiv += tmp[-1] # append the last byte
+ key = keyiv[:klen]
+ iv = keyiv[klen:klen+ilen]
+ return key, iv
+ except UnicodeDecodeError:
+ return None, None
+
+
+# ================================================================
+# encrypt
+# ================================================================
+def encrypt(password, plaintext, chunkit=True, msgdgst='md5'):
+ '''
+ Encrypt the plaintext using the password using an openssl
+ compatible encryption algorithm. It is the same as creating a file
+ with plaintext contents and running openssl like this:
+
+ $ cat plaintext
+
+ $ openssl enc -e -aes-256-cbc -base64 -salt \\
+ -pass pass: -n plaintext
+
+ @param password The password.
+ @param plaintext The plaintext to encrypt.
+ @param chunkit Flag that tells encrypt to split the ciphertext
+ into 64 character (MIME encoded) lines.
+ This does not affect the decrypt operation.
+ @param msgdgst The message digest algorithm.
+ '''
+ salt = os.urandom(8)
+ key, iv = get_key_and_iv(password, salt, msgdgst=msgdgst)
+ if key is None:
+ return None
+
+ # PKCS#7 padding
+ padding_len = 16 - (len(plaintext) % 16)
+ padded_plaintext = plaintext + (chr(padding_len) * padding_len)
+
+ # Encrypt
+ cipher = AES.new(key, AES.MODE_CBC, iv)
+ ciphertext = cipher.encrypt(padded_plaintext)
+
+ # Make openssl compatible.
+ # I first discovered this when I wrote the C++ Cipher class.
+ # CITATION: http://projects.joelinoff.com/cipher-1.1/doxydocs/html/
+ openssl_ciphertext = 'Salted__' + salt + ciphertext
+ b64 = base64.b64encode(openssl_ciphertext)
+ if not chunkit:
+ return b64
+
+ LINELEN = 64
+ chunk = lambda s: '\n'.join(s[i:min(i+LINELEN, len(s))]
+ for i in xrange(0, len(s), LINELEN))
+ return chunk(b64)
+
+
+# ================================================================
+# decrypt
+# ================================================================
+def decrypt(password, ciphertext, msgdgst='md5'):
+ '''
+ Decrypt the ciphertext using the password using an openssl
+ compatible decryption algorithm. It is the same as creating a file
+ with ciphertext contents and running openssl like this:
+
+ $ cat ciphertext
+ # ENCRYPTED
+
+ $ egrep -v '^#|^$' | \\
+ openssl enc -d -aes-256-cbc -base64 -salt -pass pass: -in ciphertext
+ @param password The password.
+ @param ciphertext The ciphertext to decrypt.
+ @param msgdgst The message digest algorithm.
+ @returns the decrypted data.
+ '''
+
+ # unfilter -- ignore blank lines and comments
+ filtered = ''
+ for line in ciphertext.split('\n'):
+ line = line.strip()
+ if re.search('^\s*$', line) or re.search('^\s*#', line):
+ continue
+ filtered += line + '\n'
+
+ # Base64 decode
+ raw = base64.b64decode(filtered)
+ assert( raw[:8] == 'Salted__' )
+ salt = raw[8:16] # get the salt
+
+ # Now create the key and iv.
+ key, iv = get_key_and_iv(password, salt, msgdgst=msgdgst)
+ if key is None:
+ return None
+
+ # The original ciphertext
+ ciphertext = raw[16:]
+
+ # Decrypt
+ cipher = AES.new(key, AES.MODE_CBC, iv)
+ padded_plaintext = cipher.decrypt(ciphertext)
+
+ padding_len = ord(padded_plaintext[-1])
+ plaintext = padded_plaintext[:-padding_len]
+ return plaintext
+
+
+# ================================================================
+# _open_ios
+# ================================================================
+def _open_ios(args):
+ '''
+ Open the IO files.
+ '''
+ ifp = sys.stdin
+ ofp = sys.stdout
+
+ if args.input is not None:
+ try:
+ ifp = open(args.input, 'r')
+ except IOError:
+ print 'ERROR: can\'t read file: %s' % (args.input)
+ sys.exit(1)
+
+ if args.output is not None:
+ try:
+ ifp = open(args.output, 'w')
+ except IOError:
+ print 'ERROR: can\'t write file: %s' % (args.output)
+ sys.exit(1)
+
+ return ifp, ofp
+
+
+# ================================================================
+# _close_ios
+# ================================================================
+def _close_ios(ifp, ofp):
+ '''
+ Close the IO files if necessary.
+ '''
+ if ifp != sys.stdin:
+ ifp.close()
+
+ if ofp != sys.stdout:
+ ofp.close()
+
+
+# ================================================================
+# _runenc
+# ================================================================
+def _runenc(args):
+ '''
+ Encrypt data.
+ '''
+ if args.passphrase is None:
+ while True:
+ passphrase = getpass('Passphrase: ')
+ tmp = getpass('Re-enter passphrase: ')
+ if passphrase == tmp:
+ break
+ print
+ print 'Passphrases don\'t match, please try again.'
+ else:
+ passphrase = args.passphrase
+
+ ifp, ofp = _open_ios(args)
+ text = ifp.read()
+ out = encrypt(passphrase, text, msgdgst=args.msgdgst)
+ ofp.write(out+'\n')
+ _close_ios(ifp, ofp)
+
+
+# ================================================================
+# _rundec
+# ================================================================
+def _rundec(args):
+ '''
+ Decrypt data.
+ '''
+ if args.passphrase is None:
+ passphrase = getpass('Passphrase: ')
+ else:
+ passphrase = args.passphrase
+
+ ifp, ofp = _open_ios(args)
+ text = ifp.read()
+ out = decrypt(passphrase, text, msgdgst=args.msgdgst)
+ ofp.write(out)
+ _close_ios(ifp, ofp)
+
+
+# ================================================================
+# _runtest
+# ================================================================
+def _runtest(args):
+ '''
+ Run a series of iteration where each iteration generates a random
+ password from 8-32 characters and random text from 20 to 256
+ characters. The encrypts and decrypts the random data. It then
+ compares the results to make sure that everything works correctly.
+
+ The test output looks like this:
+
+ $ crypt 2000
+ 2000 of 2000 100.00% 15 139 2000 0
+ $ # ^ ^ ^ ^ ^ ^
+ $ # | | | | | +-- num failed
+ $ # | | | | +---------- num passed
+ $ # | | | +-------------- size of text for a test
+ $ # | | +----------------- size of passphrase for a test
+ $ # | +-------------------------- percent completed
+ $ # +------------------------------- total
+ # #+------------------------------------ current test
+
+ @param args The args parse arguments.
+ '''
+ import string
+ import random
+ from random import randint
+
+ # Encrypt/decrypt N random sets of plaintext and passwords.
+ num = args.test
+ ofp = sys.stdout
+ if args.output is not None:
+ try:
+ ofp = open(args.output, 'w')
+ except IOError:
+ print 'ERROR: can open file for writing: %s' % (args.output)
+ sys.exit(1)
+
+ chset = string.printable
+ passed = 0
+ failed = []
+ maxlen = len(str(num))
+ for i in range(num):
+ ran1 = randint(8,32)
+ password = ''.join(random.choice(chset) for x in range(ran1))
+
+ ran2 = randint(20, 256)
+ plaintext = ''.join(random.choice(chset) for x in range(ran2))
+
+ ciphertext = encrypt(password, plaintext, msgdgst=args.msgdgst)
+ verification = decrypt(password, ciphertext, msgdgst=args.msgdgst)
+
+ if plaintext != verification:
+ failed.append( [password, plaintext] )
+ else:
+ passed += 1
+
+ output = '%*d of %d %6.2f%% %3d %3d %*d %*d %s' % (maxlen,i+1,
+ num,
+ 100*(i+1)/num,
+ len(password),
+ len(plaintext),
+ maxlen, passed,
+ maxlen, len(failed),
+ args.msgdgst)
+ if args.output is None:
+ ofp.write('\b'*80)
+ ofp.write(output)
+ ofp.flush()
+ else:
+ ofp.write(output+'\n')
+
+ ofp.write('\n')
+
+ if len(failed):
+ for i in range(len(failed)):
+ ofp.write('%3d %2d %-34s %3d %s\n' % (i,
+ len(failed[i][0]),
+ '"'+failed[i][0]+'"',
+ len(failed[i][1]),
+ '"'+failed[i][1]+'"'))
+ ofp.write('\n')
+
+ if args.output is not None:
+ ofp.close()
+
+
+# ================================================================
+# _cli_opts
+# ================================================================
+def _cli_opts():
+ '''
+ Parse command line options.
+ @returns the arguments
+ '''
+ mepath = unicode(os.path.abspath(sys.argv[0]))
+ mebase = '%s' % (os.path.basename(mepath))
+
+ description = '''
+Implements encryption/decryption that is compatible with openssl
+AES-256 CBC mode.
+
+You can use it as follows:
+
+ EXAMPLE 1: %s -> %s (MD5)
+ $ # Encrypt and decrypt using %s.
+ $ echo 'Lorem ipsum dolor sit amet' | \\
+ %s -e -p secret | \\
+ %s -d -p secret
+ Lorem ipsum dolor sit amet
+
+ EXAMPLE 2: %s -> openssl (MD5)
+ $ # Encrypt using %s and decrypt using openssl.
+ $ echo 'Lorem ipsum dolor sit amet' | \\
+ %s -e -p secret | \\
+ openssl enc -d -aes-256-cbc -md md5 -base64 -salt -pass pass:secret
+ Lorem ipsum dolor sit amet
+
+ EXAMPLE 3: openssl -> %s (MD5)
+ $ # Encrypt using openssl and decrypt using %s
+ $ echo 'Lorem ipsum dolor sit amet' | \\
+ openssl enc -e -aes-256-cbc -md md5 -base64 -salt -pass pass:secret
+ %s -d -p secret
+ Lorem ipsum dolor sit amet
+
+ EXAMPLE 4: openssl -> openssl (MD5)
+ $ # Encrypt and decrypt using openssl
+ $ echo 'Lorem ipsum dolor sit amet' | \\
+ openssl enc -e -aes-256-cbc -md md5 -base64 -salt -pass pass:secret
+ openssl enc -d -aes-256-cbc -md md5 -base64 -salt -pass pass:secret
+ Lorem ipsum dolor sit amet
+
+ EXAMPLE 5: %s -> %s (SHA512)
+ $ # Encrypt and decrypt using %s.
+ $ echo 'Lorem ipsum dolor sit amet' | \\
+ %s -e -m sha512 -p secret | \\
+ %s -d -m sha512 -p secret
+ Lorem ipsum dolor sit amet
+
+ EXAMPLE 6: %s -> openssl (SHA512)
+ $ # Encrypt using %s and decrypt using openssl.
+ $ echo 'Lorem ipsum dolor sit amet' | \\
+ %s -e -m sha512 -p secret | \\
+ openssl enc -d -aes-256-cbc -md sha1=512 -base64 -salt -pass pass:secret
+ Lorem ipsum dolor sit amet
+
+ EXAMPLE 7:
+ $ # Run internal tests.
+ $ %s -t 2000
+ 2000 of 2000 100.00%% 21 104 2000 0 md5
+ $ # ^ ^ ^ ^ ^ ^ ^
+ $ # | | | | | | +- message digest
+ $ # | | | | | +--- num failed
+ $ # | | | | +----------- num passed
+ $ # | | | +--------------- size of text for a test
+ $ # | | +------------------ size of passphrase for a test
+ $ # | +--------------------------- percent completed
+ $ # +-------------------------------- total
+ # #+------------------------------------- current test
+''' % (mebase, mebase, mebase, mebase,
+ mebase, mebase, mebase, mebase,
+ mebase, mebase, mebase, mebase,
+ mebase, mebase, mebase, mebase,
+ mebase, mebase, mebase, mebase,
+ )
+
+ parser = argparse.ArgumentParser(prog=mebase,
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ description=description,
+ )
+
+ group = parser.add_mutually_exclusive_group(required=True)
+ group.add_argument('-d', '--decrypt',
+ action='store_true',
+ help='decryption mode')
+ group.add_argument('-e', '--encrypt',
+ action='store_true',
+ help='encryption mode')
+ parser.add_argument('-i', '--input',
+ action='store',
+ help='input file, default is stdin')
+ parser.add_argument('-m', '--msgdgst',
+ action='store',
+ default='md5',
+ help='message digest (md5, sha, sha1, sha256, sha512), default is md5')
+ parser.add_argument('-o', '--output',
+ action='store',
+ help='output file, default is stdout')
+ parser.add_argument('-p', '--passphrase',
+ action='store',
+ help='passphrase for encrypt/decrypt operations')
+ group.add_argument('-t', '--test',
+ action='store',
+ default=-1,
+ type=int,
+ help='test mode (TEST is an integer)')
+ parser.add_argument('-v', '--verbose',
+ action='count',
+ help='the level of verbosity')
+ parser.add_argument('-V', '--version',
+ action='version',
+ version='%(prog)s '+VERSION)
+
+ args = parser.parse_args()
+ return args
+
+
+# ================================================================
+# main
+# ================================================================
+def main():
+ args = _cli_opts()
+ if args.test > 0:
+ if args.input is not None:
+ print 'WARNING: input argument will be ignored.'
+ if args.passphrase is not None:
+ print 'WARNING: passphrase argument will be ignored.'
+ _runtest(args)
+ elif args.encrypt:
+ _runenc(args)
+ elif args.decrypt:
+ _rundec(args)
+
+
+# ================================================================
+# MAIN
+# ================================================================
+if __name__ == "__main__":
+ main()
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/pyDes.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/pyDes.py
new file mode 100644
index 0000000000000000000000000000000000000000..bff0e0b744c2fe568b4b9805551f11a813684623
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/pyDes.py
@@ -0,0 +1,853 @@
+#############################################################################
+# Documentation #
+#############################################################################
+
+# Author: Todd Whiteman
+# Date: 16th March, 2009
+# Verion: 2.0.0
+# License: Public Domain - free to do as you wish
+# Homepage: http://twhiteman.netfirms.com/des.html
+#
+# This is a pure python implementation of the DES encryption algorithm.
+# It's pure python to avoid portability issues, since most DES
+# implementations are programmed in C (for performance reasons).
+#
+# Triple DES class is also implemented, utilising the DES base. Triple DES
+# is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key.
+#
+# See the README.txt that should come with this python module for the
+# implementation methods used.
+#
+# Thanks to:
+# * David Broadwell for ideas, comments and suggestions.
+# * Mario Wolff for pointing out and debugging some triple des CBC errors.
+# * Santiago Palladino for providing the PKCS5 padding technique.
+# * Shaya for correcting the PAD_PKCS5 triple des CBC errors.
+#
+"""A pure python implementation of the DES and TRIPLE DES encryption algorithms.
+
+Class initialization
+--------------------
+pyDes.des(key, [mode], [IV], [pad], [padmode])
+pyDes.triple_des(key, [mode], [IV], [pad], [padmode])
+
+key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes
+ for Triple DES
+mode -> Optional argument for encryption type, can be either
+ pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining)
+IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
+ Length must be 8 bytes.
+pad -> Optional argument, set the pad character (PAD_NORMAL) to use during
+ all encrypt/decrpt operations done with this instance.
+padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5)
+ to use during all encrypt/decrpt operations done with this instance.
+
+I recommend to use PAD_PKCS5 padding, as then you never need to worry about any
+padding issues, as the padding can be removed unambiguously upon decrypting
+data that was encrypted using PAD_PKCS5 padmode.
+
+Common methods
+--------------
+encrypt(data, [pad], [padmode])
+decrypt(data, [pad], [padmode])
+
+data -> Bytes to be encrypted/decrypted
+pad -> Optional argument. Only when using padmode of PAD_NORMAL. For
+ encryption, adds this characters to the end of the data block when
+ data is not a multiple of 8 bytes. For decryption, will remove the
+ trailing characters that match this pad character from the last 8
+ bytes of the unencrypted data block.
+padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL
+ or PAD_PKCS5). Defaults to PAD_NORMAL.
+
+
+Example
+-------
+from pyDes import *
+
+data = "Please encrypt my data"
+k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
+# For Python3, you'll need to use bytes, i.e.:
+# data = b"Please encrypt my data"
+# k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
+d = k.encrypt(data)
+print "Encrypted: %r" % d
+print "Decrypted: %r" % k.decrypt(d)
+assert k.decrypt(d, padmode=PAD_PKCS5) == data
+
+
+See the module source (pyDes.py) for more examples of use.
+You can also run the pyDes.py file without and arguments to see a simple test.
+
+Note: This code was not written for high-end systems needing a fast
+ implementation, but rather a handy portable solution with small usage.
+
+"""
+
+import sys
+
+
+# _pythonMajorVersion is used to handle Python2 and Python3 differences.
+_pythonMajorVersion = sys.version_info[0]
+
+# Modes of crypting / cyphering
+ECB = 0
+CBC = 1
+
+# Modes of padding
+PAD_NORMAL = 1
+PAD_PKCS5 = 2
+
+# PAD_PKCS5: is a method that will unambiguously remove all padding
+# characters after decryption, when originally encrypted with
+# this padding mode.
+# For a good description of the PKCS5 padding technique, see:
+# http://www.faqs.org/rfcs/rfc1423.html
+
+# The base class shared by des and triple des.
+class _baseDes(object):
+ def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
+ if IV:
+ IV = self._guardAgainstUnicode(IV)
+ if pad:
+ pad = self._guardAgainstUnicode(pad)
+ self.block_size = 8
+ # Sanity checking of arguments.
+ if pad and padmode == PAD_PKCS5:
+ raise ValueError("Cannot use a pad character with PAD_PKCS5")
+ if IV and len(IV) != self.block_size:
+ raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
+
+ # Set the passed in variables
+ self._mode = mode
+ self._iv = IV
+ self._padding = pad
+ self._padmode = padmode
+
+ def getKey(self):
+ """getKey() -> bytes"""
+ return self.__key
+
+ def setKey(self, key):
+ """Will set the crypting key for this object."""
+ key = self._guardAgainstUnicode(key)
+ self.__key = key
+
+ def getMode(self):
+ """getMode() -> pyDes.ECB or pyDes.CBC"""
+ return self._mode
+
+ def setMode(self, mode):
+ """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
+ self._mode = mode
+
+ def getPadding(self):
+ """getPadding() -> bytes of length 1. Padding character."""
+ return self._padding
+
+ def setPadding(self, pad):
+ """setPadding() -> bytes of length 1. Padding character."""
+ if pad is not None:
+ pad = self._guardAgainstUnicode(pad)
+ self._padding = pad
+
+ def getPadMode(self):
+ """getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
+ return self._padmode
+
+ def setPadMode(self, mode):
+ """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
+ self._padmode = mode
+
+ def getIV(self):
+ """getIV() -> bytes"""
+ return self._iv
+
+ def setIV(self, IV):
+ """Will set the Initial Value, used in conjunction with CBC mode"""
+ if not IV or len(IV) != self.block_size:
+ raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
+ IV = self._guardAgainstUnicode(IV)
+ self._iv = IV
+
+ def _padData(self, data, pad, padmode):
+ # Pad data depending on the mode
+ if padmode is None:
+ # Get the default padding mode.
+ padmode = self.getPadMode()
+ if pad and padmode == PAD_PKCS5:
+ raise ValueError("Cannot use a pad character with PAD_PKCS5")
+
+ if padmode == PAD_NORMAL:
+ if len(data) % self.block_size == 0:
+ # No padding required.
+ return data
+
+ if not pad:
+ # Get the default padding.
+ pad = self.getPadding()
+ if not pad:
+ raise ValueError("Data must be a multiple of " + str(self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.")
+ data += (self.block_size - (len(data) % self.block_size)) * pad
+
+ elif padmode == PAD_PKCS5:
+ pad_len = 8 - (len(data) % self.block_size)
+ if _pythonMajorVersion < 3:
+ data += pad_len * chr(pad_len)
+ else:
+ data += bytes([pad_len] * pad_len)
+
+ return data
+
+ def _unpadData(self, data, pad, padmode):
+ # Unpad data depending on the mode.
+ if not data:
+ return data
+ if pad and padmode == PAD_PKCS5:
+ raise ValueError("Cannot use a pad character with PAD_PKCS5")
+ if padmode is None:
+ # Get the default padding mode.
+ padmode = self.getPadMode()
+
+ if padmode == PAD_NORMAL:
+ if not pad:
+ # Get the default padding.
+ pad = self.getPadding()
+ if pad:
+ data = data[:-self.block_size] + \
+ data[-self.block_size:].rstrip(pad)
+
+ elif padmode == PAD_PKCS5:
+ if _pythonMajorVersion < 3:
+ pad_len = ord(data[-1])
+ else:
+ pad_len = data[-1]
+ data = data[:-pad_len]
+
+ return data
+
+ def _guardAgainstUnicode(self, data):
+ # Only accept byte strings or ascii unicode values, otherwise
+ # there is no way to correctly decode the data into bytes.
+ if _pythonMajorVersion < 3:
+ if isinstance(data, unicode):
+ raise ValueError("pyDes can only work with bytes, not Unicode strings.")
+ else:
+ if isinstance(data, str):
+ # Only accept ascii unicode values.
+ try:
+ return data.encode('ascii')
+ except UnicodeEncodeError:
+ pass
+ raise ValueError("pyDes can only work with encoded strings, not Unicode.")
+ return data
+
+#############################################################################
+# DES #
+#############################################################################
+class des(_baseDes):
+ """DES encryption/decrytpion class
+
+ Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.
+
+ pyDes.des(key,[mode], [IV])
+
+ key -> Bytes containing the encryption key, must be exactly 8 bytes
+ mode -> Optional argument for encryption type, can be either pyDes.ECB
+ (Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
+ IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
+ Must be 8 bytes in length.
+ pad -> Optional argument, set the pad character (PAD_NORMAL) to use
+ during all encrypt/decrpt operations done with this instance.
+ padmode -> Optional argument, set the padding mode (PAD_NORMAL or
+ PAD_PKCS5) to use during all encrypt/decrpt operations done
+ with this instance.
+ """
+
+
+ # Permutation and translation tables for DES
+ __pc1 = [56, 48, 40, 32, 24, 16, 8,
+ 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26,
+ 18, 10, 2, 59, 51, 43, 35,
+ 62, 54, 46, 38, 30, 22, 14,
+ 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28,
+ 20, 12, 4, 27, 19, 11, 3
+ ]
+
+ # number left rotations of pc1
+ __left_rotations = [
+ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
+ ]
+
+ # permuted choice key (table 2)
+ __pc2 = [
+ 13, 16, 10, 23, 0, 4,
+ 2, 27, 14, 5, 20, 9,
+ 22, 18, 11, 3, 25, 7,
+ 15, 6, 26, 19, 12, 1,
+ 40, 51, 30, 36, 46, 54,
+ 29, 39, 50, 44, 32, 47,
+ 43, 48, 38, 55, 33, 52,
+ 45, 41, 49, 35, 28, 31
+ ]
+
+ # initial permutation IP
+ __ip = [57, 49, 41, 33, 25, 17, 9, 1,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5,
+ 63, 55, 47, 39, 31, 23, 15, 7,
+ 56, 48, 40, 32, 24, 16, 8, 0,
+ 58, 50, 42, 34, 26, 18, 10, 2,
+ 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6
+ ]
+
+ # Expansion table for turning 32 bit blocks into 48 bits
+ __expansion_table = [
+ 31, 0, 1, 2, 3, 4,
+ 3, 4, 5, 6, 7, 8,
+ 7, 8, 9, 10, 11, 12,
+ 11, 12, 13, 14, 15, 16,
+ 15, 16, 17, 18, 19, 20,
+ 19, 20, 21, 22, 23, 24,
+ 23, 24, 25, 26, 27, 28,
+ 27, 28, 29, 30, 31, 0
+ ]
+
+ # The (in)famous S-boxes
+ __sbox = [
+ # S1
+ [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
+ 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
+ 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
+ 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],
+
+ # S2
+ [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
+ 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
+ 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
+ 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],
+
+ # S3
+ [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
+ 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
+ 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
+ 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],
+
+ # S4
+ [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
+ 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
+ 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
+ 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
+
+ # S5
+ [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
+ 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
+ 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
+ 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],
+
+ # S6
+ [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
+ 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
+ 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
+ 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],
+
+ # S7
+ [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
+ 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
+ 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
+ 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],
+
+ # S8
+ [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
+ 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
+ 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
+ 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11],
+ ]
+
+
+ # 32-bit permutation function P used on the output of the S-boxes
+ __p = [
+ 15, 6, 19, 20, 28, 11,
+ 27, 16, 0, 14, 22, 25,
+ 4, 17, 30, 9, 1, 7,
+ 23,13, 31, 26, 2, 8,
+ 18, 12, 29, 5, 21, 10,
+ 3, 24
+ ]
+
+ # final permutation IP^-1
+ __fp = [
+ 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30,
+ 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28,
+ 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26,
+ 33, 1, 41, 9, 49, 17, 57, 25,
+ 32, 0, 40, 8, 48, 16, 56, 24
+ ]
+
+ # Type of crypting being done
+ ENCRYPT = 0x00
+ DECRYPT = 0x01
+
+ # Initialisation
+ def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
+ # Sanity checking of arguments.
+ if len(key) != 8:
+ raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.")
+ _baseDes.__init__(self, mode, IV, pad, padmode)
+ self.key_size = 8
+
+ self.L = []
+ self.R = []
+ self.Kn = [ [0] * 48 ] * 16 # 16 48-bit keys (K1 - K16)
+ self.final = []
+
+ self.setKey(key)
+
+ def setKey(self, key):
+ """Will set the crypting key for this object. Must be 8 bytes."""
+ _baseDes.setKey(self, key)
+ self.__create_sub_keys()
+
+ def __String_to_BitList(self, data):
+ """Turn the string data, into a list of bits (1, 0)'s"""
+ if _pythonMajorVersion < 3:
+ # Turn the strings into integers. Python 3 uses a bytes
+ # class, which already has this behaviour.
+ data = [ord(c) for c in data]
+ l = len(data) * 8
+ result = [0] * l
+ pos = 0
+ for ch in data:
+ i = 7
+ while i >= 0:
+ if ch & (1 << i) != 0:
+ result[pos] = 1
+ else:
+ result[pos] = 0
+ pos += 1
+ i -= 1
+
+ return result
+
+ def __BitList_to_String(self, data):
+ """Turn the list of bits -> data, into a string"""
+ result = []
+ pos = 0
+ c = 0
+ while pos < len(data):
+ c += data[pos] << (7 - (pos % 8))
+ if (pos % 8) == 7:
+ result.append(c)
+ c = 0
+ pos += 1
+
+ if _pythonMajorVersion < 3:
+ return ''.join([ chr(c) for c in result ])
+ else:
+ return bytes(result)
+
+ def __permutate(self, table, block):
+ """Permutate this block with the specified table"""
+ return list(map(lambda x: block[x], table))
+
+ # Transform the secret key, so that it is ready for data processing
+ # Create the 16 subkeys, K[1] - K[16]
+ def __create_sub_keys(self):
+ """Create the 16 subkeys K[1] to K[16] from the given key"""
+ key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey()))
+ i = 0
+ # Split into Left and Right sections
+ self.L = key[:28]
+ self.R = key[28:]
+ while i < 16:
+ j = 0
+ # Perform circular left shifts
+ while j < des.__left_rotations[i]:
+ self.L.append(self.L[0])
+ del self.L[0]
+
+ self.R.append(self.R[0])
+ del self.R[0]
+
+ j += 1
+
+ # Create one of the 16 subkeys through pc2 permutation
+ self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R)
+
+ i += 1
+
+ # Main part of the encryption algorithm, the number cruncher :)
+ def __des_crypt(self, block, crypt_type):
+ """Crypt the block of data through DES bit-manipulation"""
+ block = self.__permutate(des.__ip, block)
+ self.L = block[:32]
+ self.R = block[32:]
+
+ # Encryption starts from Kn[1] through to Kn[16]
+ if crypt_type == des.ENCRYPT:
+ iteration = 0
+ iteration_adjustment = 1
+ # Decryption starts from Kn[16] down to Kn[1]
+ else:
+ iteration = 15
+ iteration_adjustment = -1
+
+ i = 0
+ while i < 16:
+ # Make a copy of R[i-1], this will later become L[i]
+ tempR = self.R[:]
+
+ # Permutate R[i - 1] to start creating R[i]
+ self.R = self.__permutate(des.__expansion_table, self.R)
+
+ # Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here
+ self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration]))
+ B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]]
+ # Optimization: Replaced below commented code with above
+ #j = 0
+ #B = []
+ #while j < len(self.R):
+ # self.R[j] = self.R[j] ^ self.Kn[iteration][j]
+ # j += 1
+ # if j % 6 == 0:
+ # B.append(self.R[j-6:j])
+
+ # Permutate B[1] to B[8] using the S-Boxes
+ j = 0
+ Bn = [0] * 32
+ pos = 0
+ while j < 8:
+ # Work out the offsets
+ m = (B[j][0] << 1) + B[j][5]
+ n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4]
+
+ # Find the permutation value
+ v = des.__sbox[j][(m << 4) + n]
+
+ # Turn value into bits, add it to result: Bn
+ Bn[pos] = (v & 8) >> 3
+ Bn[pos + 1] = (v & 4) >> 2
+ Bn[pos + 2] = (v & 2) >> 1
+ Bn[pos + 3] = v & 1
+
+ pos += 4
+ j += 1
+
+ # Permutate the concatination of B[1] to B[8] (Bn)
+ self.R = self.__permutate(des.__p, Bn)
+
+ # Xor with L[i - 1]
+ self.R = list(map(lambda x, y: x ^ y, self.R, self.L))
+ # Optimization: This now replaces the below commented code
+ #j = 0
+ #while j < len(self.R):
+ # self.R[j] = self.R[j] ^ self.L[j]
+ # j += 1
+
+ # L[i] becomes R[i - 1]
+ self.L = tempR
+
+ i += 1
+ iteration += iteration_adjustment
+
+ # Final permutation of R[16]L[16]
+ self.final = self.__permutate(des.__fp, self.R + self.L)
+ return self.final
+
+
+ # Data to be encrypted/decrypted
+ def crypt(self, data, crypt_type):
+ """Crypt the data in blocks, running it through des_crypt()"""
+
+ # Error check the data
+ if not data:
+ return ''
+ if len(data) % self.block_size != 0:
+ if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks
+ raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.")
+ if not self.getPadding():
+ raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n. Try setting the optional padding character")
+ else:
+ data += (self.block_size - (len(data) % self.block_size)) * self.getPadding()
+ # print "Len of data: %f" % (len(data) / self.block_size)
+
+ if self.getMode() == CBC:
+ if self.getIV():
+ iv = self.__String_to_BitList(self.getIV())
+ else:
+ raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering")
+
+ # Split the data into blocks, crypting each one seperately
+ i = 0
+ dict = {}
+ result = []
+ #cached = 0
+ #lines = 0
+ while i < len(data):
+ # Test code for caching encryption results
+ #lines += 1
+ #if dict.has_key(data[i:i+8]):
+ #print "Cached result for: %s" % data[i:i+8]
+ # cached += 1
+ # result.append(dict[data[i:i+8]])
+ # i += 8
+ # continue
+
+ block = self.__String_to_BitList(data[i:i+8])
+
+ # Xor with IV if using CBC mode
+ if self.getMode() == CBC:
+ if crypt_type == des.ENCRYPT:
+ block = list(map(lambda x, y: x ^ y, block, iv))
+ #j = 0
+ #while j < len(block):
+ # block[j] = block[j] ^ iv[j]
+ # j += 1
+
+ processed_block = self.__des_crypt(block, crypt_type)
+
+ if crypt_type == des.DECRYPT:
+ processed_block = list(map(lambda x, y: x ^ y, processed_block, iv))
+ #j = 0
+ #while j < len(processed_block):
+ # processed_block[j] = processed_block[j] ^ iv[j]
+ # j += 1
+ iv = block
+ else:
+ iv = processed_block
+ else:
+ processed_block = self.__des_crypt(block, crypt_type)
+
+
+ # Add the resulting crypted block to our list
+ #d = self.__BitList_to_String(processed_block)
+ #result.append(d)
+ result.append(self.__BitList_to_String(processed_block))
+ #dict[data[i:i+8]] = d
+ i += 8
+
+ # print "Lines: %d, cached: %d" % (lines, cached)
+
+ # Return the full crypted string
+ if _pythonMajorVersion < 3:
+ return ''.join(result)
+ else:
+ return bytes.fromhex('').join(result)
+
+ def encrypt(self, data, pad=None, padmode=None):
+ """encrypt(data, [pad], [padmode]) -> bytes
+
+ data : Bytes to be encrypted
+ pad : Optional argument for encryption padding. Must only be one byte
+ padmode : Optional argument for overriding the padding mode.
+
+ The data must be a multiple of 8 bytes and will be encrypted
+ with the already specified key. Data does not have to be a
+ multiple of 8 bytes if the padding character is supplied, or
+ the padmode is set to PAD_PKCS5, as bytes will then added to
+ ensure the be padded data is a multiple of 8 bytes.
+ """
+ data = self._guardAgainstUnicode(data)
+ if pad is not None:
+ pad = self._guardAgainstUnicode(pad)
+ data = self._padData(data, pad, padmode)
+ return self.crypt(data, des.ENCRYPT)
+
+ def decrypt(self, data, pad=None, padmode=None):
+ """decrypt(data, [pad], [padmode]) -> bytes
+
+ data : Bytes to be encrypted
+ pad : Optional argument for decryption padding. Must only be one byte
+ padmode : Optional argument for overriding the padding mode.
+
+ The data must be a multiple of 8 bytes and will be decrypted
+ with the already specified key. In PAD_NORMAL mode, if the
+ optional padding character is supplied, then the un-encrypted
+ data will have the padding characters removed from the end of
+ the bytes. This pad removal only occurs on the last 8 bytes of
+ the data (last data block). In PAD_PKCS5 mode, the special
+ padding end markers will be removed from the data after decrypting.
+ """
+ data = self._guardAgainstUnicode(data)
+ if pad is not None:
+ pad = self._guardAgainstUnicode(pad)
+ data = self.crypt(data, des.DECRYPT)
+ return self._unpadData(data, pad, padmode)
+
+
+
+#############################################################################
+# Triple DES #
+#############################################################################
+class triple_des(_baseDes):
+ """Triple DES encryption/decrytpion class
+
+ This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or
+ the DES-EDE2 (when a 16 byte key is supplied) encryption methods.
+ Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.
+
+ pyDes.des(key, [mode], [IV])
+
+ key -> Bytes containing the encryption key, must be either 16 or
+ 24 bytes long
+ mode -> Optional argument for encryption type, can be either pyDes.ECB
+ (Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
+ IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
+ Must be 8 bytes in length.
+ pad -> Optional argument, set the pad character (PAD_NORMAL) to use
+ during all encrypt/decrpt operations done with this instance.
+ padmode -> Optional argument, set the padding mode (PAD_NORMAL or
+ PAD_PKCS5) to use during all encrypt/decrpt operations done
+ with this instance.
+ """
+ def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
+ _baseDes.__init__(self, mode, IV, pad, padmode)
+ self.setKey(key)
+
+ def setKey(self, key):
+ """Will set the crypting key for this object. Either 16 or 24 bytes long."""
+ self.key_size = 24 # Use DES-EDE3 mode
+ if len(key) != self.key_size:
+ if len(key) == 16: # Use DES-EDE2 mode
+ self.key_size = 16
+ else:
+ raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long")
+ if self.getMode() == CBC:
+ if not self.getIV():
+ # Use the first 8 bytes of the key
+ self._iv = key[:self.block_size]
+ if len(self.getIV()) != self.block_size:
+ raise ValueError("Invalid IV, must be 8 bytes in length")
+ self.__key1 = des(key[:8], self._mode, self._iv,
+ self._padding, self._padmode)
+ self.__key2 = des(key[8:16], self._mode, self._iv,
+ self._padding, self._padmode)
+ if self.key_size == 16:
+ self.__key3 = self.__key1
+ else:
+ self.__key3 = des(key[16:], self._mode, self._iv,
+ self._padding, self._padmode)
+ _baseDes.setKey(self, key)
+
+ # Override setter methods to work on all 3 keys.
+
+ def setMode(self, mode):
+ """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
+ _baseDes.setMode(self, mode)
+ for key in (self.__key1, self.__key2, self.__key3):
+ key.setMode(mode)
+
+ def setPadding(self, pad):
+ """setPadding() -> bytes of length 1. Padding character."""
+ _baseDes.setPadding(self, pad)
+ for key in (self.__key1, self.__key2, self.__key3):
+ key.setPadding(pad)
+
+ def setPadMode(self, mode):
+ """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
+ _baseDes.setPadMode(self, mode)
+ for key in (self.__key1, self.__key2, self.__key3):
+ key.setPadMode(mode)
+
+ def setIV(self, IV):
+ """Will set the Initial Value, used in conjunction with CBC mode"""
+ _baseDes.setIV(self, IV)
+ for key in (self.__key1, self.__key2, self.__key3):
+ key.setIV(IV)
+
+ def encrypt(self, data, pad=None, padmode=None):
+ """encrypt(data, [pad], [padmode]) -> bytes
+
+ data : bytes to be encrypted
+ pad : Optional argument for encryption padding. Must only be one byte
+ padmode : Optional argument for overriding the padding mode.
+
+ The data must be a multiple of 8 bytes and will be encrypted
+ with the already specified key. Data does not have to be a
+ multiple of 8 bytes if the padding character is supplied, or
+ the padmode is set to PAD_PKCS5, as bytes will then added to
+ ensure the be padded data is a multiple of 8 bytes.
+ """
+ ENCRYPT = des.ENCRYPT
+ DECRYPT = des.DECRYPT
+ data = self._guardAgainstUnicode(data)
+ if pad is not None:
+ pad = self._guardAgainstUnicode(pad)
+ # Pad the data accordingly.
+ data = self._padData(data, pad, padmode)
+ if self.getMode() == CBC:
+ self.__key1.setIV(self.getIV())
+ self.__key2.setIV(self.getIV())
+ self.__key3.setIV(self.getIV())
+ i = 0
+ result = []
+ while i < len(data):
+ block = self.__key1.crypt(data[i:i+8], ENCRYPT)
+ block = self.__key2.crypt(block, DECRYPT)
+ block = self.__key3.crypt(block, ENCRYPT)
+ self.__key1.setIV(block)
+ self.__key2.setIV(block)
+ self.__key3.setIV(block)
+ result.append(block)
+ i += 8
+ if _pythonMajorVersion < 3:
+ return ''.join(result)
+ else:
+ return bytes.fromhex('').join(result)
+ else:
+ data = self.__key1.crypt(data, ENCRYPT)
+ data = self.__key2.crypt(data, DECRYPT)
+ return self.__key3.crypt(data, ENCRYPT)
+
+ def decrypt(self, data, pad=None, padmode=None):
+ """decrypt(data, [pad], [padmode]) -> bytes
+
+ data : bytes to be encrypted
+ pad : Optional argument for decryption padding. Must only be one byte
+ padmode : Optional argument for overriding the padding mode.
+
+ The data must be a multiple of 8 bytes and will be decrypted
+ with the already specified key. In PAD_NORMAL mode, if the
+ optional padding character is supplied, then the un-encrypted
+ data will have the padding characters removed from the end of
+ the bytes. This pad removal only occurs on the last 8 bytes of
+ the data (last data block). In PAD_PKCS5 mode, the special
+ padding end markers will be removed from the data after
+ decrypting, no pad character is required for PAD_PKCS5.
+ """
+ ENCRYPT = des.ENCRYPT
+ DECRYPT = des.DECRYPT
+ data = self._guardAgainstUnicode(data)
+ if pad is not None:
+ pad = self._guardAgainstUnicode(pad)
+ if self.getMode() == CBC:
+ self.__key1.setIV(self.getIV())
+ self.__key2.setIV(self.getIV())
+ self.__key3.setIV(self.getIV())
+ i = 0
+ result = []
+ while i < len(data):
+ iv = data[i:i+8]
+ block = self.__key3.crypt(iv, DECRYPT)
+ block = self.__key2.crypt(block, ENCRYPT)
+ block = self.__key1.crypt(block, DECRYPT)
+ self.__key1.setIV(iv)
+ self.__key2.setIV(iv)
+ self.__key3.setIV(iv)
+ result.append(block)
+ i += 8
+ if _pythonMajorVersion < 3:
+ data = ''.join(result)
+ else:
+ data = bytes.fromhex('').join(result)
+ else:
+ data = self.__key3.crypt(data, DECRYPT)
+ data = self.__key2.crypt(data, ENCRYPT)
+ data = self.__key1.crypt(data, DECRYPT)
+ return self._unpadData(data, pad, padmode)
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/pyaes.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/pyaes.py
new file mode 100644
index 0000000000000000000000000000000000000000..cb543d276abe83e3f13c044d6c9f28d161b2e14a
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/pyaes.py
@@ -0,0 +1,502 @@
+"""Simple AES cipher implementation in pure Python following PEP-272 API
+
+Homepage: https://bitbucket.org/intgr/pyaes/
+
+The goal of this module is to be as fast as reasonable in Python while still
+being Pythonic and readable/understandable. It is licensed under the permissive
+MIT license.
+
+Hopefully the code is readable and commented enough that it can serve as an
+introduction to the AES cipher for Python coders. In fact, it should go along
+well with the Stick Figure Guide to AES:
+http://www.moserware.com/2009/09/stick-figure-guide-to-advanced.html
+
+Contrary to intuition, this implementation numbers the 4x4 matrices from top to
+bottom for efficiency reasons::
+
+ 0 4 8 12
+ 1 5 9 13
+ 2 6 10 14
+ 3 7 11 15
+
+Effectively it's the transposition of what you'd expect. This actually makes
+the code simpler -- except the ShiftRows step, but hopefully the explanation
+there clears it up.
+
+"""
+
+####
+# Copyright (c) 2010 Marti Raudsepp
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+####
+
+
+from array import array
+
+# Globals mandated by PEP 272:
+# http://www.python.org/dev/peps/pep-0272/
+MODE_ECB = 1
+MODE_CBC = 2
+#MODE_CTR = 6
+
+block_size = 16
+key_size = None
+
+def new(key, mode, IV=None):
+ if mode == MODE_ECB:
+ return ECBMode(AES(key))
+ elif mode == MODE_CBC:
+ if IV is None:
+ raise ValueError, "CBC mode needs an IV value!"
+
+ return CBCMode(AES(key), IV)
+ else:
+ raise NotImplementedError
+
+#### AES cipher implementation
+
+class AES(object):
+ block_size = 16
+
+ def __init__(self, key):
+ self.setkey(key)
+
+ def setkey(self, key):
+ """Sets the key and performs key expansion."""
+
+ self.key = key
+ self.key_size = len(key)
+
+ if self.key_size == 16:
+ self.rounds = 10
+ elif self.key_size == 24:
+ self.rounds = 12
+ elif self.key_size == 32:
+ self.rounds = 14
+ else:
+ raise ValueError, "Key length must be 16, 24 or 32 bytes"
+
+ self.expand_key()
+
+ def expand_key(self):
+ """Performs AES key expansion on self.key and stores in self.exkey"""
+
+ # The key schedule specifies how parts of the key are fed into the
+ # cipher's round functions. "Key expansion" means performing this
+ # schedule in advance. Almost all implementations do this.
+ #
+ # Here's a description of AES key schedule:
+ # http://en.wikipedia.org/wiki/Rijndael_key_schedule
+
+ # The expanded key starts with the actual key itself
+ exkey = array('B', self.key)
+
+ # extra key expansion steps
+ if self.key_size == 16:
+ extra_cnt = 0
+ elif self.key_size == 24:
+ extra_cnt = 2
+ else:
+ extra_cnt = 3
+
+ # 4-byte temporary variable for key expansion
+ word = exkey[-4:]
+ # Each expansion cycle uses 'i' once for Rcon table lookup
+ for i in xrange(1, 11):
+
+ #### key schedule core:
+ # left-rotate by 1 byte
+ word = word[1:4] + word[0:1]
+
+ # apply S-box to all bytes
+ for j in xrange(4):
+ word[j] = aes_sbox[word[j]]
+
+ # apply the Rcon table to the leftmost byte
+ word[0] = word[0] ^ aes_Rcon[i]
+ #### end key schedule core
+
+ for z in xrange(4):
+ for j in xrange(4):
+ # mix in bytes from the last subkey
+ word[j] ^= exkey[-self.key_size + j]
+ exkey.extend(word)
+
+ # Last key expansion cycle always finishes here
+ if len(exkey) >= (self.rounds+1) * self.block_size:
+ break
+
+ # Special substitution step for 256-bit key
+ if self.key_size == 32:
+ for j in xrange(4):
+ # mix in bytes from the last subkey XORed with S-box of
+ # current word bytes
+ word[j] = aes_sbox[word[j]] ^ exkey[-self.key_size + j]
+ exkey.extend(word)
+
+ # Twice for 192-bit key, thrice for 256-bit key
+ for z in xrange(extra_cnt):
+ for j in xrange(4):
+ # mix in bytes from the last subkey
+ word[j] ^= exkey[-self.key_size + j]
+ exkey.extend(word)
+
+ self.exkey = exkey
+
+ def add_round_key(self, block, round):
+ """AddRoundKey step in AES. This is where the key is mixed into plaintext"""
+
+ offset = round * 16
+ exkey = self.exkey
+
+ for i in xrange(16):
+ block[i] ^= exkey[offset + i]
+
+ #print 'AddRoundKey:', block
+
+ def sub_bytes(self, block, sbox):
+ """SubBytes step, apply S-box to all bytes
+
+ Depending on whether encrypting or decrypting, a different sbox array
+ is passed in.
+ """
+
+ for i in xrange(16):
+ block[i] = sbox[block[i]]
+
+ #print 'SubBytes :', block
+
+ def shift_rows(self, b):
+ """ShiftRows step. Shifts 2nd row to left by 1, 3rd row by 2, 4th row by 3
+
+ Since we're performing this on a transposed matrix, cells are numbered
+ from top to bottom::
+
+ 0 4 8 12 -> 0 4 8 12 -- 1st row doesn't change
+ 1 5 9 13 -> 5 9 13 1 -- row shifted to left by 1 (wraps around)
+ 2 6 10 14 -> 10 14 2 6 -- shifted by 2
+ 3 7 11 15 -> 15 3 7 11 -- shifted by 3
+ """
+
+ b[1], b[5], b[ 9], b[13] = b[ 5], b[ 9], b[13], b[ 1]
+ b[2], b[6], b[10], b[14] = b[10], b[14], b[ 2], b[ 6]
+ b[3], b[7], b[11], b[15] = b[15], b[ 3], b[ 7], b[11]
+
+ #print 'ShiftRows :', b
+
+ def shift_rows_inv(self, b):
+ """Similar to shift_rows above, but performed in inverse for decryption."""
+
+ b[ 5], b[ 9], b[13], b[ 1] = b[1], b[5], b[ 9], b[13]
+ b[10], b[14], b[ 2], b[ 6] = b[2], b[6], b[10], b[14]
+ b[15], b[ 3], b[ 7], b[11] = b[3], b[7], b[11], b[15]
+
+ #print 'ShiftRows :', b
+
+ def mix_columns(self, block):
+ """MixColumns step. Mixes the values in each column"""
+
+ # Cache global multiplication tables (see below)
+ mul_by_2 = gf_mul_by_2
+ mul_by_3 = gf_mul_by_3
+
+ # Since we're dealing with a transposed matrix, columns are already
+ # sequential
+ for i in xrange(4):
+ col = i * 4
+
+ #v0, v1, v2, v3 = block[col : col+4]
+ v0, v1, v2, v3 = (block[col], block[col + 1], block[col + 2],
+ block[col + 3])
+
+ block[col ] = mul_by_2[v0] ^ v3 ^ v2 ^ mul_by_3[v1]
+ block[col+1] = mul_by_2[v1] ^ v0 ^ v3 ^ mul_by_3[v2]
+ block[col+2] = mul_by_2[v2] ^ v1 ^ v0 ^ mul_by_3[v3]
+ block[col+3] = mul_by_2[v3] ^ v2 ^ v1 ^ mul_by_3[v0]
+
+ #print 'MixColumns :', block
+
+ def mix_columns_inv(self, block):
+ """Similar to mix_columns above, but performed in inverse for decryption."""
+
+ # Cache global multiplication tables (see below)
+ mul_9 = gf_mul_by_9
+ mul_11 = gf_mul_by_11
+ mul_13 = gf_mul_by_13
+ mul_14 = gf_mul_by_14
+
+ # Since we're dealing with a transposed matrix, columns are already
+ # sequential
+ for i in xrange(4):
+ col = i * 4
+
+ v0, v1, v2, v3 = (block[col], block[col + 1], block[col + 2],
+ block[col + 3])
+ #v0, v1, v2, v3 = block[col:col+4]
+
+ block[col ] = mul_14[v0] ^ mul_9[v3] ^ mul_13[v2] ^ mul_11[v1]
+ block[col+1] = mul_14[v1] ^ mul_9[v0] ^ mul_13[v3] ^ mul_11[v2]
+ block[col+2] = mul_14[v2] ^ mul_9[v1] ^ mul_13[v0] ^ mul_11[v3]
+ block[col+3] = mul_14[v3] ^ mul_9[v2] ^ mul_13[v1] ^ mul_11[v0]
+
+ #print 'MixColumns :', block
+
+ def encrypt_block(self, block):
+ """Encrypts a single block. This is the main AES function"""
+
+ # For efficiency reasons, the state between steps is transmitted via a
+ # mutable array, not returned.
+ self.add_round_key(block, 0)
+
+ for round in xrange(1, self.rounds):
+ self.sub_bytes(block, aes_sbox)
+ self.shift_rows(block)
+ self.mix_columns(block)
+ self.add_round_key(block, round)
+
+ self.sub_bytes(block, aes_sbox)
+ self.shift_rows(block)
+ # no mix_columns step in the last round
+ self.add_round_key(block, self.rounds)
+
+ def decrypt_block(self, block):
+ """Decrypts a single block. This is the main AES decryption function"""
+
+ # For efficiency reasons, the state between steps is transmitted via a
+ # mutable array, not returned.
+ self.add_round_key(block, self.rounds)
+
+ # count rounds down from 15 ... 1
+ for round in xrange(self.rounds-1, 0, -1):
+ self.shift_rows_inv(block)
+ self.sub_bytes(block, aes_inv_sbox)
+ self.add_round_key(block, round)
+ self.mix_columns_inv(block)
+
+ self.shift_rows_inv(block)
+ self.sub_bytes(block, aes_inv_sbox)
+ self.add_round_key(block, 0)
+ # no mix_columns step in the last round
+
+
+#### ECB mode implementation
+
+class ECBMode(object):
+ """Electronic CodeBook (ECB) mode encryption.
+
+ Basically this mode applies the cipher function to each block individually;
+ no feedback is done. NB! This is insecure for almost all purposes
+ """
+
+ def __init__(self, cipher):
+ self.cipher = cipher
+ self.block_size = cipher.block_size
+
+ def ecb(self, data, block_func):
+ """Perform ECB mode with the given function"""
+
+ if len(data) % self.block_size != 0:
+ raise ValueError, "Plaintext length must be multiple of 16"
+
+ block_size = self.block_size
+ data = array('B', data)
+
+ for offset in xrange(0, len(data), block_size):
+ block = data[offset : offset+block_size]
+ block_func(block)
+ data[offset : offset+block_size] = block
+
+ return data.tostring()
+
+ def encrypt(self, data):
+ """Encrypt data in ECB mode"""
+
+ return self.ecb(data, self.cipher.encrypt_block)
+
+ def decrypt(self, data):
+ """Decrypt data in ECB mode"""
+
+ return self.ecb(data, self.cipher.decrypt_block)
+
+#### CBC mode
+
+class CBCMode(object):
+ """Cipher Block Chaining (CBC) mode encryption. This mode avoids content leaks.
+
+ In CBC encryption, each plaintext block is XORed with the ciphertext block
+ preceding it; decryption is simply the inverse.
+ """
+
+ # A better explanation of CBC can be found here:
+ # http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
+
+ def __init__(self, cipher, IV):
+ self.cipher = cipher
+ self.block_size = cipher.block_size
+ self.IV = array('B', IV)
+
+ def encrypt(self, data):
+ """Encrypt data in CBC mode"""
+
+ block_size = self.block_size
+ if len(data) % block_size != 0:
+ raise ValueError, "Plaintext length must be multiple of 16"
+
+ data = array('B', data)
+ IV = self.IV
+
+ for offset in xrange(0, len(data), block_size):
+ block = data[offset : offset+block_size]
+
+ # Perform CBC chaining
+ for i in xrange(block_size):
+ block[i] ^= IV[i]
+
+ self.cipher.encrypt_block(block)
+ data[offset : offset+block_size] = block
+ IV = block
+
+ self.IV = IV
+ return data.tostring()
+
+ def decrypt(self, data):
+ """Decrypt data in CBC mode"""
+
+ block_size = self.block_size
+ if len(data) % block_size != 0:
+ raise ValueError, "Ciphertext length must be multiple of 16"
+
+ data = array('B', data)
+ IV = self.IV
+
+ for offset in xrange(0, len(data), block_size):
+ ctext = data[offset : offset+block_size]
+ block = ctext[:]
+ self.cipher.decrypt_block(block)
+
+ # Perform CBC chaining
+ #for i in xrange(block_size):
+ # data[offset + i] ^= IV[i]
+ for i in xrange(block_size):
+ block[i] ^= IV[i]
+ data[offset : offset+block_size] = block
+
+ IV = ctext
+ #data[offset : offset+block_size] = block
+
+ self.IV = IV
+ return data.tostring()
+
+####
+
+def galois_multiply(a, b):
+ """Galois Field multiplicaiton for AES"""
+ p = 0
+ while b:
+ if b & 1:
+ p ^= a
+ a <<= 1
+ if a & 0x100:
+ a ^= 0x1b
+ b >>= 1
+
+ return p & 0xff
+
+# Precompute the multiplication tables for encryption
+gf_mul_by_2 = array('B', [galois_multiply(x, 2) for x in range(256)])
+gf_mul_by_3 = array('B', [galois_multiply(x, 3) for x in range(256)])
+# ... for decryption
+gf_mul_by_9 = array('B', [galois_multiply(x, 9) for x in range(256)])
+gf_mul_by_11 = array('B', [galois_multiply(x, 11) for x in range(256)])
+gf_mul_by_13 = array('B', [galois_multiply(x, 13) for x in range(256)])
+gf_mul_by_14 = array('B', [galois_multiply(x, 14) for x in range(256)])
+
+####
+
+# The S-box is a 256-element array, that maps a single byte value to another
+# byte value. Since it's designed to be reversible, each value occurs only once
+# in the S-box
+#
+# More information: http://en.wikipedia.org/wiki/Rijndael_S-box
+
+aes_sbox = array('B',
+ '637c777bf26b6fc53001672bfed7ab76'
+ 'ca82c97dfa5947f0add4a2af9ca472c0'
+ 'b7fd9326363ff7cc34a5e5f171d83115'
+ '04c723c31896059a071280e2eb27b275'
+ '09832c1a1b6e5aa0523bd6b329e32f84'
+ '53d100ed20fcb15b6acbbe394a4c58cf'
+ 'd0efaafb434d338545f9027f503c9fa8'
+ '51a3408f929d38f5bcb6da2110fff3d2'
+ 'cd0c13ec5f974417c4a77e3d645d1973'
+ '60814fdc222a908846eeb814de5e0bdb'
+ 'e0323a0a4906245cc2d3ac629195e479'
+ 'e7c8376d8dd54ea96c56f4ea657aae08'
+ 'ba78252e1ca6b4c6e8dd741f4bbd8b8a'
+ '703eb5664803f60e613557b986c11d9e'
+ 'e1f8981169d98e949b1e87e9ce5528df'
+ '8ca1890dbfe6426841992d0fb054bb16'.decode('hex')
+)
+
+# This is the inverse of the above. In other words:
+# aes_inv_sbox[aes_sbox[val]] == val
+
+aes_inv_sbox = array('B',
+ '52096ad53036a538bf40a39e81f3d7fb'
+ '7ce339829b2fff87348e4344c4dee9cb'
+ '547b9432a6c2233dee4c950b42fac34e'
+ '082ea16628d924b2765ba2496d8bd125'
+ '72f8f66486689816d4a45ccc5d65b692'
+ '6c704850fdedb9da5e154657a78d9d84'
+ '90d8ab008cbcd30af7e45805b8b34506'
+ 'd02c1e8fca3f0f02c1afbd0301138a6b'
+ '3a9111414f67dcea97f2cfcef0b4e673'
+ '96ac7422e7ad3585e2f937e81c75df6e'
+ '47f11a711d29c5896fb7620eaa18be1b'
+ 'fc563e4bc6d279209adbc0fe78cd5af4'
+ '1fdda8338807c731b11210592780ec5f'
+ '60517fa919b54a0d2de57a9f93c99cef'
+ 'a0e03b4dae2af5b0c8ebbb3c83539961'
+ '172b047eba77d626e169146355210c7d'.decode('hex')
+)
+
+# The Rcon table is used in AES's key schedule (key expansion)
+# It's a pre-computed table of exponentation of 2 in AES's finite field
+#
+# More information: http://en.wikipedia.org/wiki/Rijndael_key_schedule
+
+aes_Rcon = array('B',
+ '8d01020408102040801b366cd8ab4d9a'
+ '2f5ebc63c697356ad4b37dfaefc59139'
+ '72e4d3bd61c29f254a943366cc831d3a'
+ '74e8cb8d01020408102040801b366cd8'
+ 'ab4d9a2f5ebc63c697356ad4b37dfaef'
+ 'c5913972e4d3bd61c29f254a943366cc'
+ '831d3a74e8cb8d01020408102040801b'
+ '366cd8ab4d9a2f5ebc63c697356ad4b3'
+ '7dfaefc5913972e4d3bd61c29f254a94'
+ '3366cc831d3a74e8cb8d010204081020'
+ '40801b366cd8ab4d9a2f5ebc63c69735'
+ '6ad4b37dfaefc5913972e4d3bd61c29f'
+ '254a943366cc831d3a74e8cb8d010204'
+ '08102040801b366cd8ab4d9a2f5ebc63'
+ 'c697356ad4b37dfaefc5913972e4d3bd'
+ '61c29f254a943366cc831d3a74e8cb'.decode('hex')
+)
\ No newline at end of file
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/regexUtils.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/regexUtils.py
new file mode 100644
index 0000000000000000000000000000000000000000..9eaf3eec352e6a45a4a33cb3d98851213785f349
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/regexUtils.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+def findall(data,regex):
+ p_reg = re.compile(regex, re.DOTALL + re.MULTILINE + re.UNICODE)
+ result = p_reg.findall(data)
+ return result
+
+def parseTextToGroups(txt, regex):
+ p = re.compile(regex, re.DOTALL + re.MULTILINE + re.UNICODE)
+ m = p.match(txt)
+ if m:
+ return m.groups()
+ else:
+ return None
+
+def parseText(txt, regex, variables=[]):
+ groups = parseTextToGroups(txt, regex)
+ if variables == []:
+ if groups:
+ return groups[0]
+ else:
+ return ''
+ else:
+ resultArr = {}
+ i = 0
+ for v in variables:
+ if groups:
+ resultArr[v] = groups[i]
+ else:
+ resultArr[v] = ''
+ i += 1
+ return resultArr
\ No newline at end of file
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/rowbalance.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/rowbalance.py
new file mode 100644
index 0000000000000000000000000000000000000000..27104241bb68d70c979bcd11cda22ae79c863d20
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/rowbalance.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python2
+# -*- coding: UTF-8 -*-
+import random
+
+servers = [ '37.130.227.140:1935',
+ '46.28.49.36:1935',
+ '46.28.55.182:1935',
+ '46.28.55.217:1935',
+ '75.126.5.155:1935',
+ '75.126.5.190:1935',
+ '91.109.247.180:1935',
+ '91.109.247.184:1935',
+ '91.109.247.186:1935',
+ '107.182.224.109:1935',
+ '107.182.224.111:1935',
+ '107.182.224.113:1935',
+ '107.182.224.115:1935',
+ '107.182.224.117:1935',
+ '107.182.233.79:1935',
+ '107.182.233.80:1935',
+ '109.123.113.94:1935',
+ '109.202.101.108:1935',
+ '109.232.224.37:1935',
+ '146.185.16.6:1935',
+ '146.185.16.74:1935',
+ '146.185.25.130:1935',
+ '146.185.25.162:1935',
+ '146.185.25.186:1935',
+ '159.8.244.200:1935',
+ '159.8.244.201:1935',
+ '159.8.244.202:1935',
+ '159.8.244.253:1935',
+ '173.192.81.169:1935',
+ '176.67.175.156:1935',
+ '179.43.158.195:1935',
+ '179.43.158.196:1935',
+ '179.43.158.197:1935',
+ '179.43.158.198:1935',
+ '179.43.158.199:1935',
+ '179.43.158.200:1935',
+ '179.43.158.201:1935',
+ '179.43.158.202:1935',
+ '179.43.158.203:1935',
+ '185.80.220.7:1935',
+ '185.80.220.8:1935',
+ '185.80.220.9:1935',
+ '188.95.48.71:1935',
+ '199.189.105.12:1935',
+ '199.195.194.244:1935',
+ '199.195.195.20:1935',
+ '199.195.195.76:1935',
+ '199.195.195.100:1935',
+ '206.190.129.4:1935',
+ '209.95.51.143:1935',
+ '209.95.51.144:1935',
+ '209.95.51.145:1935',
+ '209.95.51.146:1935',
+ '209.95.51.147:1935',
+ '213.152.162.122:1935',
+ '213.152.180.151:1935',
+ '213.152.180.243:1935',
+ '213.152.180.250:1935',
+ '213.152.181.10:1935',
+ '213.152.181.11:1935',
+ '213.152.181.12:1935' ]
+
+def get():
+ return random.choice(servers)
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/scrapingUtils.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/scrapingUtils.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e2d81ec62338768447faa73339845a8f86cbf04
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/scrapingUtils.py
@@ -0,0 +1,228 @@
+# -*- coding: utf-8 -*-
+
+
+import regexUtils
+import re
+import urllib
+import urlparse
+
+
+def findJS(data):
+ idName = '(?:f*id|ch)'
+ jsName = '([^\"\']+?\.js[^\"\']*?)'
+ regex = "(?:java)?scr(?:'\+')?ipt.*?" + idName + "\s*=\s*[\"']([^\"']+)[\"'][^<]*[^<]* max_idx:
+ end = max_idx
+
+ area = text[start:end]
+
+ clipStart = idx+len(tmpRtmp)
+ if clipStart < max_idx:
+ text = text[clipStart:]
+ if method1:
+ playpath = regexUtils.findall(area, sep1 + 'file' + sep2 + value)
+ if method2:
+ playpath = regexUtils.findall(area, sep1 + 'id' + sep2 + value)
+ if playpath:
+ tmpRtmp = tmpRtmp + '/' + playpath[0]
+
+ if playpath:
+ swfUrl = regexUtils.findall(area, 'SWFObject\([\'"]([^\'"]+)[\'"]')
+ if not swfUrl:
+ swfUrl = regexUtils.findall(area, sep1 + '([^\'"& ]+\.swf)')
+ if not swfUrl:
+ swfUrl = regexUtils.findall(data, sep1 + '([^\'"& ]+\.swf)')
+
+ if swfUrl:
+ finalSwfUrl = swfUrl[0]
+ if not finalSwfUrl.startswith('http'):
+ finalSwfUrl = urlparse.urljoin(url, finalSwfUrl)
+
+ regex = '://(.*?)/'
+ server = regexUtils.findall(tmpRtmp, regex)
+ if server:
+ if server[0].find(':') == -1:
+ tmpRtmp = tmpRtmp.replace(server[0], server[0] + ':1935')
+
+ return [tmpRtmp, playpath[0], finalSwfUrl]
+
+ return None
+
+
+def getHostName(url):
+ scheme = urlparse.urlparse(url)
+ if scheme:
+ return scheme.netloc.replace('www.','')
+ return None
+
+
+def findFrames(data):
+ if data.lower().find('frame') == -1:
+ return None
+ return regexUtils.findall(data, "(frame[^>]*)>")
+
+
+def findContentRefreshLink(page, data):
+
+ regex = '0;\s*url=([^\'" ]+)'
+ links = regexUtils.findall(data, regex)
+ if links:
+ return links[0]
+
+ regex = 'window.location\s*=\s*[\'"]([^\'"]+)[\'"]'
+ links = regexUtils.findall(data, regex)
+ if links:
+ return links[0]
+
+ regex = 'frame\s*scrolling=\"auto\"\s*noresize\s*src\s*=\s*[\'"]([^\'"]+)[\'"]'
+ links = regexUtils.findall(data, regex)
+ if links:
+ return links[0]
+
+ #hd**ee.fv/cr**hd.fv/sp**ts4u.tv
+ regex = ']*cbox\.ws)(?![^>]*Publi)(?![^>]*dailymotion)(?![^>]*blacktvlive\.)(?![^>]*chat\d*\.\w+)(?![^>]*ad122m)(?![^>]*adshell)(?![^>]*capacanal)(?![^>]*waframedia)(?![^>]*Beba.tv/embed)(?![^>]*maxtags)(?![^>]*s/a1\.php)(?![^>]*right-sidebar)[^>]*\sheight\s*=\s*[\"']*([\%\d]+)(?:px)?[\"']*[^>]*>)")
+
+ if iframes:
+ for iframe in iframes:
+ if iframe[1] == '100%':
+ height = minheight+1
+ else:
+ height = int(iframe[1])
+ if height > minheight:
+ m = regexUtils.findall(iframe[0], "[\"' ]width\s*=\s*[\"']*(\d+[%]*)(?:px)?[\"']*")
+ if m:
+ if m[0] == '100%':
+ width = minwidth+1
+ else:
+ width = int(m[0])
+ if width > minwidth:
+ m = regexUtils.findall(iframe[0], '[\'"\s]+(?:src|SRC)\s*=\s*["\']*\s*([^>"\' ]+)\s*[>"\']*')
+ if m:
+ if 'premiertv' in page:
+ page = page+'/'
+ return urlparse.urljoin(urllib.unquote(page), m[0]).strip()
+
+
+ # Alternative 1
+ iframes = regexUtils.findall(data, "(frame(?![^>]*cbox\.ws)(?![^>]*capacanal)(?![^>]*dailymotion)[^>]*[\"; ]height:\s*(\d+)[^>]*>)")
+ if iframes:
+ for iframe in iframes:
+ height = int(iframe[1])
+ if height > minheight:
+ m = regexUtils.findall(iframe[0], "[\"; ]width:\s*(\d+)")
+ if m:
+ width = int(m[0])
+ if width > minwidth:
+ m = regexUtils.findall(iframe[0], '[\"; ](?:src|SRC)=["\']*\s*([^>"\' ]+)\s*[>"\']*')
+ if m:
+ return urlparse.urljoin(urllib.unquote(page), m[0]).strip()
+
+ # Alternative 2 (Frameset)
+ m = regexUtils.findall(data, '<(?:FRAMESET|frameset)[^>]+100%[^>]+>\s*<(?:FRAME|frame)[^>]+src="([^"]+)"')
+ if m:
+ return urlparse.urljoin(urllib.unquote(page), m[0]).strip()
+
+ m = regexUtils.findall(data, r'playStream\(\'iframe\', \'[^\']*(https*:[^\']+)\'\)')
+ if m:
+ return urlparse.urljoin(urllib.unquote(page), m[0]).strip()
+
+ return None
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/unpack95High.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/unpack95High.py
new file mode 100644
index 0000000000000000000000000000000000000000..17f8019dfce7a41c1d5e8c7e89c6151467c7bf2e
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/unpack95High.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+#
+# Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier
+# by Einar Lielmanis
+#
+# written by Stefano Sanfilippo
+#
+# usage:
+#
+# if detect(some_string):
+# unpacked = unpack(some_string)
+#
+
+"""Unpacker for Dean Edward's p.a.c.k.e.r"""
+import re
+import string
+
+class UnpackingError(Exception):
+ """Badly packed source or general error. Argument is a
+ meaningful description."""
+ pass
+
+PRIORITY = 1
+
+def detect(source):
+ """Detects whether `source` is P.A.C.K.E.R. coded."""
+ return source.replace(' ', '').startswith('eval(function(p,a,c,k,e,r')
+
+def unpack(source):
+ """Unpacks P.A.C.K.E.R. packed js code."""
+ payload, symtab, radix, count = _filterargs(source)
+
+ if count != len(symtab):
+ raise UnpackingError('Malformed p.a.c.k.e.r. symtab.')
+
+ def lookup(match):
+ """Look up symbols in the synthetic symtab."""
+ word = 0
+ for i, char in enumerate(reversed(match.group(0))):
+ word = word + (ord(char)-161)*(95**i)
+
+ return symtab[word] or word
+
+ source = re.sub(ur'[\xa1-\xff]+', lookup, payload)
+ return _replacestrings(source)
+
+def _filterargs(source):
+ """Juice from a source file the four args needed by decoder."""
+ argsregex = (r"}\('(.*)', *(\d+), *(\d+), *'(.*)'\."
+ r"split\('\|'\), *(\d+), *(.*)\)\)")
+ args = re.search(argsregex, source, re.DOTALL).groups()
+
+ try:
+ return args[0], args[3].split('|'), int(args[1]), int(args[2])
+ except ValueError:
+ raise UnpackingError('Corrupted p.a.c.k.e.r. data.')
+
+def _replacestrings(source):
+ """Strip string lookup table (list) and replace values in source."""
+ match = re.search(r'var *(_\w+)\=\["(.*?)"\];', source, re.DOTALL)
+
+ if match:
+ varname, strings = match.groups()
+ startpoint = len(match.group(0))
+ lookup = strings.split('","')
+ variable = '%s[%%d]' % varname
+ for index, value in enumerate(lookup):
+ source = source.replace(variable % index, '"%s"' % value)
+ return source[startpoint:]
+ return source
+
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/unpackstd.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/unpackstd.py
new file mode 100644
index 0000000000000000000000000000000000000000..f3945dd902939760abdee4fee758348ccaf9b5a0
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/unpackstd.py
@@ -0,0 +1,116 @@
+#
+# Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier
+# by Einar Lielmanis
+#
+# written by Stefano Sanfilippo
+#
+# usage:
+#
+# if detect(some_string):
+# unpacked = unpack(some_string)
+#
+
+"""Unpacker for Dean Edward's p.a.c.k.e.r"""
+
+import re
+import string
+
+class UnpackingError(Exception):
+ """Badly packed source or general error. Argument is a
+ meaningful description."""
+ pass
+
+PRIORITY = 1
+
+def detect(source):
+ """Detects whether `source` is P.A.C.K.E.R. coded."""
+ return source.replace(' ', '').startswith('eval(function(p,a,c,k,e,')
+
+def unpack(source):
+ """Unpacks P.A.C.K.E.R. packed js code."""
+ payload, symtab, radix, count = _filterargs(source)
+
+ if count != len(symtab):
+ raise UnpackingError('Malformed p.a.c.k.e.r. symtab.')
+
+ try:
+ unbase = Unbaser(radix)
+ except TypeError:
+ raise UnpackingError('Unknown p.a.c.k.e.r. encoding.')
+
+ def lookup(match):
+ """Look up symbols in the synthetic symtab."""
+ word = match.group(0)
+ return symtab[unbase(word)] or word
+
+ source = re.sub(r'\b\w+\b', lookup, payload)
+ return _replacestrings(source)
+
+def _filterargs(source):
+ """Juice from a source file the four args needed by decoder."""
+ juicers = [ (r"}\('(.*)', *(\d+), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)"),
+ (r"}\('(.*)', *(\d+), *(\d+), *'(.*)'\.split\('\|'\)"),
+ ]
+ for juicer in juicers:
+ args = re.search(juicer, source, re.DOTALL)
+ if args:
+ a = args.groups()
+ try:
+ return a[0], a[3].split('|'), int(a[1]), int(a[2])
+ except ValueError:
+ raise UnpackingError('Corrupted p.a.c.k.e.r. data.')
+
+ # could not find a satisfying regex
+ raise UnpackingError('Could not make sense of p.a.c.k.e.r data (unexpected code structure)')
+
+
+
+def _replacestrings(source):
+ """Strip string lookup table (list) and replace values in source."""
+ match = re.search(r'var *(_\w+)\=\["(.*?)"\];', source, re.DOTALL)
+
+ if match:
+ varname, strings = match.groups()
+ startpoint = len(match.group(0))
+ lookup = strings.split('","')
+ variable = '%s[%%d]' % varname
+ for index, value in enumerate(lookup):
+ source = source.replace(variable % index, '"%s"' % value)
+ return source[startpoint:]
+ return source
+
+
+class Unbaser(object):
+ """Functor for a given base. Will efficiently convert
+ strings to natural numbers."""
+ ALPHABET = {
+ 62 : '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 95 : (' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ '[\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
+ }
+
+ def __init__(self, base):
+ self.base = base
+
+ # If base can be handled by int() builtin, let it do it for us
+ if 2 <= base <= 36:
+ self.unbase = lambda string: int(string, base)
+ else:
+ # Build conversion dictionary cache
+ try:
+ self.dictionary = dict((cipher, index) for
+ index, cipher in enumerate(self.ALPHABET[base]))
+ except KeyError:
+ raise TypeError('Unsupported base encoding.')
+
+ self.unbase = self._dictunbaser
+
+ def __call__(self, string):
+ return self.unbase(string)
+
+ def _dictunbaser(self, string):
+ """Decodes a value to an integer."""
+ ret = 0
+ for index, cipher in enumerate(string[::-1]):
+ ret += (self.base ** index) * self.dictionary[cipher]
+ return ret
diff --git a/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/webUtils.py b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/webUtils.py
new file mode 100644
index 0000000000000000000000000000000000000000..1946b5d91d94f0c0914828f93ddfbf62bf780437
--- /dev/null
+++ b/packages/mediacenter/kodi/config/base-addons/plugin.video.SportsDevil/lib/utils/webUtils.py
@@ -0,0 +1,182 @@
+# -*- coding: utf-8 -*-
+
+import os
+import re
+import urllib
+import urlparse
+import requests
+import cookielib
+import socket
+from HTMLParser import HTMLParser
+from fileUtils import fileExists, setFileContent, getFileContent
+
+#------------------------------------------------------------------------------
+socket.setdefaulttimeout(30)
+
+#use ipv4 only
+origGetAddrInfo = socket.getaddrinfo
+
+def getAddrInfoWrapper(host, port, family=0, socktype=0, proto=0, flags=0):
+ return origGetAddrInfo(host, port, socket.AF_INET, socktype, proto, flags)
+
+# replace the original socket.getaddrinfo by our version
+socket.getaddrinfo = getAddrInfoWrapper
+#------------------------------------------------------------------------------
+
+'''
+ REQUEST classes
+'''
+
+class BaseRequest(object):
+
+ def __init__(self, cookie_file=None):
+ self.cookie_file = cookie_file
+ self.s = requests.Session()
+ if fileExists(self.cookie_file):
+ self.s.cookies = self.load_cookies_from_lwp(self.cookie_file)
+ self.s.headers.update({'User-Agent' : 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36'})
+ self.s.headers.update({'Accept-Language' : 'en'})
+ self.url = ''
+
+ def save_cookies_lwp(self, cookiejar, filename):
+ lwp_cookiejar = cookielib.LWPCookieJar()
+ for c in cookiejar:
+ args = dict(vars(c).items())
+ args['rest'] = args['_rest']
+ del args['_rest']
+ c = cookielib.Cookie(**args)
+ lwp_cookiejar.set_cookie(c)
+ lwp_cookiejar.save(filename, ignore_discard=True)
+
+ def load_cookies_from_lwp(self, filename):
+ lwp_cookiejar = cookielib.LWPCookieJar()
+ lwp_cookiejar.load(filename, ignore_discard=True)
+ return lwp_cookiejar
+
+ def fixurl(self, url):
+ #url is unicode (quoted or unquoted)
+ try:
+ #url is already quoted
+ url = url.encode('ascii')
+ except:
+ #quote url if it is unicode
+ parsed_link = urlparse.urlsplit(url)
+ parsed_link = parsed_link._replace(netloc=parsed_link.netloc.encode('idna'),path=urllib.quote(parsed_link.path.encode('utf-8')))
+ url = parsed_link.geturl().encode('ascii')
+ #url is str (quoted)
+ return url
+
+ def getSource(self, url, form_data, referer, xml=False, mobile=False):
+ url = self.fixurl(url)
+
+ if not referer:
+ referer = url
+ else:
+ referer = self.fixurl(referer)
+
+ headers = {'Referer': referer}
+ if mobile:
+ self.s.headers.update({'User-Agent' : 'Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'})
+
+ if xml:
+ headers['X-Requested-With'] = 'XMLHttpRequest'
+
+ if 'dinozap.info' in urlparse.urlsplit(url).netloc:
+ headers['X-Forwarded-For'] = '178.162.222.111'
+ if 'playerhd2.pw' in urlparse.urlsplit(url).netloc:
+ headers['X-Forwarded-For'] = '178.162.222.121'
+ if 'playerapp1.pw' in urlparse.urlsplit(url).netloc:
+ headers['X-Forwarded-For'] = '178.162.222.122'
+
+ if form_data:
+ #zo**tv
+ if 'uagent' in form_data[0]:
+ form_data[0] = ('uagent',urllib.quote(self.s.headers['User-Agent']))
+
+ r = self.s.post(url, headers=headers, data=form_data, timeout=20)
+ else:
+ try:
+ r = self.s.get(url, headers=headers, timeout=20)
+ except (requests.exceptions.MissingSchema):
+ return 'pass'
+
+ #many utf8 encodings are specified in HTTP body not headers and requests only checks headers, maybe use html5lib
+ #https://github.com/kennethreitz/requests/issues/2086
+ if 'streamlive.to' in urlparse.urlsplit(url).netloc \
+ or 'sport365.live' in urlparse.urlsplit(url).netloc:
+ r.encoding = 'utf-8'
+ if 'lfootball.ws' in urlparse.urlsplit(url).netloc:
+ r.encoding = 'windows-1251'
+
+ response = r.text
+ if len(response) > 10:
+ if self.cookie_file:
+ self.save_cookies_lwp(self.s.cookies, self.cookie_file)
+ return HTMLParser().unescape(response)
+
+#------------------------------------------------------------------------------
+
+class DemystifiedWebRequest(BaseRequest):
+
+ def __init__(self, cookiePath):
+ super(DemystifiedWebRequest,self).__init__(cookiePath)
+
+ def getSource(self, url, form_data, referer='', xml=False, mobile=False, demystify=False):
+ data = super(DemystifiedWebRequest, self).getSource(url, form_data, referer, xml, mobile)
+ if not data:
+ return None
+
+ if not demystify:
+ # remove comments
+ r = re.compile(')?\s*)
+skill=autoselect
+catcher=streams
+
+########################################################
+# Streams
+########################################################
+
+# example:
+# javascript:openWindow("index1.php?option=com_lsh&view=lsh&event_id=126687&tv_id=871&tid=31589&channel=0&tmpl=component&layout=popup&Itemid=322","735","665")
+
+item_infos=javascript:openwindow\("(http://live.\w+.com/index.*?&event_id=([^&]+)&tv_id=([^&]+)&tid=([^&]+)&channel=([^&]+)[^"]+)".*?>Link#([^<]+)
+item_order=url.tmp|eid|tvid|tid|channel|link
+item_skill=rss
+
+item_info_name=entry.tmp
+item_info_from=url.tmp
+item_info_convert=replace('%s','?','.')
+item_info_convert=getInfo('@PARAM2@','.*
', source, re.DOTALL)
+ number_of_items = len(items)
+ if items:
+ for item in items:
+ match =re.compile('href="(.+?)"').findall(item)
+ if match:
+ if "sop://" or "torrentstream" or "acestream://" in match[-1]:
+ stream_quality = re.compile('>(.+?) kbps
').findall(html)
+
+ for link, desc, img, title in match:
+ AddCartoon(title, img, link, desc)
+
+ if next in html:
+ AddMore(ALL, URL, page+1)
+
+
+def MostRecent():
+ html = GetHTML(URL)
+
+ match = re.compile('