Version 142

This commit is contained in:
Hydrus 2014-12-31 16:56:38 -06:00
parent b7edfb9719
commit 4927016a76
12 changed files with 266 additions and 34 deletions

View File

@ -6,6 +6,16 @@
</head>
<body>
<div class="content">
<h3>searching with wildcards</h3>
<p>The autocomplete tag dropdown supports wildcard searching with '*'.</p>
<p><img src="wildcard_gelion.png"/></p>
<p>The '*' will match <i>any</i> number of characters. Every normal search has a secret '*' on the end that you don't see, which is how full words get matched from you only typing in a few letters.</p>
<p>This is useful when you can only remember part of a word, or can't spell its first half. You can put the '*' anywhere, but you may want to experiment to get used to the exact way these searches work. Some results can be surprising!</p>
<p><img src="wildcard_vage.png"/></p>
<p>You can select the special predicate inserted at the top of your autocomplete results (the highlighted '*vagelion' and '*va*ge*' above), and it will work like any other predicate. <b>It will return all files that match that wildcard,</b> i.e. every file for every other tag in the current dropdown list.</p>
<p>This is particularly useful if you have a number of files with commonly structured over-informationed tags, like this:</p>
<p><img src="wildcard_cool_pic.png"/></p>
<p>In this case, selecting the 'title:cool pic*' predicate will return all three images in the same search, where you can conveniently give them some more-easily searched tags like 'series:cool pic' and 'page:1', 'page:2', 'page:3'.</p>
<h3>using flash in fullscreen view</h3>
<p>Flash files are sometimes interested in inputs (like spacebar or mouse-scrollwheel) that mean something to hydrus's fullscreen view, and the libraries I have to use to show flash don't handle these events like normal windows. I now have it set so if your mouse is inside the flash window, the input will go to the flash, and if it is outside, it goes to the fullscreen window.</p>
<p>So, if you want to play a flash game in fullscreen, keep your mouse inside the window.</p>

View File

@ -8,6 +8,26 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 142</h3></li>
<ul>
<li>added wildcards to autocomplete results and file queries</li>
<li>autocomplete results will match your wildcard _exactly_</li>
<li>a new predicate type will appear at the top of wildcard queries; selecting it will search files with that wildcard</li>
<li>the new wildcard predicate can be prepended with a minus sign to exclude from results just like normal tag and namespace queries</li>
<li>in wildcard predicates, namespace and/or tag can contain wildcard characters</li>
<li>added some wildcard help</li>
<li>putting in '***' as an autocomplete query is now a pretty bad idea!</li>
<li>fixed some logic in how tags are matched against unusual search input in the db</li>
<li>sped up and cleaned how tags are matched against search input</li>
<li>fixed some namespace logic in how tags are matched against search input</li>
<li>below-character-threshold autocomplete queries will now return all applicable namespace results (e.g. putting in '1' will return [ 1, page:1, chapter:1, volume:1 ], not just [ 1 ])</li>
<li>added 'open externally' to launch the file in default external program to thumbnail and media viewer menu</li>
<li>added a five second delay between gallery-page fetches in the downloader to reduce chance of 429 errors (this was affecting big sankaku searches)</li>
<li>added danbooru webm downloading</li>
<li>fixed a typo in the thread watcher</li>
<li>fixed a bit-rot bug that was stopping the 'like' ratings filter from launching</li>
<li>fixed right click menu in custom filter</li>
</ul>
<li><h3>version 141</h3></li>
<ul>
<li>combined mappings are no longer calculated and stored</li>

