def open_connection(self, token=None): self.cached_data = {} refresh_token = self.get_password() if not refresh_token: return False if not token: # create expired token token = { 'access_token': 'xxx', 'refresh_token': refresh_token, 'expires_in': -30, } client_id = key_store.get('googlephotos', 'client_id') client_secret = key_store.get('googlephotos', 'client_secret') auto_refresh_kwargs = { 'client_id': client_id, 'client_secret': client_secret, } token_url = self.oauth_url + 'v4/token' self.api = OAuth2Session(client_id=client_id, token=token, token_updater=self.save_token, auto_refresh_kwargs=auto_refresh_kwargs, auto_refresh_url=token_url) if token['expires_in'] < 0: # refresh token self.api.refresh_token(token_url, **auto_refresh_kwargs) self.connection_changed.emit(self.api.authorized) return self.api.authorized
def get_auth_url(self, level): logger.info('using %s', keyring.get_keyring().__module__) api_key = key_store.get('flickr', 'api_key') api_secret = key_store.get('flickr', 'api_secret') token = flickrapi.auth.FlickrAccessToken('', '', self.perms[level]) self.api = flickrapi.FlickrAPI( api_key, api_secret, token=token, store_token=False) self.api.get_request_token(oauth_callback='oob') return self.api.auth_url(perms=self.perms[level])
def get_access_token(self, auth_code): # Fix for requests-oauthlib bug #157 # https://github.com/requests/requests-oauthlib/issues/157 os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = 'True' client_id = key_store.get('picasa', 'client_id') client_secret = key_store.get('picasa', 'client_secret') self.token = self.session.fetch_token( self.token_url, code=auth_code, auth=requests.auth.HTTPBasicAuth(client_id, client_secret)) self._save_token(self.token) self.session = None
def get_auth_url(self, level): api_key = key_store.get('flickr', 'api_key') api_secret = key_store.get('flickr', 'api_secret') token = flickrapi.auth.FlickrAccessToken('', '', 'write') self.api = flickrapi.FlickrAPI(api_key, api_secret, token=token, store_token=False, format='parsed-json') self.api.get_request_token(oauth_callback='oob') return self.api.auth_url(perms='write')
def permitted(self, level): stored_token = keyring.get_password('photini', 'flickr') if not stored_token: self.api = None return False if not self.api: api_key = key_store.get('flickr', 'api_key') api_secret = key_store.get('flickr', 'api_secret') token, token_secret = stored_token.split('&') token = flickrapi.auth.FlickrAccessToken( token, token_secret, self.perms[level]) self.api = flickrapi.FlickrAPI( api_key, api_secret, token=token, store_token=False) return self.api.token_valid(perms=self.perms[level])
class TabWidget(PhotiniMap): api_key = key_store.get('bingmap', 'api_key') @staticmethod def tab_name(): return translate('MapTabBing', 'Map (&Bing)') def get_geocoder(self): return BingGeocoder(parent=self) def get_head(self): url = 'http://www.bing.com/api/maps/mapcontrol?callback=initialize' url += '&key=' + self.api_key lang, encoding = locale.getdefaultlocale() if lang: culture = lang.replace('_', '-') url += '&setMkt=' + culture language, sep, region = culture.partition('-') url += '&setLang=' + language if self.app.options.test: url += '&branch=experimental' return ''' <script type="text/javascript" src="{}" async> </script>'''.format(url) @catch_all def new_status(self, status): super(TabWidget, self).new_status(status) if 'session_id' in status: # use map session key to make API calls non-billable self.geocoder.api_key = status['session_id']
def __init__(self, *arg, **kw): super(OpenStreetMap, self).__init__(*arg, **kw) self.block_timer = QtCore.QTimer(self) self.block_timer.setInterval(10000) self.block_timer.setSingleShot(True) self.block_timer.timeout.connect(self.enable_search) self.api_key = key_store.get('opencagedata', 'api_key')
def permitted(self, level): refresh_token = keyring.get_password('photini', 'picasa') if not refresh_token: self.session = None self.token = None return False if not self.token: # create expired token self.token = { 'access_token': 'xxx', 'refresh_token': refresh_token, 'expires_in': -30, } self.session = None if not self.session: # create new session client_id = key_store.get('picasa', 'client_id') client_secret = key_store.get('picasa', 'client_secret') auto_refresh_kwargs = { 'client_id': client_id, 'client_secret': client_secret, } if self.auto_refresh: self.session = OAuth2Session( client_id, token=self.token, token_updater=self._save_token, auto_refresh_kwargs=auto_refresh_kwargs, auto_refresh_url=self.token_url, ) else: self.session = OAuth2Session(client_id, token=self.token) self.session.verify = certifi.old_where() # refresh manually to get a valid token now self.token = self.session.refresh_token(self.token_url, **auto_refresh_kwargs) self.session.headers.update({'GData-Version': '2'}) # verify the token resp = self._check_response( self.session.get( 'https://www.googleapis.com/oauth2/v3/tokeninfo', params={'access_token': self.token['access_token']})).json() if resp['scope'] != self.scope[level] or resp['aud'] != client_id: return False return True
def permitted(self, level): stored_token = self.get_password() if not stored_token: self.api = None return False if not self.api: api_key = key_store.get('flickr', 'api_key') api_secret = key_store.get('flickr', 'api_secret') token, token_secret = stored_token.split('&') token = flickrapi.auth.FlickrAccessToken(token, token_secret, 'write') self.api = flickrapi.FlickrAPI(api_key, api_secret, token=token, store_token=False, format='parsed-json') return self.api.token_valid(perms='write')
def get_auth_url(self, level): logger.info('using %s', keyring.get_keyring().__module__) client_id = key_store.get('picasa', 'client_id') self.session = OAuth2Session( client_id, scope=self.scope[level], redirect_uri='urn:ietf:wg:oauth:2.0:oob') return self.session.authorization_url( 'https://accounts.google.com/o/oauth2/v2/auth')[0]
def get_auth_url(self, level): logger.info('using %s', keyring.get_keyring().__module__) client_id = key_store.get('picasa', 'client_id') self.session = OAuth2Session(client_id, scope=self.scope[level], redirect_uri='urn:ietf:wg:oauth:2.0:oob') return self.session.authorization_url( 'https://accounts.google.com/o/oauth2/v2/auth')[0]
def get_auth_url(self, redirect_uri): code_verifier = '' while len(code_verifier) < 43: code_verifier += OAuth2Session().new_state() self.auth_params = { 'client_id': key_store.get('googlephotos', 'client_id'), 'client_secret': key_store.get('googlephotos', 'client_secret'), 'code_verifier': code_verifier, 'redirect_uri': redirect_uri, } url = 'https://accounts.google.com/o/oauth2/v2/auth' url += '?client_id=' + self.auth_params['client_id'] url += '&redirect_uri=' + self.auth_params['redirect_uri'] url += '&response_type=code' url += '&scope=' + urllib.parse.quote(' '.join(self.scope)) url += '&code_challenge=' + self.auth_params['code_verifier'] return url
class GoogleGeocoder(GeocoderBase): api_key = key_store.get('googlemap', 'api_key') interval = 50 def query(self, url, params): params['key'] = self.api_key with Busy(): self.rate_limit() try: rsp = requests.get(url, params=params, timeout=5) except Exception as ex: logger.error(str(ex)) return [] rsp = rsp.json() if rsp['status'] != 'OK': if 'error_message' in rsp: logger.error('Search error: %s: %s', rsp['status'], rsp['error_message']) else: logger.error('Search error: %s', rsp['status']) return [] results = rsp['results'] if not results: logger.error('No results found') return [] return results def get_altitude(self, coords): params = {'locations': coords.replace(' ', '')} results = self.query( 'https://maps.googleapis.com/maps/api/elevation/json', params) if results: return results[0]['elevation'] return None def search(self, search_string, bounds=None): params = {'address': search_string} lang, encoding = locale.getdefaultlocale() if lang: params['language'] = lang if bounds: north, east, south, west = bounds params['bounds'] = '{!r},{!r}|{!r},{!r}'.format( south, west, north, east) for result in self.query( 'https://maps.googleapis.com/maps/api/geocode/json', params): bounds = result['geometry']['viewport'] yield (bounds['northeast']['lat'], bounds['northeast']['lng'], bounds['southwest']['lat'], bounds['southwest']['lng'], result['formatted_address']) def search_terms(self): widget = QtWidgets.QLabel( translate('MapTabGoogle', 'Search and altitude lookup') + '\npowered by Google') widget.setAlignment(Qt.AlignRight) scale_font(widget, 80) return [widget]
def connect(self): api_key = key_store.get('flickr', 'api_key') api_secret = key_store.get('flickr', 'api_secret') stored_token = self.get_password() if stored_token: token, token_secret = stored_token.split('&') else: token, token_secret = '', '' token = flickrapi.auth.FlickrAccessToken(token, token_secret, 'write') self.api = flickrapi.FlickrAPI( api_key, api_secret, token=token, store_token=False, format='parsed-json') self.cached_data = {} try: if self.api.token_valid(perms='write'): self.connection_changed.emit(True) return True except flickrapi.FlickrError as ex: logger.error(str(ex)) return False
def permitted(self, level): refresh_token = keyring.get_password('photini', 'picasa') if not refresh_token: self.session = None self.token = None return False if not self.token: # create expired token self.token = { 'access_token' : 'xxx', 'refresh_token' : refresh_token, 'expires_in' : -30, } self.session = None if not self.session: # create new session client_id = key_store.get('picasa', 'client_id') client_secret = key_store.get('picasa', 'client_secret') auto_refresh_kwargs = { 'client_id' : client_id, 'client_secret': client_secret, } if self.auto_refresh: self.session = OAuth2Session( client_id, token=self.token, token_updater=self._save_token, auto_refresh_kwargs=auto_refresh_kwargs, auto_refresh_url=self.token_url, ) else: self.session = OAuth2Session(client_id, token=self.token) # refresh manually to get a valid token now self.token = self.session.refresh_token( self.token_url, **auto_refresh_kwargs) self.session.headers.update({'GData-Version': 2}) # verify the token resp = self._check_response(self.session.get( 'https://www.googleapis.com/oauth2/v3/tokeninfo', params={'access_token': self.token['access_token']})).json() if resp['scope'] != self.scope[level] or resp['aud'] != client_id: return False return True
def get_auth_url(self, level): logger.info('using %s', keyring.get_keyring().__module__) app_id = key_store.get('facebook', 'app_id') client = oauthlib.oauth2.MobileApplicationClient(app_id) self.session = OAuth2Session( client=client, scope=self.scope[level], redirect_uri='https://www.facebook.com/connect/login_success.html', ) result = self.session.authorization_url( 'https://www.facebook.com/dialog/oauth', display='popup', auth_type='rerequest')[0] # use unquote to prevent "redirect_uri URL is not properly # formatted" error on Windows return unquote(result)
def load_api(self): url = 'http://maps.googleapis.com/maps/api/js?v=3' if self.app.test_mode: url += '.exp' url += '&key=' + key_store.get('google', 'api_key') lang, encoding = locale.getdefaultlocale() if lang: match = re.match('[a-zA-Z]+[-_]([A-Z]+)', lang) if match: name = match.group(1) if name: url += '®ion=' + name return """ <script type="text/javascript" src="{}"> </script> """.format(url)
class TabWidget(PhotiniMap): api_key = key_store.get('mapboxmap', 'api_key') @staticmethod def tab_name(): return translate('MapTabMapbox', 'Map (&Mapbox)') def get_geocoder(self): return MapboxGeocoder(parent=self) def get_head(self): return ''' <link rel="stylesheet" href="{url}/mapbox.css" /> <script type="text/javascript" src="{url}/mapbox.js"> </script> <script type="text/javascript"> L.mapbox.accessToken = "{key}"; </script> <script type="text/javascript" src="../openstreetmap/common.js"> </script>'''.format(key=self.api_key, url='https://api.mapbox.com/mapbox.js/v3.2.1')
def get_page_elements(self): url = 'http://maps.googleapis.com/maps/api/js?callback=initialize&v=3' if self.app.test_mode: url += '.exp' url += '&key=' + key_store.get('google', 'api_key') lang, encoding = locale.getdefaultlocale() if lang: match = re.match('[a-zA-Z]+[-_]([A-Z]+)', lang) if match: name = match.group(1) if name: url += '®ion=' + name return { 'head': '', 'body': ''' <script type="text/javascript" src="{}" async defer> </script> '''.format(url), }
def get_page_elements(self): url = 'http://www.bing.com/api/maps/mapcontrol?callback=initialize' lang, encoding = locale.getdefaultlocale() if lang: url += '&mkt={0},ngt'.format(lang.replace('_', '-')) else: url += '&mkt=ngt' if self.app.test_mode: url += '&branch=experimental' return { 'head': ''' <script type="text/javascript"> var api_key = "{}"; </script> <script type="text/javascript" src="{}" async defer> </script> '''.format(key_store.get('bing', 'api_key'), url), 'body': '', }
def load_api(self): if self.app.test_mode: src = 'http://www.bing.com/api/maps/mapcontrol?callback=initialize' src += '&branch=experimental' version_8 = 'true' else: src = 'http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0' version_8 = 'false' self.setProperty('api_key', key_store.get('bing', 'api_key')) if not self.app.test_mode: lang, encoding = locale.getdefaultlocale() if lang: src += '&mkt={0},ngt'.format(lang.replace('_', '-')) else: src += '&mkt=ngt' return """ <script type="text/javascript"> var VERSION_8 = {1}; </script> <script charset="UTF-8" type="text/javascript" src="{0}"> </script> """.format(src, version_8)
class TabWidget(PhotiniMap): api_key = key_store.get('googlemap', 'api_key') @staticmethod def tab_name(): return translate('MapTabGoogle', 'Map (&Google)') def get_geocoder(self): return GoogleGeocoder(parent=self) def get_head(self): url = 'http://maps.googleapis.com/maps/api/js?callback=initialize&v=3' if self.app.test_mode: url += '.exp' url += '&key=' + self.api_key lang, encoding = locale.getdefaultlocale() if lang: language, sep, region = lang.replace('_', '-').partition('-') url += '&language=' + language if region: url += '®ion=' + region return ''' <script type="text/javascript" src="{}" async> </script>'''.format(url)
def __init__(self, *arg, **kwds): super(UploaderSession, self).__init__(*arg, **kwds) self.api = None # get api client id and secret for option in key_store.config.options(self.name): setattr(self, option, key_store.get(self.name, option))
class MapboxGeocoder(GeocoderBase): api_key = key_store.get('mapboxmap', 'api_key') def do_geocode(self, query, params={}): params['access_token'] = self.api_key params['autocomplete '] = 'false' lang, encoding = locale.getdefaultlocale() if lang: params['language'] = lang query += '.json' url = 'https://api.mapbox.com/geocoding/v5/mapbox.places/' + query with Busy(): self.rate_limit() try: rsp = requests.get(url, params=params, timeout=5) except Exception as ex: logger.error(str(ex)) return [] if rsp.status_code >= 400: logger.error('Search error %d', rsp.status_code) return [] self.block_timer.setInterval( self.interval * 600 // max(int(rsp.headers['X-Rate-Limit-Limit']), 1)) rsp = rsp.json() return rsp['features'] def search(self, search_string, bounds=None): params = { 'limit': 10, } if bounds: north, east, south, west = bounds margin = (10.0 + west - east) / 2.0 if margin > 0.0: east += margin west -= margin margin = (10.0 + south - north) / 2.0 if margin > 0.0: north = min(north + margin, 90.0) south = max(south - margin, -90.0) params['bbox'] = '{!r},{!r},{!r},{!r}'.format( west, south, east, north) for feature in self.do_geocode(search_string, params=params): if 'place_name' not in feature: continue if 'bbox' in feature: west, south, east, north = feature['bbox'] yield north, east, south, west, feature['place_name'] elif 'center' in feature: east, north = feature['center'] yield north, east, None, None, feature['place_name'] def search_terms(self): widget = CompactButton( translate('MapTabMapbox', 'Search powered by Mapbox')) widget.clicked.connect(self.load_mapbox_tos) return [widget] @QtCore.Slot() @catch_all def load_mapbox_tos(self): QtGui.QDesktopServices.openUrl( QtCore.QUrl('https://www.mapbox.com/tos/'))
def __init__(self, image_list, parent=None): super(PhotiniMap, self).__init__(parent) self.app = QtWidgets.QApplication.instance() self.image_list = image_list self.geocode_cache = OrderedDict() name = self.__module__.split('.')[-1] self.api_key = key_store.get(name, 'api_key') self.search_key = key_store.get('opencage', 'api_key') self.script_dir = pkg_resources.resource_filename( 'photini', 'data/' + name + '/') self.drag_icon = QtGui.QPixmap( os.path.join(self.script_dir, '../map_pin_grey.png')) self.drag_hotspot = 11, 35 self.search_string = None self.map_loaded = False self.marker_info = {} self.map_status = {} self.dropped_images = [] self.setChildrenCollapsible(False) left_side = QtWidgets.QWidget() self.addWidget(left_side) left_side.setLayout(QtWidgets.QFormLayout()) left_side.layout().setContentsMargins(0, 0, 0, 0) left_side.layout().setFieldGrowthPolicy( QtWidgets.QFormLayout.AllNonFixedFieldsGrow) # map # create handler for calls from JavaScript self.call_handler = CallHandler(parent=self) self.map = MapWebView(self.call_handler) self.map.drop_text.connect(self.drop_text) self.map.setAcceptDrops(False) self.addWidget(self.map) # search search_layout = QtWidgets.QFormLayout() search_layout.setContentsMargins(0, 0, 0, 0) search_layout.setVerticalSpacing(0) search_layout.setFieldGrowthPolicy( QtWidgets.QFormLayout.AllNonFixedFieldsGrow) self.edit_box = ComboBox() self.edit_box.setEditable(True) self.edit_box.setInsertPolicy(QtWidgets.QComboBox.NoInsert) self.edit_box.lineEdit().setPlaceholderText( translate('PhotiniMap', '<new search>')) self.edit_box.lineEdit().returnPressed.connect(self.search) self.edit_box.activated.connect(self.goto_search_result) self.clear_search() self.edit_box.setEnabled(False) search_layout.addRow(translate('PhotiniMap', 'Search'), self.edit_box) # search terms and conditions terms = self.search_terms() if terms: search_layout.addRow(*terms) left_side.layout().addRow(search_layout) if terms: divider = QtWidgets.QFrame() divider.setFrameStyle(QtWidgets.QFrame.HLine) left_side.layout().addRow(divider) left_side.layout().addItem( QtWidgets.QSpacerItem(1, 1000, vPolicy=QtWidgets.QSizePolicy.Expanding)) # latitude & longitude layout = QtWidgets.QHBoxLayout() self.coords = SingleLineEdit() self.coords.editingFinished.connect(self.new_coords) self.coords.setEnabled(False) layout.addWidget(self.coords) # convert lat/lng to location info self.auto_location = QtWidgets.QPushButton( translate('PhotiniMap', six.unichr(0x21e8) + ' address')) self.auto_location.setFixedHeight(self.coords.height()) self.auto_location.setEnabled(False) self.auto_location.clicked.connect(self.get_address) layout.addWidget(self.auto_location) left_side.layout().addRow(translate('PhotiniMap', 'Lat, long'), layout) # location info self.location_widgets = [] self.location_info = QtWidgets.QTabWidget() tab_bar = QTabBar() self.location_info.setTabBar(tab_bar) tab_bar.context_menu.connect(self.location_tab_context_menu) tab_bar.tabMoved.connect(self.location_tab_moved) self.location_info.setElideMode(Qt.ElideLeft) self.location_info.setMovable(True) self.location_info.setEnabled(False) left_side.layout().addRow(self.location_info) # address lookup (and default search) terms and conditions layout = QtWidgets.QHBoxLayout() if terms: widget = CompactButton( self.tr('Address lookup\npowered by OpenCage')) else: widget = CompactButton( self.tr('Search && lookup\npowered by OpenCage')) widget.clicked.connect(self.load_tou_opencage) layout.addWidget(widget) widget = CompactButton( self.tr('Geodata © OpenStreetMap\ncontributors')) widget.clicked.connect(self.load_tou_osm) layout.addWidget(widget) left_side.layout().addRow(layout) # other init self.image_list.image_list_changed.connect(self.image_list_changed) self.splitterMoved.connect(self.new_split) self.block_timer = QtCore.QTimer(self) self.block_timer.setInterval(5000) self.block_timer.setSingleShot(True) self.block_timer.timeout.connect(self.enable_search)
class OpenCage(GeocoderBase): api_key = key_store.get('opencage', 'api_key') def __init__(self, *args, **kwds): super(OpenCage, self).__init__(*args, **kwds) self.geocode_cache = OrderedDict() def do_geocode(self, params): cache_key = params['q'] if 'bounds' in params: cache_key += params['bounds'] if cache_key in self.geocode_cache: return self.geocode_cache[cache_key] params['key'] = self.api_key params['abbrv'] = '1' params['no_annotations'] = '1' lang, encoding = locale.getdefaultlocale() if lang: params['language'] = lang with Busy(): self.rate_limit() try: rsp = requests.get( 'https://api.opencagedata.com/geocode/v1/json', params=params, timeout=5) except Exception as ex: logger.error(str(ex)) return [] if rsp.status_code >= 400: logger.error('Search error %d', rsp.status_code) return [] rsp = rsp.json() status = rsp['status'] if status['code'] != 200: logger.error( 'Search error %d: %s', status['code'], status['message']) return [] if rsp['total_results'] < 1: logger.error('No results found') return [] rate = rsp['rate'] self.block_timer.setInterval( 5000 * rate['limit'] // max(rate['remaining'], 1)) self.geocode_cache[cache_key] = rsp['results'] while len(self.geocode_cache) > 20: self.geocode_cache.popitem(last=False) return rsp['results'] def search(self, search_string, bounds=None): params = { 'q' : search_string, 'limit' : '20', } if bounds: north, east, south, west = bounds margin = (10.0 + west - east) / 2.0 if margin > 0.0: east += margin west -= margin margin = (10.0 + south - north) / 2.0 if margin > 0.0: north = min(north + margin, 90.0) south = max(south - margin, -90.0) params['bounds'] = '{!r},{!r},{!r},{!r}'.format( west, south, east, north) for result in self.do_geocode(params): yield (result['bounds']['northeast']['lat'], result['bounds']['northeast']['lng'], result['bounds']['southwest']['lat'], result['bounds']['southwest']['lng'], result['formatted']) # Map OpenCage address components to IPTC address heirarchy. There # are many possible components (user generated data) so any # unrecognised ones are put in 'sublocation'. See # https://github.com/OpenCageData/address-formatting/blob/master/conf/components.yaml address_map = { 'world_region' :('continent',), 'country_code' :('ISO_3166-1_alpha-3', 'ISO_3166-1_alpha-2', 'country_code'), 'country_name' :('country', 'country_name'), 'province_state':('county', 'county_code', 'local_administrative_area', 'state_district', 'state', 'state_code', 'province', 'region', 'island'), 'city' :('neighbourhood', 'suburb', 'city_district', 'district', 'quarter', 'residential', 'commercial', 'industrial', 'houses', 'subdivision', 'city', 'town', 'municipality', 'postcode'), 'sublocation' :('house_number', 'street_number', 'house', 'public_building', 'building', 'water', 'road', 'pedestrian', 'path', 'street_name', 'street', 'cycleway', 'footway', 'place', 'square', 'village', 'locality', 'hamlet', 'croft'), 'ignore' :('political_union', 'road_reference', 'road_reference_intl', 'road_type', '_category', '_type'), } def get_address(self, coords): results = self.do_geocode({'q': coords.replace(' ', '')}) if not results: return None address = dict(results[0]['components']) if 'county_code' in address and 'county' in address: del address['county_code'] if 'state_code' in address and 'state' in address: del address['state_code'] return Location.from_address(address, self.address_map) def search_terms(self, search=True): if search: text = self.tr('Search powered by OpenCage') else: text = self.tr('Address lookup powered by OpenCage') tou_opencage = CompactButton(text) tou_opencage.clicked.connect(self.load_tou_opencage) tou_osm = CompactButton( self.tr('Geodata © OpenStreetMap contributors')) tou_osm.clicked.connect(self.load_tou_osm) return [tou_opencage, tou_osm] @QtSlot() @catch_all def load_tou_opencage(self): QtGui.QDesktopServices.openUrl( QtCore.QUrl('https://geocoder.opencagedata.com/')) @QtSlot() @catch_all def load_tou_osm(self): QtGui.QDesktopServices.openUrl( QtCore.QUrl('http://www.openstreetmap.org/copyright'))