Version 202
This commit is contained in:
parent
505db9306e
commit
596667f655
|
@ -8,6 +8,42 @@
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h3>changelog</h3>
|
<h3>changelog</h3>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li><h3>version 202</h3></li>
|
||||||
|
<ul>
|
||||||
|
<li>fixed a problem with the v198->v199 update step</li>
|
||||||
|
<li>added a bad error catch in the vacuum step in v200->v201</li>
|
||||||
|
<li>added some rollback exception handling for unusual situations</li>
|
||||||
|
<li>the autocomplete caches under the client_cache subdirectory are dynamically folded into a single client.caches.db file, and that file is folded into the main db journal like the other attached dbs</li>
|
||||||
|
<li>ac_cache file and tag processing lag and edge-case autocomplete miscounting should be removed as a result</li>
|
||||||
|
<li>cache generation is optimised for empty services</li>
|
||||||
|
<li>specific file_caches' add and delete file commands now modify their tables directly, saving a whole load of time that was previously done superfluously filtering</li>
|
||||||
|
<li>the specific ac_caches' add mappings command is a bit more logically efficient</li>
|
||||||
|
<li>increased cache size for each attached database</li>
|
||||||
|
<li>folded all 'attached' db update code into the main update_db routine</li>
|
||||||
|
<li>folded all 'attached' db init code into the main create_db routine</li>
|
||||||
|
<li>made the vacuum checking routine safer</li>
|
||||||
|
<li>reduced analyze log spam</li>
|
||||||
|
<li>analyze maintenance breaks its larger jobs up better</li>
|
||||||
|
<li>analyze mainenance now analyzes external databases</li>
|
||||||
|
<li>the db connection now refreshes every half hour, to regularly clear out journal files</li>
|
||||||
|
<li>the hydrus db assumes it generally has exclusive control over its db, so it no longer wraps its read-only requests in transactions</li>
|
||||||
|
<li>session load now happens off the main gui thread, and media generation is broken into 256-file chunks, which reduces startup db and gui lag for large sessions</li>
|
||||||
|
<li>the youtube video downloader now lists a webm option if youtube provides one (they seem to offer 360p webms on everything(?) now)</li>
|
||||||
|
<li>the timedeltacontrol (where you set the period for the thread checker and import folders and so on) is replaced by the more compact timedeltabutton, which will launch a dialog with the old control when clicked</li>
|
||||||
|
<li>the server will no longer revisit old updates and cull since-deleted content, as this was proving cpu-burdensome and not helpful to future plans for update dissemination, which will rely on static update files</li>
|
||||||
|
<li>misc cleanup of some server db stuff</li>
|
||||||
|
<li>content update processing will not spam menu updates throughout, but only notify once at the end, which should reduce idle gui hang due to db access choke</li>
|
||||||
|
<li>the autocomplete tag entry will not refresh system pred menu during idle, which should reduce idle gui hang due to db access choke</li>
|
||||||
|
<li>shutdown repo sync will now report the update summary text and will not spam the intervening statuses to the log</li>
|
||||||
|
<li>moved timestamp out of the serviceless media_result and into the locations_manager, to reflect the database's knowledge of each current service having a different timestamp</li>
|
||||||
|
<li>local and trash timestamps are generated from the locations_manager now, and non-local file service timestamps will soon follow (e.g. 'pinned to ipfs_service 3 days ago')</li>
|
||||||
|
<li>if the timestamp is unknown for the current service context, it will not be displayed</li>
|
||||||
|
<li>file repositories will now only sync thumbnails when their updates are completely synced (to stop 404s from since-deleted files)</li>
|
||||||
|
<li>fixed a ( 0, 0 ) resize event bug that was sometimes causing borked media containers on media viewer shutdown</li>
|
||||||
|
<li>syncing to a sha256 tag archive will still import the data as requested, but a popup note will explain that as everything will be imported, further syncing is pointless., and the sync will not be saved</li>
|
||||||
|
<li>fixed a bug in hta export if you click cancel on the hash_type choosing dialogs</li>
|
||||||
|
<li>misc cleanup</li>
|
||||||
|
</ul>
|
||||||
<li><h3>version 201</h3></li>
|
<li><h3>version 201</h3></li>
|
||||||
<ul>
|
<ul>
|
||||||
<li>exported hash and tag master tables to external database files for both client and server</li>
|
<li>exported hash and tag master tables to external database files for both client and server</li>
|
||||||
|
|
|
@ -598,16 +598,15 @@ class Controller( HydrusController.HydrusController ):
|
||||||
|
|
||||||
if HydrusData.TimeHasPassed( shutdown_timestamps[ CC.SHUTDOWN_TIMESTAMP_VACUUM ] + maintenance_vacuum_period ):
|
if HydrusData.TimeHasPassed( shutdown_timestamps[ CC.SHUTDOWN_TIMESTAMP_VACUUM ] + maintenance_vacuum_period ):
|
||||||
|
|
||||||
self.WriteSynchronous( 'vacuum' )
|
self.WriteInterruptable( 'vacuum' )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
stale_time_delta = 14 * 86400
|
|
||||||
stop_time = HydrusData.GetNow() + 120
|
stop_time = HydrusData.GetNow() + 120
|
||||||
|
|
||||||
self.pub( 'splash_set_status_text', 'analyzing' )
|
self.pub( 'splash_set_status_text', 'analyzing' )
|
||||||
|
|
||||||
self.WriteSynchronous( 'analyze', stale_time_delta, stop_time )
|
self.WriteInterruptable( 'analyze', stop_time )
|
||||||
|
|
||||||
if self._timestamps[ 'last_service_info_cache_fatten' ] == 0:
|
if self._timestamps[ 'last_service_info_cache_fatten' ] == 0:
|
||||||
|
|
||||||
|
|
1222
include/ClientDB.py
1222
include/ClientDB.py
File diff suppressed because it is too large
Load Diff
|
@ -678,7 +678,7 @@ class Imageboard( HydrusData.HydrusYAMLBase ):
|
||||||
|
|
||||||
def IsOkToPost( self, media_result ):
|
def IsOkToPost( self, media_result ):
|
||||||
|
|
||||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings ) = media_result.ToTuple()
|
( hash, inbox, size, mime, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings ) = media_result.ToTuple()
|
||||||
|
|
||||||
if CC.RESTRICTION_MIN_RESOLUTION in self._restrictions:
|
if CC.RESTRICTION_MIN_RESOLUTION in self._restrictions:
|
||||||
|
|
||||||
|
@ -1300,18 +1300,18 @@ class ServiceRepository( ServiceRestricted ):
|
||||||
|
|
||||||
def CanDownloadUpdate( self ):
|
def CanDownloadUpdate( self ):
|
||||||
|
|
||||||
update_due = HydrusData.TimeHasPassed( self._info[ 'next_download_timestamp' ] + HC.UPDATE_DURATION + 1800 )
|
work_to_do = self.IsUpdateDueForDownload()
|
||||||
|
|
||||||
return self.CanDownload() and update_due and not self.IsPaused()
|
return work_to_do and self.CanDownload() and not self.IsPaused()
|
||||||
|
|
||||||
|
|
||||||
def CanProcessUpdate( self ):
|
def CanProcessUpdate( self ):
|
||||||
|
|
||||||
update_is_downloaded = self._info[ 'next_download_timestamp' ] > self._info[ 'next_processing_timestamp' ]
|
work_to_do = self.IsUpdateDueForProcessing()
|
||||||
|
|
||||||
it_is_time = HydrusData.TimeHasPassed( self._info[ 'next_processing_timestamp' ] + HC.UPDATE_DURATION + HC.options[ 'processing_phase' ] )
|
it_is_time = HydrusData.TimeHasPassed( self._info[ 'next_processing_timestamp' ] + HC.UPDATE_DURATION + HC.options[ 'processing_phase' ] )
|
||||||
|
|
||||||
return update_is_downloaded and it_is_time and not self.IsPaused()
|
return work_to_do and it_is_time and not self.IsPaused()
|
||||||
|
|
||||||
|
|
||||||
def GetTimestamps( self ): return ( self._info[ 'first_timestamp' ], self._info[ 'next_download_timestamp' ], self._info[ 'next_processing_timestamp' ] )
|
def GetTimestamps( self ): return ( self._info[ 'first_timestamp' ], self._info[ 'next_download_timestamp' ], self._info[ 'next_processing_timestamp' ] )
|
||||||
|
@ -1368,6 +1368,16 @@ class ServiceRepository( ServiceRestricted ):
|
||||||
return self._info[ 'paused' ]
|
return self._info[ 'paused' ]
|
||||||
|
|
||||||
|
|
||||||
|
def IsUpdateDueForDownload( self ):
|
||||||
|
|
||||||
|
return HydrusData.TimeHasPassed( self._info[ 'next_download_timestamp' ] + HC.UPDATE_DURATION + 1800 )
|
||||||
|
|
||||||
|
|
||||||
|
def IsUpdateDueForProcessing( self ):
|
||||||
|
|
||||||
|
return self._info[ 'next_download_timestamp' ] > self._info[ 'next_processing_timestamp' ]
|
||||||
|
|
||||||
|
|
||||||
def Sync( self, only_when_idle = False, stop_time = None ):
|
def Sync( self, only_when_idle = False, stop_time = None ):
|
||||||
|
|
||||||
if self.IsPaused():
|
if self.IsPaused():
|
||||||
|
@ -1672,8 +1682,6 @@ class ServiceRepository( ServiceRestricted ):
|
||||||
|
|
||||||
HydrusGlobals.client_controller.Write( 'service_updates', service_keys_to_service_updates )
|
HydrusGlobals.client_controller.Write( 'service_updates', service_keys_to_service_updates )
|
||||||
|
|
||||||
HydrusGlobals.client_controller.pub( 'notify_new_pending' )
|
|
||||||
|
|
||||||
HydrusGlobals.client_controller.WaitUntilPubSubsEmpty()
|
HydrusGlobals.client_controller.WaitUntilPubSubsEmpty()
|
||||||
|
|
||||||
|
|
||||||
|
@ -1720,11 +1728,19 @@ class ServiceRepository( ServiceRestricted ):
|
||||||
|
|
||||||
time.sleep( 3 )
|
time.sleep( 3 )
|
||||||
|
|
||||||
|
finally:
|
||||||
|
|
||||||
|
HydrusGlobals.client_controller.pub( 'notify_new_pending' )
|
||||||
|
HydrusGlobals.client_controller.pub( 'notify_new_siblings' )
|
||||||
|
HydrusGlobals.client_controller.pub( 'notify_new_parents' )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def SyncThumbnails( self, job_key ):
|
def SyncThumbnails( self, job_key ):
|
||||||
|
|
||||||
if self._service_type == HC.FILE_REPOSITORY and self.CanDownload():
|
synced = not ( self.IsUpdateDueForDownload() or self.IsUpdateDueForProcessing() )
|
||||||
|
|
||||||
|
if self._service_type == HC.FILE_REPOSITORY and synced and self.CanDownload():
|
||||||
|
|
||||||
options = HydrusGlobals.client_controller.GetOptions()
|
options = HydrusGlobals.client_controller.GetOptions()
|
||||||
|
|
||||||
|
|
|
@ -211,7 +211,7 @@ def GetYoutubeFormats( youtube_url ):
|
||||||
raise Exception( 'Could not fetch video info from youtube!' + os.linesep + HydrusData.ToUnicode( e ) )
|
raise Exception( 'Could not fetch video info from youtube!' + os.linesep + HydrusData.ToUnicode( e ) )
|
||||||
|
|
||||||
|
|
||||||
info = { ( s.extension, s.resolution ) : ( s.url, s.title ) for s in p.streams if s.extension in ( 'flv', 'mp4' ) }
|
info = { ( s.extension, s.resolution ) : ( s.url, s.title ) for s in p.streams if s.extension in ( 'flv', 'mp4', 'webm' ) }
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,8 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
|
||||||
|
|
||||||
self._focus_holder = wx.Window( self, size = ( 0, 0 ) )
|
self._focus_holder = wx.Window( self, size = ( 0, 0 ) )
|
||||||
|
|
||||||
|
self._loading_session = False
|
||||||
|
self._media_status_override = None
|
||||||
self._closed_pages = []
|
self._closed_pages = []
|
||||||
self._deleted_page_keys = set()
|
self._deleted_page_keys = set()
|
||||||
self._lock = threading.Lock()
|
self._lock = threading.Lock()
|
||||||
|
@ -1166,6 +1168,15 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
|
||||||
|
|
||||||
def _LoadGUISession( self, name ):
|
def _LoadGUISession( self, name ):
|
||||||
|
|
||||||
|
if self._loading_session:
|
||||||
|
|
||||||
|
HydrusData.ShowText( 'Sorry, currently loading a session. Please wait.' )
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
self._loading_session = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
session = self._controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION, name )
|
session = self._controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_GUI_SESSION, name )
|
||||||
|
@ -1194,30 +1205,57 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
|
||||||
self._CloseCurrentPage( polite = False )
|
self._CloseCurrentPage( polite = False )
|
||||||
|
|
||||||
|
|
||||||
for ( page_name, management_controller, initial_hashes ) in session.IteratePages():
|
def do_it():
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
if len( initial_hashes ) > 0:
|
for ( page_name, management_controller, initial_hashes ) in session.IteratePages():
|
||||||
|
|
||||||
file_service_key = management_controller.GetKey( 'file_service' )
|
try:
|
||||||
|
|
||||||
initial_media_results = self._controller.Read( 'media_results', file_service_key, initial_hashes )
|
if len( initial_hashes ) > 0:
|
||||||
|
|
||||||
else:
|
file_service_key = management_controller.GetKey( 'file_service' )
|
||||||
|
|
||||||
initial_media_results = []
|
initial_media_results = []
|
||||||
|
|
||||||
|
for group_of_inital_hashes in HydrusData.SplitListIntoChunks( initial_hashes, 256 ):
|
||||||
|
|
||||||
|
more_media_results = self._controller.Read( 'media_results', file_service_key, group_of_inital_hashes )
|
||||||
|
|
||||||
|
initial_media_results.extend( more_media_results )
|
||||||
|
|
||||||
|
self._media_status_override = u'Loading session page \'' + page_name + u'\'\u2026 ' + HydrusData.ConvertValueRangeToPrettyString( len( initial_media_results ), len( initial_hashes ) )
|
||||||
|
|
||||||
|
self._controller.pub( 'refresh_status' )
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
initial_media_results = []
|
||||||
|
|
||||||
|
|
||||||
|
wx.CallAfter( self._NewPage, page_name, management_controller, initial_media_results = initial_media_results )
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
|
||||||
|
HydrusData.ShowException( e )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self._NewPage( page_name, management_controller, initial_media_results = initial_media_results )
|
if HC.PLATFORM_OSX:
|
||||||
|
|
||||||
|
wx.CallAfter( self._ClosePage, 0 )
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
finally:
|
||||||
|
|
||||||
HydrusData.ShowException( e )
|
self._loading_session = False
|
||||||
|
self._media_status_override = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if HC.PLATFORM_OSX: self._ClosePage( 0 )
|
self._controller.CallToThread( do_it )
|
||||||
|
|
||||||
|
|
||||||
def _Manage4chanPass( self ):
|
def _Manage4chanPass( self ):
|
||||||
|
@ -1502,10 +1540,23 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
|
||||||
|
|
||||||
def _RefreshStatusBar( self ):
|
def _RefreshStatusBar( self ):
|
||||||
|
|
||||||
page = self._notebook.GetCurrentPage()
|
if self._media_status_override is not None:
|
||||||
|
|
||||||
if page is None: media_status = ''
|
media_status = self._media_status_override
|
||||||
else: media_status = page.GetPrettyStatus()
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
page = self._notebook.GetCurrentPage()
|
||||||
|
|
||||||
|
if page is None:
|
||||||
|
|
||||||
|
media_status = ''
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
media_status = page.GetPrettyStatus()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if self._controller.CurrentlyIdle():
|
if self._controller.CurrentlyIdle():
|
||||||
|
|
||||||
|
@ -1670,6 +1721,13 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
|
||||||
|
|
||||||
def _SaveGUISession( self, name = None ):
|
def _SaveGUISession( self, name = None ):
|
||||||
|
|
||||||
|
if self._loading_session:
|
||||||
|
|
||||||
|
HydrusData.ShowText( 'Sorry, currently loading a session. Please wait.' )
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
if name is None:
|
if name is None:
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
@ -2062,7 +2120,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
||||||
|
|
||||||
service.Request( HC.POST, 'file', { 'file' : file } )
|
service.Request( HC.POST, 'file', { 'file' : file } )
|
||||||
|
|
||||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings ) = media_result.ToTuple()
|
( hash, inbox, size, mime, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings ) = media_result.ToTuple()
|
||||||
|
|
||||||
timestamp = HydrusData.GetNow()
|
timestamp = HydrusData.GetNow()
|
||||||
|
|
||||||
|
@ -2458,7 +2516,10 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
||||||
HydrusGlobals.restart = True
|
HydrusGlobals.restart = True
|
||||||
|
|
||||||
|
|
||||||
self._SaveGUISession( 'last session' )
|
if not self._loading_session:
|
||||||
|
|
||||||
|
self._SaveGUISession( 'last session' )
|
||||||
|
|
||||||
|
|
||||||
self._message_manager.CleanBeforeDestroy()
|
self._message_manager.CleanBeforeDestroy()
|
||||||
|
|
||||||
|
@ -3732,6 +3793,7 @@ class FrameSplash( ClientGUICommon.Frame ):
|
||||||
|
|
||||||
self._controller.sub( self, 'SetTitleText', 'splash_set_title_text' )
|
self._controller.sub( self, 'SetTitleText', 'splash_set_title_text' )
|
||||||
self._controller.sub( self, 'SetStatusText', 'splash_set_status_text' )
|
self._controller.sub( self, 'SetStatusText', 'splash_set_status_text' )
|
||||||
|
self._controller.sub( self, 'SetStatusTextNoLog', 'splash_set_status_text_no_log' )
|
||||||
self._controller.sub( self, 'Destroy', 'splash_destroy' )
|
self._controller.sub( self, 'Destroy', 'splash_destroy' )
|
||||||
|
|
||||||
self.Raise()
|
self.Raise()
|
||||||
|
@ -3819,9 +3881,12 @@ class FrameSplash( ClientGUICommon.Frame ):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def SetStatusText( self, text ):
|
def SetStatusText( self, text, print_to_log = True ):
|
||||||
|
|
||||||
HydrusData.Print( text )
|
if print_to_log:
|
||||||
|
|
||||||
|
HydrusData.Print( text )
|
||||||
|
|
||||||
|
|
||||||
self._status_text = text
|
self._status_text = text
|
||||||
|
|
||||||
|
@ -3830,9 +3895,12 @@ class FrameSplash( ClientGUICommon.Frame ):
|
||||||
self.Refresh()
|
self.Refresh()
|
||||||
|
|
||||||
|
|
||||||
def SetTitleText( self, text ):
|
def SetTitleText( self, text, print_to_log = True ):
|
||||||
|
|
||||||
HydrusData.Print( text )
|
if print_to_log:
|
||||||
|
|
||||||
|
HydrusData.Print( text )
|
||||||
|
|
||||||
|
|
||||||
self._title_text = text
|
self._title_text = text
|
||||||
|
|
||||||
|
|
|
@ -127,7 +127,7 @@ class Animation( wx.Window ):
|
||||||
( initial_width, initial_height ) = initial_size
|
( initial_width, initial_height ) = initial_size
|
||||||
|
|
||||||
self._media = media
|
self._media = media
|
||||||
self._video_container = self._video_container = ClientRendering.RasterContainerVideo( self._media, initial_size )
|
self._video_container = ClientRendering.RasterContainerVideo( self._media, initial_size )
|
||||||
|
|
||||||
self._animation_bar = None
|
self._animation_bar = None
|
||||||
|
|
||||||
|
@ -1237,19 +1237,24 @@ class Canvas( wx.Window ):
|
||||||
|
|
||||||
self._current_display_media = self._current_media.GetDisplayMedia()
|
self._current_display_media = self._current_media.GetDisplayMedia()
|
||||||
|
|
||||||
if self._current_display_media.GetLocationsManager().HasLocal():
|
( initial_size, initial_position ) = self._GetMediaContainerSizeAndPosition()
|
||||||
|
|
||||||
|
( initial_width, initial_height ) = initial_size
|
||||||
|
|
||||||
|
if self._current_display_media.GetLocationsManager().HasLocal() and initial_width > 0 and initial_height > 0:
|
||||||
|
|
||||||
self._RecalcZoom()
|
self._RecalcZoom()
|
||||||
|
|
||||||
( initial_size, initial_position ) = self._GetMediaContainerSizeAndPosition()
|
|
||||||
|
|
||||||
self._media_container = MediaContainer( self, self._image_cache, self._current_display_media, initial_size, initial_position )
|
self._media_container = MediaContainer( self, self._image_cache, self._current_display_media, initial_size, initial_position )
|
||||||
|
|
||||||
if self._claim_focus: self._media_container.SetFocus()
|
if self._claim_focus: self._media_container.SetFocus()
|
||||||
|
|
||||||
self._PrefetchNeighbours()
|
self._PrefetchNeighbours()
|
||||||
|
|
||||||
else: self._current_media = None
|
else:
|
||||||
|
|
||||||
|
self._current_media = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
HydrusGlobals.client_controller.pub( 'canvas_new_display_media', self._canvas_key, self._current_display_media )
|
HydrusGlobals.client_controller.pub( 'canvas_new_display_media', self._canvas_key, self._current_display_media )
|
||||||
|
@ -1502,7 +1507,11 @@ class CanvasWithDetails( Canvas ):
|
||||||
|
|
||||||
def _GetInfoString( self ):
|
def _GetInfoString( self ):
|
||||||
|
|
||||||
info_string = self._current_media.GetPrettyInfo() + ' | ' + ClientData.ConvertZoomToPercentage( self._current_zoom ) + ' | ' + self._current_media.GetPrettyAge()
|
lines = self._current_display_media.GetPrettyInfoLines()
|
||||||
|
|
||||||
|
lines.insert( 1, ClientData.ConvertZoomToPercentage( self._current_zoom ) )
|
||||||
|
|
||||||
|
info_string = ' | '.join( lines )
|
||||||
|
|
||||||
return info_string
|
return info_string
|
||||||
|
|
||||||
|
@ -1569,8 +1578,10 @@ class CanvasPanel( Canvas ):
|
||||||
|
|
||||||
menu = wx.Menu()
|
menu = wx.Menu()
|
||||||
|
|
||||||
menu.Append( CC.ID_NULL, self._current_display_media.GetPrettyInfo() )
|
for line in self._current_display_media.GetPrettyInfoLines():
|
||||||
menu.Append( CC.ID_NULL, self._current_display_media.GetPrettyAge() )
|
|
||||||
|
menu.Append( CC.ID_NULL, line )
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
||||||
|
@ -1792,14 +1803,14 @@ class CanvasMediaList( ClientMedia.ListeningMediaList, CanvasWithDetails ):
|
||||||
|
|
||||||
delay_base = 800
|
delay_base = 800
|
||||||
|
|
||||||
num_to_go_back = 2
|
num_to_go_back = 1
|
||||||
num_to_go_forward = 2
|
num_to_go_forward = 1
|
||||||
|
|
||||||
self._just_started = False
|
self._just_started = False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
delay_base = 200
|
delay_base = 400
|
||||||
|
|
||||||
num_to_go_back = 3
|
num_to_go_back = 3
|
||||||
num_to_go_forward = 5
|
num_to_go_forward = 5
|
||||||
|
@ -2656,8 +2667,10 @@ class CanvasMediaListBrowser( CanvasMediaListNavigable ):
|
||||||
|
|
||||||
menu = wx.Menu()
|
menu = wx.Menu()
|
||||||
|
|
||||||
menu.Append( CC.ID_NULL, self._current_display_media.GetPrettyInfo() )
|
for line in self._current_display_media.GetPrettyInfoLines():
|
||||||
menu.Append( CC.ID_NULL, self._current_display_media.GetPrettyAge() )
|
|
||||||
|
menu.Append( CC.ID_NULL, line )
|
||||||
|
|
||||||
|
|
||||||
menu.AppendSeparator()
|
menu.AppendSeparator()
|
||||||
|
|
||||||
|
@ -3103,8 +3116,10 @@ class CanvasMediaListCustomFilter( CanvasMediaListNavigable ):
|
||||||
|
|
||||||
menu = wx.Menu()
|
menu = wx.Menu()
|
||||||
|
|
||||||
menu.Append( CC.ID_NULL, self._current_display_media.GetPrettyInfo() )
|
for line in self._current_display_media.GetPrettyInfoLines():
|
||||||
menu.Append( CC.ID_NULL, self._current_display_media.GetPrettyAge() )
|
|
||||||
|
menu.Append( CC.ID_NULL, line )
|
||||||
|
|
||||||
|
|
||||||
menu.AppendSeparator()
|
menu.AppendSeparator()
|
||||||
|
|
||||||
|
|
|
@ -706,7 +706,10 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
||||||
|
|
||||||
num_chars = len( self._text_ctrl.GetValue() )
|
num_chars = len( self._text_ctrl.GetValue() )
|
||||||
|
|
||||||
if num_chars == 0: self._lag_timer.Start( 5 * 60 * 1000, wx.TIMER_ONE_SHOT )
|
if num_chars == 0:
|
||||||
|
|
||||||
|
self._lag_timer.Start( 5 * 60 * 1000, wx.TIMER_ONE_SHOT )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def EventFileButton( self, event ):
|
def EventFileButton( self, event ):
|
||||||
|
@ -914,13 +917,22 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
||||||
|
|
||||||
if search_text in ( '', ':' ):
|
if search_text in ( '', ':' ):
|
||||||
|
|
||||||
self._cache_text = ''
|
input_just_changed = self._cache_text != ''
|
||||||
self._current_namespace = ''
|
|
||||||
|
|
||||||
if self._file_service_key == CC.COMBINED_FILE_SERVICE_KEY: search_service_key = self._tag_service_key
|
db_not_going_to_hang_if_we_hit_it = not HydrusGlobals.client_controller.CurrentlyIdle()
|
||||||
else: search_service_key = self._file_service_key
|
|
||||||
|
|
||||||
matches = HydrusGlobals.client_controller.Read( 'file_system_predicates', search_service_key )
|
if input_just_changed or db_not_going_to_hang_if_we_hit_it:
|
||||||
|
|
||||||
|
self._cache_text = ''
|
||||||
|
self._current_namespace = ''
|
||||||
|
|
||||||
|
if self._file_service_key == CC.COMBINED_FILE_SERVICE_KEY: search_service_key = self._tag_service_key
|
||||||
|
else: search_service_key = self._file_service_key
|
||||||
|
|
||||||
|
self._cached_results = HydrusGlobals.client_controller.Read( 'file_system_predicates', search_service_key )
|
||||||
|
|
||||||
|
|
||||||
|
matches = self._cached_results
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
|
@ -6090,6 +6102,109 @@ class StaticBoxSorterForListBoxTags( StaticBox ):
|
||||||
self._tags_box.SetTagsByMedia( media, force_reload = force_reload )
|
self._tags_box.SetTagsByMedia( media, force_reload = force_reload )
|
||||||
|
|
||||||
|
|
||||||
|
class TimeDeltaButton( wx.Button ):
|
||||||
|
|
||||||
|
def __init__( self, parent, min = 1, days = False, hours = False, minutes = False, seconds = False ):
|
||||||
|
|
||||||
|
wx.Button.__init__( self, parent )
|
||||||
|
|
||||||
|
self._min = min
|
||||||
|
self._show_days = days
|
||||||
|
self._show_hours = hours
|
||||||
|
self._show_minutes = minutes
|
||||||
|
self._show_seconds = seconds
|
||||||
|
|
||||||
|
self._value = self._min
|
||||||
|
|
||||||
|
self.SetLabelText( 'initialising' )
|
||||||
|
|
||||||
|
self.Bind( wx.EVT_BUTTON, self.EventButton )
|
||||||
|
|
||||||
|
|
||||||
|
def _RefreshLabel( self ):
|
||||||
|
|
||||||
|
text_components = []
|
||||||
|
|
||||||
|
value = self._value
|
||||||
|
|
||||||
|
if self._show_days:
|
||||||
|
|
||||||
|
days = value / 86400
|
||||||
|
|
||||||
|
if days > 0:
|
||||||
|
|
||||||
|
text_components.append( HydrusData.ConvertIntToPrettyString( days ) + ' days' )
|
||||||
|
|
||||||
|
|
||||||
|
value %= 86400
|
||||||
|
|
||||||
|
|
||||||
|
if self._show_hours:
|
||||||
|
|
||||||
|
hours = value / 3600
|
||||||
|
|
||||||
|
if hours > 0:
|
||||||
|
|
||||||
|
text_components.append( HydrusData.ConvertIntToPrettyString( hours ) + ' hours' )
|
||||||
|
|
||||||
|
|
||||||
|
value %= 3600
|
||||||
|
|
||||||
|
|
||||||
|
if self._show_minutes:
|
||||||
|
|
||||||
|
minutes = value / 60
|
||||||
|
|
||||||
|
if minutes > 0:
|
||||||
|
|
||||||
|
text_components.append( HydrusData.ConvertIntToPrettyString( minutes ) + ' minutes' )
|
||||||
|
|
||||||
|
|
||||||
|
value %= 60
|
||||||
|
|
||||||
|
|
||||||
|
if self._show_seconds:
|
||||||
|
|
||||||
|
if value > 0 or len( text_components ) == 0:
|
||||||
|
|
||||||
|
text_components.append( HydrusData.ConvertIntToPrettyString( value ) + ' seconds' )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
text = ' '.join( text_components )
|
||||||
|
|
||||||
|
self.SetLabelText( text )
|
||||||
|
|
||||||
|
|
||||||
|
def EventButton( self, event ):
|
||||||
|
|
||||||
|
import ClientGUIDialogs
|
||||||
|
|
||||||
|
with ClientGUIDialogs.DialogInputTimeDelta( self, self._value, min = self._min, days = self._show_days, hours = self._show_hours, minutes = self._show_minutes, seconds = self._show_seconds ) as dlg:
|
||||||
|
|
||||||
|
if dlg.ShowModal() == wx.ID_OK:
|
||||||
|
|
||||||
|
value = dlg.GetValue()
|
||||||
|
|
||||||
|
self.SetValue( value )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def GetValue( self ):
|
||||||
|
|
||||||
|
return self._value
|
||||||
|
|
||||||
|
|
||||||
|
def SetValue( self, value ):
|
||||||
|
|
||||||
|
self._value = value
|
||||||
|
|
||||||
|
self._RefreshLabel()
|
||||||
|
|
||||||
|
self.GetParent().Layout()
|
||||||
|
|
||||||
|
|
||||||
class TimeDeltaCtrl( wx.Panel ):
|
class TimeDeltaCtrl( wx.Panel ):
|
||||||
|
|
||||||
def __init__( self, parent, min = 1, days = False, hours = False, minutes = False, seconds = False ):
|
def __init__( self, parent, min = 1, days = False, hours = False, minutes = False, seconds = False ):
|
||||||
|
|
|
@ -71,6 +71,8 @@ def ExportToHTA( parent, service_key, hashes ):
|
||||||
message += os.linesep * 2
|
message += os.linesep * 2
|
||||||
message += 'If you do not know what this stuff means, click \'normal\'.'
|
message += 'If you do not know what this stuff means, click \'normal\'.'
|
||||||
|
|
||||||
|
hash_type = None
|
||||||
|
|
||||||
with DialogYesNo( parent, message, title = 'Choose which hash type.', yes_label = 'normal', no_label = 'alternative' ) as dlg:
|
with DialogYesNo( parent, message, title = 'Choose which hash type.', yes_label = 'normal', no_label = 'alternative' ) as dlg:
|
||||||
|
|
||||||
result = dlg.ShowModal()
|
result = dlg.ShowModal()
|
||||||
|
@ -2210,6 +2212,49 @@ class DialogInputTags( Dialog ):
|
||||||
self.EndModal( wx.ID_OK )
|
self.EndModal( wx.ID_OK )
|
||||||
|
|
||||||
|
|
||||||
|
class DialogInputTimeDelta( Dialog ):
|
||||||
|
|
||||||
|
def __init__( self, parent, initial_value, min = 1, days = False, hours = False, minutes = False, seconds = False ):
|
||||||
|
|
||||||
|
Dialog.__init__( self, parent, 'input time delta' )
|
||||||
|
|
||||||
|
self._time_delta = ClientGUICommon.TimeDeltaCtrl( self, min = min, days = days, hours = hours, minutes = minutes, seconds = seconds )
|
||||||
|
|
||||||
|
self._ok = wx.Button( self, id = wx.ID_OK, label = 'Ok' )
|
||||||
|
self._ok.SetForegroundColour( ( 0, 128, 0 ) )
|
||||||
|
|
||||||
|
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'Cancel' )
|
||||||
|
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
self._time_delta.SetValue( initial_value )
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
b_box = wx.BoxSizer( wx.HORIZONTAL )
|
||||||
|
b_box.AddF( self._ok, CC.FLAGS_MIXED )
|
||||||
|
b_box.AddF( self._cancel, CC.FLAGS_MIXED )
|
||||||
|
|
||||||
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||||
|
|
||||||
|
vbox.AddF( self._time_delta, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||||
|
vbox.AddF( b_box, CC.FLAGS_BUTTON_SIZER )
|
||||||
|
|
||||||
|
self.SetSizer( vbox )
|
||||||
|
|
||||||
|
( x, y ) = self.GetEffectiveMinSize()
|
||||||
|
|
||||||
|
self.SetInitialSize( ( x, y ) )
|
||||||
|
|
||||||
|
wx.CallAfter( self._ok.SetFocus )
|
||||||
|
|
||||||
|
|
||||||
|
def GetValue( self ):
|
||||||
|
|
||||||
|
return self._time_delta.GetValue()
|
||||||
|
|
||||||
|
|
||||||
class DialogInputUPnPMapping( Dialog ):
|
class DialogInputUPnPMapping( Dialog ):
|
||||||
|
|
||||||
def __init__( self, parent, external_port, protocol_type, internal_port, description, duration ):
|
def __init__( self, parent, external_port, protocol_type, internal_port, description, duration ):
|
||||||
|
|
|
@ -1663,7 +1663,7 @@ class DialogManageExportFoldersEdit( ClientGUIDialogs.Dialog ):
|
||||||
|
|
||||||
self._period_box = ClientGUICommon.StaticBox( self, 'export period' )
|
self._period_box = ClientGUICommon.StaticBox( self, 'export period' )
|
||||||
|
|
||||||
self._period = ClientGUICommon.TimeDeltaCtrl( self._period_box, min = 3 * 60, days = True, hours = True, minutes = True )
|
self._period = ClientGUICommon.TimeDeltaButton( self._period_box, min = 3 * 60, days = True, hours = True, minutes = True )
|
||||||
|
|
||||||
self._period.SetValue( period )
|
self._period.SetValue( period )
|
||||||
|
|
||||||
|
@ -2727,7 +2727,7 @@ class DialogManageImportFoldersEdit( ClientGUIDialogs.Dialog ):
|
||||||
|
|
||||||
self._open_popup = wx.CheckBox( self._folder_box )
|
self._open_popup = wx.CheckBox( self._folder_box )
|
||||||
|
|
||||||
self._period = ClientGUICommon.TimeDeltaCtrl( self._folder_box, min = 3 * 60, days = True, hours = True, minutes = True )
|
self._period = ClientGUICommon.TimeDeltaButton( self._folder_box, min = 3 * 60, days = True, hours = True, minutes = True )
|
||||||
|
|
||||||
self._paused = wx.CheckBox( self._folder_box )
|
self._paused = wx.CheckBox( self._folder_box )
|
||||||
|
|
||||||
|
@ -3612,7 +3612,7 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
|
||||||
self._thread_times_to_check = wx.SpinCtrl( thread_checker, min = 0, max = 100 )
|
self._thread_times_to_check = wx.SpinCtrl( thread_checker, min = 0, max = 100 )
|
||||||
self._thread_times_to_check.SetToolTipString( 'how many times the thread checker will check' )
|
self._thread_times_to_check.SetToolTipString( 'how many times the thread checker will check' )
|
||||||
|
|
||||||
self._thread_check_period = ClientGUICommon.TimeDeltaCtrl( thread_checker, min = 30, hours = True, minutes = True, seconds = True )
|
self._thread_check_period = ClientGUICommon.TimeDeltaButton( thread_checker, min = 30, hours = True, minutes = True, seconds = True )
|
||||||
self._thread_check_period.SetToolTipString( 'how long the checker will wait between checks' )
|
self._thread_check_period.SetToolTipString( 'how long the checker will wait between checks' )
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -6787,7 +6787,10 @@ class DialogManageServices( ClientGUIDialogs.Dialog ):
|
||||||
|
|
||||||
namespaces = HydrusData.ConvertPrettyStringsToUglyNamespaces( dlg.GetChecked() )
|
namespaces = HydrusData.ConvertPrettyStringsToUglyNamespaces( dlg.GetChecked() )
|
||||||
|
|
||||||
else: return
|
else:
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
name_to_display = self._GetArchiveNameToDisplay( archive_name, namespaces )
|
name_to_display = self._GetArchiveNameToDisplay( archive_name, namespaces )
|
||||||
|
@ -7334,7 +7337,7 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
|
||||||
self._booru_selector = wx.ListBox( self._query_panel )
|
self._booru_selector = wx.ListBox( self._query_panel )
|
||||||
self._booru_selector.Bind( wx.EVT_LISTBOX, self.EventBooruSelected )
|
self._booru_selector.Bind( wx.EVT_LISTBOX, self.EventBooruSelected )
|
||||||
|
|
||||||
self._period = ClientGUICommon.TimeDeltaCtrl( self._query_panel, min = 3600 * 4, days = True, hours = True )
|
self._period = ClientGUICommon.TimeDeltaButton( self._query_panel, min = 3600 * 4, days = True, hours = True )
|
||||||
|
|
||||||
self._info_panel = ClientGUICommon.StaticBox( self, 'info' )
|
self._info_panel = ClientGUICommon.StaticBox( self, 'info' )
|
||||||
|
|
||||||
|
|
|
@ -332,7 +332,9 @@ class FullscreenHoverFrameCommands( FullscreenHoverFrame ):
|
||||||
|
|
||||||
else: self._title_text.Hide()
|
else: self._title_text.Hide()
|
||||||
|
|
||||||
label = self._current_media.GetPrettyInfo() + ' | ' + self._current_media.GetPrettyAge()
|
lines = self._current_media.GetPrettyInfoLines()
|
||||||
|
|
||||||
|
label = ' | '.join( lines )
|
||||||
|
|
||||||
self._info_text.SetLabelText( label )
|
self._info_text.SetLabelText( label )
|
||||||
|
|
||||||
|
|
|
@ -2842,7 +2842,7 @@ class ManagementPanelThreadWatcherImport( ManagementPanel ):
|
||||||
self._thread_times_to_check.SetValue( times_to_check )
|
self._thread_times_to_check.SetValue( times_to_check )
|
||||||
self._thread_times_to_check.Bind( wx.EVT_SPINCTRL, self.EventTimesToCheck )
|
self._thread_times_to_check.Bind( wx.EVT_SPINCTRL, self.EventTimesToCheck )
|
||||||
|
|
||||||
self._thread_check_period = ClientGUICommon.TimeDeltaCtrl( self._options_panel, min = 30, hours = True, minutes = True, seconds = True )
|
self._thread_check_period = ClientGUICommon.TimeDeltaButton( self._options_panel, min = 30, hours = True, minutes = True, seconds = True )
|
||||||
self._thread_check_period.SetValue( check_period )
|
self._thread_check_period.SetValue( check_period )
|
||||||
self._thread_check_period.Bind( wx.EVT_SPINCTRL, self.EventCheckPeriod )
|
self._thread_check_period.Bind( wx.EVT_SPINCTRL, self.EventCheckPeriod )
|
||||||
|
|
||||||
|
|
|
@ -534,7 +534,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
||||||
|
|
||||||
( selected_media, ) = self._selected_media
|
( selected_media, ) = self._selected_media
|
||||||
|
|
||||||
s += selected_files_string + ' selected, ' + selected_media.GetPrettyInfo()
|
s += selected_files_string + ' selected, ' + ', '.join( selected_media.GetPrettyInfoLines() )
|
||||||
|
|
||||||
else: # 23 files - 5 selected, selection_info
|
else: # 23 files - 5 selected, selection_info
|
||||||
|
|
||||||
|
@ -2399,11 +2399,16 @@ class MediaPanelThumbnails( MediaPanel ):
|
||||||
|
|
||||||
# do the actual menu
|
# do the actual menu
|
||||||
|
|
||||||
if multiple_selected: menu.Append( CC.ID_NULL, HydrusData.ConvertIntToPrettyString( num_selected ) + ' files, ' + self._GetPrettyTotalSelectedSize() )
|
if multiple_selected:
|
||||||
|
|
||||||
|
menu.Append( CC.ID_NULL, HydrusData.ConvertIntToPrettyString( num_selected ) + ' files, ' + self._GetPrettyTotalSelectedSize() )
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
menu.Append( CC.ID_NULL, thumbnail.GetPrettyInfo() )
|
for line in thumbnail.GetPrettyInfoLines():
|
||||||
menu.Append( CC.ID_NULL, thumbnail.GetPrettyAge() )
|
|
||||||
|
menu.Append( CC.ID_NULL, line )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if len( disparate_current_file_service_keys ) > 0: AddServiceKeysToMenu( menu, disparate_current_file_service_keys, 'some uploaded to', CC.ID_NULL )
|
if len( disparate_current_file_service_keys ) > 0: AddServiceKeysToMenu( menu, disparate_current_file_service_keys, 'some uploaded to', CC.ID_NULL )
|
||||||
|
|
|
@ -64,13 +64,20 @@ class LocationsManager( object ):
|
||||||
|
|
||||||
LOCAL_LOCATIONS = { CC.LOCAL_FILE_SERVICE_KEY, CC.TRASH_SERVICE_KEY }
|
LOCAL_LOCATIONS = { CC.LOCAL_FILE_SERVICE_KEY, CC.TRASH_SERVICE_KEY }
|
||||||
|
|
||||||
def __init__( self, current, deleted, pending, petitioned ):
|
def __init__( self, current, deleted, pending, petitioned, current_to_timestamps = None ):
|
||||||
|
|
||||||
self._current = current
|
self._current = current
|
||||||
self._deleted = deleted
|
self._deleted = deleted
|
||||||
self._pending = pending
|
self._pending = pending
|
||||||
self._petitioned = petitioned
|
self._petitioned = petitioned
|
||||||
|
|
||||||
|
if current_to_timestamps is None:
|
||||||
|
|
||||||
|
current_to_timestamps = {}
|
||||||
|
|
||||||
|
|
||||||
|
self._current_to_timestamps = current_to_timestamps
|
||||||
|
|
||||||
|
|
||||||
def DeletePending( self, service_key ):
|
def DeletePending( self, service_key ):
|
||||||
|
|
||||||
|
@ -148,6 +155,30 @@ class LocationsManager( object ):
|
||||||
return remote_service_strings
|
return remote_service_strings
|
||||||
|
|
||||||
|
|
||||||
|
def GetTimestamp( self, service_key = None ):
|
||||||
|
|
||||||
|
if service_key is None:
|
||||||
|
|
||||||
|
if len( self._current_to_timestamps ) > 0:
|
||||||
|
|
||||||
|
return max( self._current_to_timestamps.values() )
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if service_key in self._current_to_timestamps:
|
||||||
|
|
||||||
|
return self._current_to_timestamps[ service_key ]
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def HasDownloading( self ): return CC.LOCAL_FILE_SERVICE_KEY in self._pending
|
def HasDownloading( self ): return CC.LOCAL_FILE_SERVICE_KEY in self._pending
|
||||||
|
|
||||||
def HasLocal( self ): return len( self._current.intersection( self.LOCAL_LOCATIONS ) ) > 0
|
def HasLocal( self ): return len( self._current.intersection( self.LOCAL_LOCATIONS ) ) > 0
|
||||||
|
@ -168,6 +199,8 @@ class LocationsManager( object ):
|
||||||
self._current.discard( CC.TRASH_SERVICE_KEY )
|
self._current.discard( CC.TRASH_SERVICE_KEY )
|
||||||
|
|
||||||
|
|
||||||
|
self._current_to_timestamps[ service_key ] = HydrusData.GetNow()
|
||||||
|
|
||||||
elif action == HC.CONTENT_UPDATE_DELETE:
|
elif action == HC.CONTENT_UPDATE_DELETE:
|
||||||
|
|
||||||
self._deleted.add( service_key )
|
self._deleted.add( service_key )
|
||||||
|
@ -179,6 +212,8 @@ class LocationsManager( object ):
|
||||||
|
|
||||||
self._current.add( CC.TRASH_SERVICE_KEY )
|
self._current.add( CC.TRASH_SERVICE_KEY )
|
||||||
|
|
||||||
|
self._current_to_timestamps[ CC.TRASH_SERVICE_KEY ] = self._current_to_timestamps[ CC.LOCAL_FILE_SERVICE_KEY ]
|
||||||
|
|
||||||
|
|
||||||
elif action == HC.CONTENT_UPDATE_UNDELETE:
|
elif action == HC.CONTENT_UPDATE_UNDELETE:
|
||||||
|
|
||||||
|
@ -186,6 +221,8 @@ class LocationsManager( object ):
|
||||||
|
|
||||||
self._current.add( CC.LOCAL_FILE_SERVICE_KEY )
|
self._current.add( CC.LOCAL_FILE_SERVICE_KEY )
|
||||||
|
|
||||||
|
self._current_to_timestamps[ CC.LOCAL_FILE_SERVICE_KEY ] = self._current_to_timestamps[ CC.TRASH_SERVICE_KEY ]
|
||||||
|
|
||||||
elif action == HC.CONTENT_UPDATE_PEND:
|
elif action == HC.CONTENT_UPDATE_PEND:
|
||||||
|
|
||||||
if service_key not in self._current: self._pending.add( service_key )
|
if service_key not in self._current: self._pending.add( service_key )
|
||||||
|
@ -655,7 +692,7 @@ class MediaList( object ):
|
||||||
|
|
||||||
def deal_with_none( x ):
|
def deal_with_none( x ):
|
||||||
|
|
||||||
if x == None: return -1
|
if x is None: return -1
|
||||||
else: return x
|
else: return x
|
||||||
|
|
||||||
|
|
||||||
|
@ -755,8 +792,6 @@ class MediaCollection( MediaList, Media ):
|
||||||
self._size = 0
|
self._size = 0
|
||||||
self._size_definite = True
|
self._size_definite = True
|
||||||
|
|
||||||
self._timestamp = 0
|
|
||||||
|
|
||||||
self._width = None
|
self._width = None
|
||||||
self._height = None
|
self._height = None
|
||||||
self._duration = None
|
self._duration = None
|
||||||
|
@ -778,9 +813,6 @@ class MediaCollection( MediaList, Media ):
|
||||||
self._size = sum( [ media.GetSize() for media in self._sorted_media ] )
|
self._size = sum( [ media.GetSize() for media in self._sorted_media ] )
|
||||||
self._size_definite = not False in ( media.IsSizeDefinite() for media in self._sorted_media )
|
self._size_definite = not False in ( media.IsSizeDefinite() for media in self._sorted_media )
|
||||||
|
|
||||||
if len( self._sorted_media ) == 0: self._timestamp = 0
|
|
||||||
else: self._timestamp = max( [ media.GetTimestamp() for media in self._sorted_media ] )
|
|
||||||
|
|
||||||
duration_sum = sum( [ media.GetDuration() for media in self._sorted_media if media.HasDuration() ] )
|
duration_sum = sum( [ media.GetDuration() for media in self._sorted_media if media.HasDuration() ] )
|
||||||
|
|
||||||
if duration_sum > 0: self._duration = duration_sum
|
if duration_sum > 0: self._duration = duration_sum
|
||||||
|
@ -864,9 +896,7 @@ class MediaCollection( MediaList, Media ):
|
||||||
|
|
||||||
def GetNumWords( self ): return sum( ( media.GetNumWords() for media in self._sorted_media ) )
|
def GetNumWords( self ): return sum( ( media.GetNumWords() for media in self._sorted_media ) )
|
||||||
|
|
||||||
def GetPrettyAge( self ): return 'imported ' + HydrusData.ConvertTimestampToPrettyAgo( self._timestamp )
|
def GetPrettyInfoLines( self ):
|
||||||
|
|
||||||
def GetPrettyInfo( self ):
|
|
||||||
|
|
||||||
size = HydrusData.ConvertIntToBytes( self._size )
|
size = HydrusData.ConvertIntToBytes( self._size )
|
||||||
|
|
||||||
|
@ -876,7 +906,7 @@ class MediaCollection( MediaList, Media ):
|
||||||
|
|
||||||
info_string += ' (' + HydrusData.ConvertIntToPrettyString( self.GetNumFiles() ) + ' files)'
|
info_string += ' (' + HydrusData.ConvertIntToPrettyString( self.GetNumFiles() ) + ' files)'
|
||||||
|
|
||||||
return info_string
|
return [ info_string ]
|
||||||
|
|
||||||
|
|
||||||
def GetRatings( self ): return self._ratings
|
def GetRatings( self ): return self._ratings
|
||||||
|
@ -896,7 +926,7 @@ class MediaCollection( MediaList, Media ):
|
||||||
|
|
||||||
def GetTagsManager( self ): return self._tags_manager
|
def GetTagsManager( self ): return self._tags_manager
|
||||||
|
|
||||||
def GetTimestamp( self ): return self._timestamp
|
def GetTimestamp( self ): return None
|
||||||
|
|
||||||
def HasArchive( self ): return self._archive
|
def HasArchive( self ): return self._archive
|
||||||
|
|
||||||
|
@ -1026,19 +1056,14 @@ class MediaSingleton( Media ):
|
||||||
|
|
||||||
def GetNumWords( self ): return self._media_result.GetNumWords()
|
def GetNumWords( self ): return self._media_result.GetNumWords()
|
||||||
|
|
||||||
def GetTimestamp( self ):
|
def GetTimestamp( self, service_key = None ):
|
||||||
|
|
||||||
timestamp = self._media_result.GetTimestamp()
|
return self._media_result.GetLocationsManager().GetTimestamp( service_key )
|
||||||
|
|
||||||
if timestamp is None: return 0
|
|
||||||
else: return timestamp
|
|
||||||
|
|
||||||
|
|
||||||
def GetPrettyAge( self ): return 'imported ' + HydrusData.ConvertTimestampToPrettyAgo( self._media_result.GetTimestamp() )
|
def GetPrettyInfoLines( self ):
|
||||||
|
|
||||||
def GetPrettyInfo( self ):
|
|
||||||
|
|
||||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings ) = self._media_result.ToTuple()
|
( hash, inbox, size, mime, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings ) = self._media_result.ToTuple()
|
||||||
|
|
||||||
info_string = HydrusData.ConvertIntToBytes( size ) + ' ' + HC.mime_string_lookup[ mime ]
|
info_string = HydrusData.ConvertIntToBytes( size ) + ' ' + HC.mime_string_lookup[ mime ]
|
||||||
|
|
||||||
|
@ -1050,7 +1075,27 @@ class MediaSingleton( Media ):
|
||||||
|
|
||||||
if num_words is not None: info_string += ' (' + HydrusData.ConvertIntToPrettyString( num_words ) + ' words)'
|
if num_words is not None: info_string += ' (' + HydrusData.ConvertIntToPrettyString( num_words ) + ' words)'
|
||||||
|
|
||||||
return info_string
|
lines = [ info_string ]
|
||||||
|
|
||||||
|
locations_manager = self._media_result.GetLocationsManager()
|
||||||
|
|
||||||
|
current_service_keys = locations_manager.GetCurrent()
|
||||||
|
|
||||||
|
if CC.LOCAL_FILE_SERVICE_KEY in current_service_keys:
|
||||||
|
|
||||||
|
timestamp = locations_manager.GetTimestamp( CC.LOCAL_FILE_SERVICE_KEY )
|
||||||
|
|
||||||
|
lines.append( 'imported ' + HydrusData.ConvertTimestampToPrettyAgo( timestamp ) )
|
||||||
|
|
||||||
|
|
||||||
|
if CC.TRASH_SERVICE_KEY in current_service_keys:
|
||||||
|
|
||||||
|
timestamp = locations_manager.GetTimestamp( CC.TRASH_SERVICE_KEY )
|
||||||
|
|
||||||
|
lines.append( 'imported ' + HydrusData.ConvertTimestampToPrettyAgo( timestamp ) + ', now in the trash' )
|
||||||
|
|
||||||
|
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
def GetRatings( self ): return self._media_result.GetRatings()
|
def GetRatings( self ): return self._media_result.GetRatings()
|
||||||
|
@ -1198,14 +1243,14 @@ class MediaResult( object ):
|
||||||
|
|
||||||
def __init__( self, tuple ):
|
def __init__( self, tuple ):
|
||||||
|
|
||||||
# hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings
|
# hash, inbox, size, mime, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings
|
||||||
|
|
||||||
self._tuple = tuple
|
self._tuple = tuple
|
||||||
|
|
||||||
|
|
||||||
def DeletePending( self, service_key ):
|
def DeletePending( self, service_key ):
|
||||||
|
|
||||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings ) = self._tuple
|
( hash, inbox, size, mime, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings ) = self._tuple
|
||||||
|
|
||||||
service = HydrusGlobals.client_controller.GetServicesManager().GetService( service_key )
|
service = HydrusGlobals.client_controller.GetServicesManager().GetService( service_key )
|
||||||
|
|
||||||
|
@ -1217,33 +1262,31 @@ class MediaResult( object ):
|
||||||
|
|
||||||
def GetHash( self ): return self._tuple[0]
|
def GetHash( self ): return self._tuple[0]
|
||||||
|
|
||||||
def GetDuration( self ): return self._tuple[7]
|
def GetDuration( self ): return self._tuple[6]
|
||||||
|
|
||||||
def GetInbox( self ): return self._tuple[1]
|
def GetInbox( self ): return self._tuple[1]
|
||||||
|
|
||||||
def GetLocationsManager( self ): return self._tuple[11]
|
def GetLocationsManager( self ): return self._tuple[10]
|
||||||
|
|
||||||
def GetMime( self ): return self._tuple[3]
|
def GetMime( self ): return self._tuple[3]
|
||||||
|
|
||||||
def GetNumFrames( self ): return self._tuple[8]
|
def GetNumFrames( self ): return self._tuple[7]
|
||||||
|
|
||||||
def GetNumWords( self ): return self._tuple[9]
|
def GetNumWords( self ): return self._tuple[8]
|
||||||
|
|
||||||
def GetRatings( self ): return ( self._tuple[12], self._tuple[13] )
|
def GetRatings( self ): return ( self._tuple[11], self._tuple[12] )
|
||||||
|
|
||||||
def GetResolution( self ): return ( self._tuple[5], self._tuple[6] )
|
def GetResolution( self ): return ( self._tuple[4], self._tuple[5] )
|
||||||
|
|
||||||
def GetSize( self ): return self._tuple[2]
|
def GetSize( self ): return self._tuple[2]
|
||||||
|
|
||||||
def GetTagsManager( self ): return self._tuple[10]
|
def GetTagsManager( self ): return self._tuple[9]
|
||||||
|
|
||||||
def GetTimestamp( self ): return self._tuple[4]
|
|
||||||
|
|
||||||
def ProcessContentUpdate( self, service_key, content_update ):
|
def ProcessContentUpdate( self, service_key, content_update ):
|
||||||
|
|
||||||
( data_type, action, row ) = content_update.ToTuple()
|
( data_type, action, row ) = content_update.ToTuple()
|
||||||
|
|
||||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings ) = self._tuple
|
( hash, inbox, size, mime, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings ) = self._tuple
|
||||||
|
|
||||||
service = HydrusGlobals.client_controller.GetServicesManager().GetService( service_key )
|
service = HydrusGlobals.client_controller.GetServicesManager().GetService( service_key )
|
||||||
|
|
||||||
|
@ -1272,7 +1315,7 @@ class MediaResult( object ):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self._tuple = ( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings )
|
self._tuple = ( hash, inbox, size, mime, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings )
|
||||||
|
|
||||||
|
|
||||||
locations_manager.ProcessContentUpdate( service_key, content_update )
|
locations_manager.ProcessContentUpdate( service_key, content_update )
|
||||||
|
@ -1286,7 +1329,7 @@ class MediaResult( object ):
|
||||||
|
|
||||||
def ResetService( self, service_key ):
|
def ResetService( self, service_key ):
|
||||||
|
|
||||||
( hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings ) = self._tuple
|
( hash, inbox, size, mime, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings ) = self._tuple
|
||||||
|
|
||||||
tags_manager.ResetService( service_key )
|
tags_manager.ResetService( service_key )
|
||||||
locations_manager.ResetService( service_key )
|
locations_manager.ResetService( service_key )
|
||||||
|
|
|
@ -23,7 +23,6 @@ else:
|
||||||
BIN_DIR = os.path.join( BASE_DIR, 'bin' )
|
BIN_DIR = os.path.join( BASE_DIR, 'bin' )
|
||||||
DB_DIR = os.path.join( BASE_DIR, 'db' )
|
DB_DIR = os.path.join( BASE_DIR, 'db' )
|
||||||
CLIENT_ARCHIVES_DIR = os.path.join( DB_DIR, 'client_archives' )
|
CLIENT_ARCHIVES_DIR = os.path.join( DB_DIR, 'client_archives' )
|
||||||
CLIENT_CACHE_DIR = os.path.join( DB_DIR, 'client_cache' )
|
|
||||||
CLIENT_FILES_DIR = os.path.join( DB_DIR, 'client_files' )
|
CLIENT_FILES_DIR = os.path.join( DB_DIR, 'client_files' )
|
||||||
SERVER_FILES_DIR = os.path.join( DB_DIR, 'server_files' )
|
SERVER_FILES_DIR = os.path.join( DB_DIR, 'server_files' )
|
||||||
CLIENT_THUMBNAILS_DIR = os.path.join( DB_DIR, 'client_thumbnails' )
|
CLIENT_THUMBNAILS_DIR = os.path.join( DB_DIR, 'client_thumbnails' )
|
||||||
|
@ -54,7 +53,7 @@ options = {}
|
||||||
# Misc
|
# Misc
|
||||||
|
|
||||||
NETWORK_VERSION = 17
|
NETWORK_VERSION = 17
|
||||||
SOFTWARE_VERSION = 201
|
SOFTWARE_VERSION = 202
|
||||||
|
|
||||||
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||||
|
|
||||||
|
@ -200,6 +199,7 @@ DELETED_PENDING = 4
|
||||||
|
|
||||||
HIGH_PRIORITY = 0
|
HIGH_PRIORITY = 0
|
||||||
LOW_PRIORITY = 2
|
LOW_PRIORITY = 2
|
||||||
|
INTERRUPTABLE_PRIORITY = 4
|
||||||
|
|
||||||
SCORE_PETITION = 0
|
SCORE_PETITION = 0
|
||||||
|
|
||||||
|
|
|
@ -314,6 +314,11 @@ class HydrusController( object ):
|
||||||
return self._Write( action, HC.HIGH_PRIORITY, False, *args, **kwargs )
|
return self._Write( action, HC.HIGH_PRIORITY, False, *args, **kwargs )
|
||||||
|
|
||||||
|
|
||||||
|
def WriteInterruptable( self, action, *args, **kwargs ):
|
||||||
|
|
||||||
|
return self._Write( action, HC.INTERRUPTABLE_PRIORITY, True, *args, **kwargs )
|
||||||
|
|
||||||
|
|
||||||
def WriteSynchronous( self, action, *args, **kwargs ):
|
def WriteSynchronous( self, action, *args, **kwargs ):
|
||||||
|
|
||||||
return self._Write( action, HC.LOW_PRIORITY, True, *args, **kwargs )
|
return self._Write( action, HC.LOW_PRIORITY, True, *args, **kwargs )
|
||||||
|
|
|
@ -17,63 +17,76 @@ import threading
|
||||||
import traceback
|
import traceback
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
CONNECTION_REFRESH_TIME = 60 * 30
|
||||||
|
|
||||||
def CanVacuum( db_path, stop_time = None ):
|
def CanVacuum( db_path, stop_time = None ):
|
||||||
|
|
||||||
db = sqlite3.connect( db_path, isolation_level = None, detect_types = sqlite3.PARSE_DECLTYPES )
|
try:
|
||||||
|
|
||||||
c = db.cursor()
|
|
||||||
|
|
||||||
( page_size, ) = c.execute( 'PRAGMA page_size;' ).fetchone()
|
|
||||||
( page_count, ) = c.execute( 'PRAGMA page_count;' ).fetchone()
|
|
||||||
( freelist_count, ) = c.execute( 'PRAGMA freelist_count;' ).fetchone()
|
|
||||||
|
|
||||||
db_size = ( page_count - freelist_count ) * page_size
|
|
||||||
|
|
||||||
if stop_time is not None:
|
|
||||||
|
|
||||||
approx_vacuum_speed_mb_per_s = 1048576 * 3
|
db = sqlite3.connect( db_path, isolation_level = None, detect_types = sqlite3.PARSE_DECLTYPES )
|
||||||
|
|
||||||
approx_vacuum_duration = db_size / approx_vacuum_speed_mb_per_s
|
c = db.cursor()
|
||||||
|
|
||||||
time_i_will_have_to_start = stop_time - approx_vacuum_duration
|
( page_size, ) = c.execute( 'PRAGMA page_size;' ).fetchone()
|
||||||
|
( page_count, ) = c.execute( 'PRAGMA page_count;' ).fetchone()
|
||||||
|
( freelist_count, ) = c.execute( 'PRAGMA freelist_count;' ).fetchone()
|
||||||
|
|
||||||
if HydrusData.TimeHasPassed( time_i_will_have_to_start ):
|
db_size = ( page_count - freelist_count ) * page_size
|
||||||
|
|
||||||
|
if stop_time is not None:
|
||||||
|
|
||||||
return False
|
approx_vacuum_speed_mb_per_s = 1048576 * 3
|
||||||
|
|
||||||
|
approx_vacuum_duration = db_size / approx_vacuum_speed_mb_per_s
|
||||||
|
|
||||||
|
time_i_will_have_to_start = stop_time - approx_vacuum_duration
|
||||||
|
|
||||||
|
if HydrusData.TimeHasPassed( time_i_will_have_to_start ):
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
temp_dir = tempfile.gettempdir()
|
||||||
temp_dir = tempfile.gettempdir()
|
( db_dir, db_filename ) = os.path.split( db_path )
|
||||||
( db_dir, db_filename ) = os.path.split( db_path )
|
|
||||||
|
|
||||||
temp_disk_usage = psutil.disk_usage( temp_dir )
|
|
||||||
|
|
||||||
a = HydrusPaths.GetDevice( temp_dir )
|
|
||||||
b = HydrusPaths.GetDevice( db_dir )
|
|
||||||
|
|
||||||
if HydrusPaths.GetDevice( temp_dir ) == HydrusPaths.GetDevice( db_dir ):
|
|
||||||
|
|
||||||
if temp_disk_usage.free < db_size * 2.2:
|
temp_disk_usage = psutil.disk_usage( temp_dir )
|
||||||
|
|
||||||
|
a = HydrusPaths.GetDevice( temp_dir )
|
||||||
|
b = HydrusPaths.GetDevice( db_dir )
|
||||||
|
|
||||||
|
if HydrusPaths.GetDevice( temp_dir ) == HydrusPaths.GetDevice( db_dir ):
|
||||||
|
|
||||||
return False
|
if temp_disk_usage.free < db_size * 2.2:
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
if temp_disk_usage.free < db_size * 1.1:
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
db_disk_usage = psutil.disk_usage( db_dir )
|
||||||
|
|
||||||
|
if db_disk_usage.free < db_size * 1.1:
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
else:
|
return True
|
||||||
|
|
||||||
if temp_disk_usage.free < db_size * 1.1:
|
except Exception as e:
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
db_disk_usage = psutil.disk_usage( db_dir )
|
HydrusData.Print( 'Could not determine whether to vacuum or not:' )
|
||||||
|
|
||||||
if db_disk_usage.free < db_size * 1.1:
|
HydrusData.PrintException( e )
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def SetupDBCreatePragma( c, no_wal = False ):
|
def SetupDBCreatePragma( c, no_wal = False ):
|
||||||
|
|
||||||
|
@ -142,6 +155,8 @@ class HydrusDB( object ):
|
||||||
self._db_name = db_name
|
self._db_name = db_name
|
||||||
self._no_wal = no_wal
|
self._no_wal = no_wal
|
||||||
|
|
||||||
|
self._connection_timestamp = 0
|
||||||
|
|
||||||
main_db_filename = db_name
|
main_db_filename = db_name
|
||||||
|
|
||||||
if not main_db_filename.endswith( '.db' ):
|
if not main_db_filename.endswith( '.db' ):
|
||||||
|
@ -153,6 +168,8 @@ class HydrusDB( object ):
|
||||||
|
|
||||||
self._db_filenames[ 'main' ] = main_db_filename
|
self._db_filenames[ 'main' ] = main_db_filename
|
||||||
|
|
||||||
|
self._InitExternalDatabases()
|
||||||
|
|
||||||
if distutils.version.LooseVersion( sqlite3.sqlite_version ) < distutils.version.LooseVersion( '3.11.0' ):
|
if distutils.version.LooseVersion( sqlite3.sqlite_version ) < distutils.version.LooseVersion( '3.11.0' ):
|
||||||
|
|
||||||
self._fast_big_transaction_wal = False
|
self._fast_big_transaction_wal = False
|
||||||
|
@ -193,7 +210,7 @@ class HydrusDB( object ):
|
||||||
|
|
||||||
time.sleep( self.UPDATE_WAIT )
|
time.sleep( self.UPDATE_WAIT )
|
||||||
|
|
||||||
try: self._c.execute( 'BEGIN IMMEDIATE' )
|
try: self._c.execute( 'BEGIN IMMEDIATE;' )
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
||||||
raise HydrusExceptions.DBAccessException( HydrusData.ToUnicode( e ) )
|
raise HydrusExceptions.DBAccessException( HydrusData.ToUnicode( e ) )
|
||||||
|
@ -203,13 +220,24 @@ class HydrusDB( object ):
|
||||||
|
|
||||||
self._UpdateDB( version )
|
self._UpdateDB( version )
|
||||||
|
|
||||||
self._c.execute( 'COMMIT' )
|
self._c.execute( 'COMMIT;' )
|
||||||
|
|
||||||
except:
|
except:
|
||||||
|
|
||||||
self._c.execute( 'ROLLBACK' )
|
e = Exception( 'Updating the ' + self._db_name + ' db to version ' + str( version + 1 ) + ' caused this error:' + os.linesep + traceback.format_exc() )
|
||||||
|
|
||||||
raise Exception( 'Updating the ' + self._db_name + ' db to version ' + str( version + 1 ) + ' caused this error:' + os.linesep + traceback.format_exc() )
|
try:
|
||||||
|
|
||||||
|
self._c.execute( 'ROLLBACK;' )
|
||||||
|
|
||||||
|
except Exception as rollback_e:
|
||||||
|
|
||||||
|
HydrusData.Print( 'When the update failed, attempting to rollback the database failed.' )
|
||||||
|
|
||||||
|
HydrusData.PrintException( rollback_e )
|
||||||
|
|
||||||
|
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
( version, ) = self._c.execute( 'SELECT version FROM version;' ).fetchone()
|
( version, ) = self._c.execute( 'SELECT version FROM version;' ).fetchone()
|
||||||
|
@ -232,7 +260,29 @@ class HydrusDB( object ):
|
||||||
|
|
||||||
def _AttachExternalDatabases( self ):
|
def _AttachExternalDatabases( self ):
|
||||||
|
|
||||||
pass
|
for ( name, filename ) in self._db_filenames.items():
|
||||||
|
|
||||||
|
if name == 'main':
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
db_path = os.path.join( self._db_dir, self._db_filenames[ name ] )
|
||||||
|
|
||||||
|
if not os.path.exists( db_path ):
|
||||||
|
|
||||||
|
db = sqlite3.connect( db_path, isolation_level = None, detect_types = sqlite3.PARSE_DECLTYPES )
|
||||||
|
|
||||||
|
c = db.cursor()
|
||||||
|
|
||||||
|
SetupDBCreatePragma( c, no_wal = self._no_wal )
|
||||||
|
|
||||||
|
del c
|
||||||
|
del db
|
||||||
|
|
||||||
|
|
||||||
|
self._c.execute( 'ATTACH ? AS external_' + name + ';', ( db_path, ) )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _CleanUpCaches( self ):
|
def _CleanUpCaches( self ):
|
||||||
|
@ -309,11 +359,13 @@ class HydrusDB( object ):
|
||||||
|
|
||||||
self._db = sqlite3.connect( db_path, isolation_level = None, detect_types = sqlite3.PARSE_DECLTYPES )
|
self._db = sqlite3.connect( db_path, isolation_level = None, detect_types = sqlite3.PARSE_DECLTYPES )
|
||||||
|
|
||||||
|
self._connection_timestamp = HydrusData.GetNow()
|
||||||
|
|
||||||
self._db.create_function( 'hydrus_hamming', 2, HydrusData.GetHammingDistance )
|
self._db.create_function( 'hydrus_hamming', 2, HydrusData.GetHammingDistance )
|
||||||
|
|
||||||
self._c = self._db.cursor()
|
self._c = self._db.cursor()
|
||||||
|
|
||||||
self._c.execute( 'PRAGMA cache_size = -150000;' )
|
self._c.execute( 'PRAGMA main.cache_size = -100000;' )
|
||||||
|
|
||||||
self._c.execute( 'ATTACH ":memory:" AS mem;' )
|
self._c.execute( 'ATTACH ":memory:" AS mem;' )
|
||||||
|
|
||||||
|
@ -323,6 +375,8 @@ class HydrusDB( object ):
|
||||||
|
|
||||||
for db_name in db_names:
|
for db_name in db_names:
|
||||||
|
|
||||||
|
self._c.execute( 'PRAGMA ' + db_name + '.cache_size = -100000;' )
|
||||||
|
|
||||||
if self._no_wal:
|
if self._no_wal:
|
||||||
|
|
||||||
self._c.execute( 'PRAGMA ' + db_name + '.journal_mode = TRUNCATE;' )
|
self._c.execute( 'PRAGMA ' + db_name + '.journal_mode = TRUNCATE;' )
|
||||||
|
@ -378,6 +432,11 @@ class HydrusDB( object ):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _InitExternalDatabases( self ):
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _ManageDBError( self, job, e ):
|
def _ManageDBError( self, job, e ):
|
||||||
|
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
@ -393,15 +452,22 @@ class HydrusDB( object ):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
if job_type == 'read': self._c.execute( 'BEGIN DEFERRED' )
|
if job_type in ( 'read_write', 'write' ):
|
||||||
elif job_type in ( 'read_write', 'write' ): self._c.execute( 'BEGIN IMMEDIATE' )
|
|
||||||
|
self._c.execute( 'BEGIN IMMEDIATE;' )
|
||||||
in_transaction = True
|
|
||||||
|
in_transaction = True
|
||||||
|
|
||||||
|
|
||||||
if job_type in ( 'read', 'read_write' ): result = self._Read( action, *args, **kwargs )
|
if job_type in ( 'read', 'read_write' ): result = self._Read( action, *args, **kwargs )
|
||||||
elif job_type in ( 'write' ): result = self._Write( action, *args, **kwargs )
|
elif job_type in ( 'write' ): result = self._Write( action, *args, **kwargs )
|
||||||
|
|
||||||
self._c.execute( 'COMMIT' )
|
if in_transaction:
|
||||||
|
|
||||||
|
self._c.execute( 'COMMIT;' )
|
||||||
|
|
||||||
|
in_transaction = False
|
||||||
|
|
||||||
|
|
||||||
for ( topic, args, kwargs ) in self._pubsubs:
|
for ( topic, args, kwargs ) in self._pubsubs:
|
||||||
|
|
||||||
|
@ -415,7 +481,19 @@ class HydrusDB( object ):
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
||||||
if in_transaction: self._c.execute( 'ROLLBACK' )
|
if in_transaction:
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
self._c.execute( 'ROLLBACK;' )
|
||||||
|
|
||||||
|
except Exception as rollback_e:
|
||||||
|
|
||||||
|
HydrusData.Print( 'When the transaction failed, attempting to rollback the database failed.' )
|
||||||
|
|
||||||
|
HydrusData.PrintException( rollback_e )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self._ManageDBError( job, e )
|
self._ManageDBError( job, e )
|
||||||
|
|
||||||
|
@ -529,6 +607,11 @@ class HydrusDB( object ):
|
||||||
pass # no jobs in the past little while; let's just check if we should shutdown
|
pass # no jobs in the past little while; let's just check if we should shutdown
|
||||||
|
|
||||||
|
|
||||||
|
if HydrusData.TimeHasPassed( self._connection_timestamp + CONNECTION_REFRESH_TIME ): # just to clear out the journal files
|
||||||
|
|
||||||
|
self._InitDBCursor()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self._CleanUpCaches()
|
self._CleanUpCaches()
|
||||||
|
|
||||||
|
|
|
@ -937,6 +937,11 @@ def SplitIteratorIntoChunks( iterator, n ):
|
||||||
|
|
||||||
def SplitListIntoChunks( xs, n ):
|
def SplitListIntoChunks( xs, n ):
|
||||||
|
|
||||||
|
if isinstance( xs, set ):
|
||||||
|
|
||||||
|
xs = list( xs )
|
||||||
|
|
||||||
|
|
||||||
for i in xrange( 0, len( xs ), n ): yield xs[ i : i + n ]
|
for i in xrange( 0, len( xs ), n ): yield xs[ i : i + n ]
|
||||||
|
|
||||||
def TimeHasPassed( timestamp ):
|
def TimeHasPassed( timestamp ):
|
||||||
|
|
|
@ -344,10 +344,9 @@ class Controller( HydrusController.HydrusController ):
|
||||||
|
|
||||||
def MaintainDB( self ):
|
def MaintainDB( self ):
|
||||||
|
|
||||||
stale_time_delta = 30 * 86400
|
|
||||||
stop_time = HydrusData.GetNow() + 10
|
stop_time = HydrusData.GetNow() + 10
|
||||||
|
|
||||||
self.WriteSynchronous( 'analyze', stale_time_delta, stop_time )
|
self.WriteSynchronous( 'analyze', stop_time )
|
||||||
|
|
||||||
|
|
||||||
def NotifyPubSubs( self ):
|
def NotifyPubSubs( self ):
|
||||||
|
|
|
@ -100,7 +100,7 @@ class MessageDB( object ):
|
||||||
'''
|
'''
|
||||||
class DB( HydrusDB.HydrusDB ):
|
class DB( HydrusDB.HydrusDB ):
|
||||||
|
|
||||||
READ_WRITE_ACTIONS = []
|
READ_WRITE_ACTIONS = [ 'access_key', 'immediate_content_update', 'init', 'registration_keys' ]
|
||||||
|
|
||||||
def _AccountTypeExists( self, service_id, title ): return self._c.execute( 'SELECT 1 FROM account_types WHERE service_id = ? AND title = ?;', ( service_id, title ) ).fetchone() is not None
|
def _AccountTypeExists( self, service_id, title ): return self._c.execute( 'SELECT 1 FROM account_types WHERE service_id = ? AND title = ?;', ( service_id, title ) ).fetchone() is not None
|
||||||
|
|
||||||
|
@ -217,12 +217,8 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
|
|
||||||
splayed_hash_ids = HydrusData.SplayListForDB( hash_ids )
|
splayed_hash_ids = HydrusData.SplayListForDB( hash_ids )
|
||||||
|
|
||||||
affected_timestamps = [ timestamp for ( timestamp, ) in self._c.execute( 'SELECT DISTINCT timestamp FROM mapping_petitions WHERE service_id = ? AND tag_id = ? AND hash_id IN ' + splayed_hash_ids + ' AND status = ?;', ( service_id, tag_id, HC.DELETED ) ) ]
|
|
||||||
|
|
||||||
self._c.execute( 'DELETE FROM mapping_petitions WHERE service_id = ? AND tag_id = ? AND hash_id IN ' + splayed_hash_ids + ' AND status = ?;', ( service_id, tag_id, HC.DELETED ) )
|
self._c.execute( 'DELETE FROM mapping_petitions WHERE service_id = ? AND tag_id = ? AND hash_id IN ' + splayed_hash_ids + ' AND status = ?;', ( service_id, tag_id, HC.DELETED ) )
|
||||||
|
|
||||||
self._RefreshUpdateCache( service_id, affected_timestamps )
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
already_deleted = [ hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM mapping_petitions WHERE service_id = ? AND tag_id = ? AND hash_id IN ' + HydrusData.SplayListForDB( hash_ids ) + ' AND status = ?;', ( service_id, tag_id, HC.DELETED ) ) ]
|
already_deleted = [ hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM mapping_petitions WHERE service_id = ? AND tag_id = ? AND hash_id IN ' + HydrusData.SplayListForDB( hash_ids ) + ' AND status = ?;', ( service_id, tag_id, HC.DELETED ) ) ]
|
||||||
|
@ -312,7 +308,9 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
|
|
||||||
def _AddToExpires( self, account_ids, timespan ): self._c.execute( 'UPDATE accounts SET expires = expires + ? WHERE account_id IN ' + HydrusData.SplayListForDB( account_ids ) + ';', ( timespan, ) )
|
def _AddToExpires( self, account_ids, timespan ): self._c.execute( 'UPDATE accounts SET expires = expires + ? WHERE account_id IN ' + HydrusData.SplayListForDB( account_ids ) + ';', ( timespan, ) )
|
||||||
|
|
||||||
def _Analyze( self, stale_time_delta, stop_time ):
|
def _Analyze( self, stop_time ):
|
||||||
|
|
||||||
|
stale_time_delta = 30 * 86400
|
||||||
|
|
||||||
all_names = [ name for ( name, ) in self._c.execute( 'SELECT name FROM sqlite_master;' ) ]
|
all_names = [ name for ( name, ) in self._c.execute( 'SELECT name FROM sqlite_master;' ) ]
|
||||||
|
|
||||||
|
@ -339,7 +337,10 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
|
|
||||||
time_took = HydrusData.GetNowPrecise() - started
|
time_took = HydrusData.GetNowPrecise() - started
|
||||||
|
|
||||||
HydrusData.Print( 'Analyzed ' + name + ' in ' + HydrusData.ConvertTimeDeltaToPrettyString( time_took ) )
|
if time_took > 1:
|
||||||
|
|
||||||
|
HydrusData.Print( 'Analyzed ' + name + ' in ' + HydrusData.ConvertTimeDeltaToPrettyString( time_took ) )
|
||||||
|
|
||||||
|
|
||||||
if HydrusData.TimeHasPassed( stop_time ):
|
if HydrusData.TimeHasPassed( stop_time ):
|
||||||
|
|
||||||
|
@ -369,7 +370,7 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
|
|
||||||
def _ApproveFilePetitionOptimised( self, service_id, account_id, hash_ids ):
|
def _ApproveFilePetitionOptimised( self, service_id, account_id, hash_ids ):
|
||||||
|
|
||||||
( biggest_end, ) = self._c.execute( 'SELECT end FROM update_cache ORDER BY end DESC LIMIT 1;' ).fetchone()
|
( biggest_end, ) = self._c.execute( 'SELECT end FROM update_cache WHERE service_id = ? ORDER BY end DESC LIMIT 1;', ( service_id, ) ).fetchone()
|
||||||
|
|
||||||
self._c.execute( 'DELETE FROM file_map WHERE service_id = ? AND account_id = ? AND hash_id IN ' + HydrusData.SplayListForDB( hash_ids ) + ' AND timestamp > ?;', ( service_id, account_id, biggest_end ) )
|
self._c.execute( 'DELETE FROM file_map WHERE service_id = ? AND account_id = ? AND hash_id IN ' + HydrusData.SplayListForDB( hash_ids ) + ' AND timestamp > ?;', ( service_id, account_id, biggest_end ) )
|
||||||
|
|
||||||
|
@ -409,10 +410,6 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
|
|
||||||
self._RewardTagParentPetitioners( service_id, old_tag_id, new_tag_id, 1 )
|
self._RewardTagParentPetitioners( service_id, old_tag_id, new_tag_id, 1 )
|
||||||
|
|
||||||
# get affected timestamps here?
|
|
||||||
|
|
||||||
affected_timestamps = [ timestamp for ( timestamp, ) in self._c.execute( 'SELECT DISTINCT timestamp FROM tag_parents WHERE service_id = ? AND old_tag_id = ? AND new_tag_id = ? AND status = ?;', ( service_id, old_tag_id, new_tag_id, HC.CURRENT ) ) ]
|
|
||||||
|
|
||||||
self._c.execute( 'DELETE FROM tag_parents WHERE service_id = ? AND old_tag_id = ? AND new_tag_id = ?;', ( service_id, old_tag_id, new_tag_id ) )
|
self._c.execute( 'DELETE FROM tag_parents WHERE service_id = ? AND old_tag_id = ? AND new_tag_id = ?;', ( service_id, old_tag_id, new_tag_id ) )
|
||||||
|
|
||||||
if status == HC.PENDING: new_status = HC.CURRENT
|
if status == HC.PENDING: new_status = HC.CURRENT
|
||||||
|
@ -422,8 +419,6 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
|
|
||||||
self._c.execute( 'INSERT OR IGNORE INTO tag_parents ( service_id, account_id, old_tag_id, new_tag_id, reason_id, status, timestamp ) VALUES ( ?, ?, ?, ?, ?, ?, ? );', ( service_id, account_id, old_tag_id, new_tag_id, reason_id, new_status, now ) )
|
self._c.execute( 'INSERT OR IGNORE INTO tag_parents ( service_id, account_id, old_tag_id, new_tag_id, reason_id, status, timestamp ) VALUES ( ?, ?, ?, ?, ?, ?, ? );', ( service_id, account_id, old_tag_id, new_tag_id, reason_id, new_status, now ) )
|
||||||
|
|
||||||
if len( affected_timestamps ) > 0: self._RefreshUpdateCache( service_id, affected_timestamps )
|
|
||||||
|
|
||||||
|
|
||||||
def _ApproveTagSiblingPetition( self, service_id, account_id, old_tag_id, new_tag_id, reason_id, status ):
|
def _ApproveTagSiblingPetition( self, service_id, account_id, old_tag_id, new_tag_id, reason_id, status ):
|
||||||
|
|
||||||
|
@ -442,10 +437,6 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
|
|
||||||
self._RewardTagSiblingPetitioners( service_id, old_tag_id, new_tag_id, 1 )
|
self._RewardTagSiblingPetitioners( service_id, old_tag_id, new_tag_id, 1 )
|
||||||
|
|
||||||
# get affected timestamps here?
|
|
||||||
|
|
||||||
affected_timestamps = [ timestamp for ( timestamp, ) in self._c.execute( 'SELECT DISTINCT timestamp FROM tag_siblings WHERE service_id = ? AND old_tag_id = ? AND new_tag_id = ? AND status = ?;', ( service_id, old_tag_id, new_tag_id, HC.CURRENT ) ) ]
|
|
||||||
|
|
||||||
self._c.execute( 'DELETE FROM tag_siblings WHERE service_id = ? AND old_tag_id = ? AND new_tag_id = ?;', ( service_id, old_tag_id, new_tag_id ) )
|
self._c.execute( 'DELETE FROM tag_siblings WHERE service_id = ? AND old_tag_id = ? AND new_tag_id = ?;', ( service_id, old_tag_id, new_tag_id ) )
|
||||||
|
|
||||||
if status == HC.PENDING: new_status = HC.CURRENT
|
if status == HC.PENDING: new_status = HC.CURRENT
|
||||||
|
@ -455,61 +446,6 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
|
|
||||||
self._c.execute( 'INSERT OR IGNORE INTO tag_siblings ( service_id, account_id, old_tag_id, new_tag_id, reason_id, status, timestamp ) VALUES ( ?, ?, ?, ?, ?, ?, ? );', ( service_id, account_id, old_tag_id, new_tag_id, reason_id, new_status, now ) )
|
self._c.execute( 'INSERT OR IGNORE INTO tag_siblings ( service_id, account_id, old_tag_id, new_tag_id, reason_id, status, timestamp ) VALUES ( ?, ?, ?, ?, ?, ?, ? );', ( service_id, account_id, old_tag_id, new_tag_id, reason_id, new_status, now ) )
|
||||||
|
|
||||||
if len( affected_timestamps ) > 0: self._RefreshUpdateCache( service_id, affected_timestamps )
|
|
||||||
|
|
||||||
|
|
||||||
def _AttachExternalDatabases( self ):
|
|
||||||
|
|
||||||
self._db_filenames[ 'master' ] = self._db_name + '.master.db'
|
|
||||||
|
|
||||||
master_db_path = os.path.join( self._db_dir, self._db_filenames[ 'master' ] )
|
|
||||||
|
|
||||||
if not os.path.exists( master_db_path ):
|
|
||||||
|
|
||||||
db = sqlite3.connect( master_db_path, isolation_level = None, detect_types = sqlite3.PARSE_DECLTYPES )
|
|
||||||
|
|
||||||
c = db.cursor()
|
|
||||||
|
|
||||||
HydrusDB.SetupDBCreatePragma( c, no_wal = self._no_wal )
|
|
||||||
|
|
||||||
c.execute( 'CREATE TABLE hashes ( hash_id INTEGER PRIMARY KEY, hash BLOB_BYTES UNIQUE );' )
|
|
||||||
|
|
||||||
c.execute( 'CREATE TABLE tags ( tag_id INTEGER PRIMARY KEY, tag TEXT UNIQUE );' )
|
|
||||||
|
|
||||||
del c
|
|
||||||
del db
|
|
||||||
|
|
||||||
|
|
||||||
self._c.execute( 'ATTACH ? AS external_master;', ( master_db_path, ) )
|
|
||||||
|
|
||||||
self._db_filenames[ 'mappings' ] = self._db_name + '.mappings.db'
|
|
||||||
|
|
||||||
mappings_db_path = os.path.join( self._db_dir, self._db_filenames[ 'mappings' ] )
|
|
||||||
|
|
||||||
if not os.path.exists( mappings_db_path ):
|
|
||||||
|
|
||||||
db = sqlite3.connect( mappings_db_path, isolation_level = None, detect_types = sqlite3.PARSE_DECLTYPES )
|
|
||||||
|
|
||||||
c = db.cursor()
|
|
||||||
|
|
||||||
HydrusDB.SetupDBCreatePragma( c, no_wal = self._no_wal )
|
|
||||||
|
|
||||||
c.execute( 'CREATE TABLE mapping_petitions ( service_id INTEGER, account_id INTEGER, tag_id INTEGER, hash_id INTEGER, reason_id INTEGER, timestamp INTEGER, status INTEGER, PRIMARY KEY( service_id, account_id, tag_id, hash_id, status ) ) WITHOUT ROWID;' )
|
|
||||||
c.execute( 'CREATE INDEX mapping_petitions_service_id_account_id_reason_id_tag_id_index ON mapping_petitions ( service_id, account_id, reason_id, tag_id );' )
|
|
||||||
c.execute( 'CREATE INDEX mapping_petitions_service_id_tag_id_hash_id_index ON mapping_petitions ( service_id, tag_id, hash_id );' )
|
|
||||||
c.execute( 'CREATE INDEX mapping_petitions_service_id_status_index ON mapping_petitions ( service_id, status );' )
|
|
||||||
c.execute( 'CREATE INDEX mapping_petitions_service_id_timestamp_index ON mapping_petitions ( service_id, timestamp );' )
|
|
||||||
|
|
||||||
c.execute( 'CREATE TABLE mappings ( service_id INTEGER, tag_id INTEGER, hash_id INTEGER, account_id INTEGER, timestamp INTEGER, PRIMARY KEY( service_id, tag_id, hash_id ) ) WITHOUT ROWID;' )
|
|
||||||
c.execute( 'CREATE INDEX mappings_account_id_index ON mappings ( account_id );' )
|
|
||||||
c.execute( 'CREATE INDEX mappings_timestamp_index ON mappings ( timestamp );' )
|
|
||||||
|
|
||||||
del c
|
|
||||||
del db
|
|
||||||
|
|
||||||
|
|
||||||
self._c.execute( 'ATTACH ? AS external_mappings;', ( mappings_db_path, ) )
|
|
||||||
|
|
||||||
|
|
||||||
def _Backup( self ):
|
def _Backup( self ):
|
||||||
|
|
||||||
|
@ -667,15 +603,6 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _CleanUpdate( self, service_key, begin, end ):
|
|
||||||
|
|
||||||
self._GenerateUpdate( service_key, begin, end )
|
|
||||||
|
|
||||||
service_id = self._GetServiceId( service_key )
|
|
||||||
|
|
||||||
self._c.execute( 'UPDATE update_cache SET dirty = ? WHERE service_id = ? AND begin = ?;', ( False, service_id, begin ) )
|
|
||||||
|
|
||||||
|
|
||||||
def _ClearBans( self ):
|
def _ClearBans( self ):
|
||||||
|
|
||||||
now = HydrusData.GetNow()
|
now = HydrusData.GetNow()
|
||||||
|
@ -788,6 +715,26 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
|
|
||||||
self._c.execute( 'CREATE TABLE version ( version INTEGER, year INTEGER, month INTEGER );' )
|
self._c.execute( 'CREATE TABLE version ( version INTEGER, year INTEGER, month INTEGER );' )
|
||||||
|
|
||||||
|
# mappings
|
||||||
|
|
||||||
|
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_mappings.mapping_petitions ( service_id INTEGER, account_id INTEGER, tag_id INTEGER, hash_id INTEGER, reason_id INTEGER, timestamp INTEGER, status INTEGER, PRIMARY KEY( service_id, account_id, tag_id, hash_id, status ) ) WITHOUT ROWID;' )
|
||||||
|
self._c.execute( 'CREATE INDEX IF NOT EXISTS external_mappings.mapping_petitions_service_id_account_id_reason_id_tag_id_index ON mapping_petitions ( service_id, account_id, reason_id, tag_id );' )
|
||||||
|
self._c.execute( 'CREATE INDEX IF NOT EXISTS external_mappings.mapping_petitions_service_id_tag_id_hash_id_index ON mapping_petitions ( service_id, tag_id, hash_id );' )
|
||||||
|
self._c.execute( 'CREATE INDEX IF NOT EXISTS external_mappings.mapping_petitions_service_id_status_index ON mapping_petitions ( service_id, status );' )
|
||||||
|
self._c.execute( 'CREATE INDEX IF NOT EXISTS external_mappings.mapping_petitions_service_id_timestamp_index ON mapping_petitions ( service_id, timestamp );' )
|
||||||
|
|
||||||
|
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_mappings.mappings ( service_id INTEGER, tag_id INTEGER, hash_id INTEGER, account_id INTEGER, timestamp INTEGER, PRIMARY KEY( service_id, tag_id, hash_id ) ) WITHOUT ROWID;' )
|
||||||
|
self._c.execute( 'CREATE INDEX IF NOT EXISTS external_mappings.mappings_account_id_index ON mappings ( account_id );' )
|
||||||
|
self._c.execute( 'CREATE INDEX IF NOT EXISTS external_mappings.mappings_timestamp_index ON mappings ( timestamp );' )
|
||||||
|
|
||||||
|
# master
|
||||||
|
|
||||||
|
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_master.hashes ( hash_id INTEGER PRIMARY KEY, hash BLOB_BYTES UNIQUE );' )
|
||||||
|
|
||||||
|
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_master.tags ( tag_id INTEGER PRIMARY KEY, tag TEXT UNIQUE );' )
|
||||||
|
|
||||||
|
# inserts
|
||||||
|
|
||||||
current_time_struct = time.gmtime()
|
current_time_struct = time.gmtime()
|
||||||
|
|
||||||
( current_year, current_month ) = ( current_time_struct.tm_year, current_time_struct.tm_mon )
|
( current_year, current_month ) = ( current_time_struct.tm_year, current_time_struct.tm_mon )
|
||||||
|
@ -828,8 +775,6 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
|
|
||||||
splayed_hash_ids = HydrusData.SplayListForDB( hash_ids )
|
splayed_hash_ids = HydrusData.SplayListForDB( hash_ids )
|
||||||
|
|
||||||
affected_timestamps = [ timestamp for ( timestamp, ) in self._c.execute( 'SELECT DISTINCT timestamp FROM file_map WHERE service_id = ? AND hash_id IN ' + splayed_hash_ids + ';', ( service_id, ) ) ]
|
|
||||||
|
|
||||||
self._c.execute( 'DELETE FROM file_map WHERE service_id = ? AND hash_id IN ' + splayed_hash_ids + ';', ( service_id, ) )
|
self._c.execute( 'DELETE FROM file_map WHERE service_id = ? AND hash_id IN ' + splayed_hash_ids + ';', ( service_id, ) )
|
||||||
self._c.execute( 'DELETE FROM file_petitions WHERE service_id = ? AND hash_id IN ' + splayed_hash_ids + ' AND status = ?;', ( service_id, HC.PETITIONED ) )
|
self._c.execute( 'DELETE FROM file_petitions WHERE service_id = ? AND hash_id IN ' + splayed_hash_ids + ' AND status = ?;', ( service_id, HC.PETITIONED ) )
|
||||||
|
|
||||||
|
@ -837,22 +782,16 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
|
|
||||||
self._c.executemany( 'INSERT OR IGNORE INTO file_petitions ( service_id, account_id, hash_id, reason_id, timestamp, status ) VALUES ( ?, ?, ?, ?, ?, ? );', ( ( service_id, account_id, hash_id, reason_id, now, HC.DELETED ) for hash_id in hash_ids ) )
|
self._c.executemany( 'INSERT OR IGNORE INTO file_petitions ( service_id, account_id, hash_id, reason_id, timestamp, status ) VALUES ( ?, ?, ?, ?, ?, ? );', ( ( service_id, account_id, hash_id, reason_id, now, HC.DELETED ) for hash_id in hash_ids ) )
|
||||||
|
|
||||||
self._RefreshUpdateCache( service_id, affected_timestamps )
|
|
||||||
|
|
||||||
|
|
||||||
def _DeleteMappings( self, service_id, account_id, tag_id, hash_ids, reason_id ):
|
def _DeleteMappings( self, service_id, account_id, tag_id, hash_ids, reason_id ):
|
||||||
|
|
||||||
splayed_hash_ids = HydrusData.SplayListForDB( hash_ids )
|
splayed_hash_ids = HydrusData.SplayListForDB( hash_ids )
|
||||||
|
|
||||||
affected_timestamps = [ timestamp for ( timestamp, ) in self._c.execute( 'SELECT DISTINCT timestamp FROM mappings WHERE service_id = ? AND tag_id = ? AND hash_id IN ' + splayed_hash_ids + ';', ( service_id, tag_id ) ) ]
|
|
||||||
|
|
||||||
self._c.execute( 'DELETE FROM mappings WHERE service_id = ? AND tag_id = ? AND hash_id IN ' + splayed_hash_ids + ';', ( service_id, tag_id ) )
|
self._c.execute( 'DELETE FROM mappings WHERE service_id = ? AND tag_id = ? AND hash_id IN ' + splayed_hash_ids + ';', ( service_id, tag_id ) )
|
||||||
self._c.execute( 'DELETE FROM mapping_petitions WHERE service_id = ? AND tag_id = ? AND hash_id IN ' + splayed_hash_ids + ' AND status = ?;', ( service_id, tag_id, HC.PETITIONED ) )
|
self._c.execute( 'DELETE FROM mapping_petitions WHERE service_id = ? AND tag_id = ? AND hash_id IN ' + splayed_hash_ids + ' AND status = ?;', ( service_id, tag_id, HC.PETITIONED ) )
|
||||||
|
|
||||||
self._c.executemany( 'INSERT OR IGNORE INTO mapping_petitions ( service_id, tag_id, hash_id, account_id, reason_id, timestamp, status ) VALUES ( ?, ?, ?, ?, ?, ?, ? );', ( ( service_id, tag_id, hash_id, account_id, reason_id, HydrusData.GetNow(), HC.DELETED ) for hash_id in hash_ids ) )
|
self._c.executemany( 'INSERT OR IGNORE INTO mapping_petitions ( service_id, tag_id, hash_id, account_id, reason_id, timestamp, status ) VALUES ( ?, ?, ?, ?, ?, ?, ? );', ( ( service_id, tag_id, hash_id, account_id, reason_id, HydrusData.GetNow(), HC.DELETED ) for hash_id in hash_ids ) )
|
||||||
|
|
||||||
self._RefreshUpdateCache( service_id, affected_timestamps )
|
|
||||||
|
|
||||||
|
|
||||||
def _DeleteOrphans( self ):
|
def _DeleteOrphans( self ):
|
||||||
|
|
||||||
|
@ -1304,15 +1243,6 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
return [ account_type for ( account_type, ) in self._c.execute( 'SELECT account_type FROM account_types WHERE service_id = ?;', ( service_id, ) ) ]
|
return [ account_type for ( account_type, ) in self._c.execute( 'SELECT account_type FROM account_types WHERE service_id = ?;', ( service_id, ) ) ]
|
||||||
|
|
||||||
|
|
||||||
def _GetDirtyUpdates( self, service_key ):
|
|
||||||
|
|
||||||
service_id = self._GetServiceId( service_key )
|
|
||||||
|
|
||||||
result = self._c.execute( 'SELECT begin, end FROM update_cache WHERE service_id = ? AND dirty = ?;', ( service_id, True ) ).fetchall()
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def _GetFile( self, hash ):
|
def _GetFile( self, hash ):
|
||||||
|
|
||||||
path = ServerFiles.GetPath( 'file', hash )
|
path = ServerFiles.GetPath( 'file', hash )
|
||||||
|
@ -1778,6 +1708,12 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
self._services_over_monthly_data = set()
|
self._services_over_monthly_data = set()
|
||||||
|
|
||||||
|
|
||||||
|
def _InitExternalDatabases( self ):
|
||||||
|
|
||||||
|
self._db_filenames[ 'mappings' ] = 'server.mappings.db'
|
||||||
|
self._db_filenames[ 'master' ] = 'server.master.db'
|
||||||
|
|
||||||
|
|
||||||
def _IterateFileUpdateContentData( self, service_id, begin, end ):
|
def _IterateFileUpdateContentData( self, service_id, begin, end ):
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -2286,7 +2222,6 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
elif action == 'account_key_from_access_key': result = self._GetAccountKeyFromAccessKey( *args, **kwargs )
|
elif action == 'account_key_from_access_key': result = self._GetAccountKeyFromAccessKey( *args, **kwargs )
|
||||||
elif action == 'account_key_from_identifier': result = self._GetAccountKeyFromIdentifier( *args, **kwargs )
|
elif action == 'account_key_from_identifier': result = self._GetAccountKeyFromIdentifier( *args, **kwargs )
|
||||||
elif action == 'account_types': result = self._GetAccountTypes( *args, **kwargs )
|
elif action == 'account_types': result = self._GetAccountTypes( *args, **kwargs )
|
||||||
elif action == 'dirty_updates': result = self._GetDirtyUpdates( *args, **kwargs )
|
|
||||||
elif action == 'immediate_content_update': result = self._GenerateImmediateContentUpdate( *args, **kwargs )
|
elif action == 'immediate_content_update': result = self._GenerateImmediateContentUpdate( *args, **kwargs )
|
||||||
elif action == 'init': result = self._InitAdmin( *args, **kwargs )
|
elif action == 'init': result = self._InitAdmin( *args, **kwargs )
|
||||||
elif action == 'ip': result = self._GetIPTimestamp( *args, **kwargs )
|
elif action == 'ip': result = self._GetIPTimestamp( *args, **kwargs )
|
||||||
|
@ -2306,8 +2241,6 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _RefreshUpdateCache( self, service_id, affected_timestamps ): self._c.executemany( 'UPDATE update_cache SET dirty = ? WHERE service_id = ? AND ? BETWEEN begin AND end;', [ ( True, service_id, timestamp ) for timestamp in affected_timestamps ] )
|
|
||||||
|
|
||||||
def _RewardAccounts( self, service_id, score_type, scores ):
|
def _RewardAccounts( self, service_id, score_type, scores ):
|
||||||
|
|
||||||
self._c.executemany( 'INSERT OR IGNORE INTO account_scores ( service_id, account_id, score_type, score ) VALUES ( ?, ?, ?, ? );', [ ( service_id, account_id, score_type, 0 ) for ( account_id, score ) in scores ] )
|
self._c.executemany( 'INSERT OR IGNORE INTO account_scores ( service_id, account_id, score_type, score ) VALUES ( ?, ?, ?, ? );', [ ( service_id, account_id, score_type, 0 ) for ( account_id, score ) in scores ] )
|
||||||
|
@ -2557,12 +2490,16 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
|
|
||||||
HydrusData.Print( 'exporting hashes to external db' )
|
HydrusData.Print( 'exporting hashes to external db' )
|
||||||
|
|
||||||
|
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_master.hashes ( hash_id INTEGER PRIMARY KEY, hash BLOB_BYTES UNIQUE );' )
|
||||||
|
|
||||||
self._c.execute( 'INSERT INTO external_master.hashes SELECT * FROM main.hashes;' )
|
self._c.execute( 'INSERT INTO external_master.hashes SELECT * FROM main.hashes;' )
|
||||||
|
|
||||||
self._c.execute( 'DROP TABLE main.hashes;' )
|
self._c.execute( 'DROP TABLE main.hashes;' )
|
||||||
|
|
||||||
HydrusData.Print( 'exporting tags to external db' )
|
HydrusData.Print( 'exporting tags to external db' )
|
||||||
|
|
||||||
|
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_master.tags ( tag_id INTEGER PRIMARY KEY, tag TEXT UNIQUE );' )
|
||||||
|
|
||||||
self._c.execute( 'INSERT INTO external_master.tags SELECT * FROM main.tags;' )
|
self._c.execute( 'INSERT INTO external_master.tags SELECT * FROM main.tags;' )
|
||||||
|
|
||||||
self._c.execute( 'DROP TABLE main.tags;' )
|
self._c.execute( 'DROP TABLE main.tags;' )
|
||||||
|
@ -2582,23 +2519,25 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
self._c.execute( 'ALTER TABLE mapping_petitions RENAME TO mapping_petitions_old;' )
|
self._c.execute( 'ALTER TABLE mapping_petitions RENAME TO mapping_petitions_old;' )
|
||||||
self._c.execute( 'ALTER TABLE mappings RENAME TO mappings_old;' )
|
self._c.execute( 'ALTER TABLE mappings RENAME TO mappings_old;' )
|
||||||
|
|
||||||
self._c.execute( 'CREATE TABLE external_mappings.mapping_petitions ( service_id INTEGER, account_id INTEGER, tag_id INTEGER, hash_id INTEGER, reason_id INTEGER, timestamp INTEGER, status INTEGER, PRIMARY KEY( service_id, account_id, tag_id, hash_id, status ) ) WITHOUT ROWID;' )
|
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_mappings.mapping_petitions ( service_id INTEGER, account_id INTEGER, tag_id INTEGER, hash_id INTEGER, reason_id INTEGER, timestamp INTEGER, status INTEGER, PRIMARY KEY( service_id, account_id, tag_id, hash_id, status ) ) WITHOUT ROWID;' )
|
||||||
|
|
||||||
self._c.execute( 'CREATE TABLE external_mappings.mappings ( service_id INTEGER, tag_id INTEGER, hash_id INTEGER, account_id INTEGER, timestamp INTEGER, PRIMARY KEY( service_id, tag_id, hash_id ) ) WITHOUT ROWID;' )
|
|
||||||
|
|
||||||
self._c.execute( 'INSERT INTO mapping_petitions SELECT * FROM mapping_petitions_old;' )
|
self._c.execute( 'INSERT INTO mapping_petitions SELECT * FROM mapping_petitions_old;' )
|
||||||
self._c.execute( 'INSERT INTO mappings SELECT * FROM mappings_old;' )
|
|
||||||
|
|
||||||
self._c.execute( 'DROP TABLE mapping_petitions_old;' )
|
self._c.execute( 'DROP TABLE mapping_petitions_old;' )
|
||||||
|
|
||||||
|
self._c.execute( 'CREATE TABLE IF NOT EXISTS external_mappings.mappings ( service_id INTEGER, tag_id INTEGER, hash_id INTEGER, account_id INTEGER, timestamp INTEGER, PRIMARY KEY( service_id, tag_id, hash_id ) ) WITHOUT ROWID;' )
|
||||||
|
|
||||||
|
self._c.execute( 'INSERT INTO mappings SELECT * FROM mappings_old;' )
|
||||||
|
|
||||||
self._c.execute( 'DROP TABLE mappings_old;' )
|
self._c.execute( 'DROP TABLE mappings_old;' )
|
||||||
|
|
||||||
self._c.execute( 'CREATE INDEX external_mappings.mapping_petitions_service_id_account_id_reason_id_tag_id_index ON mapping_petitions ( service_id, account_id, reason_id, tag_id );' )
|
self._c.execute( 'CREATE INDEX IF NOT EXISTS external_mappings.mapping_petitions_service_id_account_id_reason_id_tag_id_index ON mapping_petitions ( service_id, account_id, reason_id, tag_id );' )
|
||||||
self._c.execute( 'CREATE INDEX external_mappings.mapping_petitions_service_id_tag_id_hash_id_index ON mapping_petitions ( service_id, tag_id, hash_id );' )
|
self._c.execute( 'CREATE INDEX IF NOT EXISTS external_mappings.mapping_petitions_service_id_tag_id_hash_id_index ON mapping_petitions ( service_id, tag_id, hash_id );' )
|
||||||
self._c.execute( 'CREATE INDEX external_mappings.mapping_petitions_service_id_status_index ON mapping_petitions ( service_id, status );' )
|
self._c.execute( 'CREATE INDEX IF NOT EXISTS external_mappings.mapping_petitions_service_id_status_index ON mapping_petitions ( service_id, status );' )
|
||||||
self._c.execute( 'CREATE INDEX external_mappings.mapping_petitions_service_id_timestamp_index ON mapping_petitions ( service_id, timestamp );' )
|
self._c.execute( 'CREATE INDEX IF NOT EXISTS external_mappings.mapping_petitions_service_id_timestamp_index ON mapping_petitions ( service_id, timestamp );' )
|
||||||
|
|
||||||
self._c.execute( 'CREATE INDEX external_mappings.mappings_account_id_index ON mappings ( account_id );' )
|
self._c.execute( 'CREATE INDEX IF NOT EXISTS external_mappings.mappings_account_id_index ON mappings ( account_id );' )
|
||||||
self._c.execute( 'CREATE INDEX external_mappings.mappings_timestamp_index ON mappings ( timestamp );' )
|
self._c.execute( 'CREATE INDEX IF NOT EXISTS external_mappings.mappings_timestamp_index ON mappings ( timestamp );' )
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
||||||
|
@ -2654,7 +2593,6 @@ class DB( HydrusDB.HydrusDB ):
|
||||||
elif action == 'backup': result = self._Backup( *args, **kwargs )
|
elif action == 'backup': result = self._Backup( *args, **kwargs )
|
||||||
elif action == 'check_data_usage': result = self._CheckDataUsage( *args, **kwargs )
|
elif action == 'check_data_usage': result = self._CheckDataUsage( *args, **kwargs )
|
||||||
elif action == 'check_monthly_data': result = self._CheckMonthlyData( *args, **kwargs )
|
elif action == 'check_monthly_data': result = self._CheckMonthlyData( *args, **kwargs )
|
||||||
elif action == 'clean_update': result = self._CleanUpdate( *args, **kwargs )
|
|
||||||
elif action == 'clear_bans': result = self._ClearBans( *args, **kwargs )
|
elif action == 'clear_bans': result = self._ClearBans( *args, **kwargs )
|
||||||
elif action == 'create_update': result = self._CreateUpdate( *args, **kwargs )
|
elif action == 'create_update': result = self._CreateUpdate( *args, **kwargs )
|
||||||
elif action == 'delete_orphans': result = self._DeleteOrphans( *args, **kwargs )
|
elif action == 'delete_orphans': result = self._DeleteOrphans( *args, **kwargs )
|
||||||
|
|
|
@ -77,36 +77,6 @@ def DAEMONGenerateUpdates( controller ):
|
||||||
time.sleep( 1 )
|
time.sleep( 1 )
|
||||||
|
|
||||||
|
|
||||||
time_to_stop = HydrusData.GetNow() + 30
|
|
||||||
|
|
||||||
service_keys = controller.Read( 'service_keys', HC.REPOSITORIES )
|
|
||||||
|
|
||||||
for service_key in service_keys:
|
|
||||||
|
|
||||||
num_petitions = controller.Read( 'num_petitions', service_key )
|
|
||||||
|
|
||||||
if num_petitions == 0:
|
|
||||||
|
|
||||||
dirty_updates = controller.Read( 'dirty_updates', service_key )
|
|
||||||
|
|
||||||
for ( begin, end ) in dirty_updates:
|
|
||||||
|
|
||||||
if HydrusGlobals.view_shutdown or HydrusData.TimeHasPassed( time_to_stop ):
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
HydrusGlobals.server_busy = True
|
|
||||||
|
|
||||||
controller.WriteSynchronous( 'clean_update', service_key, begin, end )
|
|
||||||
|
|
||||||
HydrusGlobals.server_busy = False
|
|
||||||
|
|
||||||
time.sleep( 1 )
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def DAEMONUPnP( controller ):
|
def DAEMONUPnP( controller ):
|
||||||
|
|
||||||
|
|
|
@ -664,7 +664,7 @@ class TestClientDB( unittest.TestCase ):
|
||||||
|
|
||||||
( media_result, ) = self._read( 'media_results', CC.LOCAL_FILE_SERVICE_KEY, ( written_hash, ) )
|
( media_result, ) = self._read( 'media_results', CC.LOCAL_FILE_SERVICE_KEY, ( written_hash, ) )
|
||||||
|
|
||||||
( mr_hash, mr_inbox, mr_size, mr_mime, mr_timestamp, mr_width, mr_height, mr_duration, mr_num_frames, mr_num_words, mr_tags_manager, mr_locations_manager, mr_local_ratings, mr_remote_ratings ) = media_result.ToTuple()
|
( mr_hash, mr_inbox, mr_size, mr_mime, mr_width, mr_height, mr_duration, mr_num_frames, mr_num_words, mr_tags_manager, mr_locations_manager, mr_local_ratings, mr_remote_ratings ) = media_result.ToTuple()
|
||||||
|
|
||||||
now = HydrusData.GetNow()
|
now = HydrusData.GetNow()
|
||||||
|
|
||||||
|
@ -672,8 +672,6 @@ class TestClientDB( unittest.TestCase ):
|
||||||
self.assertEqual( mr_inbox, True )
|
self.assertEqual( mr_inbox, True )
|
||||||
self.assertEqual( mr_size, size )
|
self.assertEqual( mr_size, size )
|
||||||
self.assertEqual( mr_mime, mime )
|
self.assertEqual( mr_mime, mime )
|
||||||
self.assertLessEqual( now - 10, mr_timestamp )
|
|
||||||
self.assertLessEqual( mr_timestamp, now + 10 )
|
|
||||||
self.assertEqual( mr_width, width )
|
self.assertEqual( mr_width, width )
|
||||||
self.assertEqual( mr_height, height )
|
self.assertEqual( mr_height, height )
|
||||||
self.assertEqual( mr_duration, duration )
|
self.assertEqual( mr_duration, duration )
|
||||||
|
@ -795,7 +793,7 @@ class TestClientDB( unittest.TestCase ):
|
||||||
|
|
||||||
( media_result, ) = self._read( 'media_results', CC.LOCAL_FILE_SERVICE_KEY, ( hash, ) )
|
( media_result, ) = self._read( 'media_results', CC.LOCAL_FILE_SERVICE_KEY, ( hash, ) )
|
||||||
|
|
||||||
( mr_hash, mr_inbox, mr_size, mr_mime, mr_timestamp, mr_width, mr_height, mr_duration, mr_num_frames, mr_num_words, mr_tags_manager, mr_locations_manager, mr_local_ratings, mr_remote_ratings ) = media_result.ToTuple()
|
( mr_hash, mr_inbox, mr_size, mr_mime, mr_width, mr_height, mr_duration, mr_num_frames, mr_num_words, mr_tags_manager, mr_locations_manager, mr_local_ratings, mr_remote_ratings ) = media_result.ToTuple()
|
||||||
|
|
||||||
now = HydrusData.GetNow()
|
now = HydrusData.GetNow()
|
||||||
|
|
||||||
|
@ -804,8 +802,6 @@ class TestClientDB( unittest.TestCase ):
|
||||||
self.assertEqual( mr_size, 5270 )
|
self.assertEqual( mr_size, 5270 )
|
||||||
self.assertEqual( mr_mime, HC.IMAGE_PNG )
|
self.assertEqual( mr_mime, HC.IMAGE_PNG )
|
||||||
self.assertEqual( mr_hash, hash )
|
self.assertEqual( mr_hash, hash )
|
||||||
self.assertLessEqual( now - 10, mr_timestamp )
|
|
||||||
self.assertLessEqual( mr_timestamp, now + 10 )
|
|
||||||
self.assertEqual( mr_width, 200 )
|
self.assertEqual( mr_width, 200 )
|
||||||
self.assertEqual( mr_height, 200 )
|
self.assertEqual( mr_height, 200 )
|
||||||
self.assertEqual( mr_duration, None )
|
self.assertEqual( mr_duration, None )
|
||||||
|
@ -814,7 +810,7 @@ class TestClientDB( unittest.TestCase ):
|
||||||
|
|
||||||
( media_result, ) = self._read( 'media_results_from_ids', CC.LOCAL_FILE_SERVICE_KEY, ( 1, ) )
|
( media_result, ) = self._read( 'media_results_from_ids', CC.LOCAL_FILE_SERVICE_KEY, ( 1, ) )
|
||||||
|
|
||||||
( mr_hash, mr_inbox, mr_size, mr_mime, mr_timestamp, mr_width, mr_height, mr_duration, mr_num_frames, mr_num_words, mr_tags_manager, mr_locations_manager, mr_local_ratings, mr_remote_ratings ) = media_result.ToTuple()
|
( mr_hash, mr_inbox, mr_size, mr_mime, mr_width, mr_height, mr_duration, mr_num_frames, mr_num_words, mr_tags_manager, mr_locations_manager, mr_local_ratings, mr_remote_ratings ) = media_result.ToTuple()
|
||||||
|
|
||||||
now = HydrusData.GetNow()
|
now = HydrusData.GetNow()
|
||||||
|
|
||||||
|
@ -823,8 +819,6 @@ class TestClientDB( unittest.TestCase ):
|
||||||
self.assertEqual( mr_size, 5270 )
|
self.assertEqual( mr_size, 5270 )
|
||||||
self.assertEqual( mr_mime, HC.IMAGE_PNG )
|
self.assertEqual( mr_mime, HC.IMAGE_PNG )
|
||||||
self.assertEqual( mr_hash, hash )
|
self.assertEqual( mr_hash, hash )
|
||||||
self.assertLessEqual( now - 10, mr_timestamp )
|
|
||||||
self.assertLessEqual( mr_timestamp, now + 10 )
|
|
||||||
self.assertEqual( mr_width, 200 )
|
self.assertEqual( mr_width, 200 )
|
||||||
self.assertEqual( mr_height, 200 )
|
self.assertEqual( mr_height, 200 )
|
||||||
self.assertEqual( mr_duration, None )
|
self.assertEqual( mr_duration, None )
|
||||||
|
|
|
@ -255,9 +255,9 @@ class TestServer( unittest.TestCase ):
|
||||||
info[ 'timeout' ] = 0
|
info[ 'timeout' ] = 0
|
||||||
info[ 'hashes' ] = hashes
|
info[ 'hashes' ] = hashes
|
||||||
|
|
||||||
# hash, inbox, size, mime, timestamp, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings
|
# hash, inbox, size, mime, width, height, duration, num_frames, num_words, tags_manager, locations_manager, local_ratings, remote_ratings
|
||||||
|
|
||||||
media_results = [ ClientMedia.MediaResult( ( hash, True, 500, HC.IMAGE_JPEG, 0, 640, 480, None, None, None, None, None, None, None ) ) for hash in hashes ]
|
media_results = [ ClientMedia.MediaResult( ( hash, True, 500, HC.IMAGE_JPEG, 640, 480, None, None, None, None, None, None, None ) ) for hash in hashes ]
|
||||||
|
|
||||||
HydrusGlobals.test_controller.SetRead( 'local_booru_share_keys', [ share_key ] )
|
HydrusGlobals.test_controller.SetRead( 'local_booru_share_keys', [ share_key ] )
|
||||||
HydrusGlobals.test_controller.SetRead( 'local_booru_share', info )
|
HydrusGlobals.test_controller.SetRead( 'local_booru_share', info )
|
||||||
|
|
1
test.py
1
test.py
|
@ -51,7 +51,6 @@ class Controller( object ):
|
||||||
HC.DB_DIR = tempfile.mkdtemp()
|
HC.DB_DIR = tempfile.mkdtemp()
|
||||||
|
|
||||||
HC.CLIENT_ARCHIVES_DIR = os.path.join( HC.DB_DIR, 'client_archives' )
|
HC.CLIENT_ARCHIVES_DIR = os.path.join( HC.DB_DIR, 'client_archives' )
|
||||||
HC.CLIENT_CACHE_DIR = os.path.join( HC.DB_DIR, 'client_cache' )
|
|
||||||
HC.CLIENT_FILES_DIR = os.path.join( HC.DB_DIR, 'client_files' )
|
HC.CLIENT_FILES_DIR = os.path.join( HC.DB_DIR, 'client_files' )
|
||||||
HC.CLIENT_THUMBNAILS_DIR = os.path.join( HC.DB_DIR, 'client_thumbnails' )
|
HC.CLIENT_THUMBNAILS_DIR = os.path.join( HC.DB_DIR, 'client_thumbnails' )
|
||||||
HC.CLIENT_UPDATES_DIR = os.path.join( HC.DB_DIR, 'client_updates' )
|
HC.CLIENT_UPDATES_DIR = os.path.join( HC.DB_DIR, 'client_updates' )
|
||||||
|
|
Loading…
Reference in New Issue