BIN
help/wildcard_cool_pic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
help/wildcard_gelion.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
help/wildcard_vage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -1571,6 +1571,19 @@ class FileSearchContext( object ):
elif operator == '-': self._namespaces_to_exclude.append( namespace )
wildcard_predicates = [ predicate for predicate in predicates if predicate.GetPredicateType() == HC.PREDICATE_TYPE_WILDCARD ]
self._wildcards_to_include = []
self._wildcards_to_exclude = []
for predicate in wildcard_predicates:
( operator, wildcard ) = predicate.GetValue()
if operator == '+': self._wildcards_to_include.append( wildcard )
elif operator == '-': self._wildcards_to_exclude.append( wildcard )
def GetFileServiceKey( self ): return self._file_service_key
def GetNamespacesToExclude( self ): return self._namespaces_to_exclude
@ -1580,6 +1593,8 @@ class FileSearchContext( object ):
def GetTagServiceKey( self ): return self._tag_service_key
def GetTagsToExclude( self ): return self._tags_to_exclude
def GetTagsToInclude( self ): return self._tags_to_include
def GetWildcardsToExclude( self ): return self._wildcards_to_exclude
def GetWildcardsToInclude( self ): return self._wildcards_to_include
def IncludeCurrentTags( self ): return self._include_current_tags
def IncludePendingTags( self ): return self._include_pending_tags

View File

