Version 111

This commit is contained in:
Hydrus 2014-04-16 15:31:59 -05:00
parent 375611ce4b
commit 44adb6ccfa
13 changed files with 199 additions and 36 deletions

View File

@ -9,6 +9,21 @@
<h3>changelog</h3>
<ul>
<li><h3>version 110</h3></li>
<ul>
<li>fixed booru searches that include unusual characters like '&'</li>
<li>added pause synchronisation to individual repositories in the manage services dialog</li>
<li>fixed gui_sessions with predicates (again!)</li>
<li>added a unit test for gui_session storage, shouldn't get problems with that again</li>
<li>fixed a getcountlesscopy predicates bug</li>
<li>first step in multi-platform upnp support is DONE, phew</li>
<li>harmonised subscription file import errors with normal hdd import errors. they nshould be a bit clearer</li>
<li>corrected how popup text width is enforced</li>
<li>added a warning to the reset cache button in the manage subscriptions dialog</li>
<li>os x autocomplete dropdown seems to be magically fixed, not sure how or when that happened</li>
<li>started HydrusBooru</li>
<li>fixed some problems with twisted startup/shutdown in unit testing</li>
</ul>
<li><h3>version 111</h3></li>
<ul>
<li>fixed a variable overwriting issue in the subscription daemon error reporting code that was causing error spam</li>
<li>fixed more actual and potential instances of this error elsewhere</li>

View File

@ -2106,7 +2106,7 @@ class Service( HC.HydrusYAMLBase ):
update_due = self._info[ 'next_download_timestamp' ] + HC.UPDATE_DURATION + 1800 < HC.GetNow()
return self.CanDownload() and update_due
return not self.IsPaused() and self.CanDownload() and update_due
def CanProcessUpdate( self ): return self._info[ 'next_download_timestamp' ] > self._info[ 'next_processing_timestamp' ]
@ -2193,6 +2193,8 @@ class Service( HC.HydrusYAMLBase ):
else: return True
def IsPaused( self ): return self._info[ 'paused' ]
def ProcessServiceUpdates( self, service_identifiers_to_service_updates ):
for ( service_identifier, service_updates ) in service_identifiers_to_service_updates.items():
@ -2333,7 +2335,6 @@ class Service( HC.HydrusYAMLBase ):
def SetCredentials( self, credentials ):
( host, port ) = credentials.GetAddress()

View File

@ -1342,6 +1342,8 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
if 'next_download_timestamp' not in info: info[ 'next_download_timestamp' ] = 0
if 'next_processing_timestamp' not in info: info[ 'next_processing_timestamp' ] = 0
info[ 'paused' ] = False
c.execute( 'INSERT INTO services ( service_key, service_type, name, info ) VALUES ( ?, ?, ?, ? );', ( sqlite3.Binary( service_key ), service_type, name, info ) )
@ -4932,6 +4934,21 @@ class DB( ServiceDB ):
c.execute( 'DROP INDEX mappings_status_index;' )
if version == 110:
all_services = c.execute( 'SELECT service_id, service_type, info FROM services;' ).fetchall()
for ( service_id, service_type, info ) in all_services:
if service_type in HC.REPOSITORIES:
info[ 'paused' ] = False
c.execute( 'UPDATE services SET info = ? WHERE service_id = ?;', ( info, service_id ) )
c.execute( 'UPDATE version SET version = ?;', ( version + 1, ) )
HC.is_db_updated = True
@ -8152,10 +8169,12 @@ def DAEMONSynchroniseSubscriptions():
except Exception as e:
text = 'While trying to execute a subscription, the url ' + url + ' caused this problem:' + os.linesep + traceback.format_exc()
text = 'While trying to execute subscription ' + name + ', the url ' + url + ' caused this problem:'
HC.ShowText( text )
HC.ShowException( e )
i += 1

View File

