diff --git a/PiTV/application.glade b/PiTV/application.glade
index 11e521b..84a8e7b 100644
--- a/PiTV/application.glade
+++ b/PiTV/application.glade
@@ -324,8 +324,7 @@ Author: GrbavaCigla
-
-
+
True
False
slide-up-down
-
+
True
True
never
@@ -383,14 +382,71 @@ Author: GrbavaCigla
1
+
+
+ True
+ False
+
+
+
+
+
+
+
+
+ page2
+ page2
+ 2
+
+
True
True
- 2
+ 1
+
+
+
+
+
+
+ True
+ False
+ 6
+
+
+
+
+
+ True
+ False
+ slide-up-down
+
+
+ True
+ True
+
+
+ True
+ False
+ none
+
+
+
+
+
+
+
+ page0
+ page0
+
+ True
+ True
+ 1
+
diff --git a/PiTV/application.py b/PiTV/application.py
index 9fc0e3d..258aef9 100644
--- a/PiTV/application.py
+++ b/PiTV/application.py
@@ -4,19 +4,19 @@
from threading import Thread
from screeninfo import get_monitors
import requests
-from entertainment import Weather, Location
+from weather import Weather
+from location import Location
from utils import check_internet
from sidebar import SideBar, ListTile, WeatherBox
from category import Category
-
# Bypass linters
if True:
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib, Gio
-
+HOST = "http://127.0.0.1"
HOME_DIR = os.path.dirname(os.path.abspath(__file__))
MONITOR_WIDTH = get_monitors()[0].width
@@ -32,7 +32,7 @@
# "TV Shows",
# "TV",
# "Songs"
- # "Settings"
+ "Settings"
]
SIDEBAR_ICONS = [
@@ -41,7 +41,7 @@
# "TV Shows",
# "TV",
# "Songs"
- # "Settings"
+ "open-menu"
]
@@ -69,8 +69,11 @@ def __init__(self):
# Website host URL (As I currenly don't have website,
# localhost is development sollution)
- # Switched to heroku basic plan (free)
- self.host = "https://pitv.herokuapp.com"
+ # Switched to heroku free plan
+ # Switched to Azure donated by Maker NS
+ self.host = HOST
+
+ self.fetch_weather = True
self.login_init() # Switch this back after debugging
@@ -79,17 +82,22 @@ def __init__(self):
def home_refresh(self):
# Fetch weather info
# TODO:Prompt user for his openweathermap api key
- if not self.weather_info:
- self.weather_info = Weather(
- self.openweather_apikey,
- self.unit_system,
- self.city_and_country
- )
- self.weather_box.set_weather_object(self.weather_info, update=True)
- else:
- self.weather_box.update_data()
- self.weather_box.refresh()
+ if self.fetch_weather:
+ if not self.weather_info:
+ self.weather_info = Weather(
+ self.openweather_apikey,
+ self.unit_system,
+ self.city_and_country
+ )
+ self.weather_box.set_weather_object(
+ self.weather_info,
+ update=True
+ )
+
+ else:
+ self.weather_box.update_data()
+ self.weather_box.refresh()
GLib.timeout_add(
REFRESH_MILLS,
@@ -99,30 +107,37 @@ def home_refresh(self):
def recheck_network(self):
self.network_state = check_internet()
-
GLib.idle_add(self.current_thread.join)
def home_init(self):
# Setting objects public variables to their object
- self.main_divider = self.builder.get_object("main_divider")
- self.main_stack = self.builder.get_object("main_stack")
+ self.home_divider = self.builder.get_object("home_divider")
+ self.home_stack = self.builder.get_object("home_stack")
self.category_view = self.builder.get_object("category_view")
- self.home_scroll_view = self.builder.get_object("home_scroll_view")
+ self.home_trending_scroll_view = self.builder.get_object(
+ "home_trending_scroll_view")
# Scrolling adjustment
self.category_view.set_focus_vadjustment(
- self.home_scroll_view.get_vadjustment()
+ self.home_trending_scroll_view.get_vadjustment()
)
# Add Sidebar to window and place it in the beginning
- self.sidebar = SideBar(self.main_stack)
- self.main_divider.pack_start(self.sidebar, False, False, 0)
- self.main_divider.reorder_child(self.sidebar, 0)
+ self.sidebar = SideBar(self.home_stack)
+ self.home_divider.pack_start(self.sidebar, False, False, 0)
+ self.home_divider.reorder_child(self.sidebar, 0)
# Fetch some environment variables
self.openweather_apikey = os.environ.get("OPEN_WEATHER_API_KEY")
self.unit_system = os.environ.get("UNIT_SYSTEM")
+ # Check if they are empty
+ if not self.unit_system:
+ self.unit_system = "metric"
+
+ if not self.openweather_apikey:
+ self.fetch_weather = False
+
# Setting weather_info to None
self.weather_info = None
@@ -146,7 +161,7 @@ def home_init(self):
# Make formatted location for open weather map
self.city_and_country = "{},{}".format(
self.location_info.city,
- self.location_info.countryCode
+ self.location_info.country_code
)
# Fetch all data that needs to be refreshed every 2 minutes
@@ -286,17 +301,6 @@ def validate_signup(self):
self.signup_error_label.set_text(
"Error code:" + str(response.status_code))
- # def on_sidebar_row_selected(self, listbox, listbox_row):
- # index = listbox_row.get_index()
- # item = SIDEBAR_LABELS[index]
- # getattr(self, item.lower()+"_stack")()
-
- # def home_stack(self):
- # print("Home")
-
- # def movies_stack(self):
- # print("Movies")
-
if __name__ == "__main__":
app = PiTV()
diff --git a/PiTV/location.py b/PiTV/location.py
new file mode 100644
index 0000000..6cccd94
--- /dev/null
+++ b/PiTV/location.py
@@ -0,0 +1,42 @@
+from requests import get
+from json import loads
+import socket
+
+
+# Returns hostname of machine
+def getHostname():
+ return socket.getfqdn()
+
+
+# Returns local IP address
+def getLocalIP():
+ return socket.gethostbyname(socket.gethostname())
+
+
+class Location:
+ def __init__(self):
+ self.location_info = loads(get("http://ip-api.com/json/").text)
+
+ @property
+ def city(self):
+ return self.location_info["city"]
+
+ @property
+ def public_ip(self):
+ return self.location_info["query"]
+
+ @property
+ def timezone(self):
+ return self.location_info["timezone"]
+
+ @property
+ def country(self):
+ return self.location_info["country"]
+
+ @property
+ def country_code(self):
+ return self.location_info["countryCode"]
+
+ @property
+ def region(self):
+ return self.location_info["regionName"]
diff --git a/PiTV/settings.py b/PiTV/settings.py
new file mode 100644
index 0000000..3fd17bd
--- /dev/null
+++ b/PiTV/settings.py
@@ -0,0 +1,89 @@
+import sqlite3
+
+CREATE_TABLE = \
+ "CREATE TABLE IF NOT EXISTS users ("\
+ "id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, "\
+ "username TEXT NOT NULL UNIQUE);"
+
+
+TYPES = {
+ "int": "INTEGER",
+ "str": "TEXT",
+ "bool": "BOOL",
+ "datetime": "DATE",
+ "float": "REAL",
+ "Percentage": "REAL"
+}
+
+SET = ""
+
+TABLES = "PRAGMA table_info(users);"
+
+
+class UserSettings(sqlite3.Connection):
+ """Saves user settings to database
+
+ :param username: username
+ :param location: full path to save the database
+
+ """
+
+ def __init__(self, username, location, *args):
+ super().__init__(location, *args)
+ self.username = username
+ self.cursor = self.cursor()
+ self.cursor.execute(CREATE_TABLE)
+
+ self.cursor.execute(
+ "SELECT username FROM users WHERE username=?",
+ (username,)
+ )
+
+ if not self.cursor.fetchone():
+ self.cursor.execute(
+ "INSERT INTO users (username) VALUES (?)",
+ (username,)
+ )
+
+ def _get_type(self, value):
+ """
+
+ :param value: value
+ :returns: SQLite3 type equivalent
+
+ """
+ val_type = type(value).__name__
+ return TYPES[val_type] if val_type in TYPES.keys() else "BLOB"
+
+ def set(self, col, value):
+ """Sets a value for setting
+
+ :param col: setting
+ :param value: value for setting
+
+ """
+ columns = [i[1] for i in self.cursor.execute(TABLES)]
+
+ if not col in columns:
+ self.cursor.execute(
+ f"ALTER TABLE users ADD COLUMN {col} {self._get_type(value)}"
+ )
+
+ self.cursor.execute(
+ f"UPDATE users SET {col}=? WHERE username=?",
+ (value, self.username)
+ )
+
+ def setsave(self, col, value):
+ """Sets a value for setting
+
+ :param col: setting
+ :param value: value for setting
+
+ """
+ self.set(col, value)
+ self.save()
+
+ def save(self):
+ """Saves changes to the database"""
+ self.commit()
diff --git a/PiTV/settings_view.glade b/PiTV/settings_view.glade
new file mode 100644
index 0000000..10c19cd
--- /dev/null
+++ b/PiTV/settings_view.glade
@@ -0,0 +1,69 @@
+
+
+
+
+
+ True
+ False
+ 4
+ 4
+ 4
+ vertical
+ 4
+
+
+ True
+ False
+ none
+ False
+
+
+ True
+ True
+ 0
+
+
+
+
+ True
+ False
+ end
+
+
+ Reset
+ True
+ True
+ True
+
+
+ False
+ True
+ 0
+
+
+
+
+ Apply
+ True
+ True
+ True
+
+
+ False
+ True
+ 1
+
+
+
+
+
+ False
+ True
+ end
+ 1
+
+
+
+
diff --git a/PiTV/settings_view.py b/PiTV/settings_view.py
new file mode 100644
index 0000000..1a14d83
--- /dev/null
+++ b/PiTV/settings_view.py
@@ -0,0 +1,109 @@
+"""
+SettingsView, SettingsRow template for PiTV, depends on settings_view.glade
+TODO: Make this file better coded, add comments, fix issues
+This file is the worst
+"""
+import os
+from settings import UserSettings
+from pathlib import Path
+
+# Bypass linters
+if True:
+ import gi
+ gi.require_version("Gtk", "3.0")
+ from gi.repository import Gtk
+
+HOME_DIR = os.path.dirname(os.path.abspath(__file__))
+
+WIDGET_GETTERS = {
+ Gtk.Scale: "get_value",
+ # Gtk.ComboBox,
+}
+
+
+Percentage = float
+
+
+class Setting:
+ name = None # REQUIRED
+ name_db = None # IF NULL, SET TO NAME
+ default = None # IF NULL, SET TO VALUE
+ available = None
+
+ def __init__(self, name_db, default, name=None, available=None):
+ self.name_db = name_db
+ self.available = available
+ self.name = name if name else name_db
+ self.default = default
+
+
+@Gtk.Template(filename=os.path.join(HOME_DIR, "settings_view.glade"))
+class SettingsView(Gtk.Box):
+ __gtype_name__ = 'SettingsView'
+
+ def __init__(self, username, settings, **kwargs):
+ super().__init__(**kwargs)
+
+ # TODO: also find better solution for this
+ self.settings_list, self.buttons = self.get_children()
+ self.apply_button, self.reset_button = self.buttons.get_children()
+
+ db_location = os.path.join(str(Path.home()), "pitv.db")
+
+ self.settings_db = UserSettings(username, db_location)
+ self.settings = settings
+ self.count = 0
+
+ self._values = []
+
+ for i in self.settings:
+ self.add_setting(i)
+
+ self.apply_button.connect("clicked", self.apply)
+ self.reset_button.connect("clicked", self.reset)
+
+ def apply(self, button):
+ # Apply code
+ # This will save settings to config directory
+ pass
+
+ def reset(self, button):
+ # Reset everything
+ pass
+
+ def add_setting(self, setting):
+ setter_widget = Gtk.Label(label=str(setting.default))
+
+ if setting.available:
+ available_store = Gtk.ListStore(type(setting.default))
+ setter_widget = Gtk.ComboBox.new_with_model(available_store)
+
+ for i, val in enumerate(setting.available):
+ available_store.append([val])
+ if val == setting.default:
+ setter_widget.set_active(i)
+
+ renderer_text = Gtk.CellRendererText()
+ setter_widget.pack_start(renderer_text, True)
+ setter_widget.add_attribute(renderer_text, "text", 0)
+
+ print(setter_widget.get_active())
+
+ elif isinstance(setting.default, Percentage):
+ setter_widget = Gtk.Scale.new_with_range(
+ Gtk.Orientation.HORIZONTAL, 0, 100, 1
+ )
+ setter_widget.set_size_request(500, -1)
+
+ self._values.append(setter_widget)
+ widget = Gtk.Box()
+ widget.pack_start(Gtk.Label(label=setting.name), False, False, 0)
+ widget.pack_end(setter_widget, False, False, 0)
+ self.settings_list.insert(widget, self.count)
+ self.count += 1
+
+ def get_value(self, widget):
+ pass
+
+ def get_values(self):
+ return [self.get_value(i) for i in self._values]
diff --git a/PiTV/sidebar.py b/PiTV/sidebar.py
index ac1a27d..80c6f86 100644
--- a/PiTV/sidebar.py
+++ b/PiTV/sidebar.py
@@ -1,5 +1,5 @@
"""
-Sidebar, ListTile and WeatherBox template for PiTV, depends on sidebar.glade
+Sidebar, ListTile and WeatherBox template for PiTV, depends on sidebar.glade, weather_box.glade, list_tile.glade
"""
import os
@@ -47,7 +47,7 @@ def __init__(self, stack, **kwargs):
self.stack = stack
self.index = 0
- # TODO:Better solution for this
+ # TODO:Better solution for this (critical)
self.actions = self.get_children()[0].get_children()[
0].get_children()[0]
diff --git a/PiTV/weather.py b/PiTV/weather.py
new file mode 100644
index 0000000..ccf411a
--- /dev/null
+++ b/PiTV/weather.py
@@ -0,0 +1,31 @@
+from requests import get
+from json import loads
+
+
+class Weather:
+ def __init__(self, apikey, measure, location):
+ self.apikey = apikey
+ self.measure = measure
+ self.location = location
+
+ self.url = "https://api.openweathermap.org/data/2.5/weather?"\
+ "q={}&units={}&appid={}"
+ self.response = get(self.url.format(location, measure, apikey)).text
+
+ self.parsed = loads(self.response)
+
+ def refresh(self):
+ self.response = get(self.url.format(
+ self.location,
+ self.measure,
+ self.apikey
+ )).text
+ self.parsed = loads(self.response)
+
+ @property
+ def temperature(self):
+ return self.parsed["main"]["temp"]
+
+ @property
+ def icon_code(self):
+ return self.parsed["weather"][0]["icon"]
diff --git a/README.md b/README.md
index 62b3d25..43ebdd1 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,5 @@
# PiTV
New version of SmartTV. Better code, faster, optimized.
-__Currently, it doesn't work, I have deleted the dependency, fix is comming soon!__
## Installation
diff --git a/bla.json b/bla.json
deleted file mode 100644
index 3904bc4..0000000
--- a/bla.json
+++ /dev/null
@@ -1,75 +0,0 @@
-{
- "status": "ok",
- "status_message": "Query was successful",
- "data": {
- "movie_count": 1,
- "limit": 20,
- "page_number": 1,
- "movies": [
- {
- "id": 1632,
- "url": "\/movie\/interstellar-2014",
- "imdb_code": "tt0816692",
- "title": "Interstellar",
- "title_english": "Interstellar",
- "title_long": "Interstellar (2014)",
- "slug": "interstellar-2014",
- "year": 2014,
- "rating": 8.6,
- "runtime": 169,
- "genres": [
- "Action",
- "Adventure",
- "Drama",
- "Sci-Fi"
- ],
- "summary": "Earth's future has been riddled by disasters, famines, and droughts. There is only one way to ensure mankind's survival: Interstellar travel. A newly discovered wormhole in the far reaches of our solar system allows a team of astronauts to go where no man has gone before, a planet that may have the right environment to sustain human life.",
- "description_full": "Earth's future has been riddled by disasters, famines, and droughts. There is only one way to ensure mankind's survival: Interstellar travel. A newly discovered wormhole in the far reaches of our solar system allows a team of astronauts to go where no man has gone before, a planet that may have the right environment to sustain human life.",
- "synopsis": "Earth's future has been riddled by disasters, famines, and droughts. There is only one way to ensure mankind's survival: Interstellar travel. A newly discovered wormhole in the far reaches of our solar system allows a team of astronauts to go where no man has gone before, a planet that may have the right environment to sustain human life.",
- "yt_trailer_code": "2LqzF5WauAw",
- "language": "English",
- "mpa_rating": "PG-13",
- "background_image": "\/assets\/images\/movies\/interstellar_2014\/background.jpg",
- "background_image_original": "\/assets\/images\/movies\/interstellar_2014\/background.jpg",
- "small_cover_image": "\/assets\/images\/movies\/interstellar_2014\/small-cover.jpg",
- "medium_cover_image": "\/assets\/images\/movies\/interstellar_2014\/medium-cover.jpg",
- "large_cover_image": "\/assets\/images\/movies\/interstellar_2014\/large-cover.jpg",
- "state": "ok",
- "torrents": [
- {
- "url": "\/torrent\/download\/6E88B3F25BA49D483D740A652BF013C341BC5373",
- "hash": "6E88B3F25BA49D483D740A652BF013C341BC5373",
- "quality": "720p",
- "type": "bluray",
- "seeds": 801,
- "peers": 142,
- "size": "1.02 GB",
- "size_bytes": 1095216660,
- "date_uploaded": "2015-11-01 00:18:06",
- "date_uploaded_unix": 1446333486
- },
- {
- "url": "\/torrent\/download\/89599BF4DC369A3A8ECA26411C5CCF922D78B486",
- "hash": "89599BF4DC369A3A8ECA26411C5CCF922D78B486",
- "quality": "1080p",
- "type": "bluray",
- "seeds": 1344,
- "peers": 237,
- "size": "2.26 GB",
- "size_bytes": 2426656522,
- "date_uploaded": "2015-11-01 00:18:07",
- "date_uploaded_unix": 1446333487
- }
- ],
- "date_uploaded": "2015-11-01 00:18:06",
- "date_uploaded_unix": 1446333486
- }
- ]
- },
- "@meta": {
- "server_time": 1585151462,
- "server_timezone": "CET",
- "api_version": 2,
- "execution_time": "0 ms"
- }
-}
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 02e8fa6..71cf202 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,3 @@
entertainment
-screeninfo
\ No newline at end of file
+screeninfo
+easysettings[all]
\ No newline at end of file