@ -1835,7 +1835,7 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
if len( half_complete_tag ) > 0:
normal_characters = 'abcdefghijklmnopqrstuvwxyz0123456789'
normal_characters = set( 'abcdefghijklmnopqrstuvwxyz0123456789' )
half_complete_tag_can_be_matched = True
@ -1855,8 +1855,21 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
# a search for '[s' actually only does 's'
# so, let's do the old and slower LIKE instead of MATCH in weird cases
# note that queries with '*' are passed to LIKE, because MATCH only supports appended wildcards 'gun*', and not complex stuff like '*gun*'
if half_complete_tag_can_be_matched: return [ tag_id for ( tag_id, ) in self._c.execute( 'SELECT docid FROM tags_fts4 WHERE tag MATCH ?;', ( '"' + half_complete_tag + '*"', ) ) ]
else: return [ tag_id for ( tag_id, ) in self._c.execute( 'SELECT tag_id FROM tags WHERE tag LIKE ?;', ( '%' + half_complete_tag + '%', ) ) ]
else:
possible_tag_ids_half_complete_tag = half_complete_tag
if '*' in possible_tag_ids_half_complete_tag:
possible_tag_ids_half_complete_tag = possible_tag_ids_half_complete_tag.replace( '*', '%' )
else: possible_tag_ids_half_complete_tag += '%'
return [ tag_id for ( tag_id, ) in self._c.execute( 'SELECT tag_id FROM tags WHERE tag LIKE ? OR tag LIKE ?;', ( possible_tag_ids_half_complete_tag, ' ' + possible_tag_ids_half_complete_tag ) ) ]
if ':' in half_complete_tag:
@ -1894,7 +1907,8 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
( namespace_id, tag_id ) = self._GetNamespaceIdTagId( tag )
predicates_phrase = 'namespace_id = ' + HC.u( namespace_id ) + ' AND tag_id = ' + HC.u( tag_id )
if ':' in tag: predicates_phrase = 'namespace_id = ' + HC.u( namespace_id ) + ' AND tag_id = ' + HC.u( tag_id )
else: predicates_phrase = 'tag_id = ' + HC.u( tag_id )
except: predicates_phrase = '1 = 1'
@ -2044,6 +2058,9 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
namespaces_to_include = search_context.GetNamespacesToInclude()
namespaces_to_exclude = search_context.GetNamespacesToExclude()
wildcards_to_include = search_context.GetWildcardsToInclude()
wildcards_to_exclude = search_context.GetWildcardsToExclude()
include_current_tags = search_context.IncludeCurrentTags()
include_pending_tags = search_context.IncludePendingTags()
@ -2120,7 +2137,7 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
else: sql_predicates.append( '( duration < ' + HC.u( max_duration ) + ' OR duration IS NULL )' )
if len( tags_to_include ) > 0 or len( namespaces_to_include ) > 0:
if len( tags_to_include ) > 0 or len( namespaces_to_include ) > 0 or len( wildcards_to_include ) > 0:
query_hash_ids = None
@ -2134,6 +2151,14 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
else: query_hash_ids.intersection_update( namespace_query_hash_ids )
if len( wildcards_to_include ) > 0:
wildcard_query_hash_ids = HC.IntelligentMassIntersect( ( self._GetHashIdsFromWildcard( file_service_key, tag_service_key, wildcard, include_current_tags, include_pending_tags ) for wildcard in wildcards_to_include ) )
if query_hash_ids is None: query_hash_ids = wildcard_query_hash_ids
else: query_hash_ids.intersection_update( wildcard_query_hash_ids )
if len( sql_predicates ) > 1: query_hash_ids.intersection_update( [ id for ( id, ) in self._c.execute( 'SELECT hash_id FROM files_info WHERE ' + ' AND '.join( sql_predicates ) + ';' ) ] )
else:
@ -2229,6 +2254,8 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
for namespace in namespaces_to_exclude: exclude_query_hash_ids.update( self._GetHashIdsFromNamespace( file_service_key, tag_service_key, namespace, include_current_tags, include_pending_tags ) )
for wildcard in wildcards_to_exclude: exclude_query_hash_ids.update( self._GetHashIdsFromWildcard( file_service_key, tag_service_key, wildcard, include_current_tags, include_pending_tags ) )
if file_service_type == HC.FILE_REPOSITORY and HC.options[ 'exclude_deleted_files' ]: exclude_query_hash_ids.update( [ hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM deleted_files WHERE service_id = ?;', ( self._local_file_service_id, ) ) ] )
query_hash_ids.difference_update( exclude_query_hash_ids )
@ -2513,6 +2540,93 @@ class ServiceDB( FileDB, MessageDB, TagDB, RatingDB ):
return hash_ids
def _GetHashIdsFromWildcard( self, file_service_key, tag_service_key, wildcard, include_current_tags, include_pending_tags ):
statuses = []
if include_current_tags: statuses.append( HC.CURRENT )
if include_pending_tags: statuses.append( HC.PENDING )
if len( statuses ) == 0: return {}
predicates = []
if len( statuses ) > 0: predicates.append( 'mappings.status IN ' + HC.SplayListForDB( statuses ) )
if file_service_key == HC.COMBINED_FILE_SERVICE_KEY:
table_phrase = 'mappings'
else:
table_phrase = 'mappings, files_info USING ( hash_id )'
file_service_id = self._GetServiceId( file_service_key )
predicates.append( 'files_info.service_id = ' + HC.u( file_service_id ) )
if tag_service_key != HC.COMBINED_TAG_SERVICE_KEY:
tag_service_id = self._GetServiceId( tag_service_key )
predicates.append( 'mappings.service_id = ' + HC.u( tag_service_id ) )
if len( predicates ) > 0: predicates_phrase = ' AND '.join( predicates ) + ' AND '
else: predicates_phrase = ''
def GetNamespaceIdsFromWildcard( w ):
if '*' in w:
w = w.replace( '*', '%' )
return { namespace_id for ( namespace_id, ) in self._c.execute( 'SELECT namespace_id FROM namespaces WHERE namespace LIKE ?;', ( w, ) ) }
else:
namespace_id = self._GetNamespaceId( w )
return [ namespace_id ]
def GetTagIdsFromWildcard( w ):
if '*' in w:
w = w.replace( '*', '%' )
return { tag_id for ( tag_id, ) in self._c.execute( 'SELECT tag_id FROM tags WHERE tag LIKE ?;', ( w, ) ) }
else:
( namespace_id, tag_id ) = self._GetNamespaceIdTagId( w )
return [ tag_id ]
if ':' in wildcard:
( namespace_wildcard, tag_wildcard ) = wildcard.split( ':', 1 )
possible_namespace_ids = GetNamespaceIdsFromWildcard( namespace_wildcard )
possible_tag_ids = GetTagIdsFromWildcard( tag_wildcard )
hash_ids = { id for ( id, ) in self._c.execute( 'SELECT hash_id FROM ' + table_phrase + ' WHERE ' + predicates_phrase + 'namespace_id IN ' + HC.SplayListForDB( possible_namespace_ids ) + ' AND tag_id IN ' + HC.SplayListForDB( possible_tag_ids ) + ';' ) }
else:
possible_tag_ids = GetTagIdsFromWildcard( wildcard )
hash_ids = { id for ( id, ) in self._c.execute( 'SELECT hash_id FROM ' + table_phrase + ' WHERE ' + predicates_phrase + 'tag_id IN ' + HC.SplayListForDB( possible_tag_ids ) + ';' ) }
return hash_ids
def _GetHydrusSessions( self ):
now = HC.GetNow()
@ -5178,15 +5292,6 @@ class DB( ServiceDB ):
def _UpdateDB( self, version ):
if version == 91:
( HC.options, ) = self._c.execute( 'SELECT options FROM options;' ).fetchone()
HC.options[ 'num_autocomplete_chars' ] = 2
self._c.execute( 'UPDATE options SET options = ?;', ( HC.options, ) )
if version == 93:
self._c.execute( 'CREATE TABLE gui_sessions ( name TEXT, info TEXT_YAML );' )

View File

@ -703,6 +703,16 @@ class Canvas( object ):
def _OpenExternally( self ):
hash = self._current_display_media.GetHash()
mime = self._current_display_media.GetMime()
path = CC.GetFilePath( hash, mime )
subprocess.call( 'start "" "' + path + '"', shell = True )
def _PrefetchImages( self ): pass
def _RecalcZoom( self ):
@ -1523,6 +1533,7 @@ class CanvasFullscreenMediaListBrowser( CanvasFullscreenMediaList ):
elif command == 'inbox': self._Inbox()
elif command == 'manage_ratings': self._ManageRatings()
elif command == 'manage_tags': self._ManageTags()
elif command == 'open_externally': self._OpenExternally()
elif command in ( 'pan_up', 'pan_down', 'pan_left', 'pan_right' ):
distance = 20
@ -1617,6 +1628,10 @@ class CanvasFullscreenMediaListBrowser( CanvasFullscreenMediaList ):
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'remove', HC.LOCAL_FILE_SERVICE_KEY ), '&remove' )
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete', HC.LOCAL_FILE_SERVICE_KEY ), '&delete' )
menu.AppendSeparator()
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'open_externally', HC.LOCAL_FILE_SERVICE_KEY ), '&open externally' )
share_menu = wx.Menu()
copy_menu = wx.Menu()
@ -1907,6 +1922,7 @@ class CanvasFullscreenMediaListCustomFilter( CanvasFullscreenMediaList ):
elif command == 'inbox': self._Inbox()
elif command == 'manage_ratings': self._ManageRatings()
elif command == 'manage_tags': self._ManageTags()
elif command == 'open_externally': self._OpenExternally()
elif command == 'remove': self._Remove()
elif command == 'slideshow': self._StartSlideshow( data )
elif command == 'slideshow_pause_play': self._PausePlaySlideshow()
@ -1938,6 +1954,14 @@ class CanvasFullscreenMediaListCustomFilter( CanvasFullscreenMediaList ):
def EventShowMenu( self, event ):
services = HC.app.GetManager( 'services' ).GetServices()
local_ratings_services = [ service for service in services if service.GetServiceType() in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) ]
i_can_post_ratings = len( local_ratings_services ) > 0
#
self._last_drag_coordinates = None # to stop successive right-click drag warp bug
menu = wx.Menu()
@ -1988,6 +2012,8 @@ class CanvasFullscreenMediaListCustomFilter( CanvasFullscreenMediaList ):
menu.AppendSeparator()
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'open_externally', HC.LOCAL_FILE_SERVICE_KEY ), '&open externally' )
share_menu = wx.Menu()
copy_menu = wx.Menu()
@ -2651,7 +2677,7 @@ class RatingsFilterFrameLike( CanvasFullscreenMediaListFilter ):
def __init__( self, my_parent, page_key, service_key, media_results ):
CanvasFullscreenMediaListFilter.__init__( self, my_parent, page_key, HC.LOCAL_FILE_SERVICE_KEY, [], media_results )
CanvasFullscreenMediaListFilter.__init__( self, my_parent, page_key, HC.LOCAL_FILE_SERVICE_KEY, media_results )
self._rating_service_key = service_key
self._service = HC.app.GetManager( 'services' ).GetService( service_key )