@ -1235,6 +1235,20 @@ class FileDropTarget( wx.FileDropTarget ):
def OnDropFiles( self, x, y, paths ): wx.CallAfter( self._callable, paths )
class FitResistantStaticText( wx.StaticText ):
def Wrap( self, width ):
wx.StaticText.Wrap( self, width )
( x, y ) = self.GetSize()
if x > width: x = width
self.SetMinSize( ( x, y ) )
self.SetMaxSize( ( width, -1 ) )
class Frame( wx.Frame ):
def __init__( self, *args, **kwargs ):
@ -2373,14 +2387,17 @@ class PopupMessageDBError( PopupMessage ):
text = wx.StaticText( self, label = HC.u( text ) )
text.Wrap( 380 )
text.SetMinSize( text.GetSize() )
text.SetMaxSize( text.GetSize() )
text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._show_caller_tb_button = wx.Button( self, label = 'show caller traceback' )
self._show_caller_tb_button.Bind( wx.EVT_BUTTON, self.EventShowCallerButton )
self._show_caller_tb_button.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._caller_tb_text = wx.StaticText( self, label = caller_traceback )
self._caller_tb_text = FitResistantStaticText( self, label = caller_traceback )
self._caller_tb_text.Wrap( 380 )
self._caller_tb_text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._caller_tb_text.Hide()
@ -2388,7 +2405,7 @@ class PopupMessageDBError( PopupMessage ):
self._show_db_tb_button.Bind( wx.EVT_BUTTON, self.EventShowDBButton )
self._show_db_tb_button.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._db_tb_text = wx.StaticText( self, label = db_traceback )
self._db_tb_text = FitResistantStaticText( self, label = db_traceback )
self._db_tb_text.Wrap( 380 )
self._db_tb_text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._db_tb_text.Hide()
@ -2463,7 +2480,7 @@ class PopupMessageError( PopupMessage ):
if len( HC.u( value ) ) > 0:
text = wx.StaticText( self, label = HC.u( value ) )
text = FitResistantStaticText( self, label = HC.u( value ) )
text.Wrap( 380 )
text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
@ -2472,7 +2489,7 @@ class PopupMessageError( PopupMessage ):
self._show_tb_button.Bind( wx.EVT_BUTTON, self.EventShowButton )
self._show_tb_button.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._tb_text = wx.StaticText( self, label = trace )
self._tb_text = FitResistantStaticText( self, label = trace )
self._tb_text.Wrap( 380 )
self._tb_text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._tb_text.Hide()
@ -2546,7 +2563,7 @@ class PopupMessageGauge( PopupMessage ):
vbox = wx.BoxSizer( wx.VERTICAL )
self._text = wx.StaticText( self, style = wx.ALIGN_CENTER )
self._text = FitResistantStaticText( self, style = wx.ALIGN_CENTER )
self._text.Wrap( 380 )
self._text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
@ -2668,7 +2685,7 @@ class PopupMessageText( PopupMessage ):
text = message.GetInfo( 'text' )
self._text = wx.StaticText( self, label = text )
self._text = FitResistantStaticText( self, label = text )
self._text.Wrap( 380 )
self._text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )

View File