View File

@ -625,6 +625,8 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
must_do_a_search = False
if '*' in search_text: must_do_a_search = True
if ':' in search_text:
( namespace, half_complete_tag ) = search_text.split( ':' )
@ -652,7 +654,6 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
media = self._media_callable()
# if synchro not on, then can't rely on current media as being accurate for current preds, so search db normally
if media is not None and self._synchronised.IsOn(): fetch_from_db = False
@ -662,18 +663,18 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
results = HC.app.Read( 'autocomplete_tags', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, tag = search_text, include_current = self._include_current, include_pending = self._include_pending )
matches = results.GetMatches( half_complete_tag )
matches = results.GetMatches( search_text )
else:
if must_do_a_search or self._first_letters == '' or not half_complete_tag.startswith( self._first_letters ):
if must_do_a_search or self._first_letters == '' or not search_text.startswith( self._first_letters ):
self._first_letters = half_complete_tag
self._first_letters = search_text
self._cached_results = HC.app.Read( 'autocomplete_tags', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, half_complete_tag = search_text, include_current = self._include_current, include_pending = self._include_pending )
matches = self._cached_results.GetMatches( half_complete_tag )
matches = self._cached_results.GetMatches( search_text )
else:
@ -695,14 +696,8 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
current_tags_flat_iterable = itertools.chain.from_iterable( lists_of_current_tags )
pending_tags_flat_iterable = itertools.chain.from_iterable( lists_of_pending_tags )
current_tags_flat = [ tag for tag in current_tags_flat_iterable if HC.SearchEntryMatchesTag( half_complete_tag, tag ) ]
pending_tags_flat = [ tag for tag in pending_tags_flat_iterable if HC.SearchEntryMatchesTag( half_complete_tag, tag ) ]
if self._current_namespace != '':
current_tags_flat = [ tag for tag in current_tags_flat if tag.startswith( self._current_namespace + ':' ) ]
pending_tags_flat = [ tag for tag in pending_tags_flat if tag.startswith( self._current_namespace + ':' ) ]
current_tags_flat = [ tag for tag in current_tags_flat_iterable if HC.SearchEntryMatchesTag( search_text, tag ) ]
pending_tags_flat = [ tag for tag in pending_tags_flat_iterable if HC.SearchEntryMatchesTag( search_text, tag ) ]
current_tags_to_count = collections.Counter( current_tags_flat )
pending_tags_to_count = collections.Counter( pending_tags_flat )
@ -714,11 +709,12 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
results = CC.AutocompleteMatchesPredicates( self._tag_service_key, [ HC.Predicate( HC.PREDICATE_TYPE_TAG, ( operator, tag ), { HC.CURRENT : current_tags_to_count[ tag ], HC.PENDING : pending_tags_to_count[ tag ] } ) for tag in tags_to_do ] )
matches = results.GetMatches( half_complete_tag )
matches = results.GetMatches( search_text )
if self._current_namespace != '': matches.insert( 0, HC.Predicate( HC.PREDICATE_TYPE_NAMESPACE, ( operator, namespace ) ) )
if '*' in search_text: matches.insert( 0, HC.Predicate( HC.PREDICATE_TYPE_WILDCARD, ( operator, search_text ) ) )
entry_predicate = HC.Predicate( HC.PREDICATE_TYPE_TAG, ( operator, search_text ) )

View File

@ -10,6 +10,7 @@ import HydrusThreading
import itertools
import os
import random
import subprocess
import threading
import time
import traceback
@ -466,6 +467,16 @@ class MediaPanel( ClientGUIMixins.ListeningMediaList, wx.ScrolledWindow ):
if len( hashes ) > 0: HC.pubsub.pub( 'new_thread_dumper', hashes )
def _OpenExternally( self ):
hash = self._focussed_media.GetHash()
mime = self._focussed_media.GetMime()
path = CC.GetFilePath( hash, mime )
subprocess.call( 'start "" "' + path + '"', shell = True )
def _PetitionFiles( self, file_service_key ):
hashes = self._GetSelectedHashes()
@ -1551,6 +1562,7 @@ class MediaPanelThumbnails( MediaPanel ):
elif command == 'manage_tags': self._ManageTags()
elif command == 'modify_account': self._ModifyUploaders( data )
elif command == 'new_thread_dumper': self._NewThreadDumper()
elif command == 'open_externally': self._OpenExternally()
elif command == 'petition': self._PetitionFiles( data )
elif command == 'ratings_filter': self._RatingsFilter( data )
elif command == 'remove': self._Remove()
@ -1931,6 +1943,10 @@ class MediaPanelThumbnails( MediaPanel ):
# share
menu.AppendSeparator()
menu.Append( CC.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'open_externally', HC.LOCAL_FILE_SERVICE_KEY ), '&open externally' )
share_menu = wx.Menu()
#