@ -4938,7 +4938,14 @@ class DialogManageServices( ClientGUIDialogs.Dialog ):
self._check_service.Bind( wx.EVT_BUTTON, self.EventCheckService )
if service_identifier.GetType() == HC.LOCAL_RATING_LIKE:
if service_type in HC.REPOSITORIES:
self._pause_synchronisation = wx.CheckBox( self._service_panel, label = 'pause synchronisation' )
self._pause_synchronisation.SetValue( self._info[ 'paused' ] )
if service_type == HC.LOCAL_RATING_LIKE:
like = self._info[ 'like' ]
dislike = self._info[ 'dislike' ]
@ -4946,7 +4953,7 @@ class DialogManageServices( ClientGUIDialogs.Dialog ):
self._like = wx.TextCtrl( self._service_panel, value = like )
self._dislike = wx.TextCtrl( self._service_panel, value = dislike )
elif service_identifier.GetType() == HC.LOCAL_RATING_NUMERICAL:
elif service_type == HC.LOCAL_RATING_NUMERICAL:
lower = self._info[ 'lower' ]
upper = self._info[ 'upper' ]
@ -4984,7 +4991,13 @@ class DialogManageServices( ClientGUIDialogs.Dialog ):
gridbox.AddF( self._check_service, FLAGS_LONE_BUTTON )
if service_identifier.GetType() == HC.LOCAL_RATING_LIKE:
if service_type in HC.REPOSITORIES:
gridbox.AddF( ( 20, 20 ), FLAGS_MIXED )
gridbox.AddF( self._pause_synchronisation, FLAGS_LONE_BUTTON )
if service_type == HC.LOCAL_RATING_LIKE:
gridbox.AddF( wx.StaticText( self._service_panel, label = 'like' ), FLAGS_MIXED )
gridbox.AddF( self._like, FLAGS_EXPAND_BOTH_WAYS )
@ -4992,7 +5005,7 @@ class DialogManageServices( ClientGUIDialogs.Dialog ):
gridbox.AddF( wx.StaticText( self._service_panel, label = 'dislike' ), FLAGS_MIXED )
gridbox.AddF( self._dislike, FLAGS_EXPAND_BOTH_WAYS )
elif service_identifier.GetType() == HC.LOCAL_RATING_NUMERICAL:
elif service_type == HC.LOCAL_RATING_NUMERICAL:
gridbox.AddF( wx.StaticText( self._service_panel, label = 'lower limit' ), FLAGS_MIXED )
gridbox.AddF( self._lower, FLAGS_EXPAND_BOTH_WAYS )
@ -5104,6 +5117,11 @@ class DialogManageServices( ClientGUIDialogs.Dialog ):
info[ 'port' ] = port
if service_type in HC.REPOSITORIES:
info[ 'paused' ] = self._pause_synchronisation.GetValue()
if service_type == HC.LOCAL_RATING_LIKE:
info[ 'like' ] = self._like.GetValue()
@ -5657,10 +5675,19 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
def EventResetCache( self, event ):
self._reset_cache = True
self._reset_cache_button.SetLabel( 'cache will be reset on dialog ok' )
self._reset_cache_button.Disable()
message = '''Resetting this subscription's cache will delete ''' + HC.ConvertIntToPrettyString( len( self._original_info[ 'url_cache' ] ) ) + ''' remembered links, meaning when the subscription next runs, it will try to download those all over again. This may be expensive in time and data. Only do it if you are willing to wait. Do you want to do it?'''
with ClientGUIDialogs.DialogYesNo( self, message ) as dlg:
if dlg.ShowModal() == wx.ID_YES:
self._reset_cache = True
self._reset_cache_button.SetLabel( 'cache will be reset on dialog ok' )
self._reset_cache_button.Disable()
def EventSiteChanged( self, event ):

40
include/HydrusBooru.py Normal file
View File

@ -0,0 +1,40 @@
import collections
import cStringIO
import HydrusConstants as HC
import itertools
import os
import re
import threading
import time
import traceback
import wx
import yaml
# think about using mako, but it may not really be what you want
# basic template for a hydrus booru page
# title and all that
# javascript
# display header
# local server info
# how long user has access
# top and tail html tags
# css and so on
# left info div
# content div
# start javascript
# gallery info
# if the user has rights to search, show search box
# if the user is seeing a static query, show info
# tag counts, which can be fetched and displayed by the javascript
# gallery content
# draw each thumb in spans or whatever flows as we want
# screw limits, just show them all
# file info
# tags, file info
# generate file page
# just write the full size image <img>

View File