View File

@ -65,7 +65,7 @@ options = {}
# Misc
NETWORK_VERSION = 15
SOFTWARE_VERSION = 141
SOFTWARE_VERSION = 142
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
@ -403,6 +403,7 @@ PREDICATE_TYPE_SYSTEM = 0
PREDICATE_TYPE_TAG = 1
PREDICATE_TYPE_NAMESPACE = 2
PREDICATE_TYPE_PARENT = 3
PREDICATE_TYPE_WILDCARD = 4
SITE_TYPE_DEVIANT_ART = 0
SITE_TYPE_GIPHY = 1
@ -1273,7 +1274,30 @@ def SearchEntryMatchesPredicate( search_entry, predicate ):
def SearchEntryMatchesTag( search_entry, tag, search_siblings = True ):
if ':' in search_entry: ( search_entry_namespace, search_entry ) = search_entry.split( ':', 1 )
def compile_re( s ):
regular_parts_of_s = s.split( '*' )
escaped_parts_of_s = [ re.escape( part ) for part in regular_parts_of_s ]
s = '.*'.join( escaped_parts_of_s )
return re.compile( '(\\A|\\s)' + s + '(\\s|\\Z)', flags = re.UNICODE )
if ':' in search_entry:
search_namespace = True
( namespace_entry, search_entry ) = search_entry.split( ':', 1 )
namespace_re_predicate = compile_re( namespace_entry )
else: search_namespace = False
if '*' not in search_entry: search_entry += '*'
re_predicate = compile_re( search_entry )
if search_siblings:
@ -1289,11 +1313,18 @@ def SearchEntryMatchesTag( search_entry, tag, search_siblings = True ):
( n, t ) = tag.split( ':', 1 )
if search_namespace and re.search( namespace_re_predicate, n ) is None: continue
comparee = t
else: comparee = tag
else:
if search_namespace: continue
comparee = tag
if comparee.startswith( search_entry ) or ' ' + search_entry in comparee: return True
if re.search( re_predicate, comparee ) is not None: return True
return False
@ -2333,6 +2364,15 @@ class Predicate( HydrusYAMLBase ):
base += namespace + u':*'
elif self._predicate_type == PREDICATE_TYPE_WILDCARD:
( operator, wildcard ) = self._value
if operator == '-': base = u'-'
elif operator == '+': base = u''
base += wildcard
return base

View File

@ -256,13 +256,15 @@ class DownloaderBooru( Downloader ):
image_string = soup.find( text = re.compile( 'Save this file' ) )
if image_string is None: image_string = soup.find( text = re.compile( 'Save this video' ) )
image = image_string.parent
image_url = image[ 'href' ]
else:
if image.name == 'img':
if image.name in ( 'img', 'video' ):
image_url = image[ 'src' ]
@ -1648,6 +1650,8 @@ class ImportQueueGeneratorGallery( ImportQueueGenerator ):
self._job_key.SetVariable( 'queue', queue )
time.sleep( 5 )
for downloader in downloaders_to_remove: downloaders.remove( downloader )
@ -1751,7 +1755,7 @@ class ImportQueueGeneratorThread( ImportQueueGenerator ):
if HC.shutdown or self._job_key.IsDone(): break
if HC.shutdown or self._ob_key.IsDone(): break
if HC.shutdown or self._job_key.IsDone(): break
thread_time = self._job_key.GetVariable( 'thread_time' )