@ -48,7 +48,7 @@ TEMP_DIR = BASE_DIR + os.path.sep + 'temp'
# Misc
NETWORK_VERSION = 13
SOFTWARE_VERSION = 110
SOFTWARE_VERSION = 111
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
@ -1981,8 +1981,13 @@ class Predicate( HydrusYAMLBase ):
self._predicate_type = predicate_type
self._value = value
self._counts = collections.Counter()
self._counts.update( counts )
self._counts = {}
self._counts[ CURRENT ] = 0
self._counts[ PENDING ] = 0
for ( type, count ) in counts.items(): self.AddToCount( type, count )
def __eq__( self, other ): return self.__hash__() == other.__hash__()
@ -1997,7 +2002,7 @@ class Predicate( HydrusYAMLBase ):
def GetCopy( self ): return Predicate( self._predicate_type, self._value, self._counts )
def GetCountlessCopy( self ): return Predicate( self._predicate_type, self._value, None )
def GetCountlessCopy( self ): return Predicate( self._predicate_type, self._value )
def GetCount( self, type = None ):
@ -2219,10 +2224,10 @@ class Predicate( HydrusYAMLBase ):
SYSTEM_PREDICATE_INBOX = Predicate( PREDICATE_TYPE_SYSTEM, ( SYSTEM_PREDICATE_TYPE_INBOX, None ), None )
SYSTEM_PREDICATE_ARCHIVE = Predicate( PREDICATE_TYPE_SYSTEM, ( SYSTEM_PREDICATE_TYPE_ARCHIVE, None ), None )
SYSTEM_PREDICATE_LOCAL = Predicate( PREDICATE_TYPE_SYSTEM, ( SYSTEM_PREDICATE_TYPE_LOCAL, None ), None )
SYSTEM_PREDICATE_NOT_LOCAL = Predicate( PREDICATE_TYPE_SYSTEM, ( SYSTEM_PREDICATE_TYPE_NOT_LOCAL, None ), None )
SYSTEM_PREDICATE_INBOX = Predicate( PREDICATE_TYPE_SYSTEM, ( SYSTEM_PREDICATE_TYPE_INBOX, None ) )
SYSTEM_PREDICATE_ARCHIVE = Predicate( PREDICATE_TYPE_SYSTEM, ( SYSTEM_PREDICATE_TYPE_ARCHIVE, None ) )
SYSTEM_PREDICATE_LOCAL = Predicate( PREDICATE_TYPE_SYSTEM, ( SYSTEM_PREDICATE_TYPE_LOCAL, None ) )
SYSTEM_PREDICATE_NOT_LOCAL = Predicate( PREDICATE_TYPE_SYSTEM, ( SYSTEM_PREDICATE_TYPE_NOT_LOCAL, None ) )
class ResponseContext():

View File

@ -181,7 +181,7 @@ class DownloaderBooru( Downloader ):
else: index = self._num_pages_done * self._gallery_advance_num
return self._search_url.replace( '%tags%', self._search_separator.join( self._tags ) ).replace( '%index%', HC.u( index ) )
return self._search_url.replace( '%tags%', self._search_separator.join( [ urllib.quote( tag ) for tag in self._tags ] ) ).replace( '%index%', HC.u( index ) )
def _ParseGalleryPage( self, html, url_base ):
@ -425,7 +425,7 @@ class DownloaderGiphy( Downloader ):
def __init__( self, tag ):
self._gallery_url = 'http://giphy.com/api/gifs?tag=' + tag.replace( ' ', '+' ) + '&page='
self._gallery_url = 'http://giphy.com/api/gifs?tag=' + urllib.quote( tag.replace( ' ', '+' ) ) + '&page='
Downloader.__init__( self )
@ -858,7 +858,7 @@ class DownloaderPixiv( Downloader ):
tag = urllib.quote( tag.encode( 'utf-8' ) )
gallery_url = 'http://www.pixiv.net/search.php?word=' + tag + '&s_mode=s_tag_full&order=date_d'
gallery_url = 'http://www.pixiv.net/search.php?word=' + urllib.quote( tag ) + '&s_mode=s_tag_full&order=date_d'
return gallery_url + '&p=' + HC.u( self._num_pages_done + 1 )

View File

@ -162,6 +162,20 @@ class Message( HC.HydrusYAMLBase ):
return verifier.verify( hash_object, self._signature )
# here begins the new stuff, I'm pretty sure
class Identity(): # should be a yamlable object
def __init__( self ):
# no name, right? we associate names and addresses with the identity, but the id only has keys
# store key_type -> key
# hence need a key_type enum
pass
class IMManager():
def __init__( self ):

View File

@ -99,7 +99,7 @@ class HydrusSessionManagerClient():
HC.app.Write( 'delete_hydrus_session_key', service_identifier )
del self._sessions[ service_identifier ]
if service_identifier in self._sessions: del self._sessions[ service_identifier ]

View File

@ -592,6 +592,21 @@ class TestClientDB( unittest.TestCase ):
for i in range( len( predicates ) ): self.assertEqual( result[i].GetCount(), predicates[i].GetCount() )
def test_gui_sessions( self ):
info = []
info.append( ( 'blank', 'class_text', ( HC.LOCAL_FILE_SERVICE_IDENTIFIER, ), { 'initial_hashes' : [], 'initial_media_results' : [], 'initial_predicates' : [] } ) )
info.append( ( 'system', 'class_text', ( HC.LOCAL_FILE_SERVICE_IDENTIFIER, ), { 'initial_hashes' : [ os.urandom( 32 ) for i in range( 8 ) ], 'initial_media_results' : [], 'initial_predicates' : [ HC.SYSTEM_PREDICATE_ARCHIVE ] } ) )
info.append( ( 'tags', 'class_text', ( HC.LOCAL_FILE_SERVICE_IDENTIFIER, ), { 'initial_hashes' : [ os.urandom( 32 ) for i in range( 4 ) ], 'initial_media_results' : [], 'initial_predicates' : [ HC.Predicate( HC.PREDICATE_TYPE_TAG, ( '+', 'tag' ), counts = { HC.CURRENT : 1, HC.PENDING : 3 } ) ] } ) )
self._write( 'gui_session', 'normal', info )
result = self._read( 'gui_sessions' )
self.assertEqual( result, { 'normal' : info } )
def test_imageboard( self ):
[ ( site_name_4chan, read_imageboards ) ] = self._read( 'imageboards' ).items()

View File

@ -0,0 +1,13 @@
import ClientConstants as CC
import HydrusConstants as HC
import HydrusNATPunch
import os
import time
import unittest
class TestNATPunch( unittest.TestCase ):
def test_info( self ):
pass

13
test.py
View File

@ -11,6 +11,7 @@ from include import TestDB
from include import TestFunctions
from include import TestHydrusDownloading
from include import TestHydrusEncryption
from include import TestHydrusNATPunch
from include import TestHydrusServer
from include import TestHydrusSessions
from include import TestHydrusTags
@ -18,6 +19,7 @@ import collections
import os
import sys
import threading
import time
import unittest
import wx
from twisted.internet import reactor
@ -79,25 +81,20 @@ class App( wx.App ):
if run_all or only_run == 'encryption': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusEncryption ) )
if run_all or only_run == 'functions': suites.append( unittest.TestLoader().loadTestsFromModule( TestFunctions ) )
if run_all or only_run == 'downloading': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusDownloading ) )
if run_all or only_run == 'nat': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusNATPunch ) )
if run_all or only_run == 'server': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusServer ) )
if run_all or only_run == 'sessions': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusSessions ) )
if run_all or only_run == 'tags': suites.append( unittest.TestLoader().loadTestsFromModule( TestHydrusTags ) )
suite = unittest.TestSuite( suites )
if run_all or only_run == 'server':
threading.Thread( target = reactor.run, kwargs = { 'installSignalHandlers' : 0 } ).start()
threading.Thread( target = reactor.run, kwargs = { 'installSignalHandlers' : 0 } ).start()
runner = unittest.TextTestRunner( verbosity = 1 )
runner.run( suite )
if run_all or only_run == 'server':
reactor.callFromThread( reactor.stop )
reactor.callFromThread( reactor.stop )
return True