QB64.org Forum

Active Forums => Programs => Topic started by: Petr on June 22, 2020, 04:40:26 pm

Title: Development of a program for automatic search for music and movies
Post by: Petr on June 22, 2020, 04:40:26 pm
Hi, I'm starting this new thread, where I'm going to add parts of one and the same program during development to search the specified folder, including subdirectories, find music and video files, get information from them, and pass them on to the user.

I will develop individual functions gradually. For a start, I had to solve the most basic thing. Select the folder that is the root. The search will start from it. To make this possible on network drives as well, I chose our wiki on QB64 and used the windows dialog. The same then to get a short name. This is required for compatibility if, for example, the music album contains unicode characters in the name.

Here is the absolute beginning: Selecting a folder. No mask filtering yet. Several libraries are used, including the SteveMcNeill DirEntry library. This will be absolutely crucial in this program.

Code: QB64: [Select]
  1. TYPE FileList
  2.     ShtName AS STRING
  3.     LngName AS STRING
  4.     Path AS STRING
  5.  
  6.  
  7.  
  8. Path$ = SelectDIR$
  9. SPath$ = ShortName$(Path$) '                                                                           is need for access to files and directories, which use unicode characters in name
  10. IF SPath$ = "" THEN SPath$ = _CWD$
  11. PRINT Path$, SPath$
  12. CHDIR SPath$ '                                                                                         if short name not exists, function ShortName return long name as result.
  13. mask$ = "AVI,MPG,MKV,MPG,MPEG,WAV,ACC,MP4,MP3,MP2,VOC,S3M,MOD,XM,TS,MOV,AC3,WMA,WMV,FLAC"
  14. REDIM files_found(0) AS STRING
  15. SearchFiles mask$, files_found()
  16.  
  17.  
  18.  
  19.  
  20.  
  21.  
  22.  
  23. SUB SearchFiles (mask AS STRING, FilesFound() AS STRING)
  24.     DECLARE CUSTOMTYPE LIBRARY ".\direntry" '                                               Steve mcNeill's library
  25.         FUNCTION load_dir& (s AS STRING)
  26.         FUNCTION has_next_entry& ()
  27.         SUB close_dir ()
  28.         SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  29.     END DECLARE
  30.     REDIM DirNew(0) AS STRING
  31.     REDIM FileNew(0) AS STRING
  32.     GetFileList _CWD$, DirNew(), FileNew()
  33.  
  34.  
  35.  
  36.     PrintIt FileNew(), DirNew()
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45. SUB PrintIt (F() AS STRING, D() AS STRING)
  46.     FOR p = LBOUND(f) TO UBOUND(f)
  47.         PRINT "Found file:"; F(p)
  48.         _DELAY .1
  49.     NEXT
  50.     _DELAY 1
  51.     FOR p = LBOUND(d) + 3 TO UBOUND(d)
  52.         PRINT "Found Directory:"; D(p)
  53.         _DELAY .1
  54.     NEXT
  55.  
  56.  
  57.  
  58.  
  59.  
  60.  
  61. SUB GetFileList (SearchDirectory AS STRING, DirList() AS STRING, FileList() AS STRING) '    Steve mcNeill's SUB
  62.     CONST IS_DIR = 1
  63.     CONST IS_FILE = 2
  64.     DIM flags AS LONG, file_size AS LONG
  65.  
  66.     REDIM _PRESERVE DirList(100), FileList(100)
  67.     DirCount = 0: FileCount = 0
  68.  
  69.     IF load_dir(SearchDirectory) THEN
  70.         DO
  71.             length = has_next_entry
  72.             IF length > -1 THEN
  73.                 nam$ = SPACE$(length)
  74.                 get_next_entry nam$, flags, file_size
  75.                 IF flags AND IS_DIR THEN
  76.                     DirCount = DirCount + 1
  77.                     IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  78.                     DirList(DirCount) = nam$
  79.                 ELSEIF flags AND IS_FILE THEN
  80.                     FileCount = FileCount + 1
  81.                     IF FileCount > UBOUND(filelist) THEN REDIM _PRESERVE FileList(UBOUND(filelist) + 100)
  82.                     FileList(FileCount) = nam$
  83.                 END IF
  84.             END IF
  85.         LOOP UNTIL length = -1
  86.         close_dir
  87.     ELSE
  88.     END IF
  89.     REDIM _PRESERVE DirList(DirCount)
  90.     REDIM _PRESERVE FileList(FileCount)
  91.  
  92.  
  93.  
  94.  
  95.  
  96. FUNCTION ShortName$ (name$)
  97.     DECLARE LIBRARY '                                                                                      Directory Information using KERNEL32
  98.         FUNCTION GetShortPathNameA (lpLongPath AS STRING, lpShortPath AS STRING, BYVAL cBufferLen AS LONG)
  99.     END DECLARE
  100.  
  101.     ShortPathName$ = SPACE$(260)
  102.     Result = GetShortPathNameA(name$ + CHR$(0), ShortPathName$, LEN(ShortPathName$))
  103.     IF Result THEN ShortName$ = ShortPathName$ ELSE ShortName$ = name$
  104.  
  105.  
  106.  
  107. FUNCTION SelectDIR$
  108.         FUNCTION FindWindow& (BYVAL ClassName AS _OFFSET, WindowName$)
  109.     END DECLARE
  110.     hwnd& = _WINDOWHANDLE
  111.     TYPE BROWSEINFO '                    typedef struct _browseinfo 'Microsoft MSDN
  112.         hwndOwner AS _OFFSET '           HWND
  113.         pidlRoot AS _OFFSET '            PCIDLIST_ABSOLUTE
  114.         pszDisplayName AS _OFFSET '      LPTSTR
  115.         lpszTitle AS _OFFSET '           LPCTSTR
  116.         ulFlags AS _UNSIGNED LONG '      UINT
  117.         lpfn AS _OFFSET '                BFFCALLBACK
  118.         lParam AS _OFFSET '              LPARAM
  119.         iImage AS LONG '                 int
  120.     END TYPE '                           BROWSEINFO, *PBROWSEINFO, *LPBROWSEINFO;
  121.  
  122.     DECLARE DYNAMIC LIBRARY "shell32"
  123.         FUNCTION SHBrowseForFolder%& (x AS BROWSEINFO) '                           Microsoft MSDN
  124.         SUB SHGetPathFromIDList (BYVAL lpItem AS _OFFSET, BYVAL szDir AS _OFFSET) 'Microsoft MSDN
  125.     END DECLARE
  126.  
  127.     DIM b AS BROWSEINFO
  128.     b.hwndOwner = hwnd
  129.     DIM s AS STRING * 1024
  130.     b.pszDisplayName = _OFFSET(s$)
  131.     a$ = "Select folder for searching movies and music:" + CHR$(0)
  132.     b.lpszTitle = _OFFSET(a$)
  133.     DIM o AS _OFFSET
  134.     o = SHBrowseForFolder(b)
  135.     IF o THEN
  136.         '   Path$ = LEFT$(s$, INSTR(s$, CHR$(0)) - 1)
  137.         DIM s2 AS STRING * 1024
  138.         SHGetPathFromIDList o, _OFFSET(s2$)
  139.         SelectDIR$ = LEFT$(s2$, INSTR(s2$, CHR$(0)) - 1)
  140.     ELSE
  141.         PRINT "Directory not selected, using current directory"
  142.         SelectDIR$ = _CWD$
  143.     END IF
  144.  
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 22, 2020, 07:11:54 pm
 
Code: [Select]
IF _FILEEXISTS("C:\ProgramData\PhotoList.txt") = 0 OR AlwaysRefreshListing THEN
    'will create the new listing if your directory doesn't contain one,
    'OR, will create one every time you run the program, if you set the flag to make it do so.
    PhotoList$ = PhotoFolder + "*.bmp " + PhotoFolder + "*.jpg " + PhotoFolder + "*.png " + PhotoFolder + "*.gif "
    SHELL "DIR " + PhotoList$ + "/b /s /a-d >C:\ProgramData\PhotoList.txt"
END IF

If you look at my little wallpaper changer, you'll see the above is the simple way I use to get my various image files.  https://www.qb64.org/forum/index.php?topic=1008.msg112746#msg112746

It's about as short and simple of a process as you can get, and might work sufficiently enough for your needs.  ;)

 

 
Title: Re: Development of a program for automatic search for music and movies
Post by: Petr on June 24, 2020, 04:27:24 pm
Thanks Steve for this help. It will be much easier this way. I was thinking about how to achieve the same result with DIRENTRY. I would have to note at what level of the directory/subdirectory/sub sub directory..../ I am in to go through the whole structure. A function that would return the number of subdirectories in each directory would be useful ... it would be a lengthy complication. But I will definitely try to do it (later). I continue to develop this program, I have no desire to complicate it unnecessarily.
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 24, 2020, 05:12:40 pm
@Petr

Just to clarify, are you going for cross platform solution which uses DirEntry, I think, or Windows only with Shell (which works fine for Windows users) or a Windows File Dialog like what Spriggsy helped me with recently?

Title: Re: Development of a program for automatic search for music and movies
Post by: Petr on June 24, 2020, 06:45:05 pm
Hi BPlus.

I cheered prematurely. DIR turned out to be - nice bitch again. He works nicely. NO UNICODE characters in filenames! He replaces unicode characters beautifully with SPACES!!!

I'm really pissed. BPlus, I'll start all over again. Absolutely again. I will use DIRENTRY.H which means that I will really have to write a directory listing function. None DIR. This time I'll make it compatible with Linux. (Windows begin stay as is now, in Linux i'll write special SUB using ls for folder finding)
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 24, 2020, 07:16:13 pm
Here is my bare bones starter with DIRENTRY.h works first time but buggy with repeated calls, at least the way I developed it.

It might do to replace FILES statement.

Code: QB64: [Select]
  1. ' direntry.h needs to be in QB64 folder
  2.     FUNCTION load_dir& (s AS STRING)
  3.     FUNCTION has_next_entry& ()
  4.     SUB close_dir ()
  5.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  6.  
  7. REDIM Dir(0) AS STRING, File(0) AS STRING
  8.  
  9. 'D$ = _CWD$
  10. D$ = "C:\\"
  11. GetFileList D$, Dir(), File()
  12.  
  13. PRINT "SUBDIRECTORIES"; UBOUND(dir)
  14. FOR i = 1 TO UBOUND(dir)
  15.     PRINT Dir(i),
  16.  
  17. PRINT "FILES"; UBOUND(file): PRINT: PRINT
  18. FOR i = 1 TO UBOUND(file)
  19.     PRINT File(i),
  20.  
  21. SUB GetFileList (SearchDirectory AS STRING, DirList() AS STRING, FileList() AS STRING)
  22.     CONST IS_DIR = 1
  23.     CONST IS_FILE = 2
  24.     DIM flags AS LONG, file_size AS LONG
  25.  
  26.     REDIM _PRESERVE DirList(100), FileList(100)
  27.     DirCount = 0: FileCount = 0
  28.  
  29.     IF load_dir(SearchDirectory) THEN
  30.         DO
  31.             length = has_next_entry
  32.             IF length > -1 THEN
  33.                 nam$ = SPACE$(length)
  34.                 get_next_entry nam$, flags, file_size
  35.                 IF (flags AND IS_DIR) OR _DIREXISTS(SearchDirectory + nam$) THEN
  36.                     DirCount = DirCount + 1
  37.                     IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  38.                     DirList(DirCount) = nam$
  39.                 ELSEIF (flags AND IS_FILE) OR _FILEEXISTS(SearchDirectory + nam$) THEN
  40.                     FileCount = FileCount + 1
  41.                     IF FileCount > UBOUND(filelist) THEN REDIM _PRESERVE FileList(UBOUND(filelist) + 100)
  42.                     FileList(FileCount) = nam$
  43.                 END IF
  44.             END IF
  45.         LOOP UNTIL length = -1
  46.         close_dir
  47.     ELSE
  48.     END IF
  49.     REDIM _PRESERVE DirList(DirCount)
  50.     REDIM _PRESERVE FileList(FileCount)
  51.  
  52.  
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 24, 2020, 08:07:48 pm
@bplus Here might be the issue in your code:

    IF load_dir(SearchDirectory) THEN
       ....
       close_dir
    ELSE
    END IF

You have a call to load a directory, but if it fails, you don't have a call to try and close it.  (Notice the lack of close_dir in the ELSE statement?)

Try moving that close_dir AFTER the END IF and see if your issue with multiple calls clears up.
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 24, 2020, 09:15:35 pm
Thanks Steve, I will run some tests on it. It's been awhile since I've looked at this.

Update:
That's it! It is working well now. BIG THANKYOU! SMcNeill :)

I do run into snags when I try directories Windows doesn't want me in like Cookies and there is a little snag when I reach C:\\Users\(me) and try to go up, getting 0 selections so nowhere to go. Try 0 anyway, I hacked a little fixer for such events, or when you have to exit the Directory Selection List. Hopefully you can figure it out as you go, nothing crashes like before.

Now maybe Tiny Navigator is cross platform (The majority of this code is for selecting items from large array lists, select by mouse, arrows, page up/dwn, home end, and enter, or type in number and enter as I recall from 10 months ago (Yes, I recall correctly)):
Code: QB64: [Select]
  1. ' B+ started 2019-08-22  restart 2020-06-24
  2. ' 2019-08-22 orig post at https://www.qb64.org/forum/index.php?topic=1646.msg108682#msg108682
  3. ' 2019-08-23_13-25 try fix (to nav all directories) with one place for tmpFile, theory can't write files in some dir's
  4. ' For some reason Windows won't write to a fully pathed file in my user folder???from a SHELL command
  5. ' Try testing if dir exists "C:\temp" exists and making one if not, yes! and I can write my temp files there
  6. ' and now I can chDir anywhere!!!!
  7. ' 2019-08-23_14-25 Take Steve's Advice to use users tmp directory for temp files
  8. ' 2019-08-23_23+ Have files window working for file selection
  9. ' ============================================ all above using SHELL to get lists
  10.  
  11. ' 2020-06-24 restart using DirEntry.h, wow looks like Steve's fix helped ALLOT! Thanks SMcNeill
  12. ' I am hitting glitches where Windows wont let me access I think. So I am offering a c enter option
  13. ' to restart at the root but this might not work cross platform.
  14.  
  15.  
  16. ' direntry.h needs to be in QB64 folder
  17.     FUNCTION load_dir& (s AS STRING)
  18.     FUNCTION has_next_entry& ()
  19.     SUB close_dir ()
  20.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  21.  
  22.  
  23. _TITLE "Tiny Navigator using DirEntry:     press f to Start a Select a File list of current directory"
  24. SCREEN _NEWIMAGE(1000, 600, 32)
  25. _SCREENMOVE 100, 50
  26.  
  27. DIM SHARED selectedFile AS STRING
  28. REDIM SHARED DIRs(0) AS STRING, FILs(0) AS STRING
  29. DIM mySelection&, done$, i, t$
  30.     COLOR _RGB32(180, 180, 255)
  31.     CLS
  32.  
  33.     t$ = "Current Directory: " + _CWD$
  34.     LOCATE 2, (_WIDTH / 8 - LEN(t$)) / 2: PRINT t$
  35.     REDIM DIRs(0) AS STRING, FILs(0) AS STRING
  36.     GetLists _CWD$, DIRs(), FILs()
  37.     FOR i = 0 TO UBOUND(FILs) ' this just offers a sample listing of files
  38.         IF i < 30 THEN LOCATE i + 4, 60: PRINT FILs(i) ELSE EXIT FOR
  39.     NEXT
  40.     mySelection& = getArrayItemNumber&(5, 5, 50, 30, DIRs())
  41.     CLS
  42.     IF selectedFile <> "" THEN
  43.         PRINT "You selected a file: "; selectedFile
  44.         INPUT "Press enter to continue navigator, any + enter to quit... "; done$
  45.         selectedFile = ""
  46.     ELSEIF mySelection& <> -1719 THEN
  47.         IF _TRIM$(DIRs(mySelection&)) <> "" THEN
  48.             CHDIR DIRs(mySelection&)
  49.         END IF
  50.     ELSE
  51.         PRINT "Nothing selected."
  52.         PRINT "There are some places your OS might not allow us to go."
  53.         PRINT "If you get stuck with 0 slections, press c (for root C:\\) enter..."
  54.         INPUT "Press enter to continue navigator, any + enter to quit... "; done$
  55.         IF done$ = "c" THEN CHDIR ("C:\\"): done$ = ""
  56.     END IF
  57.     _LIMIT 60
  58. LOOP UNTIL done$ <> ""
  59.  
  60. SUB GetLists (SearchDirectory AS STRING, DirList() AS STRING, FileList() AS STRING)
  61.  
  62.     CONST IS_DIR = 1
  63.     CONST IS_FILE = 2
  64.     DIM flags AS LONG, file_size AS LONG, DirCount AS INTEGER, FileCount AS INTEGER, length AS LONG
  65.     DIM nam$
  66.     REDIM _PRESERVE DirList(100), FileList(100)
  67.     DirCount = 0: FileCount = 0
  68.  
  69.     IF load_dir(SearchDirectory) THEN
  70.         DO
  71.             length = has_next_entry
  72.             IF length > -1 THEN
  73.                 nam$ = SPACE$(length)
  74.                 get_next_entry nam$, flags, file_size
  75.                 IF (flags AND IS_DIR) OR _DIREXISTS(SearchDirectory + nam$) THEN
  76.                     DirCount = DirCount + 1
  77.                     IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  78.                     DirList(DirCount) = nam$
  79.                 ELSEIF (flags AND IS_FILE) OR _FILEEXISTS(SearchDirectory + nam$) THEN
  80.                     FileCount = FileCount + 1
  81.                     IF FileCount > UBOUND(filelist) THEN REDIM _PRESERVE FileList(UBOUND(filelist) + 100)
  82.                     FileList(FileCount) = nam$
  83.                 END IF
  84.             END IF
  85.         LOOP UNTIL length = -1
  86.         'close_dir 'move to after end if  might correct the multi calls problem
  87.     ELSE
  88.     END IF
  89.     close_dir 'this  might correct the multi calls problem
  90.  
  91.     REDIM _PRESERVE DirList(DirCount)
  92.     REDIM _PRESERVE FileList(FileCount)
  93.  
  94. FUNCTION rightOf$ (source$, of$)
  95.     IF INSTR(source$, of$) > 0 THEN rightOf$ = MID$(source$, INSTR(source$, of$) + LEN(of$))
  96.  
  97. ' "Escape returns -1719 to allow a Cancel function and signal no slection."
  98. FUNCTION getArrayItemNumber& (locateRow, locateColumn, boxWidth, boxHeight, arr() AS STRING)
  99.     'Notes: locateRow, locateColumn for top right corner of selection box on screen in characters for LOCATE.
  100.     'boxWidth and boxHeight are in character units, again for locate and print at correct places.
  101.     'All displaying is restricted to inside the box, which has PgUP and PgDn as top and bottom lines in the display.
  102.  
  103.     DIM curRow AS INTEGER, curCol AS INTEGER, fg AS _UNSIGNED LONG, bg AS _UNSIGNED LONG
  104.     DIM maxWidth AS INTEGER, maxHeight AS INTEGER, page AS INTEGER, hlite AS INTEGER, mx AS INTEGER, my AS INTEGER
  105.     DIM lastMX AS INTEGER, lastMY AS INTEGER, row AS INTEGER, mb AS INTEGER
  106.     DIM lba AS LONG, uba AS LONG, choice AS LONG, kh AS LONG, index AS LONG
  107.     DIM clrStr AS STRING, b AS STRING, selNum&
  108.  
  109.     'save old settings to restore at end ofsub
  110.     curRow = CSRLIN
  111.     curCol = POS(0)
  112.     fg = _DEFAULTCOLOR
  113.     bg = _BACKGROUNDCOLOR
  114.     _KEYCLEAR
  115.  
  116.     maxWidth = boxWidth '       number of characters in box
  117.     maxHeight = boxHeight - 2 ' number of lines displayed of array at one time = 1 page
  118.     lba = LBOUND(arr)
  119.     uba = UBOUND(arr)
  120.     page = 0
  121.     hlite = 0 '                 line in display ready for selection by spacebar or if no number is started, enter
  122.     clrStr$ = SPACE$(maxWidth) 'clearing a display line
  123.  
  124.     GOSUB update '              show the beginning of the array items for selection
  125.  
  126.     'signal cancel selection process, exit sub with this unlikely index to signal canel
  127.     choice = -1719 'primes 7 and 8, not likely to be a select index of an array
  128.  
  129.     DO 'until get a selection or demand exit
  130.  
  131.         'handle the key stuff
  132.         kh& = _KEYHIT
  133.         IF kh& THEN
  134.             IF kh& > 0 AND kh& < 255 THEN
  135.                 IF INSTR("0123456789", CHR$(kh&)) > 0 THEN b$ = b$ + CHR$(kh&): GOSUB update
  136.                 IF CHR$(kh&) = "f" THEN
  137.                     'REDIM FILs(0) AS STRING     'hopefully this is already ready
  138.                     'loadFiles FILs()
  139.                     selNum& = getArrayItemNumber&(5, 60, 60, 30, FILs())
  140.                     COLOR _RGB32(180, 180, 255)
  141.                     CLS 'need to signal out of file selection
  142.                     IF selNum& >= LBOUND(FILs) AND selNum& <= UBOUND(FILs) THEN selectedFile = FILs(selNum&)
  143.                     EXIT DO
  144.                     'back to directory select
  145.                 END IF
  146.  
  147.                 IF CHR$(kh&) = "c" THEN b$ = "": GOSUB update
  148.                 IF kh& = 13 THEN 'enter pressed check if number is being entered?
  149.                     IF LEN(b$) THEN
  150.                         IF VAL(b$) >= lba AND VAL(b$) <= uba THEN 'we have number started
  151.                             choice = VAL(b$): EXIT DO
  152.                         ELSE 'clear b$ to show some response to enter
  153.                             b$ = "": GOSUB update 'clear the value that doesn't work
  154.                         END IF
  155.                     ELSE
  156.                         choice = hlite + page * maxHeight + lba 'must mean to select the highlighted item
  157.                     END IF
  158.                 END IF
  159.                 IF kh& = 27 THEN EXIT DO 'escape clause offered to Cancel selection process
  160.                 IF kh& = 32 THEN choice = hlite + page * maxHeight + lba 'best way to choose highlighted selection
  161.                 IF kh& = 8 THEN 'backspace to edit number
  162.                     IF LEN(b$) THEN b$ = LEFT$(b$, LEN(b$) - 1): GOSUB update
  163.                 END IF
  164.             ELSE
  165.                 SELECT CASE kh& 'choosing sections of array to display and highlighted item
  166.                     CASE 20736 'pg dn
  167.                         IF (page + 1) * maxHeight + lba <= uba THEN page = page + 1: GOSUB update
  168.                     CASE 18688 'pg up
  169.                         IF (page - 1) * maxHeight + lba >= lba THEN page = page - 1: GOSUB update
  170.                     CASE 18432 'up
  171.                         IF hlite - 1 < 0 THEN
  172.                             IF page > 0 THEN
  173.                                 page = page - 1: hlite = maxHeight - 1: GOSUB update
  174.                             END IF
  175.                         ELSE
  176.                             hlite = hlite - 1: GOSUB update
  177.                         END IF
  178.                     CASE 20480 'down
  179.                         IF (hlite + 1) + page * maxHeight + lba <= uba THEN 'ok to move up
  180.                             IF hlite + 1 > maxHeight - 1 THEN
  181.                                 page = page + 1: hlite = 0: GOSUB update
  182.                             ELSE
  183.                                 hlite = hlite + 1: GOSUB update
  184.                             END IF
  185.                         END IF
  186.                     CASE 18176 'home
  187.                         page = 0: hlite = 0: GOSUB update
  188.                     CASE 20224 ' end
  189.                         page = INT((uba - lba) / maxHeight): hlite = maxHeight - 1: GOSUB update
  190.                 END SELECT
  191.             END IF
  192.         END IF
  193.  
  194.         'handle the mouse stuff
  195.         WHILE _MOUSEINPUT
  196.             IF _MOUSEWHEEL = -1 THEN 'up?
  197.                 IF hlite - 1 < 0 THEN
  198.                     IF page > 0 THEN
  199.                         page = page - 1: hlite = maxHeight - 1: GOSUB update
  200.                     END IF
  201.                 ELSE
  202.                     hlite = hlite - 1: GOSUB update
  203.                 END IF
  204.             ELSEIF _MOUSEWHEEL = 1 THEN 'down?
  205.                 IF (hlite + 1) + page * maxHeight + lba <= uba THEN 'ok to move up
  206.                     IF hlite + 1 > maxHeight - 1 THEN
  207.                         page = page + 1: hlite = 0: GOSUB update
  208.                     ELSE
  209.                         hlite = hlite + 1: GOSUB update
  210.                     END IF
  211.                 END IF
  212.             END IF
  213.         WEND
  214.         mx = INT((_MOUSEX - locateColumn * 8) / 8) + 2: my = INT((_MOUSEY - locateRow * 16) / 16) + 2
  215.         IF _MOUSEBUTTON(1) THEN 'click contols or select array item
  216.             'clear mouse clicks
  217.             mb = _MOUSEBUTTON(1)
  218.             IF mb THEN 'clear it
  219.                 WHILE mb 'OK!
  220.                     IF _MOUSEINPUT THEN mb = _MOUSEBUTTON(1)
  221.                     _LIMIT 100
  222.                 WEND
  223.             END IF
  224.  
  225.             IF mx >= 1 AND mx <= maxWidth AND my >= 1 AND my <= maxHeight THEN
  226.                 choice = my + page * maxHeight + lba - 1 'select item clicked
  227.             ELSEIF mx >= 1 AND mx <= maxWidth AND my = 0 THEN 'page up or exit
  228.                 IF my = 0 AND (mx <= maxWidth AND mx >= maxWidth - 2) THEN 'exit sign
  229.                     EXIT DO 'escape plan for mouse click top right corner of display box
  230.                 ELSE 'PgUp bar clicked
  231.                     IF (page - 1) * maxHeight + lba >= lba THEN page = page - 1: GOSUB update
  232.                 END IF
  233.             ELSEIF mx >= 1 AND mx <= maxWidth AND my = maxHeight + 1 THEN 'page down bar clicked
  234.                 IF (page + 1) * maxHeight + lba <= uba THEN page = page + 1: GOSUB update
  235.             END IF
  236.         ELSE '   mouse over highlighting, only if mouse has moved!
  237.             IF mx >= 1 AND mx <= maxWidth AND my >= 1 AND my <= maxHeight THEN
  238.                 IF mx <> lastMX OR my <> lastMY THEN
  239.                     IF my - 1 <> hlite AND (my - 1 + page * maxHeight + lba <= uba) THEN
  240.                         hlite = my - 1
  241.                         lastMX = mx: lastMY = my
  242.                         GOSUB update
  243.                     END IF
  244.                 END IF
  245.             END IF
  246.         END IF
  247.         _LIMIT 200
  248.     LOOP UNTIL choice >= lba AND choice <= uba
  249.     getArrayItemNumber& = choice
  250.     COLOR fg, bg
  251.     'clear key presses
  252.     _KEYCLEAR
  253.     LOCATE curRow, curCol
  254.     'clear mouse clicks
  255.     mb = _MOUSEBUTTON(1)
  256.     IF mb THEN 'clear it
  257.         WHILE mb 'OK!
  258.             IF _MOUSEINPUT THEN mb = _MOUSEBUTTON(1)
  259.             _LIMIT 100
  260.         WEND
  261.     END IF
  262.     EXIT SUB
  263.  
  264.  
  265.     update: '--------------- display of array sections and controls on screen
  266.  
  267.     'fix hlite if it has dropped below last array item
  268.     WHILE hlite + page * maxHeight + lba > uba
  269.         hlite = hlite - 1
  270.     WEND
  271.  
  272.     'main display of array items at page * maxHeight (lines high)
  273.     FOR row = 0 TO maxHeight - 1
  274.         IF hlite = row THEN COLOR _RGB(200, 200, 255), _RGB32(0, 0, 88) ELSE COLOR _RGB32(0, 0, 88), _RGB(200, 200, 255)
  275.         LOCATE locateRow + row, locateColumn: PRINT clrStr$
  276.         index = row + page * maxHeight + lba
  277.         IF index >= lba AND index <= uba THEN
  278.             LOCATE locateRow + row, locateColumn
  279.             PRINT LEFT$(LTRIM$(STR$(index)) + ") " + arr(index), maxWidth)
  280.         END IF
  281.     NEXT
  282.  
  283.     'make page up and down bars to click, print PgUp / PgDn if available
  284.     COLOR _RGB32(200, 200, 255), _RGB32(0, 100, 50)
  285.     LOCATE locateRow - 1, locateColumn: PRINT SPACE$(maxWidth)
  286.     IF page <> 0 THEN LOCATE locateRow - 1, locateColumn: PRINT LEFT$(" Pg Up" + SPACE$(maxWidth), maxWidth)
  287.     LOCATE locateRow + maxHeight, locateColumn: PRINT SPACE$(maxWidth)
  288.     IF page <> INT(uba / maxHeight) THEN
  289.         LOCATE locateRow + maxHeight, locateColumn: PRINT LEFT$(" Pg Dn" + SPACE$(maxWidth), maxWidth)
  290.     END IF
  291.     'make exit sign for mouse click
  292.     COLOR _RGB32(255, 255, 255), _RGB32(200, 100, 0)
  293.     LOCATE locateRow - 1, locateColumn + maxWidth - 3
  294.     PRINT " X "
  295.  
  296.     'if a number selection has been started show it's build = b$
  297.     IF LEN(b$) THEN
  298.         COLOR _RGB(255, 255, 0), _RGB32(0, 0, 0)
  299.         LOCATE locateRow + maxHeight, locateColumn + maxWidth - LEN(b$) - 1
  300.         PRINT b$;
  301.     END IF
  302.     _DISPLAY
  303.     _LIMIT 100
  304.     RETURN
  305.  
  306.  

I can only test in Windows, so would specially appreciate feedback from Linux or other OS users.

FYI DirEnter.h is at the start of this thread for downloading.

Title: Re: Development of a program for automatic search for music and movies
Post by: Petr on June 25, 2020, 04:20:59 am
Nice work, BPlus. This can be very useful for Linux. I think, the option in navigator "." can be used for direct access to root directory.

Let me tell you one thing. It hasn't been that long since Pete and me have written here about how clear it is in the morning where you made a mistake in the evening. Well, man. It was confirmed again. Imagine just enclosing a DIR statement listing in quotation marks. And... then it works.... So on Linux, I find a command that does the same thing as a DIR with the parameters provided by Steve. Now i can continue. Here is current harddrive listening - use space for next record in array and enter for running it under windows media player. I use actaully BAT file - it is better when is program developed.


Added mask decomposition (the program correctly places files in the list according to the mask), solved access to files using long file names.

Request. Create a folder that has unicode characters in the name. Insert a supported audio and / or video format (preferably also with unicode characters in the name). Run the program. Select this or the parent folder at the beginning. Use the space bar to scroll to this file and try to run it with the enter key.


Source upgraded. Now you can see difference between system file access and QB64 file access. Some file are unacessible with QB64.  But access is needed for next work...

Code: QB64: [Select]
  1. TYPE FileList
  2.     ShtName AS STRING
  3.     LngName AS STRING
  4.  
  5.  
  6.  
  7. Path$ = SelectDIR$
  8. SPath$ = ShortName$(Path$) '                                                                           is need for access to files and directories, which use unicode characters in name
  9. IF SPath$ = "" THEN SPath$ = _CWD$
  10. PRINT Path$, SPath$
  11. CurrentPath$ = _CWD$
  12.  
  13.  
  14.  
  15.  
  16. mask$ = "AVI,MPG,MKV,MPG,MPEG,WAV,ACC,MP4,MP3,MP2,VOC,S3M,MOD,XM,TS,MOV,AC3,WMA,WMV,FLAC," '           on end must be comma!
  17.  
  18. REDIM files_found(0) AS FileList
  19. SearchFiles mask$, files_found(), SPath$
  20.  
  21. PrintIt files_found()
  22.  
  23.  
  24.  
  25.  
  26.  
  27. SUB SearchFiles (mask AS STRING, FL() AS FileList, Path AS STRING)
  28.     current$ = _CWD$
  29.     CHDIR Path$
  30.     P = INSTR(mask$, ",")
  31.     DO UNTIL P = 0
  32.         f$ = MID$(mask$, 1, P - 1)
  33.         mask$ = MID$(mask$, P + 1)
  34.         REDIM _PRESERVE mask(i) AS STRING
  35.         msk$ = msk$ + _CWD$ + "\*." + f$ + " "
  36.         i = i + 1
  37.         P = INSTR(mask$, ",")
  38.     LOOP
  39.     PRINT "Creating database..."
  40.     msk$ = LEFT$(msk$, LEN(msk$) - 1)
  41.     WorkDir$ = "C:\users\public"
  42.     t$ = "DIR " + msk$ + "/b /s /a-d >" + WorkDir$ + "\FilesList.txt"
  43.     SHELL _HIDE t$
  44.     ff = FREEFILE
  45.     OPEN WorkDir$ + "\FilesList.txt" FOR INPUT AS ff
  46.     i = 0
  47.     REDIM _PRESERVE FL(100 + i) AS FileList
  48.     DO UNTIL EOF(ff)
  49.         LINE INPUT #ff, LLL$
  50.         FL(i).LngName = LLL$
  51.         FL(i).ShtName = ShortName$(FL(i).LngName)
  52.         i = i + 1
  53.         IF i > UBOUND(fl) THEN REDIM _PRESERVE FL(i + 100) AS FileList
  54.     LOOP
  55.     REDIM _PRESERVE FL(i - 1) AS FileList
  56.     CLOSE ff
  57.     REM KILL WorkDir$ + "\FilesList.txt"
  58.     CHDIR current$
  59.  
  60.  
  61.  
  62. SUB PrintIt (D() AS FileList)
  63.     FOR p = LBOUND(d) TO UBOUND(d)
  64.         PRINT D(p).ShtName
  65.         '  PRINT D(p).LngName
  66.         DO UNTIL K$ = " "
  67.             K$ = INKEY$
  68.             IF K$ = CHR$(13) THEN
  69.                 PRINT D(p).ShtName
  70.                 ff = FREEFILE
  71.                 OPEN "dSWAP.bat" FOR OUTPUT AS #ff
  72.                 PRINT #ff, "start wmplayer " + CHR$(34) + D(p).ShtName + CHR$(34)
  73.                 CLOSE #ff
  74.                 SHELL _DONTWAIT _HIDE "dswap.bat"
  75.                 'KILL "dswap.bat"
  76.                 IF _FILEEXISTS(D(p).ShtName) THEN PRINT "QB64 accessible" ELSE PRINT "QB64 access error"
  77.             END IF
  78.         LOOP
  79.         K$ = ""
  80.     NEXT
  81.  
  82.  
  83.  
  84.  
  85.  
  86.  
  87. FUNCTION ShortName$ (name$)
  88.     DECLARE LIBRARY '                                                                                      Directory Information using KERNEL32
  89.         FUNCTION GetShortPathNameA (lpLongPath AS STRING, lpShortPath AS STRING, BYVAL cBufferLen AS LONG)
  90.     END DECLARE
  91.     ShortPathName$ = SPACE$(260)
  92.     Result = GetShortPathNameA(nam$ + CHR$(0), ShortPathName$, LEN(ShortPathName$))
  93.     IF Result THEN ShortName$ = ShortPathName$ ELSE ShortName$ = name$
  94.  
  95.  
  96.  
  97. FUNCTION SelectDIR$
  98.         FUNCTION FindWindow& (BYVAL ClassName AS _OFFSET, WindowName$)
  99.     END DECLARE
  100.     hwnd& = _WINDOWHANDLE
  101.     TYPE BROWSEINFO '                    typedef struct _browseinfo 'Microsoft MSDN
  102.         hwndOwner AS _OFFSET '           HWND
  103.         pidlRoot AS _OFFSET '            PCIDLIST_ABSOLUTE
  104.         pszDisplayName AS _OFFSET '      LPTSTR
  105.         lpszTitle AS _OFFSET '           LPCTSTR
  106.         ulFlags AS _UNSIGNED LONG '      UINT
  107.         lpfn AS _OFFSET '                BFFCALLBACK
  108.         lParam AS _OFFSET '              LPARAM
  109.         iImage AS LONG '                 int
  110.     END TYPE '                           BROWSEINFO, *PBROWSEINFO, *LPBROWSEINFO;
  111.  
  112.     DECLARE DYNAMIC LIBRARY "shell32"
  113.         FUNCTION SHBrowseForFolder%& (x AS BROWSEINFO) '                           Microsoft MSDN
  114.         SUB SHGetPathFromIDList (BYVAL lpItem AS _OFFSET, BYVAL szDir AS _OFFSET) 'Microsoft MSDN
  115.     END DECLARE
  116.  
  117.     DIM b AS BROWSEINFO
  118.     b.hwndOwner = hwnd
  119.     DIM s AS STRING * 1024
  120.     b.pszDisplayName = _OFFSET(s$)
  121.     a$ = "Select folder for searching movies and music:" + CHR$(0)
  122.     b.lpszTitle = _OFFSET(a$)
  123.     DIM o AS _OFFSET
  124.     '    _SCREENHIDE
  125.     o = SHBrowseForFolder(b)
  126.     '    _SCREENSHOW
  127.     IF o THEN
  128.         '   Path$ = LEFT$(s$, INSTR(s$, CHR$(0)) - 1)
  129.         DIM s2 AS STRING * 1024
  130.         SHGetPathFromIDList o, _OFFSET(s2$)
  131.         SelectDIR$ = LEFT$(s2$, INSTR(s2$, CHR$(0)) - 1)
  132.     ELSE
  133.         PRINT "Directory not selected, using current directory"
  134.         SelectDIR$ = _CWD$
  135.     END IF
  136.  

As you see: Windows Media Player run music correctly using long names. But the same string is invalid path for QB64.

 
Title: Re: Development of a program for automatic search for music and movies
Post by: Petr on June 25, 2020, 05:14:07 am
By testing this program of mine https://www.qb64.org/forum/index.php?topic=321.msg2875#msg2875  it was confirmed again that the only way to do it is DIRENTRY.H, which returns the correct long names (which will eventually be correctly translated to graphic program screen using MAPUNICODE depending on the national environment).
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 25, 2020, 07:08:57 am
I edited SMcNeil's GetFileList sub for usage in my programs and I rather like how it works. I'm not sure if it will work any better than what you have. This, of course, uses direntry.h:
Usage:
Code: QB64: [Select]
  1. GetFileList _STARTDIR$ + "\Images\", Directories(), File(), ".PNG,.png"
"BI"
Code: QB64: [Select]
  1.         FUNCTION load_dir& (s AS STRING)
  2.         FUNCTION has_next_entry& ()
  3.         SUB close_dir ()
  4.         SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  5.     END DECLARE
  6.  
  7.     REDIM SHARED Directories(0) AS STRING, File(0) AS STRING
"BM"
Code: QB64: [Select]
  1. SUB GetFileList (SearchDirectory AS STRING, DirList() AS STRING, FileList() AS STRING, filter AS STRING)
  2.         IF String.EndsWith(D$, "\") = 0 THEN
  3.             D$ = D$ + "\"
  4.         END IF
  5.         CONST IS_DIR = 1
  6.         CONST IS_FILE = 2
  7.         DIM flags AS LONG, file_size AS LONG
  8.         IF filter <> "" THEN
  9.             REDIM filters(0) AS STRING
  10.             String.Split filter, ",", filters(), 0
  11.         END IF
  12.         REDIM _PRESERVE DirList(100), FileList(100)
  13.         DirCount = 0: FileCount = 0
  14.  
  15.         IF load_dir(SearchDirectory) THEN
  16.             DO
  17.                 length = has_next_entry
  18.                 IF length > -1 THEN
  19.                     nam$ = SPACE$(length)
  20.                     get_next_entry nam$, flags, file_size
  21.                     IF (flags AND IS_DIR) OR _DIREXISTS(SearchDirectory + nam$) THEN
  22.                         DirCount = DirCount + 1
  23.                         IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  24.                         DirList(DirCount) = nam$
  25.                     ELSEIF (flags AND IS_FILE) OR _FILEEXISTS(SearchDirectory + nam$) THEN
  26.                         IF filter <> "" THEN
  27.                             FOR I = LBOUND(filters) TO UBOUND(filters)
  28.                                 IF String.EndsWith(nam$, filters(I)) THEN
  29.                                     FileCount = FileCount + 1
  30.                                     IF FileCount > UBOUND(filelist) THEN REDIM _PRESERVE FileList(UBOUND(filelist) + 100)
  31.                                     FileList(FileCount) = nam$
  32.                                 END IF
  33.                             NEXT
  34.                         ELSE
  35.                             FileCount = FileCount + 1
  36.                             IF FileCount > UBOUND(filelist) THEN REDIM _PRESERVE FileList(UBOUND(filelist) + 100)
  37.                             FileList(FileCount) = nam$
  38.                         END IF
  39.                     END IF
  40.                 END IF
  41.             LOOP UNTIL length = -1
  42.             close_dir
  43.         ELSE
  44.         END IF
  45.         REDIM _PRESERVE DirList(DirCount)
  46.         REDIM _PRESERVE FileList(FileCount)
  47.     END SUB
  48.     FUNCTION String.EndsWith (check AS STRING, value AS STRING)
  49.         IF INSTR(check, value) THEN
  50.             IF RIGHT$(check, LEN(value)) = value THEN
  51.                 String.EndsWith = -1
  52.             ELSE
  53.                 String.EndsWith = 0
  54.             END IF
  55.         ELSE
  56.             String.EndsWith = 0
  57.         END IF
  58.     SUB String.Split (Expression AS STRING, delimiter AS STRING, StorageArray() AS STRING, preserve AS INTEGER)
  59.         DIM copy AS STRING, p AS LONG, curpos AS LONG, arrpos AS LONG, dpos AS LONG
  60.         copy = Expression
  61.         IF delimiter = " " THEN
  62.             copy = RTRIM$(LTRIM$(copy))
  63.             p = INSTR(copy, "  ")
  64.             WHILE p > 0
  65.                 copy = MID$(copy, 1, p - 1) + MID$(copy, p + 1)
  66.                 p = INSTR(copy, "  ")
  67.             WEND
  68.         END IF
  69.         curpos = 1
  70.         IF preserve <> 0 THEN
  71.             arrpos = UBOUND(StorageArray)
  72.             dpos = INSTR(curpos, copy, delimiter)
  73.             DO UNTIL dpos = 0
  74.                 StorageArray(UBOUND(StorageArray)) = MID$(copy, curpos, dpos - curpos)
  75.                 REDIM _PRESERVE StorageArray(UBOUND(StorageArray) + 1) AS STRING
  76.                 curpos = dpos + LEN(delimiter)
  77.                 dpos = INSTR(curpos, copy, delimiter)
  78.             LOOP
  79.             StorageArray(UBOUND(StorageArray)) = MID$(copy, curpos)
  80.             REDIM _PRESERVE StorageArray(UBOUND(StorageArray) + 1) AS STRING
  81.         ELSEIF preserve = 0 THEN
  82.             arrpos = 0
  83.             dpos = INSTR(curpos, copy, delimiter)
  84.             DO UNTIL dpos = 0
  85.                 StorageArray(arrpos) = MID$(copy, curpos, dpos - curpos)
  86.                 arrpos = arrpos + 1
  87.                 IF arrpos > UBOUND(StorageArray) THEN REDIM _PRESERVE StorageArray(UBOUND(StorageArray) + 1) AS STRING
  88.                 curpos = dpos + LEN(delimiter)
  89.                 dpos = INSTR(curpos, copy, delimiter)
  90.             LOOP
  91.             StorageArray(arrpos) = MID$(copy, curpos)
  92.             REDIM _PRESERVE StorageArray(arrpos) AS STRING
  93.         END IF
  94.     END SUB
  95.  
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 25, 2020, 07:22:03 am
Your code suffers the same basic flaw, @SpriggsySpriggs:

            close_dir
        ELSE
        END IF

There's no close_dir call in the ELSE segment of the code, which can cause issues as Bplus pointed out above.

Lots of folks have made use of direntry.h, and it seems I glitched the original, which propagated into everyone else's stuff later.  Move that close_dir to after the END IF, and it should help prevent possible issues in the future in your code.  ;D
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 25, 2020, 07:42:36 am
Lots of folks have made use of direntry.h, and it seems I glitched the original, which propagated into everyone else's stuff later.  Move that close_dir to after the END IF, and it should help prevent possible issues in the future in your code.  ;D
Thanks, Steve! I'll fix that right away.
Title: Re: Development of a program for automatic search for music and movies
Post by: Petr on June 25, 2020, 12:04:59 pm
So here I try to get directories (only the names of all subdirectories in all subfolders from the default folder) and again an error occurred - probably a memory leak. This time I think I managed to locate the place. I think that this bug really doesn't make any sense at all.

Run it in the directory with QB64 (let's have the same directory structure):

The program first finds the first directories: internal, licenses, programs, source. Adds these 4 directories to the global G field. The G field will be the field of all directories. The next step is to open these 4 directories one by one (the first is the internals folder), find out the subdirectories, write them in the G field, go back one folder, open the next one (licenses), an empty field should be returned here. But it will not return empty. This causes invalid records to be added to the G field. The question is - what is the problem why the field does not return zero.

If you go all the way down in the listing, you'll find a shocking thing: the level entry contains meaningless values ​​that came from nowhere. Localized this error here on line 38. After Disabling AddDirsToGlobal does not cause this error. The current state of the program is incomprehensible. Can someone find the bug? (yes, it is to very long time)

How is this bad Level value incomming to the array? 

Code: QB64: [Select]
  1.  
  2.     FUNCTION load_dir& (s AS STRING)
  3.     FUNCTION has_next_entry& ()
  4.     SUB close_dir ()
  5.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14. TYPE GlobalDir
  15.     Level AS INTEGER
  16.     Path AS STRING
  17.  
  18. REDIM SHARED G(0) AS GlobalDir
  19. REDIM Begin(0) AS STRING
  20.  
  21. GetDIRList _CWD$, Begin()
  22. SA Begin()
  23. AddDirsToGlobal Begin(), 1
  24. ShowGlobal
  25.  
  26. FOR S = 0 TO UBOUND(begin)
  27.     'PRINT Begin(S)
  28.     SLEEP
  29.     CHDIR Begin(S)
  30.     PRINT "Current path: "; _CWD$
  31.     REDIM D(0) AS STRING
  32.     GetDIRList _CWD$, D()
  33.     SA D()
  34.     AddDirsToGlobal D(), 2 '                 comment this
  35.     CHDIR ".."
  36.     PRINT "Current path: "; _CWD$
  37.     PRINT "Global size:"; UBOUND(g) + 1
  38.  
  39.  
  40.  
  41. ShowGlobal
  42.  
  43.  
  44.  
  45.  
  46. SUB SA (a() AS STRING)
  47.     FOR s = LBOUND(a) TO UBOUND(a)
  48.         PRINT "Array List:"; a(s); " array size:"; UBOUND(a) + 1
  49.         SLEEP
  50.     NEXT
  51.  
  52.  
  53.  
  54.  
  55.  
  56.  
  57. SUB ShowGlobal
  58.     FOR s = 0 TO UBOUND(g)
  59.         PRINT G(s).Path, " Level: "; G(s).Level
  60.         SLEEP
  61.     NEXT
  62.  
  63.  
  64.  
  65. SUB AddDirsToGlobal (s() AS STRING, level AS INTEGER)
  66.     IF UBOUND(s) = 0 THEN EXIT SUB
  67.     PRINT "To global array added: "; UBOUND(g); "+"; UBOUND(s) + 1
  68.  
  69.     Start = UBOUND(g)
  70.     Eend = UBOUND(g) + UBOUND(s) + 1
  71.  
  72.     REDIM _PRESERVE G(Start + Eend) AS GlobalDir
  73.     FOR a = Start TO Eend - 1
  74.         G(a).Path = s(i)
  75.         G(a).Level = level
  76.         i = i + 1
  77.     NEXT
  78.  
  79.  
  80.  
  81.  
  82.  
  83.  
  84. 'my modification just for directory names:
  85. SUB GetDIRList (SearchDirectory AS STRING, DirList() AS STRING) 'opravena verze
  86.     REDIM DirList(0) AS STRING
  87.     CONST IS_DIR = 1
  88.     DIM flags AS LONG, file_size AS LONG
  89.     DirCount = 0
  90.     IF load_dir(SearchDirectory) THEN
  91.         DO
  92.             length = has_next_entry
  93.             IF length > -1 THEN
  94.                 nam$ = SPACE$(length)
  95.                 get_next_entry nam$, flags, file_size
  96.                 IF (flags AND IS_DIR) OR _DIREXISTS(SearchDirectory + nam$) THEN
  97.                     IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  98.                     DirList(DirCount) = _CWD$ + "\" + nam$
  99.                     DirCount = DirCount + 1
  100.                 END IF
  101.             END IF
  102.         LOOP UNTIL length = -1
  103.  
  104.  
  105.         IF RIGHT$(DirList(0), 1) = "." AND RIGHT$(DirList(1), 2) = ".." THEN
  106.             DirCount = DirCount - 3
  107.             REDIM Dirl(DirCount) AS STRING
  108.             FOR PlaceIt = 0 TO DirCount
  109.                 Dirl(PlaceIt) = DirList(PlaceIt + 2)
  110.             NEXT
  111.             REDIM DirList(DirCount) AS STRING
  112.             FOR PlaceIt = 0 TO DirCount
  113.                 DirList(PlaceIt) = Dirl(PlaceIt)
  114.             NEXT
  115.             ERASE Dirl
  116.         ELSE
  117.             REDIM _PRESERVE DirList(DirCount - 1)
  118.         END IF
  119.     ELSE
  120.     END IF
  121.     close_dir
  122.  
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 25, 2020, 12:19:02 pm
@Petr

Looks like you are trying to build a TREE, a listing of all directories or paths. There might be a utility floating around for that but don't know if cross platform. It would probably work by a recursive method.
Title: Re: Development of a program for automatic search for music and movies
Post by: Petr on June 25, 2020, 12:22:07 pm
Definitely yes, but this simple foundation would have to work first ...
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 25, 2020, 12:33:12 pm
@Petr

:) so again I ask, "Are you going for Cross Platform or Windows only."

 Windows only will have kick butt utilities for TREE that you might research but Cross Platform might be nice programming challenge. ;-))

The deciding factor, I think, is if you intend to use Windows only apps for playing these files we are digging around for?
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 25, 2020, 12:38:39 pm
I know this is probably not an answer that anyone wants to hear but PowerShell is excellent for many things. I've been able to make WPF Message Boxes and Open/Save file dialogs in it with great ease. You might consider writing a PowerShell script that you can call from inside your program and check for exit codes. I love using PowerShell.
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 25, 2020, 12:42:31 pm
Yeah, I am thinking Shell or Powershell, why reinvent the wheel, if Windows only.
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 25, 2020, 12:47:43 pm
Yeah, I am thinking Shell or Powershell, why reinvent the wheel, if Windows only.
Well there is even a cross-platform PowerShell now :)
Title: Re: Development of a program for automatic search for music and movies
Post by: Petr on June 25, 2020, 01:27:15 pm
BPlus, let's do this for windows first. When it's done, I can study on the Internet which Linux commands can be useful for the same purpose and then add functionality for Linux. But still - why it works not? I have big head from it...
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 25, 2020, 02:05:42 pm
BPlus, let's do this for windows first. When it's done, I can study on the Internet which Linux commands can be useful for the same purpose and then add functionality for Linux. But still - why it works not? I have big head from it...

;-)) "Big heads" love company.

I will look again when I finish errands.

Quote
Well there is even a cross-platform PowerShell now :)

Well then another one to learn. :)
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 25, 2020, 02:19:39 pm
This displays a recursive list of every subdirectory from a given path. Just read in the file results into your program and enjoy!
Code: QB64: [Select]
  1. SHELL _HIDE "PowerShell Get-ChildItem -Path \" + CHR$(34) + Path$ + "\" + CHR$(34) + " -Recurse -Directory -Force -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName | Format-Table -AutoSize > directorylist.txt"
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 25, 2020, 02:26:06 pm
BPlus, let's do this for windows first. When it's done, I can study on the Internet which Linux commands can be useful for the same purpose and then add functionality for Linux.
For Linux, according to the internet:
Code: QB64: [Select]
  1. SHELL "find " + CHR$(34) + Path$ + CHR$(34) + " -path */* > directory.txt"
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 25, 2020, 05:08:53 pm
Hi @Petr

Couldn't figure out what you were doing when adding dirs from s to G so I did it my own way and now not a bunch of empty fills.

This looks better to me but not sure it ends correctly:
Code: QB64: [Select]
  1.     FUNCTION load_dir& (s AS STRING)
  2.     FUNCTION has_next_entry& ()
  3.     SUB close_dir ()
  4.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  5.  
  6.  
  7. SCREEN _NEWIMAGE(1280, 740, 32) '<<<<<<<<<<<<<  let's get bigger screen!
  8. _DELAY .25
  9.  
  10.  
  11.  
  12.  
  13. TYPE GlobalDir
  14.     Level AS INTEGER
  15.     Path AS STRING
  16.  
  17. REDIM SHARED G(0) AS GlobalDir
  18. REDIM Begin(0) AS STRING
  19.  
  20. GetDIRList _CWD$, Begin()
  21. SA Begin()
  22. AddDirsToGlobal Begin(), 1
  23. ShowGlobal
  24. DIM s
  25. FOR s = 0 TO UBOUND(begin)
  26.     'PRINT Begin(S)
  27.     SLEEP
  28.     CHDIR Begin(s)
  29.     PRINT "Current path: "; _CWD$
  30.     REDIM D(0) AS STRING
  31.     GetDIRList _CWD$, D()
  32.     SA D()
  33.     AddDirsToGlobal D(), 2 '                 comment this
  34.     CLS
  35.  
  36.     ShowGlobal ' <<<<<<<<<<<<<<<<<<< lets look at Global at each loop iteration
  37.  
  38.  
  39.     CHDIR ".."
  40.     PRINT "Current path: "; _CWD$
  41.     PRINT "Global size:"; UBOUND(g) + 1
  42.  
  43. ShowGlobal
  44.  
  45.  
  46.  
  47.  
  48. SUB SA (a() AS STRING)
  49.     DIM s
  50.     FOR s = LBOUND(a) TO UBOUND(a)
  51.         PRINT "Array List:"; a(s); " array size:"; UBOUND(a) + 1
  52.         SLEEP
  53.     NEXT
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60. SUB ShowGlobal
  61.     PRINT "Start of next Golbal: keep pressing  any again until see End of global and then press 1 more time"
  62.     DIM s
  63.     FOR s = 0 TO UBOUND(g)
  64.         PRINT G(s).Path, " Level: "; G(s).Level
  65.         SLEEP
  66.     NEXT
  67.     PRINT "End of Global, press spacebar again..."
  68.     SLEEP
  69.  
  70.  
  71.  
  72.  
  73. SUB AddDirsToGlobal (s() AS STRING, level AS INTEGER)
  74.     IF UBOUND(s) = 0 THEN EXIT SUB
  75.     PRINT "To global array added: "; UBOUND(g); "+"; UBOUND(s) + 1
  76.     DIM a
  77.     FOR a = LBOUND(s) TO UBOUND(s)
  78.         sAppend G(), s(a), level
  79.     NEXT
  80.  
  81. SUB sAppend (arr() AS GlobalDir, addItem$, addlevel)
  82.     REDIM _PRESERVE arr(LBOUND(arr) TO UBOUND(arr) + 1) AS GlobalDir
  83.     arr(UBOUND(arr)).Path = addItem$
  84.     arr(UBOUND(arr)).Level = addlevel
  85.  
  86.  
  87.  
  88. 'my modification just for directory names:
  89. SUB GetDIRList (SearchDirectory AS STRING, DirList() AS STRING) 'opravena verze
  90.     REDIM DirList(0) AS STRING
  91.     CONST IS_DIR = 1
  92.     DIM flags AS LONG, file_size AS LONG, DirCount, length, nam$, placeIt
  93.     DirCount = 0
  94.     IF load_dir(SearchDirectory) THEN
  95.         DO
  96.             length = has_next_entry
  97.             IF length > -1 THEN
  98.                 nam$ = SPACE$(length)
  99.                 get_next_entry nam$, flags, file_size
  100.                 IF (flags AND IS_DIR) OR _DIREXISTS(SearchDirectory + nam$) THEN
  101.                     IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  102.                     DirList(DirCount) = _CWD$ + "\" + nam$
  103.                     DirCount = DirCount + 1
  104.                 END IF
  105.             END IF
  106.         LOOP UNTIL length = -1
  107.  
  108.  
  109.         IF RIGHT$(DirList(0), 1) = "." AND RIGHT$(DirList(1), 2) = ".." THEN
  110.             DirCount = DirCount - 3
  111.             REDIM Dirl(DirCount) AS STRING
  112.             FOR placeIt = 0 TO DirCount
  113.                 Dirl(placeIt) = DirList(placeIt + 2)
  114.             NEXT
  115.             REDIM DirList(DirCount) AS STRING
  116.             FOR placeIt = 0 TO DirCount
  117.                 DirList(placeIt) = Dirl(placeIt)
  118.             NEXT
  119.             REDIM Dirl(0)
  120.         ELSE
  121.             REDIM _PRESERVE DirList(DirCount - 1)
  122.         END IF
  123.     ELSE
  124.     END IF
  125.     close_dir
  126.  
  127.  
  128.  
  129.  
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 25, 2020, 11:21:24 pm
A slight change to my original SHELL suggestion:
Code: QB64: [Select]
  1. Path$ = "C:\Users\Zachary\Desktop\QB64 x64"
  2. SHELL _HIDE "PowerShell Get-ChildItem -Path \" + CHR$(34) + Path$ + "\" + CHR$(34) + " -Recurse -Directory -Force -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName | Format-Table -AutoSize | Out-File -FilePath directorylist.txt -Encoding ASCII"
  3. DIRECTORIES = FREEFILE
  4. n = 0
  5. DIM Dirs(n) AS STRING
  6. OPEN "directorylist.txt" FOR BINARY AS #DIRECTORIES
  7.     LINE INPUT #DIRECTORIES, Directory$
  8.     Directory$ = LTRIM$(RTRIM$(String.Remove(Directory$, CHR$(13))))
  9.     n = n + 1
  10.     REDIM _PRESERVE Dirs(n)
  11.     Dirs(n) = Directory$
  12. LOOP UNTIL EOF(DIRECTORIES)
  13. FOR i = 1 TO UBOUND(Dirs)
  14.     PRINT Dirs(i)
  15.     IF _DIREXISTS(Dirs(i)) THEN
  16.         PRINT "LOCATED"
  17.         IF LEFT$(Dirs(i), _INSTRREV(Dirs(i), "\") - 1) <> Path$ THEN 'i <> 1 OR
  18.             FOR k = 1 TO UBOUND(Dirs)
  19.                 IF LEFT$(Dirs(i), _INSTRREV(Dirs(i), "\") - 1) = Dirs(k) THEN
  20.                     PRINT "Subdirectory of "; Dirs(k)
  21.                 END IF
  22.             NEXT
  23.         ELSE
  24.             PRINT "Subdirectory of "; Path$
  25.         END IF
  26.     END IF
  27.  
  28. FUNCTION String.Remove$ (a AS STRING, b AS STRING)
  29.     DIM c AS STRING
  30.     c = ""
  31.     j = INSTR(a, b)
  32.     IF j > 0 THEN
  33.         r$ = LEFT$(a, j - 1) + c + String.Remove(RIGHT$(a, LEN(a) - j + 1 - LEN(b)), b)
  34.     ELSE
  35.         r$ = a
  36.     END IF
  37.     String.Remove = r$
If you don't add the -Encoding ASCII then qb64 cannot locate the directories because of hidden characters.
Here is a screenshot showing the program running and using _DIREXISTS to make sure it can find the directories retrieved from PowerShell.
  [ This attachment cannot be displayed inline in 'Print Page' view ]  
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 26, 2020, 01:02:27 am
@Petr

This is a cleaner version of reply #24,
https://www.qb64.org/forum/index.php?topic=2742.msg119796#msg119796
upper bounds of arrays reflect actual item counts.

I am setting this up for a recursive TREE build.

Code: QB64: [Select]
  1.     FUNCTION load_dir& (s AS STRING)
  2.     FUNCTION has_next_entry& ()
  3.     SUB close_dir ()
  4.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  5.  
  6. REDIM SHARED G(0) AS STRING '<<<<<<<<<<<<<<<<<<<<<< G(0) will remain empty
  7.  
  8. SCREEN _NEWIMAGE(1280, 740, 32) '<<<<<<<<<<<<<  let's get bigger screen!
  9. _DELAY .25
  10.  
  11. REDIM Begin(0) AS STRING, D(0) AS STRING, w$, s
  12.  
  13. GetSubDirs _CWD$, Begin()
  14. SA Begin()
  15. AddDirsToGlobal Begin()
  16. ShowGlobal
  17. FOR s = 1 TO UBOUND(begin)
  18.     PRINT "About to change dir to: "; Begin(s)
  19.     INPUT "Enter to continue... "; w$
  20.     CHDIR Begin(s)
  21.     PRINT "Current path: "; _CWD$
  22.     GetSubDirs _CWD$, D()
  23.     SA D()
  24.     AddDirsToGlobal D()
  25.     ShowGlobal
  26. PRINT "Last Global List: press any... "
  27. ShowGlobal
  28.  
  29. SUB SA (a() AS STRING)
  30.     DIM s
  31.     PRINT "Start of SubDirs List: press any until End SubDirs."
  32.     SLEEP
  33.     FOR s = 1 TO UBOUND(a)
  34.         PRINT "Array List:"; a(s); " array size:"; UBOUND(a)
  35.         SLEEP
  36.     NEXT
  37.     PRINT "End of SubDirs List: press any..."
  38.     SLEEP
  39.  
  40. SUB ShowGlobal
  41.     PRINT "Start of next Golbal: keep pressing  any again until see End of global and then press 1 more time"
  42.     DIM s
  43.     FOR s = 1 TO UBOUND(g)
  44.         PRINT G(s)
  45.         SLEEP
  46.     NEXT
  47.     PRINT "Global size:"; UBOUND(g)
  48.     PRINT "End of Global List, size now:"; UBOUND(g); "   press any..."
  49.     SLEEP
  50.  
  51. SUB AddDirsToGlobal (s() AS STRING)
  52.     IF UBOUND(s) = 0 THEN EXIT SUB
  53.     DIM a
  54.     FOR a = 1 TO UBOUND(s)
  55.         sAppend G(), s(a)
  56.     NEXT
  57.     PRINT "Ubound of Global Array after addition: "; UBOUND(g); "  press any..."
  58.     SLEEP
  59.  
  60. SUB sAppend (arr() AS STRING, addItem$)
  61.     REDIM _PRESERVE arr(LBOUND(arr) TO UBOUND(arr) + 1) AS STRING
  62.     arr(UBOUND(arr)) = addItem$
  63.  
  64. SUB GetSubDirs (SearchDirectory AS STRING, DirList() AS STRING)
  65.     REDIM DirList(0) AS STRING
  66.     CONST IS_DIR = 1
  67.     DIM flags AS LONG, file_size AS LONG, DirCount, length, nam$
  68.     REDIM _PRESERVE DirList(100)
  69.     IF load_dir(SearchDirectory) THEN
  70.         DO
  71.             length = has_next_entry
  72.             IF length > -1 THEN
  73.                 nam$ = SPACE$(length)
  74.                 get_next_entry nam$, flags, file_size
  75.                 IF (flags AND IS_DIR) OR _DIREXISTS(SearchDirectory + nam$) THEN
  76.                     IF RIGHT$(nam$, 1) <> "." AND RIGHT$(nam$, 2) <> ".." THEN
  77.                         DirCount = DirCount + 1
  78.                         IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  79.                         DirList(DirCount) = _CWD$ + "\" + nam$
  80.                     END IF
  81.                 END IF
  82.             END IF
  83.         LOOP UNTIL length = -1
  84.     END IF
  85.     close_dir
  86.     REDIM _PRESERVE DirList(DirCount)
  87.  
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 26, 2020, 02:53:15 am
Well it almost works:
Code: QB64: [Select]
  1. OPTION _EXPLICIT 'Tree builder recursive.bas  b+ Petr 2020-06-26
  2. DEFLNG A-Z
  3.     FUNCTION load_dir& (s AS STRING)
  4.     FUNCTION has_next_entry& ()
  5.     SUB close_dir ()
  6.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  7.  
  8. REDIM SHARED Tree(0) AS STRING '<<<<<<<<<<<<<<<<<<<<<< G(0) will remain empty
  9.  
  10. SCREEN _NEWIMAGE(1280, 740, 32) '<<<<<<<<<<<<<  let's get bigger screen!
  11. _DELAY .25
  12. DIM cd$, i, w$
  13.  
  14. cd$ = _CWD$   'testing in QB64 folder
  15. MakeTree cd$
  16. FOR i = 1 TO UBOUND(Tree) 'show tree
  17.     PRINT _TRIM$(STR$(i)); ": "; Tree(i)
  18.     IF i MOD 40 = 0 THEN INPUT "Press enter to continue... "; w$: CLS
  19.  
  20. SUB sAppend (arr() AS STRING, addItem$)
  21.     REDIM _PRESERVE arr(LBOUND(arr) TO UBOUND(arr) + 1) AS STRING
  22.     arr(UBOUND(arr)) = addItem$
  23.  
  24. SUB GetSubDirs (SearchDirectory AS STRING, DirList() AS STRING)
  25.     REDIM DirList(0) AS STRING
  26.     CONST IS_DIR = 1
  27.     DIM flags AS LONG, file_size AS LONG, DirCount, length, nam$
  28.     REDIM _PRESERVE DirList(100)
  29.     IF load_dir(SearchDirectory) THEN
  30.         DO
  31.             length = has_next_entry
  32.             IF length > -1 THEN
  33.                 nam$ = SPACE$(length)
  34.                 get_next_entry nam$, flags, file_size
  35.                 IF (flags AND IS_DIR) OR _DIREXISTS(SearchDirectory + nam$) THEN
  36.                     IF RIGHT$(nam$, 1) <> "." AND RIGHT$(nam$, 2) <> ".." THEN
  37.                         DirCount = DirCount + 1
  38.                         IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  39.                         DirList(DirCount) = _CWD$ + "\" + nam$
  40.                     END IF
  41.                 END IF
  42.             END IF
  43.         LOOP UNTIL length = -1
  44.     END IF
  45.     close_dir
  46.     REDIM _PRESERVE DirList(DirCount)
  47.  
  48. SUB MakeTree (startDir AS STRING)
  49.     DIM i
  50.     REDIM D(0) AS STRING
  51.     GetSubDirs startDir, D()
  52.     IF UBOUND(D) THEN
  53.         FOR i = 1 TO UBOUND(D)
  54.             sAppend Tree(), D(i)
  55.             CHDIR D(i)
  56.             MakeTree D(i)
  57.         NEXT
  58.     END IF
  59.  
  60.  

I start in QB64 folder, looks like it gets pretty deep into some directories but not \programs with one sub folder and then more in it. I am only getting 125 directories.
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 26, 2020, 10:19:57 am
Try this version:

Code: QB64: [Select]
  1. OPTION _EXPLICIT 'Tree builder recursive.bas  b+ Petr 2020-06-26
  2. DEFLNG A-Z
  3.     FUNCTION load_dir& (s AS STRING)
  4.     FUNCTION has_next_entry& ()
  5.     SUB close_dir ()
  6.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  7.  
  8. REDIM SHARED Tree(0) AS STRING '<<<<<<<<<<<<<<<<<<<<<< G(0) will remain empty
  9.  
  10. SCREEN _NEWIMAGE(1280, 740, 32) '<<<<<<<<<<<<<  let's get bigger screen!
  11. _DELAY .25
  12. DIM cd$, i, w$
  13.  
  14. cd$ = _CWD$ 'testing in QB64 folder
  15. MakeTree cd$
  16. FOR i = 1 TO UBOUND(Tree) 'show tree
  17.     PRINT _TRIM$(STR$(i)); ": "; Tree(i)
  18.     IF i MOD 40 = 0 THEN INPUT "Press enter to continue... "; w$: CLS
  19.  
  20. SUB sAppend (arr() AS STRING, addItem$)
  21.     REDIM _PRESERVE arr(LBOUND(arr) TO UBOUND(arr) + 1) AS STRING
  22.     arr(UBOUND(arr)) = addItem$
  23.  
  24. SUB GetSubDirs (SearchDirectory AS STRING, DirList() AS STRING)
  25.     REDIM DirList(0) AS STRING
  26.     CONST IS_DIR = 1
  27.     DIM flags AS LONG, file_size AS LONG, DirCount, length, nam$
  28.     REDIM _PRESERVE DirList(100)
  29.     IF load_dir(SearchDirectory) THEN
  30.         DO
  31.             length = has_next_entry
  32.             IF length > -1 THEN
  33.                 nam$ = SPACE$(length)
  34.                 get_next_entry nam$, flags, file_size
  35.                 IF (flags AND IS_DIR) OR _DIREXISTS(SearchDirectory + nam$) THEN
  36.                     IF RIGHT$(nam$, 1) <> "." AND RIGHT$(nam$, 2) <> ".." THEN
  37.                         DirCount = DirCount + 1
  38.                         IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  39.                         DirList(DirCount) = SearchDirectory + "\" + nam$
  40.                     END IF
  41.                 END IF
  42.             END IF
  43.         LOOP UNTIL length = -1
  44.     END IF
  45.     close_dir
  46.     REDIM _PRESERVE DirList(DirCount)
  47.  
  48. SUB MakeTree (startDir AS STRING)
  49.     DIM i
  50.     REDIM D(0) AS STRING
  51.     GetSubDirs startDir, D()
  52.     IF UBOUND(D) THEN
  53.         FOR i = 1 TO UBOUND(D)
  54.             sAppend Tree(), D(i)
  55.             CHDIR D(i)
  56.             MakeTree D(i)
  57.         NEXT
  58.     END IF
  59.  
  60.  
  61.  

Edit:  Something still seems off here.  I'm thinking it's because you're not building the directories with the "\" as the last character.

Print the results as you go, and you'll see things such as :  C:\QB64\internal.. -- this should instead be C:\QB64\internal\..   Save the directories with that \ on the end of them, and it seems to work much better.

I'd dig deeper into this, but my stupid mouse has died on me, and it's a total PITA to try and work without one.  I'll make a trip to the store later today for a set of batteries (wherever my wife has them hidden in the house is beyond my ability to snoop and find), and once I get it back up and going, I'll delve into this once again.  ;)
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 26, 2020, 12:01:59 pm
Hi @SMcNeill

I see one line changed mine:
DirList(DirCount) = _CWD$ + "\" + nam$

Yours:
DirList(DirCount) = SearchDirectory + "\" + nam$

Am I missing any others?

Same exact results. 125 Sub-Directories.

Quote
I'd dig deeper into this,
It is cool problem ;-))
Title: Re: Development of a program for automatic search for music and movies
Post by: Petr on June 26, 2020, 12:31:17 pm
I'm very pleased. I am very pleased with your help. I'm completely lost in mine code. I have to say that you know recursion much better than I can. Now I'm trying to figure out why subdirectories are missing in the list of folders in the last two directories. In any case, your programs are much clearer than mine.

I didn't realize one thing. That I have written the full path in array. So I'm not going to go back one directory level, so I don't need to write down the current nesting level. Now, looking at the BPlus and Steve code, I've figured it out. It just occurred to me now. I did it absolutely wrong.

I think, maybe... see to BPlus source. To MakeTree SUB. If is this SUB recursive running, then source list of arrays is deleted in next step (in recursive call), or not?
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 26, 2020, 12:44:03 pm
I just tried modified version of @SpriggsySpriggs, very nice!

According to his version, we are shooting for 607 sub-dirs, so 125 is definitely falling short.
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 26, 2020, 12:54:49 pm
@Petr

Glad to have cleared the "big head", working on your code did inspire me to try the recursive tree making.

Quote
I think, maybe... see to BPlus source. To MakeTree SUB. If is this SUB recursive running, then source list of arrays is deleted in next step (in recursive call), or not?

In recursive call (when setup properly), every new call to sub makes it's own set of variables including arrays, be careful with the calling parameters sometimes making a copy of them helps in case they get altered by code. Hey... is that what I missed?

The calls go down and down and down until no more down, then up one level and finish laterally, then up next level and finish that. I learned this by watching recursive drawings.

Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 26, 2020, 01:22:14 pm
I just tried modified version of @SpriggsySpriggs, very nice!
According to his version, we are shooting for 607 sub-dirs, so 125 is definitely falling short.
@bplus Perhaps not, actually. That version might have been bugged. Try this one:
Code: QB64: [Select]
  1. Path$ = _STARTDIR$
  2. SHELL _HIDE "PowerShell Get-ChildItem -Path \" + CHR$(34) + Path$ + "\" + CHR$(34) + " -Recurse -Directory -Force -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName | Format-Table -AutoSize | Out-File -FilePath directorylist.txt -Encoding ASCII"
  3. DIRECTORIES = FREEFILE
  4. n = 0
  5.  
  6. DIM Dirs(n) AS STRING
  7. OPEN "directorylist.txt" FOR BINARY AS #DIRECTORIES
  8.     LINE INPUT #DIRECTORIES, Directory$
  9.     Directory$ = LTRIM$(RTRIM$(String.Remove(Directory$, CHR$(13))))
  10.     n = n + 1
  11.     REDIM _PRESERVE Dirs(n)
  12.     Dirs(n) = Directory$
  13. LOOP UNTIL EOF(DIRECTORIES)
  14.  
  15. FOR i = 1 TO UBOUND(Dirs)
  16.     IF LEFT$(Dirs(i), _INSTRREV(Dirs(i), "\") - 1) <> LEFT$(Dirs(i - 1), _INSTRREV(Dirs(i - 1), "\") - 1) THEN
  17.         h = h + 1
  18.         a = Array_String.FirstOrDefault(Dirs(), LEFT$(Dirs(i), _INSTRREV(Dirs(i), "\") - 1), 1)
  19.         PRINT h, Dirs(a)
  20.     END IF
  21.  
  22.  
  23. FUNCTION String.Remove$ (a AS STRING, b AS STRING)
  24.     DIM c AS STRING
  25.     c = ""
  26.     j = INSTR(a, b)
  27.     IF j > 0 THEN
  28.         r$ = LEFT$(a, j - 1) + c + String.Remove(RIGHT$(a, LEN(a) - j + 1 - LEN(b)), b)
  29.     ELSE
  30.         r$ = a
  31.     END IF
  32.     String.Remove = r$
  33.  
  34. FUNCTION Array_String.FirstOrDefault (SearchArray() AS STRING, SearchString AS STRING, contains AS INTEGER)
  35.     FOR i = LBOUND(SearchArray) TO UBOUND(SearchArray)
  36.         IF contains = 1 THEN
  37.             IF INSTR(SearchArray(i), SearchString) THEN
  38.                 Array_String.FirstOrDefault = i
  39.                 EXIT FUNCTION
  40.             END IF
  41.         ELSEIF contains = 0 THEN
  42.             IF SearchArray(i) = SearchString THEN
  43.                 Array_String.FirstOrDefault = i
  44.                 EXIT FUNCTION
  45.             END IF
  46.         END IF
  47.     NEXT
Title: Re: Development of a program for automatic search for music and movies
Post by: Petr on June 26, 2020, 01:29:18 pm
Hi SpriggsySpriggs, your program create empty file directorylist.txt here.

Which PowerShell version is need? I use Win 7.
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 26, 2020, 01:31:39 pm
Hi SpriggsySpriggs, your program create empty file directorylist.txt here.
That would probably be because your PowerShell failed and didn't create a file. Don't end the Path$ with a backslash. The backslash is an escape character to escape the quotes in the SHELL string and pass the quoted path. If you add a backslash it fails unless you add two.
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 26, 2020, 02:24:04 pm
@bplus Perhaps not, actually. That version might have been bugged. Try this one:
Code: QB64: [Select]
  1. Path$ = _STARTDIR$
  2. SHELL _HIDE "PowerShell Get-ChildItem -Path \" + CHR$(34) + Path$ + "\" + CHR$(34) + " -Recurse -Directory -Force -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName | Format-Table -AutoSize | Out-File -FilePath directorylist.txt -Encoding ASCII"
  3. DIRECTORIES = FREEFILE
  4. n = 0
  5.  
  6. DIM Dirs(n) AS STRING
  7. OPEN "directorylist.txt" FOR BINARY AS #DIRECTORIES
  8.     LINE INPUT #DIRECTORIES, Directory$
  9.     Directory$ = LTRIM$(RTRIM$(String.Remove(Directory$, CHR$(13))))
  10.     n = n + 1
  11.     REDIM _PRESERVE Dirs(n)
  12.     Dirs(n) = Directory$
  13. LOOP UNTIL EOF(DIRECTORIES)
  14.  
  15. FOR i = 1 TO UBOUND(Dirs)
  16.     IF LEFT$(Dirs(i), _INSTRREV(Dirs(i), "\") - 1) <> LEFT$(Dirs(i - 1), _INSTRREV(Dirs(i - 1), "\") - 1) THEN
  17.         h = h + 1
  18.         a = Array_String.FirstOrDefault(Dirs(), LEFT$(Dirs(i), _INSTRREV(Dirs(i), "\") - 1), 1)
  19.         PRINT h, Dirs(a)
  20.     END IF
  21.  
  22.  
  23. FUNCTION String.Remove$ (a AS STRING, b AS STRING)
  24.     DIM c AS STRING
  25.     c = ""
  26.     j = INSTR(a, b)
  27.     IF j > 0 THEN
  28.         r$ = LEFT$(a, j - 1) + c + String.Remove(RIGHT$(a, LEN(a) - j + 1 - LEN(b)), b)
  29.     ELSE
  30.         r$ = a
  31.     END IF
  32.     String.Remove = r$
  33.  
  34. FUNCTION Array_String.FirstOrDefault (SearchArray() AS STRING, SearchString AS STRING, contains AS INTEGER)
  35.     FOR i = LBOUND(SearchArray) TO UBOUND(SearchArray)
  36.         IF contains = 1 THEN
  37.             IF INSTR(SearchArray(i), SearchString) THEN
  38.                 Array_String.FirstOrDefault = i
  39.                 EXIT FUNCTION
  40.             END IF
  41.         ELSEIF contains = 0 THEN
  42.             IF SearchArray(i) = SearchString THEN
  43.                 Array_String.FirstOrDefault = i
  44.                 EXIT FUNCTION
  45.             END IF
  46.         END IF
  47.     NEXT

This has obvious errors from get go InForm files. What did you think was buggy from first version?
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 26, 2020, 02:30:42 pm
Well I was getting
This has obvious errors from get go InForm files. What did you think was buggy from first version?
I don't get two InForm folders when I run it. Did you change any of the logic unintentionally? From my first version I would get around 700 directories. This version gets around 200 on mine right now. That seems to be more consistent with what you and Petr found.
Oh crap, I think I see something wrong. Maybe permissions? Apologies, let me investigate further.
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 26, 2020, 02:37:57 pm
Shoot, yeah there is something wrong. 700 folders actually is correct for what I should have on my PC. My logic is wrong in the loop. I'll post a different version later. So sorry!
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 26, 2020, 02:42:12 pm
@SpriggsySpriggs

I ran your 2nd version from a straight copy/paste from forum. It is possible the forum editor changed something.

BTW what determines the Console Width, man Spriggsy, yours looks to have acre's of room!

Don't go by my Tree, I was getting (125 sub-dirs), kind of fails to get more sub-Folders after License (that has no sub-dirs).

Time for an independent evaluation of QB64 folder, I am thinking your first was right. ;-) too!
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 26, 2020, 02:44:46 pm
@SpriggsySpriggs

I ran your 2nd version from a straight copy/paste from forum. It is possible the forum editor changed something.

BTW what determines the Console Width, man Spriggsy, yours looks to have acre's of room!

Don't go by my Tree, I was getting (125 sub-dirs), kind of fails to get more sub-Folders after License (that has no sub-dirs).

Time for an independent evaluation of QB64 folder, I am thinking your first was right. ;-) too!
The first one was literally just reading in the file and then printing the results. It doesn't quite follow the tree structure, though. I'm going to have to see about how to make PowerShell organize it as such or how to sort the array (argh). So yeah, more work :( Thank you for catching that issue.
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 26, 2020, 03:43:19 pm
@SpriggsySpriggs
I ran your 2nd version from a straight copy/paste from forum. It is possible the forum editor changed something.
@bplus It wasn't your fault at all. I neglected to sort the items. They were there all along! I sorted them using the SHELL with Sort-Object and now they are in the correct recursive order and with the 700~ directories. Check it out:
Code: QB64: [Select]
  1. Path$ = _STARTDIR$
  2. SHELL _HIDE "PowerShell Get-ChildItem -Path \" + CHR$(34) + Path$ + "\" + CHR$(34) + " -Recurse -Directory -Force -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName | Sort-Object | Format-Table -AutoSize | Out-File -FilePath directorylist.txt -Encoding ASCII"
  3. DIRECTORIES = FREEFILE
  4. n = 0
  5.  
  6. DIM Dirs(n) AS STRING
  7. OPEN "directorylist.txt" FOR BINARY AS #DIRECTORIES
  8.     LINE INPUT #DIRECTORIES, Directory$
  9.     Directory$ = LTRIM$(RTRIM$(STRING.Remove(Directory$, CHR$(13))))
  10.     n = n + 1
  11.     REDIM _PRESERVE Dirs(n)
  12.     Dirs(n) = Directory$
  13. LOOP UNTIL EOF(DIRECTORIES)
  14. CLOSE #DIRECTORIES
  15. FOR i = 1 TO UBOUND(Dirs)
  16.     PRINT i, Dirs(i)
  17.  
  18.  
  19. FUNCTION STRING.Remove$ (a AS STRING, b AS STRING)
  20.     DIM c AS STRING
  21.     c = ""
  22.     j = INSTR(a, b)
  23.     IF j > 0 THEN
  24.         r$ = LEFT$(a, j - 1) + c + STRING.Remove(RIGHT$(a, LEN(a) - j + 1 - LEN(b)), b)
  25.     ELSE
  26.         r$ = a
  27.     END IF
  28.     STRING.Remove = r$
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 26, 2020, 03:48:06 pm
@SpriggsySpriggs
BTW what determines the Console Width, man Spriggsy, yours looks to have acre's of room!
I just maximized the console window. I'm on a 24 inch monitor so I guess that's why I have so much space.
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 26, 2020, 04:11:17 pm
Ah maximize window, yeah that helps, only one Directory doesn't fit on line then for me.

I get 607 folders. I don't think I've added any folders to QB64 folder, plenty of files but no folders. What is the difference I wonder. Update: oh I thought it was 700+ the first time, no, same as first 607. OK consistency is good!

I've been messing with my recursive thing, got the recursive up to 267 files was it? but forgot what I did.
Man I am making little changes to variables and getting differences in counts, but nothing like 600 listings.

Oh! I might be loosing folders with decimal points!
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 26, 2020, 05:35:32 pm
Or, alternatively, this:
Code: QB64: [Select]
  1.  
  2. Path$ = "C:\Program Files (x86)\Steam\steamapps\common"
  3. SHELL _HIDE "PowerShell Tree \" + CHR$(34) + Path$ + "\" + CHR$(34) + " /a | Select-Object -Skip 2 | Set-Content tree.txt"
  4. tree = FREEFILE
  5. OPEN "tree.txt" FOR BINARY AS #tree
  6.     LINE INPUT #tree, branch$
  7.     PRINT branch$
  8. LOOP UNTIL EOF(tree)
  9. CLOSE #tree
  10. FUNCTION STRING.Remove$ (a AS STRING, b AS STRING)
  11.     DIM c AS STRING
  12.     c = ""
  13.     j = INSTR(a, b)
  14.     IF j > 0 THEN
  15.         r$ = LEFT$(a, j - 1) + c + STRING.Remove(RIGHT$(a, LEN(a) - j + 1 - LEN(b)), b)
  16.     ELSE
  17.         r$ = a
  18.     END IF
  19.     STRING.Remove = r$
PowerShell has a built in function for displaying folders and files in a tree format
 
 
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 26, 2020, 06:22:03 pm
Yes! I was trying to get that from something like: _SHELLHIDE "Tree / >Tree.txt"
but no file was getting created. My DOS command lines is rusty, maybe some CHR$(34)'s were needed?

I forgot that you didn't get full paths on each line.
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 26, 2020, 06:46:28 pm
@bplus; @Petr:

Repeat after me: "I'm a big dummy.  I'm a big dummy.  I'm a big dummy."   Repeat for the next several minutes, then read WHY we're all such failures with such simple code -- yours truly included....


C strings are NOT the same as QB64 strings!!!

Everyone knows this, and yet, we all let it bite us in the ass, in this case!

Let's take for example a simple call to our function like so:

Code: [Select]
cd$ = _CWD$ 'testing in QB64 folder
MakeTree cd$

This fails to perform as expected.  (In some ways, I'm surprised to find that it performs at all!)

WHY??

Because _CWD$ returns a QB64 formatted string, and *NOT* a string as we'd normally expect to find in C.  It's *not* null terminated!!

Maketree cd$ + CHR$(0) is probably what we need to get it to work, in all honesty!  At least, playing around with my own little recursive routine, it's what was needed to (mostly) fix things.  My problem now is that I'm capturing too many recursions, and rechecking everything over and over endlessly...  The exit to my loop is broken somewhere.  :P

Anyway, I'd think if you want to sort out the bugs in your routines, you might want to null terminate your strings, and then a lot of the issues might end up going away for you guys.  It's worth a shot anyways.  I'm going to keep playing around with my own little thing, and I'll keep you updated with what I come up with as I progress.  ;)
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 26, 2020, 06:58:12 pm
See if this works as expected for getting the directories:

Code: QB64: [Select]
  1. OPTION _EXPLICIT 'Tree builder recursive.bas  b+ Petr 2020-06-26
  2. DEFLNG A-Z
  3.     FUNCTION load_dir& (s AS STRING)
  4.     FUNCTION has_next_entry& ()
  5.     SUB close_dir ()
  6.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  7.  
  8.  
  9. SCREEN _NEWIMAGE(1280, 740, 32)
  10. _DELAY .25
  11. DIM cd$, i, w$
  12.  
  13.  
  14. MakeTree _CWD$ 'testing in QB64 folder
  15.  
  16. PRINT "Showing Tree"
  17. FOR i = 1 TO UBOUND(Tree) 'show tree
  18.     PRINT _TRIM$(STR$(i)); ": "; Tree(i)
  19.     IF i MOD 40 = 0 THEN INPUT "Press enter to continue... "; w$: CLS
  20.  
  21.  
  22. SUB GetSubDirs (SearchDirectory AS STRING)
  23.     CONST IS_DIR = 1
  24.     DIM flags AS LONG, file_size AS LONG, length, nam$
  25.     IF load_dir(SearchDirectory + CHR$(0)) THEN
  26.         DO
  27.             length = has_next_entry
  28.             IF length > -1 THEN
  29.                 nam$ = SPACE$(length)
  30.                 get_next_entry nam$, flags, file_size
  31.                 IF RIGHT$(nam$, 1) <> "." AND RIGHT$(nam$, 2) <> ".." THEN
  32.                     IF flags = IS_DIR OR _DIREXISTS(SearchDirectory + "\" + nam$) THEN
  33.                         REDIM _PRESERVE Tree(UBOUND(Tree) + 1)
  34.                         Tree(UBOUND(Tree)) = SearchDirectory + "\" + nam$
  35.                     END IF
  36.                 END IF
  37.             END IF
  38.         LOOP UNTIL length = -1
  39.     ELSE
  40.         PRINT "Dir not loaded"
  41.     END IF
  42.     close_dir
  43.  
  44. SUB MakeTree (Dir$)
  45.     DIM OnDir AS LONG
  46.     REDIM Tree(0) AS STRING
  47.     Tree(0) = Dir$
  48.     DO
  49.         GetSubDirs Tree(OnDir)
  50.         OnDir = OnDir + 1
  51.     LOOP UNTIL OnDir > UBOUND(Tree)
  52.  

Biggest and most important change??

As I pointed out above -- null terminate your strings:     IF load_dir(SearchDirectory + CHR$(0)) THEN


NOTE: This change should probably carry over to ANY routine which uses load_dir with direntry.h.  I think we've all been goofing on calling the function all this time, and I'm surprised nobody has noticed or found the glitch before now.  Multiple, repetitive calls, all in a row, is a good way to stress test these routines, I guess.  :D

Anywho....  Test it out, see if it looks and acts about like it should, and we'll debug and sort out any issues with things as we go.  ;D
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 26, 2020, 07:42:54 pm
Bravo! Steve! that did the trick alright.

Curiously, I get one more Directory with your code, that I like because you eliminated the need to CHDIR.
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 26, 2020, 10:10:11 pm
I forgot that you didn't get full paths on each line.
It doesn't matter because since you have the formatting of the tree, you always know what the path is. You would just have to write a function to parse that tree.
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 26, 2020, 10:18:06 pm
See if this works as expected for getting the directories:
@SMcNeill I'm thinking the tree structure isn't correct, Steve. Or maybe this is intentional but see my screenshot from running your code:
 

Shouldn't all the folders in that auto\ folder be together and not separated out? Same for include\
Something seems to be amiss in the sorting, perhaps.
It appears to do that in several spots from what I can see. I would have assumed that the tree structure puts all subdirectories of a folder with the parent folder and then move on to the next folder like my version:
 
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 26, 2020, 11:17:08 pm
Easy way to test that Spriggsy:  run both routines, sort them, and then compare line by line entries to see what's different (if anything).  To be honest, I'm not certain why there's jumps in the output like that either, yet.  ;)
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 26, 2020, 11:20:27 pm
Easy way to test that Spriggsy:  run both routines, sort them, and then compare line by line entries to see what's different (if anything).  To be honest, I'm not certain why there's jumps in the output like that either, yet.  ;)
Both programs give me the same number of directories. It's just a matter of the structure.
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 26, 2020, 11:42:43 pm
Both programs give me the same number of directories. It's just a matter of the structure.

It's just the sorting method.  I'm thinking, if you look close, my little routine finds the directories in levels deep that it goes.

....\auto\core is 14 levels deep.
....\build\vc10 is 14 levels deep.
....\auto\core\gl is 15 levels deep.

As long as it finds all the directories properly, they can be sorted and arraigned as desired later.  ;)
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 27, 2020, 12:12:56 am
Well the disadvantage of not using CHDIR is having to sort later?

This looks to be in pretty good order:
Code: QB64: [Select]
  1. 'Tree builder recursive.bas  b+ Petr 2020-06-26  now with SMCNeill Fix!! add CHR$(0) to C string
  2.     FUNCTION load_dir& (s AS STRING)
  3.     FUNCTION has_next_entry& ()
  4.     SUB close_dir ()
  5.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  6.  
  7. REDIM SHARED Tree(0) AS STRING '<<<<<<<<<<<<<<<<<<<<<< G(0) will remain empty
  8.  
  9. MakeTree _CWD$
  10. FOR i = 1 TO UBOUND(Tree) 'show tree
  11.     PRINT _TRIM$(STR$(i)); ": "; Tree(i)
  12.  
  13. SUB sAppend (arr() AS STRING, addItem$)
  14.     REDIM _PRESERVE arr(LBOUND(arr) TO UBOUND(arr) + 1) AS STRING
  15.     arr(UBOUND(arr)) = addItem$
  16.  
  17. SUB GetSubDirs (SearchDirectory AS STRING, DirList() AS STRING)
  18.     CONST IS_DIR = 1
  19.     DIM flags AS LONG, file_size AS LONG
  20.     IF load_dir(SearchDirectory + CHR$(0)) THEN 'Steve's fix here with CHR$(0) for C call
  21.         DO
  22.             length = has_next_entry
  23.             IF length > -1 THEN
  24.                 nam$ = SPACE$(length)
  25.                 get_next_entry nam$, flags, file_size
  26.                 IF (flags AND IS_DIR) OR _DIREXISTS(SearchDirectory + nam$) THEN
  27.                     IF nam$ <> "." AND nam$ <> ".." THEN
  28.                         DirCount = DirCount + 1
  29.                         IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  30.                         DirList(DirCount) = SearchDirectory + "\" + nam$
  31.                     END IF
  32.                 END IF
  33.             END IF
  34.         LOOP UNTIL length = -1
  35.     END IF
  36.     close_dir 'Steve first fix that got navigator working
  37.     REDIM _PRESERVE DirList(DirCount)
  38.  
  39. SUB MakeTree (startDir AS STRING)
  40.     REDIM D(100) AS STRING
  41.     GetSubDirs startDir, D()
  42.     FOR i = 1 TO UBOUND(D)
  43.         sAppend Tree(), D(i)
  44.         CHDIR D(i)
  45.         MakeTree D(i)
  46.     NEXT
  47.  
  48.  

 
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 12:18:34 am
As long as it finds all the directories properly, they can be sorted and arraigned as desired later.  ;)
Your logic is showing a file in the QB64 directory as a folder but is just a file without an extension.

\internal\c\c_compiler\licenses\libffi\LICENSE

I can run a sort on your file using a PowerShell command and the entire program takes around 6 seconds to complete from getting the directories to spitting out the sorted version.
If I run my program using the PowerShell version, it only takes 3 seconds because it is already sorted. I verified this by using the PowerShell script to sort my file and then compared it in Notepad ++. The files were identical.
EDIT: It looks like @bplus has solved the issue with using direntry
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 27, 2020, 12:22:40 am
I am impressed that Steve managed it without a recursive procedure.
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 12:28:37 am
@bplus I did a test to see which way was faster and it looks like your code and mine are quite close. Mine finished in 3.380 seconds whereas yours took 3.626 seconds. I also compared the files in Notepad++ and they were identical. Way to go! EDIT: I ran it again using only _TRIM$ on the value from the file and mine finished in 3.109 seconds. Only a half second's difference is quite impressive! I thought for sure that mine would lose against yours.
See screenshot:
 

 
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 27, 2020, 12:39:24 am
Quote
I thought for sure that mine would lose against yours.

Ha! mine was probably slowed down by all the CHDIR's. Your's is working under the Power of Power Shell ;-))
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 12:42:19 am
Ha! mine was probably slowed down by all the CHDIR's. Your's is working under the Power of Power Shell ;-))
I expected that with shelling out to PowerShell that I would lose precious time because I'm having to wait on an external process to complete. I know that when I generate my WPF Message Boxes from PowerShell they are much slower than calling a native message box (of course, mine are more graphical and the script is quite long. Eh, whatever)
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 27, 2020, 12:47:31 am
List retrieved and sorted in about 1 second, on my PC.

Code: QB64: [Select]
  1. DEFLNG A-Z
  2.     FUNCTION load_dir& (s AS STRING)
  3.     FUNCTION has_next_entry& ()
  4.     SUB close_dir ()
  5.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  6.  
  7.  
  8. SCREEN _NEWIMAGE(1280, 740, 32)
  9. _DELAY .25
  10. DIM cd$, i, w$
  11.  
  12. PRINT "Creating tree."
  13. t# = TIMER(0.001)
  14. MakeTree _CWD$ 'testing in QB64 folder
  15. PRINT "Tree created."
  16. PRINT USING "##.##### seconds creating and sorting tree."; TIMER - t#
  17. PRINT "Showing Tree"
  18. FOR i = 0 TO UBOUND(Tree) 'show tree
  19.     PRINT _TRIM$(STR$(i)); ": "; Tree(i)
  20.     IF i MOD 40 = 0 AND i > 0 THEN INPUT "Press enter to continue... "; w$: CLS
  21.  
  22.  
  23. SUB GetSubDirs (SearchDirectory AS STRING)
  24.     CONST IS_DIR = 1
  25.     DIM flags AS LONG, file_size AS LONG, length, nam$
  26.     IF load_dir(SearchDirectory + CHR$(0)) THEN
  27.         DO
  28.             length = has_next_entry
  29.             IF length > -1 THEN
  30.                 nam$ = SPACE$(length)
  31.                 get_next_entry nam$, flags, file_size
  32.                 IF RIGHT$(nam$, 1) <> "." AND RIGHT$(nam$, 2) <> ".." THEN
  33.                     IF flags = IS_DIR OR _DIREXISTS(SearchDirectory + "\" + nam$) THEN
  34.                         REDIM _PRESERVE Tree(UBOUND(Tree) + 1)
  35.                         Tree(UBOUND(Tree)) = SearchDirectory + "\" + nam$
  36.                     END IF
  37.                 END IF
  38.             END IF
  39.         LOOP UNTIL length = -1
  40.     ELSE
  41.         PRINT "Dir not loaded"
  42.     END IF
  43.     close_dir
  44.  
  45. SUB MakeTree (Dir$)
  46.     DIM OnDir AS LONG, gap AS LONG, i AS LONG, swapped AS LONG
  47.     REDIM Tree(0) AS STRING
  48.     Tree(0) = Dir$
  49.     DO
  50.         GetSubDirs Tree(OnDir)
  51.         OnDir = OnDir + 1
  52.     LOOP UNTIL OnDir > UBOUND(Tree)
  53.     gap = UBOUND(Tree)
  54.     DO
  55.         gap = 10 * gap \ 13
  56.         IF gap < 1 THEN gap = 1
  57.         i = 0
  58.         swapped = 0
  59.         DO
  60.             IF Tree(i) > Tree(i + gap) THEN
  61.                 SWAP Tree(i), Tree(i + gap)
  62.                 swapped = -1
  63.             END IF
  64.             i = i + 1
  65.         LOOP UNTIL i + gap > UBOUND(Tree)
  66.     LOOP UNTIL gap = 1 AND swapped = 0
  67.  
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 12:51:45 am
List retrieved and sorted in about 1 second, on my PC.
@SMcNeill
Steve,
I was timing the printing of the output as well. Not only the creation of the array. When I run your code and put the timers in the same spot as I did for bplus and myself, I get 4.301 seconds.
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 27, 2020, 12:52:49 am
Your logic is showing a file in the QB64 directory as a folder but is just a file without an extension.

\internal\c\c_compiler\licenses\libffi\LICENSE

Running on my machine, I'm not getting this false result.  What system are you running under?  Is this difference something in 32-bit versions of QB64 vs 64-bit?  Linux vs Windows?  On Win 10, QB64x64, I'm not seeing LICENSE in the file listing.
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 12:53:40 am
Running on my machine, I'm not getting this false result.  What system are you running under?  Is this difference something in 32-bit versions of QB64 vs 64-bit?  Linux vs Windows?  On Win 10, QB64x64, I'm not seeing LICENSE in the file listing.
I'm running QB64 x64 v1.4 on Windows 10 build 1909
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 27, 2020, 12:56:39 am
@SMcNeill
Steve,
I was timing the printing of the output as well. Not only the creation of the array. When I run your code and put the timers in the same spot as I did for bplus and myself, I get 4.301 seconds.

I'm still only getting about 1.2 seconds to get, sort, and print the listing.  I have no idea why it'd take 3+ seconds to print the results on your machine.  Something seems off there.

Code: QB64: [Select]
  1. DEFLNG A-Z
  2.     FUNCTION load_dir& (s AS STRING)
  3.     FUNCTION has_next_entry& ()
  4.     SUB close_dir ()
  5.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  6.  
  7.  
  8. SCREEN _NEWIMAGE(1280, 740, 32)
  9. _DELAY .25
  10. DIM cd$, i, w$
  11.  
  12. PRINT "Creating tree."
  13. t# = TIMER(0.001)
  14. MakeTree _CWD$ 'testing in QB64 folder
  15. PRINT "Showing Tree"
  16. FOR i = 0 TO UBOUND(Tree) 'show tree
  17.     PRINT i; ": "; Tree(i)
  18. PRINT USING "##.##### seconds creating, printing, and sorting tree."; TIMER - t#
  19.  
  20.  
  21. SUB GetSubDirs (SearchDirectory AS STRING)
  22.     CONST IS_DIR = 1
  23.     DIM flags AS LONG, file_size AS LONG, length, nam$
  24.     IF load_dir(SearchDirectory + CHR$(0)) THEN
  25.         DO
  26.             length = has_next_entry
  27.             IF length > -1 THEN
  28.                 nam$ = SPACE$(length)
  29.                 get_next_entry nam$, flags, file_size
  30.                 IF RIGHT$(nam$, 1) <> "." AND RIGHT$(nam$, 2) <> ".." THEN
  31.                     IF flags = IS_DIR OR _DIREXISTS(SearchDirectory + "\" + nam$) THEN
  32.                         REDIM _PRESERVE Tree(UBOUND(Tree) + 1)
  33.                         Tree(UBOUND(Tree)) = SearchDirectory + "\" + nam$
  34.                     END IF
  35.                 END IF
  36.             END IF
  37.         LOOP UNTIL length = -1
  38.     ELSE
  39.         PRINT "Dir not loaded"
  40.     END IF
  41.     close_dir
  42.  
  43. SUB MakeTree (Dir$)
  44.     DIM OnDir AS LONG, gap AS LONG, i AS LONG, swapped AS LONG
  45.     REDIM Tree(0) AS STRING
  46.     Tree(0) = Dir$
  47.     DO
  48.         GetSubDirs Tree(OnDir)
  49.         OnDir = OnDir + 1
  50.     LOOP UNTIL OnDir > UBOUND(Tree)
  51.     gap = UBOUND(Tree)
  52.     DO
  53.         gap = 10 * gap \ 13
  54.         IF gap < 1 THEN gap = 1
  55.         i = 0
  56.         swapped = 0
  57.         DO
  58.             IF Tree(i) > Tree(i + gap) THEN
  59.                 SWAP Tree(i), Tree(i + gap)
  60.                 swapped = -1
  61.             END IF
  62.             i = i + 1
  63.         LOOP UNTIL i + gap > UBOUND(Tree)
  64.     LOOP UNTIL gap = 1 AND swapped = 0
  65.  

Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 12:58:10 am
I'm still only getting about 1.2 seconds to get, sort, and print the listing.  I have no idea why it'd take 3+ seconds to print the results on your machine.  Something seems off there.
I'm placing the timer variable at the beginning of the program. First line for both mine and bplus's. Then showing the time for everything being completed at the end after everything has been printed completely. If I run your code you just posted I still get an output slower than both mine and bplus's at a speed of 3.709 seconds. I also have many more directories than you and bplus do (about 1300) , so that's why I'm in the 3 seconds + range on my PC. This time, running your code, your program failed to find two directories:
internal\c\c_compiler\x86_64-w64-mingw32\include\GL
internal\c\c_compiler\x86_64-w64-mingw32\lib\ldscripts

It found them, but for some reason, the strings are showing as being different from the ones that I have. If I try finding the directory in your file it can't find it unless I trim off some of the directory. Hmm. I'll need to investigate further. When I compare your file and mine, it shows that it can't find those two directories in your file. Something is definitely different about the characters. EDIT: After reinvestigating and sorting your file with PowerShell so ours would have the same structure, it no longer showed those two missing when comparing them. The way your program gets the directory string must have some different characters in there from what PowerShell provides. I'm exporting my strings as ASCII from PowerShell. What encoding are your strings?

And, your program still finds the libffi\LICENSE file as a folder.
Also, the sort isn't sorting in the order I would expect. After InForm I would expect internal to be next as it is the next folder alphabetically, even in File Explorer. However, yours goes to LICENSE then jumps to my folder called QB64Library then goes to internal. I guess you are sorting alphabetically by capital letters first, then lowercase?
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 27, 2020, 01:34:04 am
\internal\c\c_compiler\licenses\libffi\LICENSE doesn't come up in the listing as a file for me.  Perhaps you could do a manual check and see what QB64 calls it on your machine.

Code: [Select]
IF _DIREXISTS("internal\c\c_compiler\licenses\libffi\LICENSE") THEN
    PRINT "QB64 is recognizing this as a directory on my PC, for some odd reason."
ELSE
    PRINT "It either doesn't exist, as I typed it in, or else QB64 recognizes it as a file."
    IF _FILEEXISTS("internal\c\c_compiler\licenses\libffi\LICENSE") THEN
        PRINT "QB64 found it, recognized it as a file."
    ELSE
        PRINT "File not found at all, as entered into the search parameters."
    END IF
END IF

We're relying on QB64 to basically tell us if it's a file or a directory, and for some odd reason, you see to be generating a false positive on your machine.  I dunno if that's a glitch in the version of QB64 you're using, something odd that Windows is reporting wrong, or where the problem actually comes from, but it's not from the direntry routines themselves. 



As for sorting, I just did a basic combsort alphabetically, which places uppercase separate from lowercase.  If you want to sort without comparing case, change the sort to using _STRICMP instead of the simple > which I tossed in there.  ;)
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 01:39:22 am
As for sorting, I just did a basic combsort alphabetically, which places uppercase separate from lowercase.  If you want to sort without comparing case, change the sort to using _STRICMP instead of the simple > which I tossed in there.  ;)
I did this and it sorts incorrectly once more, placing some subdirectories in the wrong spots on the tree. For instance, this is how it should sort:
x86_64-w64-mingw32\lib
x86_64-w64-mingw32\lib\ldscripts
x86_64-w64-mingw32\lib32\

How your program sorts using _STRICMP:
x86_64-w64-mingw32\lib
x86_64-w64-mingw32\lib32\
x86_64-w64-mingw32\lib\ldscripts

Also, see below from testing your _DIREXISTS for that file/folder issue: (And yes, it is a file. I opened it in Notepad to be sure)
 

The issue appears to be your flag IS_DIR. If I remove that from the check to see if it is a directory, it doesn't show up. It only appears if I leave the flag as an argument to your IF statement for determining a directory.
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 27, 2020, 01:41:56 am
Well the disadvantage of not using CHDIR is having to sort later?

It's not the lack of CHDIR which dictates the order which mine shows up in -- its the fact that mine's a non-recursive routine, compared to yours.  Mine does one whole level first, before going to the next depth of directories and then doing them next.  ;)

Title: Re: Development of a program for automatic search for music and movies
Post by: codeguy on June 27, 2020, 03:59:16 am
When you can match or beat Search Everything at this, let me know. https://www.voidtools.com/ (https://www.voidtools.com/).

Modified your code slightly, Steve
Code: [Select]
DEFLNG A-Z
DECLARE CUSTOMTYPE LIBRARY ".\direntry"
    FUNCTION load_dir& (s AS STRING)
    FUNCTION has_next_entry& ()
    SUB close_dir ()
    SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
END DECLARE

REDIM SHARED Tree(0) AS STRING

SCREEN _NEWIMAGE(1280, 740, 32)
_DELAY .25
_SCREENMOVE 80, 0
DIM cd$, i, w$

PRINT "Creating tree."
t# = TIMER(0.001)
MakeTree _CWD$ 'testing in QB64 folder
PRINT "Tree created."
PRINT USING "##.##### seconds creating and sorting tree."; TIMER - t#
SLEEP
CLS
_KEYCLEAR
PRINT "Showing Tree"
FOR i = 0 TO UBOUND(Tree) 'show tree
    PRINT _TRIM$(STR$(i)); ": "; Tree(i)
    IF i MOD 40 = 0 AND i > 0 THEN INPUT "Press enter to continue... "; w$: CLS
NEXT


SUB GetSubDirs (SearchDirectory AS STRING)
    CONST IS_DIR = 1
    DIM flags AS LONG, file_size AS LONG, length, nam$
    IF load_dir(SearchDirectory + CHR$(0)) THEN
        DO
            length = has_next_entry
            IF length > -1 THEN
                nam$ = SPACE$(length)
                get_next_entry nam$, flags, file_size
                IF RIGHT$(nam$, 1) <> "." AND RIGHT$(nam$, 2) <> ".." THEN
                    IF flags = IS_DIR OR _DIREXISTS(SearchDirectory + "\" + nam$) THEN
                        REDIM _PRESERVE Tree(UBOUND(Tree) + 1)
                        Tree(UBOUND(Tree)) = SearchDirectory + "\" + nam$
                    END IF
                END IF
            END IF
        LOOP UNTIL length = -1
    ELSE
        PRINT "Dir not loaded"
    END IF
    close_dir
END SUB

SUB MakeTree (Dir$)
    DIM OnDir AS LONG
    REDIM Tree(0) AS STRING
    Tree(0) = Dir$
    DO
        GetSubDirs Tree(OnDir)
        OnDir = OnDir + 1
    LOOP UNTIL OnDir > UBOUND(Tree)
    QuickSortIterativeString Tree(), LBOUND(tree), OnDir - 1, 1
    'gap = UBOUND(Tree)
    'DO
    '    gap = 10 * gap \ 13
    '    IF gap < 1 THEN gap = 1
    '    i = 0
    '    swapped = 0
    '    DO
    '        IF Tree(i) > Tree(i + gap) THEN
    '            SWAP Tree(i), Tree(i + gap)
    '            swapped = -1
    '        END IF
    '        i = i + 1
    '    LOOP UNTIL i + gap > UBOUND(Tree)
    'LOOP UNTIL gap = 1 AND swapped = 0
END SUB

SUB QuickSortIterativeString (CGSortLibArr() AS String, QSIStart AS LONG, QSIFinish AS LONG, order&)
    DIM QSI_Local_Compare AS String '* MUST be same type as element of CGSortLibArr()
    '* These MUST be the appropriate type for the range being sorted
    DIM QSI_Local_I AS LONG
    DIM QSI_local_J AS LONG
    DIM QSI_Local_Hi AS LONG
    DIM QSI_Local_Low AS LONG
    DIM QSI_Local_Mid AS LONG
    '****************************************************************

    '* Integer suffices for QSI_Local_MinStackPtr unless you're sorting more than 2^32767 elements.
    DIM QSI_Local_MinStackPtr AS INTEGER: QSI_Local_MinStackPtr = 0
    DIM QSI_Local_QSI_local_CurrentStackPtr AS INTEGER: QSI_Local_QSI_local_CurrentStackPtr = 0
    DIM QSI_Local_FinishMinusStart AS LONG: QSI_Local_FinishMinusStart = QSIFinish - QSIStart
    DIM QSI_local_Remainder AS INTEGER

    '* yes, the equation log(QSIfinish-QSIstart)/log(2)+1 works too
    DO
        QSI_local_Remainder = QSI_Local_FinishMinusStart - (2 * INT(QSI_Local_FinishMinusStart / 2))
        QSI_Local_FinishMinusStart = (QSI_Local_FinishMinusStart - QSI_local_Remainder) / 2
        QSI_Local_MinStackPtr = QSI_Local_MinStackPtr + 1
    LOOP UNTIL QSI_Local_FinishMinusStart < 1

    '* MUST be appropriate type to handle the range (QSIfinish-QSIstart) being sorted
    DIM QSI_LStack(0 TO QSI_Local_MinStackPtr, 0 TO 1) AS LONG

    QSI_local_CurrentStackPtr = 0
    QSI_LStack(QSI_local_CurrentStackPtr, 0) = QSIStart
    QSI_LStack(QSI_local_CurrentStackPtr, 1) = QSIFinish
    DO
        QSI_Local_Low = QSI_LStack(QSI_local_CurrentStackPtr, 0)
        QSI_Local_Hi = QSI_LStack(QSI_local_CurrentStackPtr, 1)
        DO
            QSI_Local_I = QSI_Local_Low
            QSI_local_J = QSI_Local_Hi
            QSI_Local_Mid = QSI_Local_Low + (QSI_Local_Hi - QSI_Local_Low) \ 2
            QSI_Local_Compare = CGSortLibArr(QSI_Local_Mid)
            SELECT CASE order&
                CASE 1
                    DO
                        DO WHILE CGSortLibArr(QSI_Local_I) < QSI_Local_Compare
                            QSI_Local_I = QSI_Local_I + 1
                        LOOP
                        DO WHILE CGSortLibArr(QSI_local_J) > QSI_Local_Compare
                            QSI_local_J = QSI_local_J - 1
                        LOOP
                        IF QSI_Local_I <= QSI_local_J THEN
                            SWAP CGSortLibArr(QSI_Local_I), CGSortLibArr(QSI_local_J)
                            QSI_Local_I = QSI_Local_I + 1
                            QSI_local_J = QSI_local_J - 1
                        END IF
                    LOOP UNTIL QSI_Local_I > QSI_local_J
                CASE ELSE
                    DO
                        DO WHILE CGSortLibArr(QSI_Local_I) > QSI_Local_Compare
                            QSI_Local_I = QSI_Local_I + 1
                        LOOP
                        DO WHILE CGSortLibArr(QSI_local_J) < QSI_Local_Compare
                            QSI_local_J = QSI_local_J - 1
                        LOOP
                        IF QSI_Local_I <= QSI_local_J THEN
                            SWAP CGSortLibArr(QSI_Local_I), CGSortLibArr(QSI_local_J)
                            QSI_Local_I = QSI_Local_I + 1
                            QSI_local_J = QSI_local_J - 1
                        END IF
                    LOOP UNTIL QSI_Local_I > QSI_local_J
            END SELECT
            IF QSI_local_J - QSI_Local_Low < QSI_Local_Hi - QSI_Local_I THEN
                IF QSI_Local_I < QSI_Local_Hi THEN
                    QSI_LStack(QSI_local_CurrentStackPtr, 0) = QSI_Local_I
                    QSI_LStack(QSI_local_CurrentStackPtr, 1) = QSI_Local_Hi
                    QSI_local_CurrentStackPtr = QSI_local_CurrentStackPtr + 1
                END IF
                QSI_Local_Hi = QSI_local_J
            ELSE
                IF QSI_Local_Low < QSI_local_J THEN
                    QSI_LStack(QSI_local_CurrentStackPtr, 0) = QSI_Local_Low
                    QSI_LStack(QSI_local_CurrentStackPtr, 1) = QSI_local_J
                    QSI_local_CurrentStackPtr = QSI_local_CurrentStackPtr + 1
                END IF
                QSI_Local_Low = QSI_Local_I
            END IF
        LOOP WHILE QSI_Local_Low < QSI_Local_Hi
        QSI_local_CurrentStackPtr = QSI_local_CurrentStackPtr - 1
    LOOP UNTIL QSI_local_CurrentStackPtr < 0
END SUB
Title: Re: Development of a program for automatic search for music and movies
Post by: Petr on June 27, 2020, 06:35:40 am
Thank you all for your generous and successful help. How I want to continue: (if anyone has any suggestions on how to do it even better, write it here)

Now that we get the valid subdirectory names (and thank you again), I want to go through the individual folders and do list the files in each of them. If the file in a particular folder contains a name with a valid mask extension (this is at the beginning of this thread), the file name, including the path, will be added to the field.

After I get the valid names of all these files by mask, I will open each individual file for reading in binary mode and retrieve some information from the files (according to the file type), I will need to study the entries in the heads of specific files or, if someone knows about to any library that would do this for us, it would be another great easing of work. This information is:
- ID3 TAG - records in music files such as WAV, MP3, MP2, MP4 (I have already written this program, but it does not support all possible tags, it will need a heavy upgrade), but it can extract a photo from the file, if it is attached,
- sound length (this is the simplest, QB64 returns it to us with its own function)
- sound format (in some music formats it will be a purely sadomasochistic work)
- resolution (for video formats)
- number of frames per second
and other information, as required. Of course, this is a long job.

It would also like to set a rule for related photos right at the beginning. Suppose you want to add a photo of the band to the list (in this band folder), this photo will not be part of ID3. What rules do we set? Does the photo have to have the same name as its folder? Or will any photo in this folder be used? Or make it optional?

As you can see, there is still a lot.
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 10:10:21 am
This information is:
- ID3 TAG - records in music files such as WAV, MP3, MP2, MP4 (I have already written this program, but it does not support all possible tags, it will need a heavy upgrade), but it can extract a
Which tags do you already have code to get? Here is code I have for tags:
Code: QB64: [Select]
  1. SUB GetSongTags (OFile$)
  2.     g% = FREEFILE
  3.     OPEN OFile$ FOR BINARY AS #g%
  4.  
  5.     DIM Songname AS STRING * 30
  6.     DIM Artist AS STRING * 30
  7.     DIM Album AS STRING * 30
  8.     DIM Year AS STRING * 4
  9.     DIM position AS SINGLE
  10.  
  11.     position = LOF(g%) - 124
  12.     GET #g%, position, Songname
  13.     position = LOF(g%) - 94
  14.     GET #g%, position, Artist
  15.     position = LOF(g%) - 64
  16.     GET #g%, position, Album
  17.     position = LOF(g%) - 34
  18.     GET #g%, position, Year
  19.     CLOSE #g%
  20.     SongTitle$ = Songname
  21.     Caption(TitleLB) = "Title: " + (Songname)
  22.     Caption(ArtistLB) = "Artist: " + (Artist)
  23.     Caption(AlbumLB) = "Album: " + (Album)
  24.     Caption(YearLB) = "Year: " + (Year)
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 27, 2020, 11:19:13 am
Here is the timings I get on my Windows 10 System (not brand new) putting everyone's in Console window and timing from start to print of listing.

Did you guys figure out already why Steve shows an extra folder?

BTW Steve's sorting is case sensitive and Windows is not.

 


Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 11:23:48 am
Here is the timings I get on my Windows 10 System (not brand new) putting everyone's in Console window and timing from start to print of listing.

Did you guys figure out already why Steve shows an extra folder?

 

@bplus
As for the speed, less directories will mean that your function will be faster than mine because I have to shell out to PowerShell. You can shave off some time by replacing my LTRIM$ and RTRIM with just _TRIM$. I shaved half a second with that. More directories, like the 1300 I have, will do better on mine because PowerShell quickly catches up on a larger process as proven by only having 3.109 seconds for 1300 directories where your computer spent that long on half that. As for the extra file, it's because of his flag IS_DIR. When I remove it, I don't have that file giving a false positive as a folder. It only shows up when I leave his flag in the statement.
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 27, 2020, 12:20:55 pm
@bplus
As for the speed, less directories will mean that your function will be faster than mine because I have to shell out to PowerShell. You can shave off some time by replacing my LTRIM$ and RTRIM with just _TRIM$. I shaved half a second with that. More directories, like the 1300 I have, will do better on mine because PowerShell quickly catches up on a larger process as proven by only having 3.109 seconds for 1300 directories where your computer spent that long on half that. As for the extra file, it's because of his flag IS_DIR. When I remove it, I don't have that file giving a false positive as a folder. It only shows up when I leave his flag in the statement.

I just spent morning going over the tiny differences between Steve's, SUB GetSubDirs, the one that makes the calls to DirEntry.h

This line is very, very interesting!
Code: QB64: [Select]
  1. IF flags = IS_DIR OR _DIREXISTS(SearchDirectory + "\" + nam$) THEN
my recursive code really only needs If flags = IS_DIR (original it was IF flags AND IS_DIR, either way works)

Steves basically only needs IF _DIREXISTS(SearchDirectory + "\" + nam$) THEN
and the extra Directory, the false positive, is eliminated! without flags = IS_DIR
his won't even work without checking if _DIREXISTS, mine wont work without checking flags = IS_DIR

What is interesting is that when I don't double check _DIREXISTS(SearchDirectory + "\" + nam$)
my run time is significantly reduced!!! and I still get the 607 files on my system without the false positive.

I cut about a half second off my build and print time and if I can do this without changing directories (which should be quite possible) will have another big time savings.
Title: Re: Development of a program for automatic search for music and movies
Post by: Petr on June 27, 2020, 12:26:08 pm
Quote
Which tags do you already have code to get? Here is code I have for tags:

Hi, SpriggsySpriggs i find my source code now. It is from 7.3.2017 :) so it is really not fresh in my memory.
Here:

Code: QB64: [Select]
  1. 'After writing ID3v1.1 decoder for Qb64 I wrote this decoder ID3TAGV2.3.0 for QB64. I want in first thank Clippy for sharing his program, which dealt with the same thing in another way.
  2. 'Hardest of all was to understand how the author ID3TAG thinks with the calculation of the size HEAD in binary code. If he wrote it straight - sort by itself without the eighth bit and convert to decimal form,
  3. 'probably would have consumed much paper. That gave me the most work.
  4.  
  5. 'this is extended version. Program first use V2.3.0 program. IF this ends with error (no record found), is automatically runned upgraded version ID3V1.1. Only when one can not find even a record
  6. 'identifier TAG, then the program will report that in the file is none ID3 and ends. Read NOT ID3V2.2 (V2.2 is with 3 characters indetificator), but read ID3V2.4
  7.  
  8.  
  9.  
  10. _TITLE "ID3V2.3 PLUS ID3V1.1 (V1.1 upgraded) Reader"
  11. INPUT "Input MP3 filename without extension to view ID3:"; file$
  12. file$ = file$ + ".MP3"
  13. 'file$ = "14.mp3" 's.mp3 hav e APIC.
  14. IF _FILEEXISTS(file$) = 0 THEN PRINT "Not found": SLEEP: SYSTEM
  15.  
  16. TYPE ID3HEAD
  17.     Identifier AS STRING * 3
  18.     VersionMajor AS STRING * 1 '                                           Classical way - ASC number of this characters in file is Version number
  19.     VersionRevis AS STRING * 1
  20.     BinarFlags AS _UNSIGNED _BYTE
  21.     BinarSizeA AS STRING * 1
  22.     BinarSizeB AS STRING * 1
  23.     BinarSizeC AS STRING * 1
  24.     BinarSizeD AS STRING * 1
  25.  
  26. TYPE FRAME
  27.     Id AS STRING * 4
  28.     SizeA AS STRING * 1
  29.     SizeB AS STRING * 1
  30.     SizeC AS STRING * 1
  31.     SizeD AS STRING * 1
  32.     Flags AS _UNSIGNED INTEGER
  33.  
  34. DIM SHARED ID3 AS ID3HEAD, FRM AS FRAME
  35. DIM OUTList(150) AS STRING * 80 '                                                 output list with infos from ID3TAG
  36.  
  37. OPEN file$ FOR BINARY AS #1
  38. GET #1, , ID3
  39.  
  40. IF ID3.Identifier$ <> "ID3" THEN PRINT "ID3 mark not found": IDENTIT$ = "ID3V1.1": GOTO ID3V11 ' BEEP: PRINT ID3.Identifier$: SLEEP: SYSTEM
  41. Version$ = "2." + LTRIM$(RTRIM$(STR$(ASC(ID3.VersionMajor$)))) + "." + LTRIM$(STR$(ASC(ID3.VersionRevis$))): PRINT "Detected ID3 v."; Version$: IF Version$ <> "2.3.0" THEN PRINT "WARNING, WRONG VERSION, THIS IS V2.3!!!"
  42. IDENTIT$ = Version$
  43. PRINT "Binaries (bites) in FLAGS: ";: PRINT DECtoBIN$(ID3.BinarFlags);: PRINT " HEX: ";: PRINT BINtoHEX$(DECtoBIN$(ID3.BinarFlags))
  44. Ba$ = DECtoBIN$(ASC(ID3.BinarSizeA$))
  45. Bb$ = DECtoBIN$(ASC(ID3.BinarSizeB$)) '                                     FUNCTION converted decimal numbers to binar numbers
  46. Bc$ = DECtoBIN$(ASC(ID3.BinarSizeC$))
  47. Bd$ = DECtoBIN$(ASC(ID3.BinarSizeD$))
  48.  
  49.  
  50. PRINT "Binaries (bites) in SizeRecordA Byte: "; Ba$;: PRINT " HEX: ";: PRINT HEX$(ASC(ID3.BinarSizeA$));: PRINT " DEC: "; ID3.BinarSizeA$; " ("; ASC(ID3.BinarSizeA$); ")"
  51. PRINT "Binaries (bites) in SizeRecordB Byte: "; Bb$;: PRINT " HEX: ";: PRINT HEX$(ASC(ID3.BinarSizeB$));: PRINT " DEC: "; ID3.BinarSizeB$; " ("; ASC(ID3.BinarSizeB$); ")" ' It is such a pleasantry
  52. PRINT "Binaries (bites) in SizeRecordC Byte: "; Bc$;: PRINT " HEX: ";: PRINT HEX$(ASC(ID3.BinarSizeC$));: PRINT " DEC: "; ID3.BinarSizeC$; " ("; ASC(ID3.BinarSizeC$); ")"
  53. PRINT "Binaries (bites) in SizeRecordD Byte: "; Bd$;: PRINT " HEX: ";: PRINT HEX$(ASC(ID3.BinarSizeD$));: PRINT " DEC: "; ID3.BinarSizeD$; " ("; ASC(ID3.BinarSizeD$); ")"
  54. headd$ = RIGHT$(Ba$, 7) + RIGHT$(Bb$, 7) + RIGHT$(Bc$, 7) + RIGHT$(Bd$, 7) '            BINARY WAR
  55. h = HEAD(headd$) / 2
  56. PRINT "Head size calculated AS STRING: "; h; " bytes"
  57.  
  58. IF VAL(LEFT$(DECtoBIN$(ID3.BinarFlags), 1)) = 0 THEN PRINT "Synchronisation is not used" ELSE PRINT "Synchronisation is used"
  59. IF VAL(RIGHT$(LEFT$(DECtoBIN$(ID3.BinarFlags), 1), 1)) = 0 THEN PRINT "Extended header for ID3TAG is not used" ELSE PRINT "Extended header for ID3TAG is used - not supported by this program"
  60. IF VAL(RIGHT$(LEFT$(DECtoBIN$(ID3.BinarFlags), 2), 1)) = 1 THEN PRINT "Experimental ID3 TAG!" 'VAL is possile to use because strings contains zero or one
  61.  
  62.  
  63. '----------- cteni framu --------------               frames reading
  64. w = 0 '                                                                    FRAME head is 10 bytes long. 4 byte = name "AENC" or other, 4 byte size, 2 byte flags. Size is calculated as ASC sum of all four
  65. home: '                                                                    bytes.
  66. GET #1, , FRM '                                                             hlava ma 10 bytu: 4 byty jmeno, 4 byty velikost, 2 byty flags. Vse je psano klasicky hexadecimalne, uz zadny nesmysly s bitama.
  67. '                                                                                                   minimalni krok k dalsimu zaznamu ma byt 11 bytu (1byt je minimum pro kazdy identifikator, 10 byt
  68. '                                                                                                  je velikost hlavy. Velikost framu je dana souctem ASC ctyr bytu v hlave Framu.
  69. FrameSize = ASC(FRM.SizeA$) + ASC(FRM.SizeB$) + ASC(FRM.SizeC$) + ASC(FRM.SizeD$)
  70. InFrame$ = SPACE$(FrameSize) 'this statement using is Clippy method, thank Clippy!
  71. GET #1, , InFrame$
  72. q$ = FRM.Id$
  73. SELECT CASE FRM.Id$ '   -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - X =  HAVE OWN SECOND HEAD and can be more defined
  74.     CASE "AENC": q$ = "Audio encryption:" '                    X
  75.     CASE "APIC": q$ = "Attached Picture:": APICSUB: GOTO home 'X
  76.     CASE "COMM": q$ = "Comments:" '                            X
  77.     CASE "COMR": q$ = "Commercial frame:" '                    X
  78.     CASE "ENCR": q$ = "Encryption method:" '                   X
  79.     CASE "EQUA": q$ = "Equalization:" '                        X
  80.     CASE "ETCO": q$ = "Event timing codes:" '                  X
  81.     CASE "GEOB": q$ = "General encapsulated object:" '         X
  82.     CASE "GRID": q$ = "Group identification registration:" '   X
  83.     CASE "IPLS": q$ = "Involved people list:"
  84.     CASE "LINK": q$ = "Linked information:" '                  X
  85.     CASE "MCDI": q$ = "Music CD identifier:" '                 X
  86.     CASE "MLLT": q$ = "MPEG location lookup table:" '          X
  87.     CASE "OWNE": q$ = "Ownership frame:" '                     X
  88.     CASE "PRIV": q$ = "Private frame:" '                       X
  89.     CASE "PCNT": q$ = "Play counter:" '                        X
  90.     CASE "POPM": q$ = "Popularimeter:"
  91.     CASE "POSS": q$ = "Position synchronisation frame:" '      X
  92.     CASE "RBUF": q$ = "Recommended buffer size:" '             X
  93.     CASE "RVAD": q$ = "Relative volume adjustment:" '          X
  94.     CASE "RVRB": q$ = "Reverb" '                               X
  95.     CASE "SYLT": q$ = "Synchronized lyric / text:"
  96.     CASE "SYTC": q$ = "Synchronized tempo codes:" '            X
  97.     CASE "TALB": q$ = "Album / Movie / Show title:"
  98.     CASE "TBPM": q$ = "Beats per minute:"
  99.     CASE "TCOM": q$ = "Composer: "
  100.     CASE "TCON": q$ = "Content type:" '                        X - number = style as V1.1
  101.     CASE "TCOP": q$ = "Copyright message:"
  102.     CASE "TDAT": q$ = "Date:" '                                    numeric record always 4 bytes long
  103.     CASE "TDLY": q$ = "Playlist delay:"
  104.     CASE "TENC": q$ = "Encoded by:"
  105.     CASE "TEXT": q$ = "Lyricist / Text Writer"
  106.     CASE "TFLT": q$ = "File type:" '                           X -  1/2/3/2.5(MPGs)/AAC/VQF/PCM
  107.     CASE "TIME": q$ = "Time:" '                                      format HH:MM
  108.     CASE "TIT1": q$ = "Content group description:"
  109.     CASE "TIT2": q$ = "Title / songname:"
  110.     CASE "TIT3": q$ = "Subtitle / Description refinement:"
  111.     CASE "TKEY": q$ = "Initial key:"
  112.     CASE "TLAN": q$ = "Language:"
  113.     CASE "TLEN": q$ = "Length:"
  114.     CASE "TMED": q$ = "Media type" '                           X
  115.     CASE "TOAL": q$ = "Original album / movie / show title:"
  116.     CASE "TOFN": q$ = "Original filename:"
  117.     CASE "TOLY": q$ = "Original lyricist / text writer:"
  118.     CASE "TOPE": q$ = "Original artist / performer:"
  119.     CASE "TORY": q$ = "Original release year:"
  120.     CASE "TOWN": q$ = "File owner / license:"
  121.     CASE "TPE1": q$ = "Lead performer / Soloist:"
  122.     CASE "TPE2": q$ = "Band / orchestra / accompaniment:"
  123.     CASE "TPE3": q$ = "Conductor / performer refinement:"
  124.     CASE "TPE4": q$ = "Modified by:"
  125.     CASE "TPOS": q$ = "Part of a set:"
  126.     CASE "TPUB": q$ = "Publisher:"
  127.     CASE "TRCK": q$ = "Track number / Position in set:"
  128.     CASE "TRDA": q$ = "Recording dates:"
  129.     CASE "TRSN": q$ = "Internet radio station name:"
  130.     CASE "TRSO": q$ = "Internet radio station owner:"
  131.     CASE "TSIZ": q$ = "Size:"
  132.     CASE "TSRC": q$ = "International standard recording code (ISRC):"
  133.     CASE "TSSE": q$ = "Software / Hardware used for encoding:"
  134.     CASE "TYER": q$ = "Year:"
  135.     CASE "TXXX": q$ = "User defined text frame:" '             X - text encoding, description and value
  136.     CASE "UFID": q$ = "Unique file identifier:"
  137.     CASE "USER": q$ = "Terms of use:" '                        X
  138.     CASE "USLT": q$ = "Unsychronized lyric/text transcription:": USLTSUB 'X
  139.     CASE "WCOM": q$ = "Commercial information:"
  140.     CASE "WCOP": q$ = "Copyright / Legal information:"
  141.     CASE "WOAF": q$ = "Official audio file webpage:"
  142.     CASE "WOAR": q$ = "Official artist / performer webpage:"
  143.     CASE "WOAS": q$ = "Official audio source webpage:"
  144.     CASE "WORS": q$ = "Official internet radio webpage:"
  145.     CASE "WPAY": q$ = "Payment:"
  146.     CASE "WPUB": q$ = "Publishers official webpage:"
  147.     CASE "WXXX": q$ = "User defined URL link frame:" '           X
  148. 'GET #1, , InFrame$ ' first muss i analyzing metadata
  149.  
  150. IF FRM.Id$ = "USLT" THEN
  151.     textencod1 = ASC(LEFT$(InFrame$, 1))
  152.     textencod2 = ASC(LEFT$(MID$(InFrame$, 1), 1))
  153.     PRINT textencod1, textencod2
  154.  
  155. IF LEFT$(FRM.Id$, 1) = CHR$(0) OR LEFT$(FRM.Id$, 1) = CHR$(32) OR LEFT$(FRM.Id$, 1) = CHR$(255) OR RIGHT$(FRM.Id$, 1) = "d" THEN GOTO EndRec '+ LTRIM$(CHR$(0)) + LTRIM$(CHR$(0)) + LTRIM$(CHR$(0)) THEN GOTO EndRec
  156. OUTList$(w) = q$ + InFrame$
  157. PRINT "Frame: "; FRM.Id$; " Frame size: "; FrameSize; " and record in this frame: "; InFrame$
  158.  
  159. InFrame$ = ""
  160. readet = readet + 10 + FrameSize
  161. 'PRINT readet, FrameSize
  162. SLEEP: w = w + 1: GOTO home
  163. EndRec:
  164. PRINT "End of file"
  165. PRINT "List: "; IDENTIT$
  166. FOR z = 1 TO w
  167.     PRINT OUTList$(z)
  168. PRINT "Press any key to end"
  169.  
  170. ID3V11: 'upgraded ID3V1.1 program (upgraded in end file access, many better)
  171. OPEN file$ FOR BINARY AS #1 'upgraded file access style
  172. DIM re AS STRING * 256
  173. DIM re0 AS STRING * 128
  174. DIM re2 AS STRING * 128
  175. GET #1, , re0$: IF LEFT$(re0$, 3) <> "ID3" THEN PRINT "ID3 mark not found! - but i try this" ' its opened, also its posibble to have uncorrect outputs.
  176. PRINT "Wait..."
  177. 'UPGRADE ////
  178. e = LOF(1) - 128
  179. IF e < 128 THEN PRINT "Error: LOF returned file length < 128 bytes!"
  180. SEEK #1, e 'better and many faster access             i  am still learning
  181. 'UPGRADE END
  182. GET #1, e - 1, re0$
  183. GET #1, e, re2$
  184. re$ = re0$ + re2$ '                                   After several attempts, I realized that the begin of the last record may not be the very last recording at the end of the file...    :-D
  185. 'PRINT re$, e: SLEEP
  186. FOR scan = 1 TO LEN(re$) '                            Here this loop byte to byte scanned re$ for text "TAG" - its definition for ID3 tag begin
  187.     id$ = LEFT$(MID$(re$, scan), 3)
  188.     IF id$ = "TAG" THEN sca = scan: found = 1 '       byte position in string, "found" is myself method to prevent uncorrect outputs if file have none or ID3 V.2.2 ID3TAG, but its not usefull at 100%
  189. NEXT scan
  190. IF found = 0 THEN PRINT "Uncorrect record or no ID3V1.1 / ID3V2.3": SLEEP 1: SYSTEM
  191. dal:
  192. 'COLOR , 5                                          i have here tested long text arrays
  193. SongName$ = LEFT$(MID$(re$, 3 + sca), 30)
  194. Autor$ = LEFT$(MID$(re$, 33 + sca), 30) '           filtering strings
  195. Album$ = LEFT$(MID$(re$, 63 + sca), 30) '           This way "integer = ASC(LEFT$(MID$(string$, position), long)) is way how read MP3 HEAD. BUT MP3 HEAD contains MANY tables and recordings.
  196. Rok$ = LEFT$(MID$(re$, 93 + sca), 4)
  197. Coment$ = LEFT$(MID$(re$, 97 + sca), 28)
  198. TrackID$ = LEFT$(MID$(re$, 125 + sca), 1)
  199. IF TrackID$ = CHR$(0) THEN track$ = STR$(ASC(LEFT$(MID$(re$, 126 + sca), 1))) ELSE track$ = "Not writed" '  See to https://en.wikipedia.org/wiki/ID3
  200. Gen$ = LEFT$(MID$(re$, 127 + sca), 1)
  201. IF Gen$ = "" THEN GOTO none
  202. genre = ASC(Gen$) + 1
  203. IF genre = 0 OR genre > 125 THEN GOTO none
  204. FOR gnr = 1 TO genre
  205.     READ genre$
  206. NEXT gnr
  207. none:
  208. 'PRINT re$ '  If you delete this mark "'", you see as its writed in file
  209. PRINT IDENTIT$
  210. PRINT "Song name: "; SongName$ '         Song
  211. PRINT "Author: "; Autor$ '               Author name
  212. PRINT "Album: "; Album$ '                Album
  213. PRINT "Year: "; Rok$ '                   Year
  214. PRINT "Comment: "; Coment$ '             Comment
  215. PRINT "Track: "; track$ '                Track number
  216. PRINT "Genre: "; genre$ '                Genre
  217. genre:
  218.  
  219. DATA Blues,Classic Rock,Country,Dance,Disco,Funk,Grunge,Hip-Hop,Jazz,Metal,New Age,Oldies,Other,Pop,R&B,Rap,Reggae,Rock,Techno,Industrial,Alternative,Ska,Death Metal,Pranks,Soundtrack,Eurotechno,Ambient
  220. DATA Trip-Hop,Vocal,Jazz+Funk,Fusion,Trance,Classical,Instrumental,Acid,House,Game,Sound Clip,Gospel,Noise,Alternative Rock,Bass,Soul,Punk,Space,Meditative,Instrumental Pop,Instrumental Rock,Ethnic
  221. DATA Gothic,Darkwave,Techno-Industrial,Electronic,Jungle,Pop-Folk,Eurodance,Dream,Southern Rock,Comedy,Cult,Gangsta,Top 40,Christian Rap,Pop/Funk,Native American,Cabaret,New Wave,Psychadelic,Rave,Show Tunes
  222. DATA Trailer,Lo-Fi,Tribal,Acid Punk,Acid Jazz,Polka,Retro,Musical,Rock & Roll,Hard Rock,Folk,Folk/Rock,National Folk,Swing,Fast-Fusion,Bebop,Latin,Revival,Celtic,Bluegrass,Avantgarde,Gothic Rock,Progressive Rock
  223. DATA Psychedelic Rock,Symphonic Rock,Slow Rock,Big Band,Chorus,Easy Listening,Acoustic,Humour,Speech,Chanson,Opera,Chamber Music,Sonata,Symphony,Booty Bass,Primus,Porn Groove,Satire,Slow Jam,Club,Tango,Samba
  224. DATA Folklore,Ballad,Power Ballad,Rhytmic Soul,Freestyle,Duet,Punk Rock,Drum Solo,Acapella,Euro-House,Dance Hall,Goa,Drum & Bass,Club-House,Hardcore,Terror,Indie,BritPop,Negerpunk,Polsk Punk,Beat,Christian Gangsta Rap
  225. DATA Heavy Metal,Black Metal,Crossover,Contemporary Christian,Christian Rock
  226.  
  227. '                                                                                       HAPPY CODING!
  228.  
  229. SUB USLTSUB
  230. SHARED InFrame$, a
  231. 'FOR a = 1 TO LEN(InFrame$)
  232. 'r$ = LEFT$(MID$(InFrame$, a), 1)
  233. 'PRINT a, r$, ASC(r$)
  234. 'SLEEP
  235. 'NEXT a
  236.  
  237.  
  238.  
  239.  
  240.  
  241.  
  242.  
  243.  
  244. SUB APICSUB
  245. SHARED InFrame$, a, h ' h is ID3TAGV2.3.0 size
  246. pokusnavelikost = h - SEEK(1)
  247.  
  248.  
  249.  
  250. ' FOR a = 1 TO LEN(InFrame$)
  251. ' r$ = LEFT$(MID$(InFrame$, a), 1)
  252. ' PRINT a, r$, ASC(r$)
  253. ' SLEEP
  254. ' NEXT a                                            ' LADENI  (program setup)
  255.  
  256. textencoding = ASC(LEFT$(InFrame$, 1))
  257. IF textencoding = 0 THEN PRINT "Text encoding NO" ELSE PRINT "Text encoding YES" 'can be 0 or 1
  258. mime$ = LTRIM$(RTRIM$(LEFT$(MID$(InFrame$, 2), 9)))
  259. PRINT "MIME TYPE:"; mime$
  260. PRINT "TEXT:"; te$; "DELKA inframe$ je: (primo bez odecteni): "; LEN(InFrame$); "jsi na pozici:"; SEEK(1)
  261. oll = SEEK(1)
  262. ext$ = ".jpg"
  263. 'GOTO pokus
  264.  
  265.     CASE "image/jpe": ext$ = ".jpg": GOTO POKUS  'k.mp3
  266.     CASE "image/jpg": ext$ = ".jpg" 'c++ source
  267.  
  268.  
  269.  
  270.  
  271.  
  272.  
  273.  
  274.  
  275.  
  276.  
  277.  
  278.  
  279.  
  280.  
  281.  
  282.  
  283.  
  284.  
  285.  
  286.  
  287.  
  288. 'HERE is comming a very BIG problem for me: HOW TO FIND PICTURE SIZE? All sources was i can are bad. Also i use one old method - because record in image head is unussable - bytes with size are bad - or
  289. 'WIKI specification is not correct, author ID3 write about this nothing and frame lenght is bad, i muss scanning file for next idetificator after "APIC". This write to array and the one that is closest
  290. 'to me precisely determines the length of the picture. Although this is smut, but i do not know how else to achieve results - if cant right bytes with size.
  291. 'if you need repairing me, so:  image is MIME format and this is NOT JPEG! its other format with the same name.
  292.  
  293. DIM HYPERBLOCK AS STRING * 20
  294. DIM Identity AS STRING * 4
  295. FOR FATALITY = SEEK(1) TO LOF(1)
  296.     GET #1, , HYPERBLOCK$
  297.     RESTORE IDENTIT
  298.     Identity$ = SPACE$(4)
  299.     FOR iScan = 0 TO 73
  300.         READ IDENTIT$
  301.         FOR D4FF = 1 TO LEN(HYPERBLOCK$)
  302.             Identity$ = LEFT$(MID$(HYPERBLOCK$, D4FF), 4)
  303.             IF Identity$ = IDENTIT$ THEN GOTO VOALA '   First correct detection ends this loop and then with PEEK i calculate correct image size
  304.             ' LOCATE 13, 5: PRINT LOF(1), "/", FATALITY, IDENTIT$, iScan, Identity$
  305.         NEXT D4FF
  306.     NEXT iScan
  307. NEXT FATALITY
  308. EXIT SUB 'next record not found...views outputs and ends
  309.  
  310.  
  311. VOALA: new = SEEK(1)
  312. Size = new - oll
  313. 'PRINT "LOOPSIZE", Size, new, oll, IDENTIT$: BEEP: SLEEP
  314. Size = Size + 64
  315.  
  316. REM ///////////////////////////////
  317. POKUS:
  318. Size = pokusnavelikost
  319. REM //////////////////////////////
  320.  
  321.  
  322. SEEK #1, oll - 77
  323. mega$ = SPACE$(Size) 'if here is correct size, its maked right copy of image to disc.
  324. DIM mega(Size) AS LONG
  325. GET #1, , mega$
  326. IF _FILEEXISTS("swap.???") THEN KILL "swap.???"
  327. swap$ = "swap" + LTRIM$(ext$)
  328. PUT #4, , mega$
  329. SEEK #1, Size + 60
  330. 'InFrame$ = ""
  331.  
  332. j& = _LOADIMAGE(swap$, 32)
  333. IF j& < -1 THEN SCREEN j&
  334.  
  335.  
  336. IDENTIT:
  337.  
  338. DATA AENC,APIC,COMM,COMR,ENCR,EQUA,ETCO,GEOB,GRID,IPLS,LINK,MCDI,MLLT,OWNE,PRIV,PCNT,POPM,POSS,RBUF,RVAD,RVRB,SYLT,SYTC,TALB,TBPM,TCOM,TCON,TCOP,TDAT,TDLY,TENC,TEXT,TFLT,TIME,TIT1,TIT2,TIT3
  339. DATA TKEY,TLAN,TLEN,TMED,TOAL,TOFN,TOLY,TOPE,TORY,TOWN,TPE1,TPE2,TPE3,TPE4,TPOS,TPUB,TRCK,TRDA,TRSN,TRSO,TSIZ,TSRC,TSSE,TYER,TXXX,UFID,USER,USLT,WCOM,WCOP,WOAF,WOAR,WOAS,WORS,WPAY,WPUB,WXXX
  340. 'Although it works, but if no other identifiers after APIC file, so it will end up in long loop. So if this view, then, alone with previous tests of that there is a trailing support, the identifier
  341. 'or simply not show. It's stupid, but a master programmer ID3 tag is inherently inconsistent to man. Head ID3 tag fits binary level, and here size does not deliver. That would put a medal.
  342. 'co taky chtit od svedu. Maximalne ten Hulmiho Ukolen by jeste sel...
  343.  
  344.  
  345.  
  346.  
  347.  
  348.  
  349.  
  350.  
  351. FUNCTION HEAD (b AS STRING) 'BIN to DEC vystup je integer, vstup je string (jmeno FUNKCE je promenna s hodnotou z funkce)
  352. FOR Si = 0 TO LEN(b$)
  353.     e$ = LEFT$(MID$(b$, Si + 1), 1)
  354.     c = VAL(e$)
  355.     Sj = LEN(b$) - Si
  356.     DECtoBI2 = DECtoBI2 + (c * 2 ^ Sj)
  357. NEXT Si
  358. PRINT "Si:"; Si
  359. HEAD = DECtoBI2
  360.  
  361.  
  362. '---- FUNCTIONs TEST ----       if is this copyed alone as new program
  363. 'b$ = DECtoBIN$(255)
  364. 'b = BINtoDEC(b$)
  365. 'c$ = HEX$(b)
  366. 'c = HEXtoDEC(c$)
  367.  
  368. 'PRINT b$, b, c$, c
  369.  
  370. FUNCTION DECtoBIN$ (vstup) 'DEC to BIN ok vystup je string, vstup je integer          decimal to binar number convertor   -   FROM QB64WIKI
  371. '   BINARY$ = ""
  372. FOR rj = 7 TO 0 STEP -1
  373.     IF vstup AND 2 ^ rj THEN BINtoDE$ = BINtoDE$ + "1" ELSE BINtoDE$ = BINtoDE$ + "0"
  374. NEXT rj
  375. DECtoBIN$ = BINtoDE$
  376.  
  377. FUNCTION BINtoDEC (b AS STRING) 'BIN to DEC vystup je integer, vstup je string (jmeno FUNKCE je promenna s hodnotou z funkce)
  378. FOR Si = 0 TO 7
  379.     e$ = LEFT$(MID$(b$, Si + 1), 1)
  380.     c = VAL(e$) '                                                                  binar to decimal number convertor
  381.     Sj = 7 - Si
  382.     DECtoBI = DECtoBI + (c * 2 ^ Sj)
  383. NEXT Si
  384. BINtoDEC = DECtoBI
  385.  
  386. FUNCTION HEXtoDEC (h AS STRING) 'Vystup je integer, vstup je string, opacne k funkci HEX$
  387. HEXtoDEC = VAL("&H" + h$) '                                                        hexadecimal to decimal number convertor
  388.  
  389. FUNCTION BINtoHEX$ (bi$)
  390. c = BINtoDEC(bi$) '                                                                 binar to hexadecimal number convertor (use binar to decimal convertor)
  391. BINtoHEX$ = HEX$(c)
  392.  

i think, it is for first and one new id3 version. The first id3 is always writed in end the file, new versions are in begin file.
Title: Re: Development of a program for automatic search for music and movies
Post by: Petr on June 27, 2020, 12:28:17 pm
List directories using Steve version in QB64 folder duration is 1.5 sec here.
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 12:53:44 pm
What is interesting is that when I don't double check _DIREXISTS(SearchDirectory + "\" + nam$)
my run time is significantly reduced!!! and I still get the 607 files on my system without the false positive.

I cut about a half second off my build and print time and if I can do this without changing directories (which should be quite possible) will have another big time savings.
Good work, @bplus ! When I get home I'll try your new code if you post it and see how fast it runs on my system.
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 12:55:43 pm
List directories using Steve version in QB64 folder duration is 1.5 sec here.
@Petr   Yes, but his code returns false positives and doesn't sort correctly. Also, he places his timer in a spot differently from where I timed mine and bplus's. So far, @bplus has the most efficient code as his sorts exactly right and doesn't display false positives
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 27, 2020, 01:02:15 pm
I've spent a little time digging into the flags issue.  The problem arises when we're dealing with the subfolders.  Somehow the path isn't working properly with them, as illustrated with the quick demo here:

Code: [Select]
DEFLNG A-Z
DECLARE CUSTOMTYPE LIBRARY ".\direntry"
    FUNCTION load_dir& (s AS STRING)
    FUNCTION has_next_entry& ()
    SUB close_dir ()
    SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
END DECLARE

REDIM SHARED Tree(0) AS STRING

SCREEN _NEWIMAGE(1280, 740, 32)
_DELAY .25
_SCREENMOVE 80, 0
DIM cd$, i, w$

PRINT "Creating tree."
t# = TIMER(0.001)
MakeTree _CWD$ 'testing in QB64 folder
PRINT "Showing Tree"

FOR i = 0 TO UBOUND(Tree) 'show tree
    PRINT Tree(i)
NEXT
PRINT USING "##.##### seconds creating, printing, and sorting tree."; TIMER - t#


SUB GetSubDirs (SearchDirectory AS STRING)
    DIM flags AS LONG, file_size AS LONG, length, nam$
    IF load_dir(SearchDirectory + CHR$(0)) THEN
        DO
            length = has_next_entry
            IF length > -1 THEN
                nam$ = SPACE$(length)
                get_next_entry nam$, flags, file_size
                IF nam$ <> "." AND nam$ <> ".." THEN
                    t$ = SearchDirectory + "\" + nam$
                    IF flags AND 1 THEN
                        u = UBOUND(Tree) + 1
                        REDIM _PRESERVE Tree(u)
                        Tree(u) = t$
                    END IF
                END IF
            END IF
        LOOP UNTIL length = -1
    ELSE
        PRINT "Dir not loaded"
    END IF
    close_dir
END SUB

SUB MakeTree (Dir$)
    DIM OnDir AS LONG, gap AS LONG, i AS LONG, swapped AS LONG
    REDIM Tree(0) AS STRING
    Tree(0) = Dir$
    DO
        GetSubDirs Tree(OnDir)
        OnDir = OnDir + 1
    LOOP UNTIL OnDir > UBOUND(Tree)
    gap = UBOUND(Tree)
    DO
        gap = 10 * gap \ 13
        IF gap < 1 THEN gap = 1
        i = 0
        swapped = 0
        DO
            IF Tree(i) > Tree(i + gap) THEN
                SWAP Tree(i), Tree(i + gap)
                swapped = -1
            END IF
            i = i + 1
        LOOP UNTIL i + gap > UBOUND(Tree)
    LOOP UNTIL gap = 1 AND swapped = 0
END SUB

This lists the root directories of our folder and flags them properly, but then it fails to properly identify any of the subfolders.  Some digging into the C source will be required to sort out what the heck is going on with the false flag results, and with my current schedule, I can't promise how long that might take me.  For now, I'd go the route Sprigg and bplus suggested:  Remove the IS_DIR flag from consideration completely and just let QB64 use _DIREXISTS and _FILEEXISTS to verify if it's a file or directory.
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 27, 2020, 01:17:14 pm
@Petr   Yes, but his code returns false positives and doesn't sort correctly. Also, he places his timer in a spot differently from where I timed mine and bplus's. So far, @bplus has the most efficient code as his sorts exactly right and doesn't display false positives

The single false positive can be removed just by taking out the flags AND IS_DIR check.  That's something which POSIX is tossing us, and then it's only tossing the result on some systems.  I imagine debugging it is going to be a PITA.

I'd say as for sorting, that's just a matter of preference and how you decide to order your data.  You can sort by file name, extension, size -- whatever you like.  The only difference is bplus's routine runs recursively -- which expands folders and subfolders as it finds them -- and mine runs a whole level at once before expanding the next level of subfolders.

As for the placement of the timer, you only want to time how quickly you find and build your data tree.  Counting time for printing and stuff is a waste of time.  One could toss a _DISPLAY in there and completely change the result.  If one was using Inform and printing to a form's list box, it'd have a different time.  Printing to console vs a graphical window would give different times.  What you want to know is, "how long does it take to build this data tree" -- not, "How long does everything in my program take from start to finish". 

Heck, on my system, since I'm printing to a RAMdrive, it's faster for me to dump the results to a file than it is to print them and scroll the screen with them...   Time and optimize your tree creation routine first; then worry about timing and optimizing the rest of your program after.  :)
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 27, 2020, 02:13:04 pm
I've spent a little time digging into the flags issue.  The problem arises when we're dealing with the subfolders.  Somehow the path isn't working properly with them, as illustrated with the quick demo here:

Code: [Select]
DEFLNG A-Z
DECLARE CUSTOMTYPE LIBRARY ".\direntry"
    FUNCTION load_dir& (s AS STRING)
    FUNCTION has_next_entry& ()
    SUB close_dir ()
    SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
END DECLARE

REDIM SHARED Tree(0) AS STRING

SCREEN _NEWIMAGE(1280, 740, 32)
_DELAY .25
_SCREENMOVE 80, 0
DIM cd$, i, w$

PRINT "Creating tree."
t# = TIMER(0.001)
MakeTree _CWD$ 'testing in QB64 folder
PRINT "Showing Tree"

FOR i = 0 TO UBOUND(Tree) 'show tree
    PRINT Tree(i)
NEXT
PRINT USING "##.##### seconds creating, printing, and sorting tree."; TIMER - t#


SUB GetSubDirs (SearchDirectory AS STRING)
    DIM flags AS LONG, file_size AS LONG, length, nam$
    IF load_dir(SearchDirectory + CHR$(0)) THEN
        DO
            length = has_next_entry
            IF length > -1 THEN
                nam$ = SPACE$(length)
                get_next_entry nam$, flags, file_size
                IF nam$ <> "." AND nam$ <> ".." THEN
                    t$ = SearchDirectory + "\" + nam$
                    IF flags AND 1 THEN
                        u = UBOUND(Tree) + 1
                        REDIM _PRESERVE Tree(u)
                        Tree(u) = t$
                    END IF
                END IF
            END IF
        LOOP UNTIL length = -1
    ELSE
        PRINT "Dir not loaded"
    END IF
    close_dir
END SUB

SUB MakeTree (Dir$)
    DIM OnDir AS LONG, gap AS LONG, i AS LONG, swapped AS LONG
    REDIM Tree(0) AS STRING
    Tree(0) = Dir$
    DO
        GetSubDirs Tree(OnDir)
        OnDir = OnDir + 1
    LOOP UNTIL OnDir > UBOUND(Tree)
    gap = UBOUND(Tree)
    DO
        gap = 10 * gap \ 13
        IF gap < 1 THEN gap = 1
        i = 0
        swapped = 0
        DO
            IF Tree(i) > Tree(i + gap) THEN
                SWAP Tree(i), Tree(i + gap)
                swapped = -1
            END IF
            i = i + 1
        LOOP UNTIL i + gap > UBOUND(Tree)
    LOOP UNTIL gap = 1 AND swapped = 0
END SUB

This lists the root directories of our folder and flags them properly, but then it fails to properly identify any of the subfolders.  Some digging into the C source will be required to sort out what the heck is going on with the false flag results, and with my current schedule, I can't promise how long that might take me.  For now, I'd go the route Sprigg and bplus suggested:  Remove the IS_DIR flag from consideration completely and just let QB64 use _DIREXISTS and _FILEEXISTS to verify if it's a file or directory.

This explains why my recursive function falls flat when I don't change directory before each call to GetSubDirs. I am beginning to think startDirectory is meaningless in part of the call, it's just flagging the directories directly under the current directory, which is why my recursive only works with changing directories and reading flags.
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 27, 2020, 03:10:51 pm
Here is Tree building by CHDIR and then checking if Directory with Flags which do work in immediate sub dir of current directory, this is fastest time:
Code: QB64: [Select]
  1. 'Tree builder recursive.bas  b+ Petr 2020-06-26  now with SMCNeill Fix!! add CHR$(0) to C string
  2. DEFLNG A-Z
  3.     FUNCTION load_dir& (s AS STRING)
  4.     FUNCTION has_next_entry& ()
  5.     SUB close_dir ()
  6.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  7.  
  8. REDIM SHARED Tree(0) AS STRING '<<<<<<<<<<<<<<<<<<<<<< G(0) will remain empty
  9. t# = TIMER(0.001)
  10.  
  11.  
  12. MakeTree _CWD$
  13. FOR i = 1 TO UBOUND(Tree) 'show tree
  14.     PRINT _TRIM$(STR$(i)); ": "; Tree(i)
  15. PRINT USING "##.##### seconds creating, printing."; TIMER - t#
  16.  
  17. SUB sAppend (arr() AS STRING, addItem$)
  18.     REDIM _PRESERVE arr(LBOUND(arr) TO UBOUND(arr) + 1) AS STRING
  19.     arr(UBOUND(arr)) = addItem$
  20.  
  21. SUB GetSubDirs (SearchDirectory AS STRING, DirList() AS STRING)
  22.     CONST IS_DIR = 1
  23.     DIM flags AS LONG, file_size AS LONG, length, nam$
  24.     IF load_dir(SearchDirectory + CHR$(0)) THEN 'Steve's fix here with CHR$(0) for C call
  25.         DO
  26.             length = has_next_entry
  27.             IF length > -1 THEN
  28.                 nam$ = SPACE$(length)
  29.                 get_next_entry nam$, flags, file_size
  30.                 IF (flags = IS_DIR) THEN
  31.                     IF nam$ <> "." AND nam$ <> ".." THEN
  32.                         DirCount = DirCount + 1
  33.                         IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  34.                         DirList(DirCount) = SearchDirectory + "\" + nam$
  35.                     END IF
  36.                 END IF
  37.             END IF
  38.         LOOP UNTIL length = -1
  39.     END IF
  40.     close_dir 'Steve first fix that got navigator working
  41.     REDIM _PRESERVE DirList(DirCount)
  42.  
  43. SUB MakeTree (startDir AS STRING)
  44.     copyStart$ = startDir
  45.     REDIM D(100) AS STRING
  46.     GetSubDirs copyStart$, D()
  47.     FOR i = 1 TO UBOUND(D)
  48.         sAppend Tree(), D(i)
  49.         CHDIR D(i)
  50.         MakeTree D(i)
  51.     NEXT
  52.  
  53.  
  54.  

Recursion will work without changing directories but you have to check 
Code: QB64: [Select]
  1. IF _DIREXISTS(SearchDirectory + "\" + nam$) THEN
which turns out takes longer than just changing directories (~1.69 sec to ~2.93)!

Code: QB64: [Select]
  1. 'Tree builder recursive.bas  b+ Petr 2020-06-26  now with SMCNeill Fix!! add CHR$(0) to C string
  2. DEFLNG A-Z
  3.     FUNCTION load_dir& (s AS STRING)
  4.     FUNCTION has_next_entry& ()
  5.     SUB close_dir ()
  6.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  7.  
  8. REDIM SHARED Tree(0) AS STRING '<<<<<<<<<<<<<<<<<<<<<< G(0) will remain empty
  9. t# = TIMER(0.001)
  10.  
  11.  
  12. MakeTree _CWD$
  13. FOR i = 1 TO UBOUND(Tree) 'show tree
  14.     PRINT _TRIM$(STR$(i)); ": "; Tree(i)
  15. PRINT USING "##.##### seconds creating, printing."; TIMER - t#
  16.  
  17. SUB sAppend (arr() AS STRING, addItem$)
  18.     REDIM _PRESERVE arr(LBOUND(arr) TO UBOUND(arr) + 1) AS STRING
  19.     arr(UBOUND(arr)) = addItem$
  20.  
  21. SUB GetSubDirs (SearchDirectory AS STRING, DirList() AS STRING)
  22.     CONST IS_DIR = 1
  23.     DIM flags AS LONG, file_size AS LONG, length, nam$
  24.     IF load_dir(SearchDirectory + CHR$(0)) THEN 'Steve's fix here with CHR$(0) for C call
  25.         DO
  26.             length = has_next_entry
  27.             IF length > -1 THEN
  28.                 nam$ = SPACE$(length)
  29.                 get_next_entry nam$, flags, file_size
  30.                 IF nam$ <> "." AND nam$ <> ".." THEN
  31.                     IF _DIREXISTS(SearchDirectory + "\" + nam$) THEN
  32.                         DirCount = DirCount + 1
  33.                         IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  34.                         DirList(DirCount) = SearchDirectory + "\" + nam$
  35.                     END IF
  36.                 END IF
  37.             END IF
  38.         LOOP UNTIL length = -1
  39.     END IF
  40.     close_dir 'Steve first fix that got navigator working
  41.     REDIM _PRESERVE DirList(DirCount)
  42.  
  43. SUB MakeTree (startDir AS STRING)
  44.     copyStart$ = startDir
  45.     REDIM D(100) AS STRING
  46.     GetSubDirs copyStart$, D()
  47.     FOR i = 1 TO UBOUND(D)
  48.         sAppend Tree(), D(i)
  49.         MakeTree D(i)
  50.     NEXT
  51.  
  52.  
  53.  


Now if you want all files and directories (I get 19279 items in ~15.59 secs) without checking anything do this:
Code: QB64: [Select]
  1. 'Tree builder recursive.bas  b+ Petr 2020-06-26  now with SMCNeill Fix!! add CHR$(0) to C string
  2. DEFLNG A-Z
  3.     FUNCTION load_dir& (s AS STRING)
  4.     FUNCTION has_next_entry& ()
  5.     SUB close_dir ()
  6.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  7.  
  8. REDIM SHARED Tree(0) AS STRING '<<<<<<<<<<<<<<<<<<<<<< G(0) will remain empty
  9. t# = TIMER(0.001)
  10.  
  11.  
  12. MakeTree _CWD$
  13. FOR i = 1 TO UBOUND(Tree) 'show tree
  14.     PRINT _TRIM$(STR$(i)); ": "; Tree(i)
  15. PRINT USING "##.##### seconds creating, printing."; TIMER - t#
  16.  
  17. SUB sAppend (arr() AS STRING, addItem$)
  18.     REDIM _PRESERVE arr(LBOUND(arr) TO UBOUND(arr) + 1) AS STRING
  19.     arr(UBOUND(arr)) = addItem$
  20.  
  21. SUB GetSubDirs (SearchDirectory AS STRING, DirList() AS STRING)
  22.     CONST IS_DIR = 1
  23.     DIM flags AS LONG, file_size AS LONG, length, nam$
  24.     IF load_dir(SearchDirectory + CHR$(0)) THEN 'Steve's fix here with CHR$(0) for C call
  25.         DO
  26.             length = has_next_entry
  27.             IF length > -1 THEN
  28.                 nam$ = SPACE$(length)
  29.                 get_next_entry nam$, flags, file_size
  30.                 IF nam$ <> "." AND nam$ <> ".." THEN
  31.                     'IF _DIREXISTS(SearchDirectory + "\" + nam$) THEN
  32.                     DirCount = DirCount + 1
  33.                     IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  34.                     DirList(DirCount) = SearchDirectory + "\" + nam$
  35.                     'END IF
  36.                 END IF
  37.             END IF
  38.         LOOP UNTIL length = -1
  39.     END IF
  40.     close_dir 'Steve first fix that got navigator working
  41.     REDIM _PRESERVE DirList(DirCount)
  42.  
  43. SUB MakeTree (startDir AS STRING)
  44.     copyStart$ = startDir
  45.     REDIM D(100) AS STRING
  46.     GetSubDirs copyStart$, D()
  47.     FOR i = 1 TO UBOUND(D)
  48.         sAppend Tree(), D(i)
  49.         MakeTree D(i)
  50.     NEXT
  51.  
  52.  

It's probably as fast as you can get through QB64.
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 04:04:23 pm
As for the placement of the timer, you only want to time how quickly you find and build your data tree.  Counting time for printing and stuff is a waste of time.  One could toss a _DISPLAY in there and completely change the result.  If one was using Inform and printing to a form's list box, it'd have a different time.  Printing to console vs a graphical window would give different times.  What you want to know is, "how long does it take to build this data tree" -- not, "How long does everything in my program take from start to finish". 
In that case, for the speed, just checking the amount of time it takes to use @bplus code to build the array it takes 0.8 seconds on my machine and my code only takes 1.29 seconds to build an array that is already presorted. bplus's code seems to be the absolute best. I have no way of beating that with PowerShell calls. Less than one second to build an array containing 659 directories is great. I removed the other 600+ directories. They were backups from my synced folder.
Title: Re: Development of a program for automatic search for music and movies
Post by: SMcNeill on June 27, 2020, 04:12:45 pm
In that case, for the speed, just checking the amount of time it takes to use @bplus code to build the array it takes 0.8 seconds on my machine and my code only takes 1.29 seconds to build an array that is already presorted. bplus's code seems to be the absolute best. I have no way of beating that with PowerShell calls. Less than one second to build an array containing 659 directories is great. I removed the other 600+ directories. They were backups from my synced folder.

Aye, and it's cross-platform compatible.  Powershell calls only work in Windows.  ;)
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 04:15:08 pm
Aye, and it's cross-platform compatible.  Powershell calls only work in Windows.  ;)
@SMcNeill
PowerShell has a cross platform version :) And I know it is available for both Mac and Linux. Found here: https://aka.ms/pscore6
PowerShell advertises it every time I launch it.
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 04:33:20 pm
@bplus @SMcNeill There seems to be an issue:
I ran your (bplus) code on my "C:\Program Files (x86)\Steam\steamapps\common" folder and returned nothing. Is there some issue with read/write access, perhaps? I gave "Everyone" read/write access and it still failed to list any directories. Hmmm. I get 5,557 directories added to a presorted array in about 3.75 seconds through PowerShell. And running Steve's code, it took over 9 seconds to create and sort an array using strictly his code copy and pasted from the forum on his latest post. It had no trouble going through the directories but listed about 100 extra files as folders due to the false positive. So the false positive problem is much more than just "the one file". It's going to be happening in many places as a lot of programs create files without extensions and those appear to be the ones getting flagged as directories. Not sure why bplus's code fails completely.
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 27, 2020, 04:48:46 pm
@bplus There seems to be an issue:
I ran your code on my "C:\Program Files (x86)\Steam\steamapps\common" folder and returned nothing. Is there some issue with read/write access, perhaps? I gave "Everyone" read/write access and it still failed to list any directories. Hmmm. I get 5,557 directories listed in about 3.75 seconds through PowerShell.

You have to change directory every step to the folder in question because sub-dir Flag only works when you are directly above that.

Here is my Tiny Navigator that steps you through your hard drive hopefully C:\\ is root.
You can see if you can navigate to that folder, sorry it is constant CHDIR if want the fastest Tree code.
Code: QB64: [Select]
  1. ' B+ started 2019-08-22  restart 2020-06-24
  2. ' 2019-08-22 orig post at https://www.qb64.org/forum/index.php?topic=1646.msg108682#msg108682
  3. ' 2019-08-23_13-25 try fix (to nav all directories) with one place for tmpFile, theory can't write files in some dir's
  4. ' For some reason Windows won't write to a fully pathed file in my user folder???from a SHELL command
  5. ' Try testing if dir exists "C:\temp" exists and making one if not, yes! and I can write my temp files there
  6. ' and now I can chDir anywhere!!!!
  7. ' 2019-08-23_14-25 Take Steve's Advice to use users tmp directory for temp files
  8. ' 2019-08-23_23+ Have files window working for file selection
  9.  
  10. ' 2020-06-24 restart using DirEntry.h, wow looks like Steve's fix helped ALLOT! Thanks SMcNeill
  11. ' I am hitting glitches where Windows wont let me access I think. So I am offering a c enter option
  12. ' to restart at the root but this might not work cross platform.
  13.  
  14. ' 2020-06-26 another fix to DirEntry.h usage add CHR$(0) to string when calling a C program. This fixed a snag
  15. ' between marka and next folder up when heading to root with .. calls.
  16.  
  17.  
  18. ' direntry.h needs to be in QB64 folder
  19.     FUNCTION load_dir& (s AS STRING)
  20.     FUNCTION has_next_entry& ()
  21.     SUB close_dir ()
  22.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  23.  
  24.  
  25. _TITLE "Tiny Navigator using DirEntry:     press f to Start a Select a File list of current directory"
  26. SCREEN _NEWIMAGE(1000, 600, 32)
  27. _SCREENMOVE 100, 50
  28.  
  29. DIM SHARED selectedFile AS STRING
  30. REDIM SHARED DIRs(0) AS STRING, FILs(0) AS STRING
  31. DIM mySelection&, done$, i, t$
  32.     COLOR _RGB32(180, 180, 255)
  33.     CLS
  34.  
  35.     t$ = "Current Directory: " + _CWD$
  36.     LOCATE 2, (_WIDTH / 8 - LEN(t$)) / 2: PRINT t$
  37.     REDIM DIRs(0) AS STRING, FILs(0) AS STRING
  38.     GetLists _CWD$, DIRs(), FILs()
  39.     FOR i = 0 TO UBOUND(FILs) ' this just offers a sample listing of files
  40.         IF i < 30 THEN LOCATE i + 4, 60: PRINT FILs(i) ELSE EXIT FOR
  41.     NEXT
  42.     mySelection& = getArrayItemNumber&(5, 5, 50, 30, DIRs())
  43.     CLS
  44.     IF selectedFile <> "" THEN
  45.         PRINT "You selected a file: "; selectedFile
  46.         INPUT "Press enter to continue navigator, any + enter to quit... "; done$
  47.         selectedFile = ""
  48.     ELSEIF mySelection& <> -1719 THEN
  49.         IF _TRIM$(DIRs(mySelection&)) <> "" THEN
  50.             CHDIR DIRs(mySelection&)
  51.         END IF
  52.     ELSE
  53.         PRINT "Nothing selected."
  54.         PRINT "There are some places your OS might not allow us to go."
  55.         PRINT "If you get stuck with 0 slections, press c (for root C:\\) enter..."
  56.         INPUT "Press enter to continue navigator, any + enter to quit... "; done$
  57.         IF done$ = "c" THEN CHDIR ("C:\\"): done$ = ""
  58.     END IF
  59.     _LIMIT 60
  60. LOOP UNTIL done$ <> ""
  61.  
  62. SUB GetLists (SearchDirectory AS STRING, DirList() AS STRING, FileList() AS STRING)
  63.  
  64.     CONST IS_DIR = 1
  65.     CONST IS_FILE = 2
  66.     DIM flags AS LONG, file_size AS LONG, DirCount AS INTEGER, FileCount AS INTEGER, length AS LONG
  67.     DIM nam$
  68.     REDIM _PRESERVE DirList(100), FileList(100)
  69.     DirCount = 0: FileCount = 0
  70.  
  71.     IF load_dir(SearchDirectory + CHR$(0)) THEN
  72.         DO
  73.             length = has_next_entry
  74.             IF length > -1 THEN
  75.                 nam$ = SPACE$(length)
  76.                 get_next_entry nam$, flags, file_size
  77.                 IF (flags AND IS_DIR) THEN
  78.                     DirCount = DirCount + 1
  79.                     IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  80.                     DirList(DirCount) = nam$
  81.                 ELSEIF (flags AND IS_FILE) THEN
  82.                     FileCount = FileCount + 1
  83.                     IF FileCount > UBOUND(filelist) THEN REDIM _PRESERVE FileList(UBOUND(filelist) + 100)
  84.                     FileList(FileCount) = nam$
  85.                 END IF
  86.             END IF
  87.         LOOP UNTIL length = -1
  88.         'close_dir 'move to after end if  might correct the multi calls problem
  89.     ELSE
  90.     END IF
  91.     close_dir 'this  might correct the multi calls problem
  92.  
  93.     REDIM _PRESERVE DirList(DirCount)
  94.     REDIM _PRESERVE FileList(FileCount)
  95.  
  96. FUNCTION rightOf$ (source$, of$)
  97.     IF INSTR(source$, of$) > 0 THEN rightOf$ = MID$(source$, INSTR(source$, of$) + LEN(of$))
  98.  
  99. ' "Escape or Red X box returns -1719 to allow a Cancel function and signal no slection."
  100. FUNCTION getArrayItemNumber& (locateRow, locateColumn, boxWidth, boxHeight, arr() AS STRING)
  101.     'Notes: locateRow, locateColumn for top right corner of selection box on screen in characters for LOCATE.
  102.     'boxWidth and boxHeight are in character units, again for locate and print at correct places.
  103.     'All displaying is restricted to inside the box, which has PgUP and PgDn as top and bottom lines in the display.
  104.  
  105.     DIM curRow AS INTEGER, curCol AS INTEGER, fg AS _UNSIGNED LONG, bg AS _UNSIGNED LONG
  106.     DIM maxWidth AS INTEGER, maxHeight AS INTEGER, page AS INTEGER, hlite AS INTEGER, mx AS INTEGER, my AS INTEGER
  107.     DIM lastMX AS INTEGER, lastMY AS INTEGER, row AS INTEGER, mb AS INTEGER
  108.     DIM lba AS LONG, uba AS LONG, choice AS LONG, kh AS LONG, index AS LONG
  109.     DIM clrStr AS STRING, b AS STRING, selNum&
  110.  
  111.     'save old settings to restore at end ofsub
  112.     curRow = CSRLIN
  113.     curCol = POS(0)
  114.     fg = _DEFAULTCOLOR
  115.     bg = _BACKGROUNDCOLOR
  116.     _KEYCLEAR
  117.  
  118.     maxWidth = boxWidth '       number of characters in box
  119.     maxHeight = boxHeight - 2 ' number of lines displayed of array at one time = 1 page
  120.     lba = LBOUND(arr)
  121.     uba = UBOUND(arr)
  122.     page = 0
  123.     hlite = 0 '                 line in display ready for selection by spacebar or if no number is started, enter
  124.     clrStr$ = SPACE$(maxWidth) 'clearing a display line
  125.  
  126.     GOSUB update '              show the beginning of the array items for selection
  127.  
  128.     'signal cancel selection process, exit sub with this unlikely index to signal canel
  129.     choice = -1719 'primes 7 and 8, not likely to be a select index of an array
  130.  
  131.     DO 'until get a selection or demand exit
  132.  
  133.         'handle the key stuff
  134.         kh& = _KEYHIT
  135.         IF kh& THEN
  136.             IF kh& > 0 AND kh& < 255 THEN
  137.                 IF INSTR("0123456789", CHR$(kh&)) > 0 THEN b$ = b$ + CHR$(kh&): GOSUB update
  138.                 IF CHR$(kh&) = "f" THEN
  139.                     'REDIM FILs(0) AS STRING     'hopefully this is already ready
  140.                     'loadFiles FILs()
  141.                     selNum& = getArrayItemNumber&(5, 60, 60, 30, FILs())
  142.                     COLOR _RGB32(180, 180, 255)
  143.                     CLS 'need to signal out of file selection
  144.                     IF selNum& >= LBOUND(FILs) AND selNum& <= UBOUND(FILs) THEN selectedFile = FILs(selNum&)
  145.                     EXIT DO
  146.                     'back to directory select
  147.                 END IF
  148.  
  149.                 IF CHR$(kh&) = "c" THEN b$ = "": GOSUB update
  150.                 IF kh& = 13 THEN 'enter pressed check if number is being entered?
  151.                     IF LEN(b$) THEN
  152.                         IF VAL(b$) >= lba AND VAL(b$) <= uba THEN 'we have number started
  153.                             choice = VAL(b$): EXIT DO
  154.                         ELSE 'clear b$ to show some response to enter
  155.                             b$ = "": GOSUB update 'clear the value that doesn't work
  156.                         END IF
  157.                     ELSE
  158.                         choice = hlite + page * maxHeight + lba 'must mean to select the highlighted item
  159.                     END IF
  160.                 END IF
  161.                 IF kh& = 27 THEN EXIT DO 'escape clause offered to Cancel selection process
  162.                 IF kh& = 32 THEN choice = hlite + page * maxHeight + lba 'best way to choose highlighted selection
  163.                 IF kh& = 8 THEN 'backspace to edit number
  164.                     IF LEN(b$) THEN b$ = LEFT$(b$, LEN(b$) - 1): GOSUB update
  165.                 END IF
  166.             ELSE
  167.                 SELECT CASE kh& 'choosing sections of array to display and highlighted item
  168.                     CASE 20736 'pg dn
  169.                         IF (page + 1) * maxHeight + lba <= uba THEN page = page + 1: GOSUB update
  170.                     CASE 18688 'pg up
  171.                         IF (page - 1) * maxHeight + lba >= lba THEN page = page - 1: GOSUB update
  172.                     CASE 18432 'up
  173.                         IF hlite - 1 < 0 THEN
  174.                             IF page > 0 THEN
  175.                                 page = page - 1: hlite = maxHeight - 1: GOSUB update
  176.                             END IF
  177.                         ELSE
  178.                             hlite = hlite - 1: GOSUB update
  179.                         END IF
  180.                     CASE 20480 'down
  181.                         IF (hlite + 1) + page * maxHeight + lba <= uba THEN 'ok to move up
  182.                             IF hlite + 1 > maxHeight - 1 THEN
  183.                                 page = page + 1: hlite = 0: GOSUB update
  184.                             ELSE
  185.                                 hlite = hlite + 1: GOSUB update
  186.                             END IF
  187.                         END IF
  188.                     CASE 18176 'home
  189.                         page = 0: hlite = 0: GOSUB update
  190.                     CASE 20224 ' end
  191.                         page = INT((uba - lba) / maxHeight): hlite = maxHeight - 1: GOSUB update
  192.                 END SELECT
  193.             END IF
  194.         END IF
  195.  
  196.         'handle the mouse stuff
  197.         WHILE _MOUSEINPUT
  198.             IF _MOUSEWHEEL = -1 THEN 'up?
  199.                 IF hlite - 1 < 0 THEN
  200.                     IF page > 0 THEN
  201.                         page = page - 1: hlite = maxHeight - 1: GOSUB update
  202.                     END IF
  203.                 ELSE
  204.                     hlite = hlite - 1: GOSUB update
  205.                 END IF
  206.             ELSEIF _MOUSEWHEEL = 1 THEN 'down?
  207.                 IF (hlite + 1) + page * maxHeight + lba <= uba THEN 'ok to move up
  208.                     IF hlite + 1 > maxHeight - 1 THEN
  209.                         page = page + 1: hlite = 0: GOSUB update
  210.                     ELSE
  211.                         hlite = hlite + 1: GOSUB update
  212.                     END IF
  213.                 END IF
  214.             END IF
  215.         WEND
  216.         mx = INT((_MOUSEX - locateColumn * 8) / 8) + 2: my = INT((_MOUSEY - locateRow * 16) / 16) + 2
  217.         IF _MOUSEBUTTON(1) THEN 'click contols or select array item
  218.             'clear mouse clicks
  219.             mb = _MOUSEBUTTON(1)
  220.             IF mb THEN 'clear it
  221.                 WHILE mb 'OK!
  222.                     IF _MOUSEINPUT THEN mb = _MOUSEBUTTON(1)
  223.                     _LIMIT 100
  224.                 WEND
  225.             END IF
  226.  
  227.             IF mx >= 1 AND mx <= maxWidth AND my >= 1 AND my <= maxHeight THEN
  228.                 choice = my + page * maxHeight + lba - 1 'select item clicked
  229.             ELSEIF mx >= 1 AND mx <= maxWidth AND my = 0 THEN 'page up or exit
  230.                 IF my = 0 AND (mx <= maxWidth AND mx >= maxWidth - 2) THEN 'exit sign
  231.                     EXIT DO 'escape plan for mouse click top right corner of display box
  232.                 ELSE 'PgUp bar clicked
  233.                     IF (page - 1) * maxHeight + lba >= lba THEN page = page - 1: GOSUB update
  234.                 END IF
  235.             ELSEIF mx >= 1 AND mx <= maxWidth AND my = maxHeight + 1 THEN 'page down bar clicked
  236.                 IF (page + 1) * maxHeight + lba <= uba THEN page = page + 1: GOSUB update
  237.             END IF
  238.         ELSE '   mouse over highlighting, only if mouse has moved!
  239.             IF mx >= 1 AND mx <= maxWidth AND my >= 1 AND my <= maxHeight THEN
  240.                 IF mx <> lastMX OR my <> lastMY THEN
  241.                     IF my - 1 <> hlite AND (my - 1 + page * maxHeight + lba <= uba) THEN
  242.                         hlite = my - 1
  243.                         lastMX = mx: lastMY = my
  244.                         GOSUB update
  245.                     END IF
  246.                 END IF
  247.             END IF
  248.         END IF
  249.         _LIMIT 200
  250.     LOOP UNTIL choice >= lba AND choice <= uba
  251.     getArrayItemNumber& = choice
  252.     COLOR fg, bg
  253.     'clear key presses
  254.     _KEYCLEAR
  255.     LOCATE curRow, curCol
  256.     'clear mouse clicks
  257.     mb = _MOUSEBUTTON(1)
  258.     IF mb THEN 'clear it
  259.         WHILE mb 'OK!
  260.             IF _MOUSEINPUT THEN mb = _MOUSEBUTTON(1)
  261.             _LIMIT 100
  262.         WEND
  263.     END IF
  264.     EXIT SUB
  265.  
  266.  
  267.     update: '--------------- display of array sections and controls on screen
  268.  
  269.     'fix hlite if it has dropped below last array item
  270.     WHILE hlite + page * maxHeight + lba > uba
  271.         hlite = hlite - 1
  272.     WEND
  273.  
  274.     'main display of array items at page * maxHeight (lines high)
  275.     FOR row = 0 TO maxHeight - 1
  276.         IF hlite = row THEN COLOR _RGB(200, 200, 255), _RGB32(0, 0, 88) ELSE COLOR _RGB32(0, 0, 88), _RGB(200, 200, 255)
  277.         LOCATE locateRow + row, locateColumn: PRINT clrStr$
  278.         index = row + page * maxHeight + lba
  279.         IF index >= lba AND index <= uba THEN
  280.             LOCATE locateRow + row, locateColumn
  281.             PRINT LEFT$(LTRIM$(STR$(index)) + ") " + arr(index), maxWidth)
  282.         END IF
  283.     NEXT
  284.  
  285.     'make page up and down bars to click, print PgUp / PgDn if available
  286.     COLOR _RGB32(200, 200, 255), _RGB32(0, 100, 50)
  287.     LOCATE locateRow - 1, locateColumn: PRINT SPACE$(maxWidth)
  288.     IF page <> 0 THEN LOCATE locateRow - 1, locateColumn: PRINT LEFT$(" Pg Up" + SPACE$(maxWidth), maxWidth)
  289.     LOCATE locateRow + maxHeight, locateColumn: PRINT SPACE$(maxWidth)
  290.     IF page <> INT(uba / maxHeight) THEN
  291.         LOCATE locateRow + maxHeight, locateColumn: PRINT LEFT$(" Pg Dn" + SPACE$(maxWidth), maxWidth)
  292.     END IF
  293.     'make exit sign for mouse click
  294.     COLOR _RGB32(255, 255, 255), _RGB32(200, 100, 0)
  295.     LOCATE locateRow - 1, locateColumn + maxWidth - 3
  296.     PRINT " X "
  297.  
  298.     'if a number selection has been started show it's build = b$
  299.     IF LEN(b$) THEN
  300.         COLOR _RGB(255, 255, 0), _RGB32(0, 0, 0)
  301.         LOCATE locateRow + maxHeight, locateColumn + maxWidth - LEN(b$) - 1
  302.         PRINT b$;
  303.     END IF
  304.     _DISPLAY
  305.     _LIMIT 100
  306.     RETURN
  307.  
  308.  


Oh hey! Just CHDIR to that Folder and then Tree code should work.
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 27, 2020, 04:57:41 pm
@SMcNeill

Hope you get flags fixed.

We don't have another Type problem between C code and QB64 with Flags and Length... do we?

Ie is a C Long the same as a QB64 Long? or worry about _signed and _unsigned?

Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 05:01:29 pm
Oh hey! Just CHDIR to that Folder and then Tree code should work.
yep! That worked! Good thinking, @bplus ! Your code finished in 4.66 seconds on my machine (5,557 directories), simply building and sorting the array whereas Steve's took over 9 seconds to do it. Nice! See, in this case, with PowerShell, mine works faster because it is a huge directory. Mine wins against yours coming in at 3.15 seconds because the large process can be handled by PowerShell much quicker. Small folders, your code. Big folders, my code. I wish I had a way to figure out whether or not to use yours or mine in a program depending on the number of subdirectories..... Hmmm.... I could do a PowerShell call to return the number of subdirectories and if it was greater than 1000 then run mine. Less than, run yours
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 27, 2020, 05:09:26 pm
yep! That worked! Good thinking, @bplus ! Your code finished in 4.66 seconds on my machine, simply building and sorting the array whereas Steve's took over 9 seconds to do it. Nice! See, in this case, with PowerShell, mine works faster because it is a huge directory. Mine wins against yours coming in at 3.15 seconds because the large process can be handled by PowerShell much quicker. Small folders, your code. Big folders, my code. I wish I had a way to figure out whether or not to use yours or mine in a program depending on the number of subdirectories..... Hmmm.... I could do a PowerShell call to return the number of subdirectories and if it was greater than 1000 then run mine. Less than, run yours

Well we'll see some better times I think when/if Steve can find still another fix for using the DirEntry.h method. I sure hope so because of cross platform potential but I bet we have some other issues to settle with Linux (at least) once we get Windows part working to it's full potential.

In meantime, what's 2-3 secs anyway? ;-))
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 05:10:41 pm
Well we'll see some better times I think when/if Steve can find still another fix for using the DirEntry.h method. I sure hope so because of cross platform potential but I bet we have some other issues to settle with Linux (at least) once we get Windows part working to it's full potential.

In meantime, what's 2-3 secs anyway? ;-))
Like I was telling Steve, PowerShell IS cross-platform now! I haven't had a chance to try it on Linux yet but I'll be sure to give it a whirl next week.
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 27, 2020, 05:14:42 pm
Like I was telling Steve, PowerShell IS cross-platform now! I haven't had a chance to try it on Linux yet but I'll be sure to give it a whirl next week.

Hey you have Linux? Do you have QB64 up and running in there?
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 05:19:47 pm
Hey you have Linux? Do you have QB64 up and running in there?
Yes, and yes, but it's a small laptop. It does, however, have a solid state drive so it might get good numbers.
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 05:56:51 pm
I get many failures trying to compile in Linux Ubuntu 18.04. Lots of "No such file or directory" Not sure how to get past that. Not a direntry issue, specifically. I'm not able to compile anything. Latest version of QB64 Linux from the website. Wait, I think I found the issue. Didn't run setup_lnx.sh. DURR!
Also, it looks like your ( @bplus ) program isn't displaying anything more than the first level of folders. It doesn't CHDIR to the next folder. Not sure if that is a Linux incompatibility with QB64. Trying Steve's next.... So I tried mine next and while I can use almost the exact same PowerShell command in Linux to get all directories (292 of them, so far), I can't get it to call PowerShell correctly from QB64. Not sure what the hang up is. So it seems like neither mine or bplus's code is compatible, just yet, which would probably mean that Steve's isn't either. I will check later this evening/tonight.
Title: Re: Development of a program for automatic search for music and movies
Post by: bplus on June 27, 2020, 06:44:53 pm
I know 2 things about Linux, it is case sensitive and it it is slash sensitive. I m sure we have been using the wrong slashes for it.
Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on June 27, 2020, 07:02:46 pm
@bplus But it does grab the first few directories from your program. So that can't be it. And my function for PowerShell works perfectly but not directly from QB64. This will take much investigation
Title: Re: Development of a program for automatic search for music and movies
Post by: Petr on July 05, 2020, 11:42:56 am
Hi guys,

I did not sleep. I really continued to develop and I will continue to do so. I used BPlus' method to create a directory tree and then my own procedure to divide into file name / folder name. I am shocked, when found that even though the file on the disk is named track01.mp3, the listing will return it with the name from ID3 (I don't understand where DirEntry read it) but it is a valid file name, it is accessible for both QB64 and Windows Media Player. So the confusion is absolutely perfect :)

Here is another intermediate development stage, now with a known bug, the first file is not displayed in the file list (this happens during development). The program screen is a very rough preview of what I mean by that. At the top there will be icons for color settings, for print output settings and so on, in the right part of the screen there will be information read from files. For the scrolling speed, I set the condition in future version, that the file selection must take 0.3 seconds and only then the information for the file will be read.

Since last time, the program has gained for us a bit. It now creates a configuration INI file (which cannot be set otherwise yet), reads the locale code, and sets unicode characters relative to it. So you should all have legible file names in your national alphabet. What do we have next? Labels in the program. They will be adjustable according to your speech settings. The configuration already thinks about this setting. So instead of English labels you will be able to set any other, for example German, Italian and so on.

The current version does not retrieve any detailed information from the files, it only displays file names. The enter key starts the default player. There will be a lot of work to be done.

Code: QB64: [Select]
  1. 'Tree builder recursive.bas  b+ Petr 2020-06-26  now with SMCNeill Fix!! add CHR$(0) to C string
  2.  
  3. _TITLE "Video and music detective V0.1 alpha"
  4. DEFLNG A-Z
  5.     FUNCTION load_dir& (s AS STRING)
  6.     FUNCTION has_next_entry& ()
  7.     SUB close_dir ()
  8.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  9.  
  10. REDIM mask(0) AS STRING
  11. DIM SHARED ForegroundColor~&, BackgroundColor~&, UsedCOUNTRY AS STRING * 20
  12. mask$ = "AVI,MPG,MKV,MPG,MPEG,WAV,ACC,MP4,MP3,MP2,VOC,S3M,MOD,XM,TS,MOV,AC3,WMA,WMV,FLAC,M3U," '           on end must be comma!
  13. ReturnMASK mask(), mask$
  14. path$ = SelectDIR$
  15. t = TIMER(0.001)
  16. PRINT "Loading config..."
  17. LoadINI SortStyle&, ViewStyle&
  18. PRINT "Detecting country (for MAPUNICODE use)...";
  19. IF WIN THEN COUNTRY = GetLCiD
  20. PRINT "Dynamic Library output: "; COUNTRY
  21. PRINT "Creating subdirectories + files list in "; path$
  22. MakeTree path$
  23. PRINT "Subdirectories + files list created in"; TIMER - t
  24. PRINT "total records: "; UBOUND(tree) 'is match with real number files and folders
  25.  
  26. PRINT "Sorting files by mask:"
  27.  
  28. REDIM SHARED RealFiles(100) AS STRING
  29. FOR m = LBOUND(mask) TO UBOUND(mask)
  30.     FOR s = LBOUND(tree) TO UBOUND(tree)
  31.         IF LCASE$(RIGHT$(Tree(s), 3)) = LCASE$(mask(m)) THEN
  32.             RealFiles(RealCount) = Tree(s)
  33.             RealCount = RealCount + 1
  34.             IF RealCount > UBOUND(realfiles) THEN REDIM _PRESERVE RealFiles(RealCount + 100) AS STRING
  35.         END IF
  36.     NEXT s
  37. REDIM _PRESERVE RealFiles(RealCount) AS STRING
  38. PRINT "Valid files now writed to array RealFiles. Size: "; UBOUND(realfiles)
  39. ERASE Tree
  40. PRINT "Loading font arial.ttf..."
  41. font = _LOADFONT("arial.ttf", 18, "bold")
  42. my& = _NEWIMAGE(1920, 1080, 32)
  43. _FONT font, my&
  44. REDIM FileName(0) AS STRING
  45. REDIM FolderName(0) AS STRING
  46. PRINT "Sorting..."
  47. SORT RealFiles(), SortStyle&
  48. PRINT "Sort done, Separating name to file name, folder name and full path..."
  49. SeparateFileName RealFiles(), FileName(), FolderName() 'RealFiles obsahuje plnou cestu k souboru.  FileName osahuje jen jmeno souboru. FolderName obsahuje jen jmeno prvniho nadrazeneho adresare. Indexy vsech se shoduji.
  50. PRINT "Mapping characters by country..."
  51.  
  52. CountryA
  53. SCREEN my&
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60. DrawMyScreen ForegroundColor~&, BackgroundColor~& 'static draw
  61. REDIM arr(0) AS STRING
  62. CopyARR FileName(), arr()
  63.  
  64.  
  65. '----------------------------------------------------------------------------------- file browsing init (now contains bug, first file in the list is not visible)
  66. x = 25: Y = 120: Lenght = 112: Height = 40: NumDirs = 0: UseWheel = 1: first = 1
  67. Lb = 0
  68. Le = Lb + Height
  69.  
  70.  
  71.  
  72.  
  73.  
  74. DO UNTIL k$ = CHR$(27)
  75.     ListColor& = Black '_RGB32(166, 244, 244)   nema ucinek
  76.     InPosColor& = Red '_RGB32(67, 72, 238)
  77.     DirColor& = Green '_RGB32(238, 22, 28)
  78.  
  79.     IF INSTR(1, _OS$, "WINDOWS") THEN sel$ = CHR$(92)
  80.     IF INSTR(1, _OS$, "LINUX") THEN sel$ = "/"
  81.     IF first = 0 THEN first = 1
  82.  
  83.     Up$ = CHR$(0) + CHR$(72)
  84.     Dn$ = CHR$(0) + CHR$(80)
  85.     PgUp$ = CHR$(0) + CHR$(73)
  86.     PgDn$ = CHR$(0) + CHR$(81)
  87.     Home$ = CHR$(0) + CHR$(71)
  88.     End$ = CHR$(0) + CHR$(79)
  89.     k$ = INKEY$
  90.  
  91.     oldposX = posX
  92.     IF first = 1 THEN oldposX = -1 ': first = 0
  93.  
  94.  
  95.     'mouse support ---
  96.         MoX = _MOUSEX: MoY = _MOUSEY
  97.         IF MoX > x AND MoX < x + ((Lenght + 4) * 8) AND MoY > Y AND MoY < Y + (Height * 20) + 40 THEN 'podle LINE
  98.             Poloha = _CEIL((MoY - Y - 20) / 20) 'pro mys
  99.             IF UseWheel THEN ' in function last parameter: 0 = use wheel, 1 = not use wheel
  100.                 SELECT CASE SGN(_MOUSEWHEEL)
  101.                     CASE -1: k$ = Up$
  102.                     CASE 1: k$ = Dn$
  103.                 END SELECT
  104.  
  105.             ELSE
  106.  
  107.  
  108.                 IF Poloha < posX - Lb THEN k$ = Up$
  109.                 IF Poloha > posX - Lb THEN k$ = Dn$
  110.             END IF
  111.             IF _MOUSEBUTTON(1) AND Poloha >= Lb AND Poloha <= Le THEN
  112.                 IF MoX < (8 * Lenght) + x THEN K& = 13: _DELAY .1
  113.  
  114.             END IF
  115.             IF _MOUSEBUTTON(1) AND MoX > (8 * Lenght) + x AND MoY < Y + 16 THEN 'mouse / up arrow
  116.                 k$ = Up$
  117.             END IF
  118.             IF _MOUSEBUTTON(1) AND MoX > (8 * Lenght) + x AND MoY > Y + (Height * 20) + 24 THEN 'mouse / down arrow
  119.                 k$ = Dn$
  120.             END IF
  121.         END IF
  122.  
  123.     WEND
  124.  
  125.     '------------------
  126.  
  127.  
  128.     ' --- keyboard inputs
  129.  
  130.  
  131.     SELECT CASE k$
  132.         CASE IS = Up$: posX = posX - 1: GU = 1: GD = 0: act = 1 'marks: Go down disabled, go up enbabled, act = clear screen and print it again
  133.         CASE IS = Dn$: posX = posX + 1: GU = 0: GD = 1: act = 1 'marks: Go down enabled, go up disabled
  134.         CASE IS = End$: posX = UBOUND(arr) - 1: Le = UBOUND(arr): Lb = UBOUND(arr) - Height: act = 1
  135.         CASE IS = Home$: posX = LBOUND(arr) + 1: Le = Height: Lb = LBOUND(arr): act = 1
  136.         CASE IS = PgUp$: posX = posX - Height: Lb = Lb - Height: Le = Lb + Height: act = 1
  137.         CASE IS = PgDn$: posX = posX + Height: Lb = Lb + Height: Le = Lb + Height: act = 1
  138.  
  139.  
  140.         CASE IS = CHR$(13): command = posX
  141.     END SELECT
  142.     ' -------------------
  143.  
  144.     'if is link selected (not dir):
  145.     IF UBOUND(arr) < 0 THEN PRINT "Invalid array size!!!": SLEEP 2: END
  146.     'end of bug repair
  147.  
  148.     IF posX <= 0 THEN posX = 0 ': Lb = 0: Le = Lb + height
  149.     IF posX > UBOUND(arr) THEN posX = UBOUND(arr)
  150.     IF oldposX <> posX OR first THEN
  151.         IF posX > Le AND GD THEN Lb = Lb + 1: Le = Le + 1
  152.         IF GU AND posX < Lb THEN Lb = Lb - 1: Le = Le - 1
  153.         textpos = 0
  154.         IF Lb < LBOUND(arr) THEN Lb = LBOUND(arr)
  155.  
  156.         IF Lb > UBOUND(arr) - Height THEN Lb = UBOUND(arr) - Height
  157.         IF Le > UBOUND(arr) THEN Le = UBOUND(arr)
  158.         '   IF Le - Lb > height THEN Le = Lb + height
  159.         REM     IF Lb > Le THEN EXIT FUNCTION
  160.  
  161.         IF first = 1 OR act THEN
  162.             LINE (x + 1, Y + 1)-(x + 3 + (8 * Lenght) + 16, Y + 3 + (20 * Height) + 38), White, BF 'erase screen
  163.             LINE (29, 1019)-(_WIDTH - 11, _HEIGHT - 11), White, BF
  164.  
  165.             rfs$ = RealFiles(posX)
  166.             IF LEN(rfs$) > 200 THEN rfs$ = LEFT$(rfs$, 197) + "..."
  167.             _PRINTSTRING (30, 1020), rfs$
  168.  
  169.             FOR V = Lb TO Le 'List Begin to List End
  170.                 '  K& = _KEYHIT
  171.  
  172.                 textpos = textpos + 20 'row is 20 pixel height
  173.                 IF V = posX THEN COLOR InPosColor&, ListColor&
  174.                 text$ = arr(V)
  175.                 IF LEN(text$) > Lenght - 20 THEN text$ = LEFT$(text$, Lenght - 20) + LTRIM$("...") ELSE text$ = text$ + SPACE$(Lenght - LEN(text$) - 1)
  176.                 _PRINTSTRING (x + 10, Y + textpos), text$
  177.                 COLOR ListColor&, Black '_RGB32(0, 0, 0)
  178.  
  179.  
  180.                 possss = posX + 1
  181.  
  182.                 GOTO notthia
  183.                 Posuvnik_V_Procentech! = (possss / UBOUND(arr))
  184.  
  185.                 '----------------------------------------------------------------------- extremne zpomaluje pri bezpodminecnem tisku
  186.                 WindowHeight = (23 + Height * 20) - 40
  187.                 OldGC = GC
  188.                 GC = Y + WindowHeight * Posuvnik_V_Procentech!
  189.  
  190.                 COLOR Black '_RGB32(0, 0, 0)
  191.                 _PRINTSTRING (x + 5 + Lenght * _PRINTWIDTH(STRING$(Lenght, "x")), OldGC + 22), CHR$(222)
  192.                 COLOR Black '_RGB32(127, 127, 127)
  193.                 _PRINTSTRING (x + 5 + Lenght * _FONTWIDTH, GC + 22), CHR$(222)
  194.  
  195.                 _PRINTSTRING (x + 5 + Lenght * _FONTWIDTH, Y + 7), CHR$(24)
  196.                 _PRINTSTRING (x + 5 + Lenght * _FONTWIDTH, 3 + Y + 20 + Height * 20), CHR$(25)
  197.                 '---------------------------------------------------------------------------
  198.                 notthia:
  199.             NEXT V
  200.             act = 0
  201.             k$ = ""
  202.             first = 2
  203.         END IF
  204.  
  205.     END IF
  206.  
  207.     LINE (x + 1, Y + 1)-(x + 3 + (8 * Lenght) + 16, Y + 3 + (20 * Height) + 38), , B
  208.     LINE (x + 4, Y + 4)-(x + (8 * Lenght) + 16, Y + (20 * Height) + 38), , B
  209.     Browse = posX
  210.  
  211.  
  212.     IF command THEN
  213.         SHELL _DONTWAIT RealFiles(command)
  214.         _SCREENCLICK MoX, MoY 'aby okno bylo stale aktivni a neprebiral ho Windows media player (moznost toto nastavit)      For stay your program window still active
  215.         command = 0
  216.         _DELAY .3
  217.     END IF
  218.     _DISPLAY
  219.     _LIMIT 100
  220.  
  221.  
  222.  
  223. SUB DrawMyScreen (backgroundcolor~&, foregroundcolor~&)
  224.     B = backgroundcolor~&
  225.     F = foregroundcolor~&
  226.     CLS , B
  227.  
  228.     LINE (10, 60)-(_WIDTH - 10, _HEIGHT - 10), F, B
  229.     LINE (10, 60)-(_WIDTH / 2, _HEIGHT - 110), F, B
  230.  
  231.     'Labels. Labels in this program can be set to your own language, use SETUP icon in program for creating own language file. NOT IMPLANTED IN THIS ALPHA VERSION
  232.     'index list:   (in english lng file)
  233.     '0 = file list:
  234.     '1 = file details:
  235.     '2 = Full path to file:
  236.  
  237.     COLOR Black, White
  238.     _PRINTSTRING (30, 80), Label(0) 'files list
  239.     _PRINTSTRING (990, 80), Label(1) 'file details
  240.     _PRINTSTRING (30, 990), Label(2) 'full path
  241.  
  242.  
  243.  
  244.  
  245.  
  246.  
  247.  
  248.  
  249.  
  250.  
  251.  
  252. FUNCTION GetLCiD
  253.     DECLARE DYNAMIC LIBRARY "Kernel32"
  254.         FUNCTION GetUserDefaultLCID%
  255.         FUNCTION GetSystemDefaultLangID%
  256.         SUB GetTimeZoneInformation (a AS INTEGER)
  257.     END DECLARE
  258.     GetLCiD = GetUserDefaultLCID%
  259.     IF GetUserDefaultLCID% = 0 AND GetSystemDefaultLangID% THEN GetLCiD = GetSystemDefaultLangID%
  260.     IF GetUserDefaultLCID% = 0 AND GetSystemDefaultLangID% = 0 THEN GetLCiD = -1 'error finding LCID
  261.  
  262.  
  263.  
  264.  
  265. SUB LoadINI (SortStyle&, ViewStyle&)
  266.     ff = FREEFILE
  267.  
  268.     IF _FILEEXISTS("MaV.ini") THEN
  269.         OPEN "MaV.ini" FOR BINARY AS ff
  270.         GET ff, , SortStyle&
  271.         GET ff, , ViewStyle&
  272.         GET ff, , ForegroundColor~&
  273.         GET ff, , BackgroundColor~&
  274.         GET ff, , UsedCOUNTRY$
  275.  
  276.     ELSE
  277.         OPEN "MaV.ini" FOR BINARY AS ff
  278.         SortStyle& = 1
  279.         ViewStyle& = 1
  280.         PUT ff, , SortStyle&
  281.         PUT ff, , ViewStyle&
  282.         BackgroundColor~& = Black
  283.         ForegroundColor~& = White
  284.         PUT #ff, , ForegroundColor~&
  285.         PUT #ff, , BackgroundColor~&
  286.         UsedCOUNTRY$ = "english"
  287.         PUT #ff, , UsedCOUNTRY$
  288.     END IF
  289.     CLOSE ff
  290.     LoadNationalLabels "MaV-" + _TRIM$(UsedCOUNTRY$) + ".lng"
  291.     REM  showarr Label()   'developing helper
  292.  
  293.  
  294.  
  295. SUB showarr (s() AS STRING)
  296.     FOR q = LBOUND(s) TO UBOUND(s)
  297.         PRINT s(q)
  298.         SLEEP
  299.     NEXT
  300.  
  301.  
  302.  
  303.  
  304.  
  305. SUB LoadNationalLabels (file AS STRING)
  306.     IF _FILEEXISTS(file$) THEN
  307.         ff = FREEFILE
  308.         OPEN file$ FOR INPUT AS ff
  309.         WHILE NOT EOF(ff)
  310.             LINE INPUT #ff, Label$
  311.             IF LEFT$(Label$, 2) <> "//" THEN
  312.                 Label(i) = Label$
  313.                 i = i + 1
  314.                 REDIM _PRESERVE Label(i) AS STRING
  315.             END IF
  316.         WEND
  317.     ELSE
  318.         _AUTODISPLAY
  319.         BEEP: PRINT file$; " - this language file not found. Starting in english!"
  320.         SLEEP 2
  321.         LoadNationalLabels "MaV-english.lng"
  322.     END IF
  323.  
  324.  
  325.  
  326.  
  327.  
  328.  
  329. SUB SORT (arr() AS STRING, HOW AS _BYTE) ' If is HOW zero, sort descending, if is HOW <> 0, sort ascending (this is modified Eoredson method).
  330.     DIM X AS LONG, Y AS LONG '
  331.     U& = UBOUND(arr)
  332.     DO UNTIL X = U&
  333.         Y = X + 1
  334.         DO UNTIL Y = U&
  335.             IF HOW THEN
  336.                 IF arr(X) > arr(Y) THEN SWAP arr(X), arr(Y)
  337.             ELSE
  338.                 IF arr(X) < arr(Y) THEN SWAP arr(X), arr(Y)
  339.             END IF
  340.             Y = Y + 1
  341.         LOOP
  342.         X = X + 1
  343.     LOOP
  344.  
  345. SUB ReturnMASK (mask() AS STRING, mask AS STRING)
  346.     P = INSTR(mask$, ",")
  347.     DO UNTIL P = 0
  348.         f$ = MID$(mask$, 1, P - 1)
  349.         mask$ = MID$(mask$, P + 1)
  350.         REDIM _PRESERVE mask(i) AS STRING
  351.         mask(i) = f$
  352.         i = i + 1
  353.         P = INSTR(mask$, ",")
  354.     LOOP
  355.  
  356. SUB SeparateFileName (SOURCEarr() AS STRING, DESTarrFileName() AS STRING, DESTarrFolderName() AS STRING) 'vse OK     (for case, when Folder name is album name and file name is soundtrack name)  Source is array with full path.
  357.     $IF WIN THEN
  358.         s$ = "\"
  359.     $ELSE
  360.         S$ = "/"
  361.     $END IF
  362.     REDIM DESTarrFileName(UBOUND(sourcearr)) AS STRING
  363.     REDIM DESTarrFolderName(UBOUND(sourcearr)) AS STRING
  364.     FOR sep = LBOUND(sourcearr) TO UBOUND(sourcearr)
  365.         DESTarrFileName(sep) = MID$(SOURCEarr(sep), _INSTRREV(SOURCEarr(sep), s$) + 1, LEN(SOURCEarr(sep)) - _INSTRREV(SOURCEarr(sep), s$))
  366.  
  367.         '        PRINT "Full path: ";
  368.         '        PRINT SOURCEarr(sep)
  369.  
  370.         '        PRINT "File name: ";
  371.         '        PRINT DESTarrFileName(sep)
  372.  
  373.         Miss$ = LEFT$(SOURCEarr(sep), LEN(SOURCEarr(sep)) - 1 - LEN(DESTarrFileName(sep)))
  374.         '        PRINT Miss$
  375.         DESTarrFolderName(sep) = MID$(Miss$, _INSTRREV(Miss$, s$) + 1, LEN(Miss$) - _INSTRREV(Miss$, s$))
  376.         '        PRINT "Folder name: ";
  377.         '        PRINT DESTarrFolderName(sep)
  378.         '        PRINT Miss(sep)
  379.     NEXT
  380.  
  381.  
  382. FUNCTION SelectDIR$
  383.         FUNCTION FindWindow& (BYVAL ClassName AS _OFFSET, WindowName$)
  384.     END DECLARE
  385.     hwnd& = _WINDOWHANDLE
  386.     TYPE BROWSEINFO '                    typedef struct _browseinfo 'Microsoft MSDN
  387.         hwndOwner AS _OFFSET '           HWND
  388.         pidlRoot AS _OFFSET '            PCIDLIST_ABSOLUTE
  389.         pszDisplayName AS _OFFSET '      LPTSTR
  390.         lpszTitle AS _OFFSET '           LPCTSTR
  391.         ulFlags AS _UNSIGNED LONG '      UINT
  392.         lpfn AS _OFFSET '                BFFCALLBACK
  393.         lParam AS _OFFSET '              LPARAM
  394.         iImage AS LONG '                 int
  395.     END TYPE '                           BROWSEINFO, *PBROWSEINFO, *LPBROWSEINFO;
  396.  
  397.     DECLARE DYNAMIC LIBRARY "shell32"
  398.         FUNCTION SHBrowseForFolder%& (x AS BROWSEINFO) '                           Microsoft MSDN
  399.         SUB SHGetPathFromIDList (BYVAL lpItem AS _OFFSET, BYVAL szDir AS _OFFSET) 'Microsoft MSDN
  400.     END DECLARE
  401.  
  402.     DIM b AS BROWSEINFO
  403.     b.hwndOwner = hwnd
  404.     DIM s AS STRING * 1024
  405.     b.pszDisplayName = _OFFSET(s$)
  406.     a$ = "Select folder for searching movies and music:" + CHR$(0)
  407.     b.lpszTitle = _OFFSET(a$)
  408.     DIM o AS _OFFSET
  409.     '    _SCREENHIDE
  410.     o = SHBrowseForFolder(b)
  411.     '    _SCREENSHOW
  412.     IF o THEN
  413.         '   Path$ = LEFT$(s$, INSTR(s$, CHR$(0)) - 1)
  414.         DIM s2 AS STRING * 1024
  415.         SHGetPathFromIDList o, _OFFSET(s2$)
  416.         SelectDIR$ = LEFT$(s2$, INSTR(s2$, CHR$(0)) - 1)
  417.     ELSE
  418.         PRINT "Directory not selected, using current directory"
  419.         SelectDIR$ = _CWD$
  420.     END IF
  421.  
  422.  
  423. SUB sAppend (arr() AS STRING, addItem$)
  424.     REDIM _PRESERVE arr(LBOUND(arr) TO UBOUND(arr) + 1) AS STRING
  425.     arr(UBOUND(arr)) = addItem$
  426.  
  427. SUB GetSubDirs (SearchDirectory AS STRING, DirList() AS STRING)
  428.     CONST IS_DIR = 1
  429.     DIM flags AS LONG, file_size AS LONG, length, nam$
  430.     IF load_dir(SearchDirectory + CHR$(0)) THEN 'Steve's fix here with CHR$(0) for C call
  431.         DO
  432.             length = has_next_entry
  433.             IF length > -1 THEN
  434.                 nam$ = SPACE$(length)
  435.                 get_next_entry nam$, flags, file_size
  436.                 IF nam$ <> "." AND nam$ <> ".." THEN
  437.                     'IF _DIREXISTS(SearchDirectory + "\" + nam$) THEN
  438.                     DirCount = DirCount + 1
  439.                     IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
  440.                     DirList(DirCount) = SearchDirectory + "\" + nam$
  441.                     'END IF
  442.                 END IF
  443.             END IF
  444.         LOOP UNTIL length = -1
  445.     END IF
  446.     close_dir 'Steve first fix that got navigator working
  447.     REDIM _PRESERVE DirList(DirCount)
  448.  
  449. SUB MakeTree (startDir AS STRING)
  450.     copyStart$ = startDir
  451.     REDIM D(100) AS STRING
  452.     GetSubDirs copyStart$, D()
  453.     FOR i = 1 TO UBOUND(D)
  454.         sAppend Tree(), D(i)
  455.         MakeTree D(i)
  456.     NEXT
  457.  
  458.  
  459. 'nation.bm   my FIRST attempt for INTERNATIONAL QB64 language support.
  460. 'function find code page need for correct text show on screen and set (if is right font used) unicode characters.
  461.  
  462.  
  463.  
  464. SUB CountryA
  465.     IF WIN THEN
  466.         SHELL _HIDE "powershell get-culture > nation.txt"
  467.         IF _FILEEXISTS("nation.txt") THEN
  468.             c = FREEFILE
  469.             OPEN "nation.txt" FOR BINARY AS #c
  470.             LINE$ = SPACE$(LOF(c))
  471.             GET #c, , LINE$
  472.             REM            IF lin = 4 THEN LCID_DECIMAL = VAL(LEFT$(LINE$, 4)): EXIT DO
  473.             CLOSE c
  474.  
  475.  
  476.             FOR numbers = 1 TO LEN(LINE$)
  477.                 char = ASC(LINE$, numbers)
  478.                 IF char >= 47 AND char <= 58 THEN nr$ = nr$ + CHR$(char)
  479.             NEXT
  480.  
  481.             LCID_DECIMAL = VAL(nr$)
  482.  
  483.             PRINT "Alpha version developing: CountryA PowerShell output is: "; LCID_DECIMAL
  484.  
  485.         ELSE
  486.             'try find with dynamic library:
  487.             LCID_DECIMAL = GetLCiD
  488.  
  489.         END IF
  490.     ELSE 'LINUX
  491.         SHELL _HIDE ("echo $LANG |cut -c1,2,4,5 > nation.txt")
  492.         ch = FREEFILE
  493.         OPEN "nation.txt" FOR INPUT AS ch
  494.         LINE INPUT #ch, nation$
  495.         CLOSE ch
  496.     END IF
  497.  
  498.     IF WIN THEN
  499.         SELECT CASE LCID_DECIMAL
  500.             '        language               code page
  501.             REM        CASE 0: lang$ = "English"
  502.             CASE 1078: lang$ = "Afrikaans": CP = 1252
  503.             CASE 1052: lang$ = "Albanian": CP = 1250
  504.             CASE 1118: lang$ = "Amharic": 'CP unknown
  505.             CASE 5121: lang$ = "Arabic - Algeria": CP = 1401: CPW = 1256
  506.             CASE 15361: lang$ = "Arabic - Bahrain": CP = 1256
  507.             CASE 3073: lang$ = "Arabic - Egypt": CP = 1256
  508.             CASE 2049: lang$ = "Arabic - Iraq": CP = 801: CPW = 1256
  509.             CASE 11265: lang$ = "Arabic - Jordan": CP = 1256
  510.             CASE 13313: lang$ = "Arabic - Kuwait": CP = 3401: CPW = 1256
  511.             CASE 12289: lang$ = "Arabic - Lebanon": CP = 3001: CPW = 1256
  512.             CASE 4097: lang$ = "Arabic - Libya": CP = 1001: CPW = 1256
  513.             CASE 6145: lang$ = "Arabic - Morocco": CP = 1801: CPW = 1256
  514.             CASE 8193: lang$ = "Arabic - Oman": CP = 2001: CP2 = 1256
  515.             CASE 16385: lang$ = "Arabic - Qatar": CP = 4001: CP2 = 1256
  516.             CASE 1025: lang$ = "Arabic - Saudi Arabia": CP = 401: CP2 = 1256
  517.             CASE 10241: lang$ = "Arabic - Syria": CP = 2801: CP2 = 1256
  518.             CASE 7169: lang$ = "Arabic - Tunisia": CP = 1256
  519.             CASE 14337: lang$ = "Arabic - United Arab Emirates": CP = 3801: CP2 = 1256
  520.             CASE 9217: lang$ = "Arabic - Yemen": CP = 2401: CP2 = 1256
  521.             CASE 1067: lang$ = "Armenian" 'CP unknown
  522.             CASE 1101: lang$ = "Assamese" 'CP uunknown
  523.             CASE 2092: lang$ = "Azeri - Cyrillic": CP = 1251
  524.             CASE 1068: lang$ = "Azeri - Latin": CP = 1254
  525.             CASE 1069: lang$ = "Basque": CP = 1252
  526.             CASE 1059: lang$ = "Belarusian": CP = 423: CP2 = 1251
  527.             CASE 2117: lang$ = "Bengali - Bangladesh": CP = 2117: CP2 = 845
  528.             CASE 1093: lang$ = "Bengali - India": CP = 445
  529.             CASE 5146: lang$ = "Bosnian" 'CP unknown
  530.             CASE 1026: lang$ = "Bulgarian": CP = 402: CP2 = 1251
  531.             CASE 1109: lang$ = "Burmese": CP = 455
  532.             CASE 1027: lang$ = "Catalan": CP = 403: CP2 = 1252
  533.             CASE 2052: lang$ = "Chinese - China": CP = 804
  534.             CASE 3076: lang$ = "Chinese - Hong Kong": 'CP unknown
  535.             CASE 5124: lang$ = "Chinese - Macau": CP = 1404
  536.             CASE 4100: lang$ = "Chinese - Singapore": CP = 1004
  537.             CASE 1028: lang$ = "Chinese - Taiwan": CP = 404
  538.             CASE 1050: lang$ = "Croatian": CP = 1250
  539.             CASE 1029: lang$ = "Czech": CP = 405: CP2 = 1250
  540.             CASE 1030: lang$ = "Danish": CP = 406: CP2 = 1252
  541.                 'Divehi  Dhivehi Maldivian   dv  dv unknown code and CP
  542.             CASE 2067: lang$ = "Dutch - Belgium": CP = 813: CP2 = 1252
  543.             CASE 1043: lang$ = "Dutch - Netherlands": CP = 413: CP2 = 1252
  544.             CASE 1126: lang$ = "Edo": CP = 466
  545.             CASE 3081: lang$ = "English - Australia": CP = 1252
  546.             CASE 10249: lang$ = "English - Belize": CP = 2809: CP2 = 1252
  547.             CASE 4105: lang$ = "English - Canada": CP = 1009: CP2 = 1252
  548.             CASE 9225: lang$ = "English - Caribbean": CP = 2409: CP2 = 1252
  549.             CASE 2057: lang$ = "English - Great Britain": CP = 809: CP2 = 1252
  550.             CASE 16393: lang$ = "English - India": CP = 4009
  551.             CASE 6153: lang$ = "English - Ireland": CP = 1809: CP2 = 1252
  552.             CASE 8201: lang$ = "English - Jamaica": CP = 2009: CP2 = 1252
  553.             CASE 5129: lang$ = "English - New Zealand": CP = 1409: CP2 = 1252
  554.             CASE 13321: lang$ = "English - Phillippines": CP = 3409: CP2 = 1252
  555.             CASE 7177: lang$ = "English - Southern Africa": CP = 1252
  556.             CASE 11273: lang$ = "English - Trinidad": CP = 1252
  557.             CASE 1033: lang$ = "English - United States": CP = 409: CP2 = 1252
  558.             CASE 12297: lang$ = "English - Zimbabwe": CP = 3009: CP2 = 1252
  559.             CASE 1061: lang$ = "Estonian": CP = 425: CP2 = 1257
  560.             CASE 1071: lang$ = "FYRO Macedonia": CP = 1251
  561.             CASE 1080: lang$ = "Faroese": CP = 438: CP2 = 1252
  562.             CASE 1065: lang$ = "Farsi - Persian": CP = 429: CP2 = 1256
  563.             CASE 1124: lang$ = "Filipino": CP = 464
  564.             CASE 1035: lang$ = "Finnish": CP = 1252
  565.             CASE 2060: lang$ = "French - Belgium": CP = 1252
  566.             CASE 11276: lang$ = "French - Cameron": 'unknown CP
  567.             CASE 3084: lang$ = "French - Canada": CP = 1252
  568.             CASE 9228: lang$ = "French - Congo": 'unknown CP
  569.             CASE 12300: lang$ = "French - Cote d'Ivoire" 'unknown CP
  570.             CASE 1036: lang$ = "French - France": CP = 1252
  571.             CASE 5132: lang$ = "French - Luxembourg": CP = 1252
  572.             CASE 13324: lang$ = "French - Mali" 'unknown CP
  573.             CASE 6156: lang$ = "French - Monaco": CP = 1252
  574.             CASE 14348: lang$ = "French - Morocco" 'cp unknown
  575.             CASE 10252: lang$ = "French - Senegal" 'cp unknown
  576.             CASE 4108: lang$ = "French - Switzerland": CP = 1252
  577.             CASE 7180: lang$ = "French - West Indies" 'CP unknown
  578.             CASE 1122: lang$ = "Frisian - Netherlands": CP = 462
  579.             CASE 2108: lang$ = "Gaelic - Ireland" 'CP unknown
  580.             CASE 1084: lang$ = "Gaelic - Scotland" 'CP unknown
  581.             CASE 1110: lang$ = "Galician": CP = 456: CP2 = 1252
  582.             CASE 1079: lang$ = "Georgian": CP = 437
  583.             CASE 3079: lang$ = "German - Austria": CP = 1252
  584.             CASE 1031: lang$ = "German - Germany": CP = 407: CP2 = 1252
  585.             CASE 5127: lang$ = "German - Liechtenstein": CP = 1407: CP2 = 1252
  586.             CASE 4103: lang$ = "German - Luxembourg": CP = 1007: CP2 = 1252
  587.             CASE 2055: lang$ = "German - Switzerland": CP = 807: CP2 = 1252
  588.             CASE 1032: lang$ = "Greek": CP = 408: CP2 = 1253
  589.             CASE 1140: lang$ = "Guarani - Paraguay": CP = 474
  590.             CASE 1095: lang$ = "Gujarati": CP = 447
  591.             CASE 1279: lang$ = "HID (Human Interface Device)": CP = -1
  592.             CASE 1037: lang$ = "Hebrew": CP = 1255
  593.             CASE 1081: lang$ = "Hindi": CP = 439
  594.             CASE 1038: lang$ = "Hungarian": CP = 1250
  595.             CASE 1039: lang$ = "Icelandic": CP = 1252
  596.             CASE 1136: lang$ = "Igbo - Nigeria": CP = 470
  597.             CASE 1057: lang$ = "Indonesian": CP = 421: CP2 = 1252
  598.             CASE 1040: lang$ = "Italian - Italy": CP = 410: CP2 = 1252
  599.             CASE 2064: lang$ = "Italian - Switzerland": CP = 810: CP2 = 1252
  600.             CASE 1041: lang$ = "Japanese ": CP = 411
  601.             CASE 1099: lang$ = "Kannada" ' CP unknown
  602.             CASE 1120: lang$ = "Kashmiri": CP = 460
  603.             CASE 1087: lang$ = "Kazakh": CP = 1251
  604.             CASE 1107: lang$ = "Khmer": CP = 453
  605.             CASE 1111: lang$ = "Konkani": CP = 457
  606.             CASE 1042: lang$ = "Korean": CP = 412
  607.             CASE 1088: lang$ = "Kyrgyz - Cyrillic": CP = 440: CP2 = 1251
  608.             CASE 1108: lang$ = "Lao": CP = 454
  609.             CASE 1142: lang$ = "Latin": CP = 476
  610.             CASE 1062: lang$ = "Latvian": CP = 426: CP2 = 1257
  611.             CASE 1063: lang$ = "Lithuanian": CP = 427: CP2 = 1257
  612.             CASE 2110: lang$ = "Malay - Brunei": CP = 1252
  613.             CASE 1086: lang$ = "Malay - Malaysia": CP = 1252
  614.             CASE 1100: lang$ = "Malayalam" 'unknown CP
  615.             CASE 1082: lang$ = "Maltese" 'unknown CP
  616.             CASE 1112: lang$ = "Manipuri": CP = 458
  617.             CASE 1153: lang$ = "Maori": CP = 481
  618.             CASE 1102: lang$ = "Marathi" 'unknown CP
  619.             CASE 2128: lang$ = "Mongolian": CP = 850
  620.             CASE 1104: lang$ = "Mongolian": CP = 450: CP2 = 1251
  621.             CASE 1121: lang$ = "Nepali": CP = 461
  622.             CASE 1044: lang$ = "Norwegian - Bokml": CP = 414: CP2 = 1252
  623.             CASE 2068: lang$ = "Norwegian - Nynorsk": CP = 814: CP2 = 1252
  624.             CASE 1096: lang$ = "Oriya": CP = 448
  625.             CASE 1045: lang$ = "Polish": CP = 415: CP2 = 1250
  626.             CASE 1046: lang$ = "Portuguese - Brazil": CP = 416: CP2 = 1252
  627.             CASE 2070: lang$ = "Portuguese - Portugal": CP = 816: CP2 = 1252
  628.             CASE 1094: lang$ = "Punjabi": CP = 446
  629.             CASE 1047: lang$ = "Raeto-Romance": CP = 417
  630.             CASE 2072: lang$ = "Romanian - Moldova": CP = 818
  631.             CASE 1048: lang$ = "Romanian - Romania": CP = 418: CP2 = 1250
  632.             CASE 1049: lang$ = "Russian": CP = 419: CP2 = 1251
  633.             CASE 2073: lang$ = "Russian - Moldova": CP = 2073: CP2 = 819
  634.             CASE 1083: lang$ = "Sami Lappish" 'CP unknown
  635.             CASE 1103: lang$ = "Sanskrit" 'CP unknown
  636.             CASE 3098: lang$ = "Serbian - Cyrillic": CP = 1251
  637.             CASE 2074: lang$ = "Serbian - Latin": CP = 1250
  638.             CASE 1072: lang$ = "Sesotho (Sutu)": CP = 430
  639.             CASE 1074: lang$ = "Setsuana": CP = 432
  640.             CASE 1113: lang$ = "Sindhi": CP = 459
  641.             CASE 1115: lang$ = "Sinhala Sinhalese" ' cp unknown
  642.             CASE 1051: lang$ = "Slovak": CP = 1250
  643.             CASE 1060: lang$ = "Slovenian": CP = 424: CP2 = 1250
  644.             CASE 1143: lang$ = "Somali": CP = 477
  645.             CASE 1070: lang$ = "Sorbian" 'unknown CP
  646.             CASE 11274: lang$ = "Spanish - Argentina": CP = 1252
  647.             CASE 16394: lang$ = "Spanish - Bolivia": CP = 1252
  648.             CASE 13322: lang$ = "Spanish - Chile": CP = 1252
  649.             CASE 9226: lang$ = "Spanish - Colombia": CP = 1252
  650.             CASE 5130: lang$ = "Spanish - Costa Rica": CP = 1252
  651.             CASE 7178: lang$ = "Spanish - Dominican Republic": CP = 1252
  652.             CASE 12298: lang$ = "Spanish - Ecuador": CP = 1252
  653.             CASE 17418: lang$ = "Spanish - El Salvador": CP = 1252
  654.             CASE 4106: lang$ = "Spanish - Guatemala": CP = 1252
  655.             CASE 18442: lang$ = "Spanish - Honduras": CP = 1252
  656.             CASE 2058: lang$ = "Spanish - Mexico": CP = 1252
  657.             CASE 19466: lang$ = "Spanish - Nicaragua": CP = 1252
  658.             CASE 6154: lang$ = "Spanish - Panama": CP = 1252
  659.             CASE 15370: lang$ = "Spanish - Paraguay": CP = 1252
  660.             CASE 10250: lang$ = "Spanish - Peru": CP = 1252
  661.             CASE 20490: lang$ = "Spanish - Puerto Rico": CP = 1252
  662.             CASE 1034: lang$ = "Spanish - Spain (Traditional)": CP = 1252
  663.             CASE 14346: lang$ = "Spanish - Uruguay": CP = 1252
  664.             CASE 8202: lang$ = "Spanish - Venezuela": CP = 1252
  665.             CASE 1089: lang$ = "Swahili": CP = 441: CP2 = 1252
  666.             CASE 2077: lang$ = "Swedish - Finland": CP = 1252
  667.             CASE 1053: lang$ = "Swedish - Sweden": CP = 1252
  668.             CASE 1114: lang$ = "Syriac" 'unknown CP
  669.             CASE 1064: lang$ = "Tajik": CP = 428
  670.             CASE 1097: lang$ = "Tamil": CP = 449
  671.             CASE 1092: lang$ = "Tatar": CP = 444: CP2 = 1251
  672.             CASE 1098: lang$ = "Telugu" 'CP unknown
  673.             CASE 1054: lang$ = "Thai" 'CP unkown
  674.             CASE 1105: lang$ = "Tibetan": CP = 451
  675.             CASE 1073: lang$ = "Tsonga": CP = 431
  676.             CASE 1055: lang$ = "Turkish": CP = 1254
  677.             CASE 1090: lang$ = "Turkmen": CP = 442
  678.             CASE 1058: lang$ = "Ukrainian": CP = 422: CP2 = 1251
  679.             CASE 8: lang$ = "Unicode": CP = -2 '    UTF-8
  680.             CASE 1056: lang$ = "Urdu": CP = 420: CP2 = 1256
  681.             CASE 2115: lang$ = "Uzbek - Cyrillic": CP = 843: CP2 = 1251
  682.             CASE 1091: lang$ = "Uzbek - Latin": CP = 443: CP2 = 1254
  683.             CASE 1075: lang$ = "Venda": CP = 433
  684.             CASE 1066: lang$ = "Vietnamese": CP = 1258
  685.             CASE 1106: lang$ = "Welsh": CP = 452
  686.             CASE 1076: lang$ = "Xhosa": CP = 434
  687.             CASE 1085: lang$ = "Yiddish" 'unknown CP
  688.             CASE 1077: lang$ = "Zulu": CP = 435
  689.         END SELECT
  690.     ELSE
  691.         SELECT CASE LCASE$(nation$) '                     without warranty! Here are NOT all countries!
  692.             CASE "af": lang$ = "Afrikaans": CP = 1252
  693.             CASE "sq": lang$ = "Albanian": CP = 1250
  694.             CASE "am": lang$ = "Amharic": 'CP unknown
  695.             CASE "ardz": lang$ = "Arabic - Algeria": CP = 1401: CPW = 1256
  696.             CASE "arbh": lang$ = "Arabic - Bahrain": CP = 1256
  697.             CASE "areg": lang$ = "Arabic - Egypt": CP = 1256
  698.             CASE "ariq": lang$ = "Arabic - Iraq": CP = 801: CPW = 1256
  699.             CASE "arjo": lang$ = "Arabic - Jordan": CP = 1256
  700.             CASE "arkw": lang$ = "Arabic - Kuwait": CP = 3401: CPW = 1256
  701.             CASE "arlb": lang$ = "Arabic - Lebanon": CP = 3001: CPW = 1256
  702.             CASE "arly": lang$ = "Arabic - Libya": CP = 1001: CPW = 1256
  703.             CASE "arma": lang$ = "Arabic - Morocco": CP = 1801: CPW = 1256
  704.             CASE "arom": lang$ = "Arabic - Oman": CP = 2001: CP2 = 1256
  705.             CASE "arqa": lang$ = "Arabic - Qatar": CP = 4001: CP2 = 1256
  706.             CASE "arsa": lang$ = "Arabic - Saudi Arabia": CP = 401: CP2 = 1256
  707.             CASE "arsy": lang$ = "Arabic - Syria": CP = 2801: CP2 = 1256
  708.             CASE "artn": lang$ = "Arabic - Tunisia": CP = 1256
  709.             CASE "arae": lang$ = "Arabic - United Arab Emirates": CP = 3801: CP2 = 1256
  710.             CASE "arye": lang$ = "Arabic - Yemen": CP = 2401: CP2 = 1256
  711.             CASE "hy": lang$ = "Armenian" 'CP unknown
  712.             CASE "as": lang$ = "Assamese" 'CP uunknown
  713.             CASE "asaz": lang$ = "Azeri - Cyrillic": CP = 1251
  714.             CASE "asaz": lang$ = "Azeri - Latin": CP = 1254
  715.             CASE "eu": lang$ = "Basque": CP = 1252
  716.             CASE "be": lang$ = "Belarusian": CP = 423: CP2 = 1251
  717.             CASE "bn": lang$ = "Bengali - Bangladesh": CP = 2117: CP2 = 845
  718.             CASE "bn": lang$ = "Bengali - India": CP = 445
  719.             CASE "bs": lang$ = "Bosnian" 'CP unknown
  720.             CASE "bg": lang$ = "Bulgarian": CP = 402: CP2 = 1251
  721.             CASE "my": lang$ = "Burmese": CP = 455
  722.             CASE "ca": lang$ = "Catalan": CP = 403: CP2 = 1252
  723.             CASE "zhcn": lang$ = "Chinese - China": CP = 804
  724.             CASE "zhhk": lang$ = "Chinese - Hong Kong": 'CP unknown
  725.             CASE "zhmo": lang$ = "Chinese - Macau": CP = 1404
  726.             CASE "zhsg": lang$ = "Chinese - Singapore": CP = 1004
  727.             CASE "zhtw": lang$ = "Chinese - Taiwan": CP = 404
  728.             CASE "hr": lang$ = "Croatian": CP = 1250
  729.             CASE "cscz": lang$ = "Czech": CP = 405: CP2 = 1250
  730.             CASE "da": lang$ = "Danish": CP = 406: CP2 = 1252
  731.                 'Divehi  Dhivehi Maldivian   dv  dv unknown code and CP
  732.             CASE "nlbe": lang$ = "Dutch - Belgium": CP = 813: CP2 = 1252
  733.             CASE "nlnl": lang$ = "Dutch - Netherlands": CP = 413: CP2 = 1252
  734.             CASE "": lang$ = "Edo": CP = 466
  735.             CASE "enau": lang$ = "English - Australia": CP = 1252
  736.             CASE "enbz": lang$ = "English - Belize": CP = 2809: CP2 = 1252
  737.             CASE "enca": lang$ = "English - Canada": CP = 1009: CP2 = 1252
  738.             CASE "encb": lang$ = "English - Caribbean": CP = 2409: CP2 = 1252
  739.             CASE "engb": lang$ = "English - Great Britain": CP = 809: CP2 = 1252
  740.             CASE "enin": lang$ = "English - India": CP = 4009
  741.             CASE "enie": lang$ = "English - Ireland": CP = 1809: CP2 = 1252
  742.             CASE "enjm": lang$ = "English - Jamaica": CP = 2009: CP2 = 1252
  743.             CASE "ennz": lang$ = "English - New Zealand": CP = 1409: CP2 = 1252
  744.             CASE "enph": lang$ = "English - Phillippines": CP = 3409: CP2 = 1252
  745.             CASE "enza": lang$ = "English - Southern Africa": CP = 1252
  746.             CASE "entt": lang$ = "English - Trinidad": CP = 1252
  747.             CASE "enus": lang$ = "English - United States": CP = 409: CP2 = 1252
  748.             CASE "": lang$ = "English - Zimbabwe": CP = 3009: CP2 = 1252
  749.             CASE "et": lang$ = "Estonian": CP = 425: CP2 = 1257
  750.             CASE "fo": lang$ = "Faroese": CP = 438: CP2 = 1252
  751.             CASE "fa": lang$ = "Farsi - Persian": CP = 429: CP2 = 1256
  752.             CASE "": lang$ = "Filipino": CP = 464
  753.             CASE "fi": lang$ = "Finnish": CP = 1252
  754.             CASE "frbe", "frca", "frfr", "frlu", "frch": lang$ = "French": CP = 1252
  755.             CASE "mk": lang$ = "FYRO Macedonia": CP = 1251
  756.             CASE "gdie": lang$ = "Gaelic - Ireland" 'CP unknown
  757.             CASE "gd": lang$ = "Gaelic - Scotland" 'CP unknown
  758.             CASE "gl": lang$ = "Galician": CP = 456: CP2 = 1252
  759.             CASE "ka": lang$ = "Georgian": CP = 437
  760.             CASE "deat", "dede", "deli", "delu", "dech": lang$ = "German": CP = 1252
  761.             CASE "el": lang$ = "Greek": CP = 408: CP2 = 1253
  762.             CASE "gn": lang$ = "Guarani - Paraguay": CP = 474
  763.             CASE "gu": lang$ = "Gujarati": CP = 447
  764.             CASE "he": lang$ = "Hebrew": CP = 1255
  765.             CASE "hi": lang$ = "Hindi": CP = 439
  766.             CASE "hu": lang$ = "Hungarian": CP = 1250
  767.             CASE "is": lang$ = "Icelandic": CP = 1252
  768.             CASE "id": lang$ = "Indonesian": CP = 421: CP2 = 1252
  769.             CASE "itit": lang$ = "Italian - Italy": CP = 410: CP2 = 1252
  770.             CASE "itch": lang$ = "Italian - Switzerland": CP = 810: CP2 = 1252
  771.             CASE "ja": lang$ = "Japanese ": CP = 411
  772.             CASE "kn": lang$ = "Kannada" ' CP unknown
  773.             CASE "ks": lang$ = "Kashmiri": CP = 460
  774.             CASE "kk": lang$ = "Kazakh": CP = 1251
  775.             CASE "km": lang$ = "Khmer": CP = 453
  776.             CASE "ko": lang$ = "Korean": CP = 412
  777.             CASE "lo": lang$ = "Lao": CP = 454
  778.             CASE "la": lang$ = "Latin": CP = 476
  779.             CASE "lv": lang$ = "Latvian": CP = 426: CP2 = 1257
  780.             CASE "lt": lang$ = "Lithuanian": CP = 427: CP2 = 1257
  781.             CASE "msbn": lang$ = "Malay - Brunei": CP = 1252
  782.             CASE "msmy": lang$ = "Malay - Malaysia": CP = 1252
  783.             CASE "ml": lang$ = "Malayalam" 'unknown CP
  784.             CASE "mt": lang$ = "Maltese" 'unknown CP
  785.             CASE "mi": lang$ = "Maori": CP = 481
  786.             CASE "mr": lang$ = "Marathi" 'unknown CP
  787.             CASE "mn": lang$ = "Mongolian": CP = 450: CP2 = 1251
  788.             CASE "ne": lang$ = "Nepali": CP = 461
  789.             CASE "nono": lang$ = "Norwegian - Bokml": CP = 414: CP2 = 1252
  790.             CASE "or": lang$ = "Oriya": CP = 448
  791.             CASE "pl": lang$ = "Polish": CP = 415: CP2 = 1250
  792.             CASE "ptbr": lang$ = "Portuguese - Brazil": CP = 416: CP2 = 1252
  793.             CASE "ptpt": lang$ = "Portuguese - Portugal": CP = 816: CP2 = 1252
  794.             CASE "pa": lang$ = "Punjabi": CP = 446
  795.             CASE "rm": lang$ = "Raeto-Romance": CP = 417
  796.             CASE "romo": lang$ = "Romanian - Moldova": CP = 818
  797.             CASE "ro": lang$ = "Romanian - Romania": CP = 418: CP2 = 1250
  798.             CASE "ru": lang$ = "Russian": CP = 419: CP2 = 1251
  799.             CASE "rumo": lang$ = "Russian - Moldova": CP = 2073: CP2 = 819
  800.             CASE "sa": lang$ = "Sanskrit" 'CP unknown
  801.             CASE "srsp": lang$ = "Serbian - Cyrillic": CP = 1251
  802.             CASE "tn": lang$ = "Setsuana": CP = 432
  803.             CASE "sd": lang$ = "Sindhi": CP = 459
  804.             CASE "si": lang$ = "Sinhala Sinhalese" ' cp unknown
  805.             CASE "sk": lang$ = "Slovak": CP = 1250
  806.             CASE "sl": lang$ = "Slovenian": CP = 424: CP2 = 1250
  807.             CASE "so": lang$ = "Somali": CP = 477
  808.             CASE "sb": lang$ = "Sorbian" 'unknown CP
  809.             CASE "esar": lang$ = "Spanish - Argentina": CP = 1252
  810.             CASE "esbo": lang$ = "Spanish - Bolivia": CP = 1252
  811.             CASE "escl": lang$ = "Spanish - Chile": CP = 1252
  812.             CASE "esco": lang$ = "Spanish - Colombia": CP = 1252
  813.             CASE "escr": lang$ = "Spanish - Costa Rica": CP = 1252
  814.             CASE "esdo": lang$ = "Spanish - Dominican Republic": CP = 1252
  815.             CASE "esec": lang$ = "Spanish - Ecuador": CP = 1252
  816.             CASE "essv": lang$ = "Spanish - El Salvador": CP = 1252
  817.             CASE "esgt": lang$ = "Spanish - Guatemala": CP = 1252
  818.             CASE "eshn": lang$ = "Spanish - Honduras": CP = 1252
  819.             CASE "esmx": lang$ = "Spanish - Mexico": CP = 1252
  820.             CASE "esni": lang$ = "Spanish - Nicaragua": CP = 1252
  821.             CASE "espa": lang$ = "Spanish - Panama": CP = 1252
  822.             CASE "espy": lang$ = "Spanish - Paraguay": CP = 1252
  823.             CASE "espe": lang$ = "Spanish - Peru": CP = 1252
  824.             CASE "espr": lang$ = "Spanish - Puerto Rico": CP = 1252
  825.             CASE "eses": lang$ = "Spanish - Spain (Traditional)": CP = 1252
  826.             CASE "esuy": lang$ = "Spanish - Uruguay": CP = 1252
  827.             CASE "esve": lang$ = "Spanish - Venezuela": CP = 1252
  828.             CASE "sw": lang$ = "Swahili": CP = 441: CP2 = 1252
  829.             CASE "swfi": lang$ = "Swedish - Finland": CP = 1252
  830.             CASE "svse": lang$ = "Swedish - Sweden": CP = 1252
  831.             CASE "tg": lang$ = "Tajik": CP = 428
  832.             CASE "ta": lang$ = "Tamil": CP = 449
  833.             CASE "tr": lang$ = "Tatar": CP = 444: CP2 = 1251
  834.             CASE "te": lang$ = "Telugu" 'CP unknown
  835.             CASE "th": lang$ = "Thai" 'CP unkown
  836.             CASE "bo": lang$ = "Tibetan": CP = 451
  837.             CASE "ts": lang$ = "Tsonga": CP = 431
  838.             CASE "tr": lang$ = "Turkish": CP = 1254
  839.             CASE "tk": lang$ = "Turkmen": CP = 442
  840.             CASE "uk": lang$ = "Ukrainian": CP = 422: CP2 = 1251
  841.             CASE "utf8": lang$ = "Unicode": CP = -2 '    UTF-8
  842.             CASE "ur": lang$ = "Urdu": CP = 420: CP2 = 1256
  843.             CASE "uzuz": lang$ = "Uzbek - Cyrillic": CP = 843: CP2 = 1251
  844.             CASE "vi": lang$ = "Vietnamese": CP = 1258
  845.             CASE "cy": lang$ = "Welsh": CP = 452
  846.             CASE "xh": lang$ = "Xhosa": CP = 434
  847.             CASE "yi": lang$ = "Yiddish" 'unknown CP
  848.             CASE "zu": lang$ = "Zulu": CP = 435
  849.         END SELECT
  850.     END IF
  851.  
  852.     IF CPW THEN CP2 = CPW
  853.     IF CP2 THEN language = CP2 ELSE language = CP
  854.     IF language = 0 THEN PRINT "Sorry, unknown Code Page for your country.": END
  855.  
  856.  
  857.     REM _FONT _LOADFONT("cyberbit.ttf", 15, "monospace")
  858.  
  859.     SELECT CASE language '                                  set DATA READ to correct position (without it read READ first DATA block and ignore his name)
  860.         CASE 437: RESTORE Microsoft_pc_cp437
  861.         CASE 737: RESTORE Microsoft_pc_cp737
  862.         CASE 775: RESTORE Microsoft_pc_cp775
  863.         CASE 850: RESTORE Microsoft_pc_cp850
  864.         CASE 852: RESTORE Microsoft_pc_cp852
  865.         CASE 855: RESTORE Microsoft_pc_cp855
  866.         CASE 857: RESTORE Microsoft_pc_cp857
  867.         CASE 860: RESTORE Microsoft_pc_cp860
  868.         CASE 861: RESTORE Microsoft_pc_cp861
  869.         CASE 862: RESTORE Microsoft_pc_cp862
  870.         CASE 863: RESTORE Microsoft_pc_cp863
  871.         CASE 864: RESTORE Microsoft_pc_cp864
  872.         CASE 865: RESTORE Microsoft_pc_cp865
  873.         CASE 866: RESTORE Microsoft_pc_cp866
  874.         CASE 869: RESTORE Microsoft_pc_cp869
  875.         CASE 874: RESTORE Microsoft_pc_cp874
  876.         CASE 1250: RESTORE Microsoft_windows_cp1250
  877.         CASE 1251: RESTORE Microsoft_windows_cp1251
  878.         CASE 1252: RESTORE Microsoft_windows_cp1252
  879.         CASE 1253: RESTORE Microsoft_windows_cp1253
  880.         CASE 1254: RESTORE Microsoft_windows_cp1254
  881.         CASE 1255: RESTORE Microsoft_windows_cp1255
  882.         CASE 1256: RESTORE Microsoft_windows_cp1256
  883.         CASE 1257: RESTORE Microsoft_windows_cp1257
  884.         CASE 1258: RESTORE Microsoft_windows_cp1258
  885.     END SELECT
  886.  
  887.  
  888.  
  889.  
  890.     FOR ASCIIcode = 128 TO 255
  891.         SELECT CASE language
  892.             CASE 437: READ Microsoft_pc_cp437: _MAPUNICODE Microsoft_pc_cp437 TO ASCIIcode
  893.             CASE 737: READ Microsoft_pc_cp737: _MAPUNICODE Microsoft_pc_cp737 TO ASCIIcode
  894.             CASE 775: READ Microsoft_pc_cp775: _MAPUNICODE Microsoft_pc_cp775 TO ASCIIcode
  895.             CASE 850: READ Microsoft_pc_cp850: _MAPUNICODE Microsoft_pc_cp850 TO ASCIIcode
  896.             CASE 852: READ Microsoft_pc_cp852: _MAPUNICODE Microsoft_pc_cp852 TO ASCIIcode
  897.             CASE 855: READ Microsoft_pc_cp855: _MAPUNICODE Microsoft_pc_cp855 TO ASCIIcode
  898.             CASE 857: READ Microsoft_pc_cp857: _MAPUNICODE Microsoft_pc_cp857 TO ASCIIcode
  899.             CASE 860: READ Microsoft_pc_cp860: _MAPUNICODE Microsoft_pc_cp860 TO ASCIIcode
  900.             CASE 861: READ Microsoft_pc_cp861: _MAPUNICODE Microsoft_pc_cp861 TO ASCIIcode
  901.             CASE 862: READ Microsoft_pc_cp862: _MAPUNICODE Microsoft_pc_cp862 TO ASCIIcode
  902.             CASE 863: READ Microsoft_pc_cp863: _MAPUNICODE Microsoft_pc_cp863 TO ASCIIcode
  903.             CASE 864: READ Microsoft_pc_cp864: _MAPUNICODE Microsoft_pc_cp864 TO ASCIIcode
  904.             CASE 865: READ Microsoft_pc_cp865: _MAPUNICODE Microsoft_pc_cp865 TO ASCIIcode
  905.             CASE 866: READ Microsoft_pc_cp866: _MAPUNICODE Microsoft_pc_cp866 TO ASCIIcode
  906.             CASE 869: READ Microsoft_pc_cp869: _MAPUNICODE Microsoft_pc_cp869 TO ASCIIcode
  907.             CASE 874: READ Microsoft_pc_cp874: _MAPUNICODE Microsoft_pc_cp874 TO ASCIIcode
  908.             CASE 1250: READ Microsoft_windows_cp1250: _MAPUNICODE Microsoft_windows_cp1250 TO ASCIIcode
  909.             CASE 1251: READ Microsoft_windows_cp1251: _MAPUNICODE Microsoft_windows_cp1251 TO ASCIIcode
  910.             CASE 1252: READ Microsoft_windows_cp1252: _MAPUNICODE Microsoft_windows_cp1252 TO ASCIIcode
  911.             CASE 1253: READ Microsoft_windows_cp1253: _MAPUNICODE Microsoft_windows_cp1253 TO ASCIIcode
  912.             CASE 1254: READ Microsoft_windows_cp1254: _MAPUNICODE Microsoft_windows_cp1254 TO ASCIIcode
  913.             CASE 1255: READ Microsoft_windows_cp1255: _MAPUNICODE Microsoft_windows_cp1255 TO ASCIIcode
  914.             CASE 1256: READ Microsoft_windows_cp1256: _MAPUNICODE Microsoft_windows_cp1256 TO ASCIIcode
  915.             CASE 1257: READ Microsoft_windows_cp1257: _MAPUNICODE Microsoft_windows_cp1257 TO ASCIIcode
  916.             CASE 1258: READ Microsoft_windows_cp1258: _MAPUNICODE Microsoft_windows_cp1258 TO ASCIIcode
  917.         END SELECT
  918.     NEXT
  919.     KILL "nation.txt"
  920.  
  921.     PRINT "Detected nation:"; lang$; ", code pages: "; CP; ", "; CP2
  922.     'INPUT "input text with your national characters, if is displayed wrong, try other font (set it in line 392):"; text$
  923.  
  924.  
  925.     f2 = FREEFILE
  926.     OPEN "test.txt" FOR INPUT AS #f2
  927.     WHILE NOT EOF(f2)
  928.         LINE INPUT #f2, text$
  929.         PRINT text$
  930.     WEND
  931.     CLOSE f2
  932.  
  933.     Microsoft_pc_cp437:
  934.     DATA 199,252,233,226,228,224,229,231,234,235,232,239,238,236,196,197
  935.     DATA 201,230,198,244,246,242,251,249,255,214,220,162,163,165,8359,402
  936.     DATA 225,237,243,250,241,209,170,186,191,8976,172,189,188,161,171,187
  937.     DATA 9617,9618,9619,9474,9508,9569,9570,9558,9557,9571,9553,9559,9565,9564,9563,9488
  938.     DATA 9492,9524,9516,9500,9472,9532,9566,9567,9562,9556,9577,9574,9568,9552,9580,9575
  939.     DATA 9576,9572,9573,9561,9560,9554,9555,9579,9578,9496,9484,9608,9604,9612,9616,9600
  940.     DATA 945,223,915,960,931,963,181,964,934,920,937,948,8734,966,949,8745
  941.     DATA 8801,177,8805,8804,8992,8993,247,8776,176,8729,183,8730,8319,178,9632,160
  942.  
  943.     Microsoft_pc_cp737:
  944.     DATA 913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928
  945.     DATA 929,931,932,933,934,935,936,937,945,946,947,948,949,950,951,952
  946.     DATA 953,954,955,956,957,958,959,960,961,963,962,964,965,966,967,968
  947.     DATA 9617,9618,9619,9474,9508,9569,9570,9558,9557,9571,9553,9559,9565,9564,9563,9488
  948.     DATA 9492,9524,9516,9500,9472,9532,9566,9567,9562,9556,9577,9574,9568,9552,9580,9575
  949.     DATA 9576,9572,9573,9561,9560,9554,9555,9579,9578,9496,9484,9608,9604,9612,9616,9600
  950.     DATA 969,940,941,942,970,943,972,973,971,974,902,904,905,906,908,910
  951.     DATA 911,177,8805,8804,938,939,247,8776,176,8729,183,8730,8319,178,9632,160
  952.  
  953.     Microsoft_pc_cp775:
  954.     DATA 262,252,233,257,228,291,229,263,322,275,342,343,299,377,196,197
  955.     DATA 201,230,198,333,246,290,162,346,347,214,220,248,163,216,215,164
  956.     DATA 256,298,243,379,380,378,8221,166,169,174,172,189,188,321,171,187
  957.     DATA 9617,9618,9619,9474,9508,260,268,280,278,9571,9553,9559,9565,302,352,9488
  958.     DATA 9492,9524,9516,9500,9472,9532,370,362,9562,9556,9577,9574,9568,9552,9580,381
  959.     DATA 261,269,281,279,303,353,371,363,382,9496,9484,9608,9604,9612,9616,9600
  960.     DATA 211,223,332,323,245,213,181,324,310,311,315,316,326,274,325,8217
  961.     DATA 173,177,8220,190,182,167,247,8222,176,8729,183,185,179,178,9632,160
  962.  
  963.     Microsoft_pc_cp850:
  964.     DATA 199,252,233,226,228,224,229,231,234,235,232,239,238,236,196,197
  965.     DATA 201,230,198,244,246,242,251,249,255,214,220,248,163,216,215,402
  966.     DATA 225,237,243,250,241,209,170,186,191,174,172,189,188,161,171,187
  967.     DATA 9617,9618,9619,9474,9508,193,194,192,169,9571,9553,9559,9565,162,165,9488
  968.     DATA 9492,9524,9516,9500,9472,9532,227,195,9562,9556,9577,9574,9568,9552,9580,164
  969.     DATA 240,208,202,203,200,305,205,206,207,9496,9484,9608,9604,166,204,9600
  970.     DATA 211,223,212,210,245,213,181,254,222,218,219,217,253,221,175,180
  971.     DATA 173,177,8215,190,182,167,247,184,176,168,183,185,179,178,9632,160
  972.  
  973.     Microsoft_pc_cp852:
  974.     DATA 199,252,233,226,228,367,263,231,322,235,336,337,238,377,196,262
  975.     DATA 201,313,314,244,246,317,318,346,347,214,220,356,357,321,215,269
  976.     DATA 225,237,243,250,260,261,381,382,280,281,172,378,268,351,171,187
  977.     DATA 9617,9618,9619,9474,9508,193,194,282,350,9571,9553,9559,9565,379,380,9488
  978.     DATA 9492,9524,9516,9500,9472,9532,258,259,9562,9556,9577,9574,9568,9552,9580,164
  979.     DATA 273,272,270,203,271,327,205,206,283,9496,9484,9608,9604,354,366,9600
  980.     DATA 211,223,212,323,324,328,352,353,340,218,341,368,253,221,355,180
  981.     DATA 173,733,731,711,728,167,247,184,176,168,729,369,344,345,9632,160
  982.  
  983.     Microsoft_pc_cp855:
  984.     DATA 1106,1026,1107,1027,1105,1025,1108,1028,1109,1029,1110,1030,1111,1031,1112,1032
  985.     DATA 1113,1033,1114,1034,1115,1035,1116,1036,1118,1038,1119,1039,1102,1070,1098,1066
  986.     DATA 1072,1040,1073,1041,1094,1062,1076,1044,1077,1045,1092,1060,1075,1043,171,187
  987.     DATA 9617,9618,9619,9474,9508,1093,1061,1080,1048,9571,9553,9559,9565,1081,1049,9488
  988.     DATA 9492,9524,9516,9500,9472,9532,1082,1050,9562,9556,9577,9574,9568,9552,9580,164
  989.     DATA 1083,1051,1084,1052,1085,1053,1086,1054,1087,9496,9484,9608,9604,1055,1103,9600
  990.     DATA 1071,1088,1056,1089,1057,1090,1058,1091,1059,1078,1046,1074,1042,1100,1068,8470
  991.     DATA 173,1099,1067,1079,1047,1096,1064,1101,1069,1097,1065,1095,1063,167,9632,160
  992.  
  993.     Microsoft_pc_cp857:
  994.     DATA 199,252,233,226,228,224,229,231,234,235,232,239,238,305,196,197
  995.     DATA 201,230,198,244,246,242,251,249,304,214,220,248,163,216,350,351
  996.     DATA 225,237,243,250,241,209,286,287,191,174,172,189,188,161,171,187
  997.     DATA 9617,9618,9619,9474,9508,193,194,192,169,9571,9553,9559,9565,162,165,9488
  998.     DATA 9492,9524,9516,9500,9472,9532,227,195,9562,9556,9577,9574,9568,9552,9580,164
  999.     DATA 186,170,202,203,200,0,205,206,207,9496,9484,9608,9604,166,204,9600
  1000.     DATA 211,223,212,210,245,213,181,0,215,218,219,217,236,255,175,180
  1001.     DATA 173,177,0,190,182,167,247,184,176,168,183,185,179,178,9632,160
  1002.  
  1003.     Microsoft_pc_cp860:
  1004.     DATA 199,252,233,226,227,224,193,231,234,202,232,205,212,236,195,194
  1005.     DATA 201,192,200,244,245,242,218,249,204,213,220,162,163,217,8359,211
  1006.     DATA 225,237,243,250,241,209,170,186,191,210,172,189,188,161,171,187
  1007.     DATA 9617,9618,9619,9474,9508,9569,9570,9558,9557,9571,9553,9559,9565,9564,9563,9488
  1008.     DATA 9492,9524,9516,9500,9472,9532,9566,9567,9562,9556,9577,9574,9568,9552,9580,9575
  1009.     DATA 9576,9572,9573,9561,9560,9554,9555,9579,9578,9496,9484,9608,9604,9612,9616,9600
  1010.     DATA 945,223,915,960,931,963,181,964,934,920,937,948,8734,966,949,8745
  1011.     DATA 8801,177,8805,8804,8992,8993,247,8776,176,8729,183,8730,8319,178,9632,160
  1012.  
  1013.     Microsoft_pc_cp861:
  1014.     DATA 199,252,233,226,228,224,229,231,234,235,232,208,240,222,196,197
  1015.     DATA 201,230,198,244,246,254,251,221,253,214,220,248,163,216,8359,402
  1016.     DATA 225,237,243,250,193,205,211,218,191,8976,172,189,188,161,171,187
  1017.     DATA 9617,9618,9619,9474,9508,9569,9570,9558,9557,9571,9553,9559,9565,9564,9563,9488
  1018.     DATA 9492,9524,9516,9500,9472,9532,9566,9567,9562,9556,9577,9574,9568,9552,9580,9575
  1019.     DATA 9576,9572,9573,9561,9560,9554,9555,9579,9578,9496,9484,9608,9604,9612,9616,9600
  1020.     DATA 945,223,915,960,931,963,181,964,934,920,937,948,8734,966,949,8745
  1021.     DATA 8801,177,8805,8804,8992,8993,247,8776,176,8729,183,8730,8319,178,9632,160
  1022.  
  1023.     Microsoft_pc_cp862:
  1024.     DATA 1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503
  1025.     DATA 1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,162,163,165,8359,402
  1026.     DATA 225,237,243,250,241,209,170,186,191,8976,172,189,188,161,171,187
  1027.     DATA 9617,9618,9619,9474,9508,9569,9570,9558,9557,9571,9553,9559,9565,9564,9563,9488
  1028.     DATA 9492,9524,9516,9500,9472,9532,9566,9567,9562,9556,9577,9574,9568,9552,9580,9575
  1029.     DATA 9576,9572,9573,9561,9560,9554,9555,9579,9578,9496,9484,9608,9604,9612,9616,9600
  1030.     DATA 945,223,915,960,931,963,181,964,934,920,937,948,8734,966,949,8745
  1031.     DATA 8801,177,8805,8804,8992,8993,247,8776,176,8729,183,8730,8319,178,9632,160
  1032.  
  1033.     Microsoft_pc_cp863:
  1034.     DATA 199,252,233,226,194,224,182,231,234,235,232,239,238,8215,192,167
  1035.     DATA 201,200,202,244,203,207,251,249,164,212,220,162,163,217,219,402
  1036.     DATA 166,180,243,250,168,184,179,175,206,8976,172,189,188,190,171,187
  1037.     DATA 9617,9618,9619,9474,9508,9569,9570,9558,9557,9571,9553,9559,9565,9564,9563,9488
  1038.     DATA 9492,9524,9516,9500,9472,9532,9566,9567,9562,9556,9577,9574,9568,9552,9580,9575
  1039.     DATA 9576,9572,9573,9561,9560,9554,9555,9579,9578,9496,9484,9608,9604,9612,9616,9600
  1040.     DATA 4023,945,223,915,960,931,963,181,964,934,920,937,948,8734,966,949,8745
  1041.     DATA 8801,177,8805,8804,8992,8993,247,8776,176,8729,183,8730,8319,178,9632,160
  1042.  
  1043.     Microsoft_pc_cp864:
  1044.     DATA 176,183,8729,8730,9618,9472,9474,9532,9508,9516,9500,9524,9488,9484,9492,9496
  1045.     DATA 946,8734,966,177,189,188,8776,171,187,65271,65272,0,0,65275,65276,0
  1046.     DATA 160,173,65154,163,164,65156,0,0,65166,65167,65173,65177,1548,65181,65185,65189
  1047.     DATA 1632,1633,1634,1635,1636,1637,1638,1639,1640,1641,65233,1563,65201,65205,65209,1567
  1048.     DATA 162,65152,65153,65155,65157,65226,65163,65165,65169,65171,65175,65179,65183,65187,65191,65193
  1049.     DATA 65195,65197,65199,65203,65207,65211,65215,65217,65221,65227,65231,166,172,247,215,65225
  1050.     DATA 1600,65235,65239,65243,65247,65251,65255,65259,65261,65263,65267,65213,65228,65230,65229,65249
  1051.     DATA 65149,1617,65253,65257,65260,65264,65266,65232,65237,65269,65270,65245,65241,65265,9632,0
  1052.  
  1053.     Microsoft_pc_cp865:
  1054.     DATA 199,252,233,226,228,224,229,231,234,235,232,239,238,236,196,197
  1055.     DATA 201,230,198,244,246,242,251,249,255,214,220,248,163,216,8359,402
  1056.     DATA 225,237,243,250,241,209,170,186,191,8976,172,189,188,161,171,164
  1057.     DATA 9617,9618,9619,9474,9508,9569,9570,9558,9557,9571,9553,9559,9565,9564,9563,9488
  1058.     DATA 9492,9524,9516,9500,9472,9532,9566,9567,9562,9556,9577,9574,9568,9552,9580,9575
  1059.     DATA 9576,9572,9573,9561,9560,9554,9555,9579,9578,9496,9484,9608,9604,9612,9616,9600
  1060.     DATA 945,223,915,960,931,963,181,964,934,920,937,948,8734,966,949,8745
  1061.     DATA 8801,177,8805,8804,8992,8993,247,8776,176,8729,183,8730,8319,178,9632,160
  1062.  
  1063.     Microsoft_pc_cp866:
  1064.     DATA 1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055
  1065.     DATA 1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071
  1066.     DATA 1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087
  1067.     DATA 9617,9618,9619,9474,9508,9569,9570,9558,9557,9571,9553,9559,9565,9564,9563,9488
  1068.     DATA 9492,9524,9516,9500,9472,9532,9566,9567,9562,9556,9577,9574,9568,9552,9580,9575
  1069.     DATA 9576,9572,9573,9561,9560,9554,9555,9579,9578,9496,9484,9608,9604,9612,9616,9600
  1070.     DATA 1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103
  1071.     DATA 1025,1105,1028,1108,1031,1111,1038,1118,176,8729,183,8730,8470,164,9632,160
  1072.  
  1073.     Microsoft_pc_cp869:
  1074.     DATA 0,0,0,0,0,0,902,0,183,172,166,8216,8217,904,8213,905
  1075.     DATA 906,938,908,0,0,910,939,169,911,178,179,940,163,941,942,943
  1076.     DATA 970,912,972,973,913,914,915,916,917,918,919,189,920,921,171,187
  1077.     DATA 9617,9618,9619,9474,9508,922,923,924,925,9571,9553,9559,9565,926,927,9488
  1078.     DATA 9492,9524,9516,9500,9472,9532,928,929,9562,9556,9577,9574,9568,9552,9580,931
  1079.     DATA 932,933,934,935,936,937,945,946,947,9496,9484,9608,9604,948,949,9600
  1080.     DATA 950,951,952,953,954,955,956,957,958,959,960,961,963,962,964,900
  1081.     DATA 173,177,965,966,967,167,968,901,176,168,969,971,944,974,9632,160
  1082.  
  1083.     Microsoft_pc_cp874:
  1084.     DATA 8364,0,0,0,0,8230,0,0,0,0,0,0,0,0,0,0
  1085.     DATA 0,8216,8217,8220,8221,8226,8211,8212,0,0,0,0,0,0,0,0
  1086.     DATA 160,3585,3586,3587,3588,3589,3590,3591,3592,3593,3594,3595,3596,3597,3598,3599
  1087.     DATA 3600,3601,3602,3603,3604,3605,3606,3607,3608,3609,3610,3611,3612,3613,3614,3615
  1088.     DATA 3616,3617,3618,3619,3620,3621,3622,3623,3624,3625,3626,3627,3628,3629,3630,3631
  1089.     DATA 3632,3633,3634,3635,3636,3637,3638,3639,3640,3641,3642,0,0,0,0,3647
  1090.     DATA 3648,3649,3650,3651,3652,3653,3654,3655,3656,3657,3658,3659,3660,3661,3662,3663
  1091.     DATA 3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3674,3675,0,0,0,0
  1092.  
  1093.     Microsoft_windows_cp1250:
  1094.     DATA 8364,0,8218,0,8222,8230,8224,8225,0,8240,352,8249,346,356,381,377
  1095.     DATA 0,8216,8217,8220,8221,8226,8211,8212,0,8482,353,8250,347,357,382,378
  1096.     DATA 160,711,728,321,164,260,166,167,168,169,350,171,172,173,174,379
  1097.     DATA 176,177,731,322,180,181,182,183,184,261,351,187,317,733,318,380
  1098.     DATA 340,193,194,258,196,313,262,199,268,201,280,203,282,205,206,270
  1099.     DATA 272,323,327,211,212,336,214,215,344,366,218,368,220,221,354,223
  1100.     DATA 341,225,226,259,228,314,263,231,269,233,281,235,283,237,238,271
  1101.     DATA 273,324,328,243,244,337,246,247,345,367,250,369,252,253,355,729
  1102.  
  1103.     Microsoft_windows_cp1251:
  1104.     DATA 1026,1027,8218,1107,8222,8230,8224,8225,8364,8240,1033,8249,1034,1036,1035,1039
  1105.     DATA 1106,8216,8217,8220,8221,8226,8211,8212,0,8482,1113,8250,1114,1116,1115,1119
  1106.     DATA 160,1038,1118,1032,164,1168,166,167,1025,169,1028,171,172,173,174,1031
  1107.     DATA 176,177,1030,1110,1169,181,182,183,1105,8470,1108,187,1112,1029,1109,1111
  1108.     DATA 1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055
  1109.     DATA 1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071
  1110.     DATA 1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087
  1111.     DATA 1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103
  1112.  
  1113.     Microsoft_windows_cp1252:
  1114.     DATA 8364,0,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,0,381,0
  1115.     DATA 0,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,0,382,376
  1116.     DATA 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175
  1117.     DATA 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191
  1118.     DATA 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207
  1119.     DATA 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223
  1120.     DATA 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239
  1121.     DATA 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
  1122.  
  1123.     Microsoft_windows_cp1253:
  1124.     DATA 8364,0,8218,402,8222,8230,8224,8225,0,8240,0,8249,0,0,0,0
  1125.     DATA 0,8216,8217,8220,8221,8226,8211,8212,0,8482,0,8250,0,0,0,0
  1126.     DATA 160,901,902,163,164,165,166,167,168,169,0,171,172,173,174,8213
  1127.     DATA 176,177,178,179,900,181,182,183,904,905,906,187,908,189,910,911
  1128.     DATA 912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927
  1129.     DATA 928,929,0,931,932,933,934,935,936,937,938,939,940,941,942,943
  1130.     DATA 944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959
  1131.     DATA 960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,0
  1132.  
  1133.     Microsoft_windows_cp1254:
  1134.     DATA 8364,0,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,0,0,0
  1135.     DATA 0,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,0,0,376
  1136.     DATA 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175
  1137.     DATA 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191
  1138.     DATA 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207
  1139.     DATA 286,209,210,211,212,213,214,215,216,217,218,219,220,304,350,223
  1140.     DATA 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239
  1141.     DATA 287,241,242,243,244,245,246,247,248,249,250,251,252,305,351,255
  1142.  
  1143.     Microsoft_windows_cp1255:
  1144.     DATA 8364,0,8218,402,8222,8230,8224,8225,710,8240,0,8249,0,0,0,0
  1145.     DATA 0,8216,8217,8220,8221,8226,8211,8212,732,8482,0,8250,0,0,0,0
  1146.     DATA 160,161,162,163,8362,165,166,167,168,169,215,171,172,173,174,175
  1147.     DATA 176,177,178,179,180,181,182,183,184,185,247,187,188,189,190,191
  1148.     DATA 1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,0,1467,1468,1469,1470,1471
  1149.     DATA 1472,1473,1474,1475,1520,1521,1522,1523,1524,0,0,0,0,0,0,0
  1150.     DATA 1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503
  1151.     DATA 1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,0,0,8206,8207,0
  1152.  
  1153.     Microsoft_windows_cp1256:
  1154.     DATA 8364,1662,8218,402,8222,8230,8224,8225,710,8240,1657,8249,338,1670,1688,1672
  1155.     DATA 1711,8216,8217,8220,8221,8226,8211,8212,1705,8482,1681,8250,339,8204,8205,1722
  1156.     DATA 160,1548,162,163,164,165,166,167,168,169,1726,171,172,173,174,175
  1157.     DATA 176,177,178,179,180,181,182,183,184,185,1563,187,188,189,190,1567
  1158.     DATA 1729,1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583
  1159.     DATA 1584,1585,1586,1587,1588,1589,1590,215,1591,1592,1593,1594,1600,1601,1602,1603
  1160.     DATA 224,1604,226,1605,1606,1607,1608,231,232,233,234,235,1609,1610,238,239
  1161.     DATA 1611,1612,1613,1614,244,1615,1616,247,1617,249,1618,251,252,8206,8207,1746
  1162.  
  1163.     Microsoft_windows_cp1257:
  1164.     DATA 8364,0,8218,0,8222,8230,8224,8225,0,8240,0,8249,0,168,711,184
  1165.     DATA 0,8216,8217,8220,8221,8226,8211,8212,0,8482,0,8250,0,175,731,0
  1166.     DATA 160,0,162,163,164,0,166,167,216,169,342,171,172,173,174,198
  1167.     DATA 176,177,178,179,180,181,182,183,248,185,343,187,188,189,190,230
  1168.     DATA 260,302,256,262,196,197,280,274,268,201,377,278,290,310,298,315
  1169.     DATA 352,323,325,211,332,213,214,215,370,321,346,362,220,379,381,223
  1170.     DATA 261,303,257,263,228,229,281,275,269,233,378,279,291,311,299,316
  1171.     DATA 353,324,326,243,333,245,246,247,371,322,347,363,252,380,382,729
  1172.  
  1173.     Microsoft_windows_cp1258:
  1174.     DATA 8364,0,8218,402,8222,8230,8224,8225,710,8240,0,8249,338,0,0,0
  1175.     DATA 0,8216,8217,8220,8221,8226,8211,8212,732,8482,0,8250,339,0,0,376
  1176.     DATA 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175
  1177.     DATA 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191
  1178.     DATA 192,193,194,258,196,197,198,199,200,201,202,203,768,205,206,207
  1179.     DATA 272,209,777,211,212,416,214,215,216,217,218,219,220,431,771,223
  1180.     DATA 224,225,226,259,228,229,230,231,232,233,234,235,769,237,238,239
  1181.     DATA 273,241,803,243,244,417,246,247,248,249,250,251,252,432,8363,255
  1182.  
  1183.     IF INSTR(_OS$, "WINDOWS") THEN WIN = 1 ELSE WIN = 0
  1184.  
  1185. SUB CopyARR (s() AS STRING, d() AS STRING)
  1186.     REDIM d(UBOUND(s)) AS STRING
  1187.     start = LBOUND(s)
  1188.     DO UNTIL start = UBOUND(s)
  1189.         start = start + 1
  1190.         d(start) = s(start)
  1191.  
  1192.     LOOP
  1193.  
  1194.  

Title: Re: Development of a program for automatic search for music and movies
Post by: SpriggsySpriggs on August 17, 2020, 08:26:46 pm
Hey guys! So I made up some code using my API functions I've been working on that might come in handy with this project for a program that keeps track of movies and such.

Here is a screenshot of the demo app working:
  [ This attachment cannot be displayed inline in 'Print Page' view ]  

And here is the source code for the demo:
Code: QB64: [Select]
  1.  
  2. _TITLE "OMDB API - Poster"
  3. _CONSOLETITLE "OMDB API - Movie Info"
  4.  
  5. DIM poster AS STRING
  6. URL = "http://www.omdbapi.com/?apikey=a6984482&t="
  7. DIM movietitle AS STRING
  8.  
  9. LINE INPUT "Title of Movie: ", movietitle
  10.  
  11. omdb = API_request(URL + FormatAsHTTP(movietitle), "omdbreq")
  12. poster = GetKey(omdb, "Poster")
  13. DIM i&
  14. i& = OnlineImage(poster, "testposter")
  15. IF i& <> -1 AND i& <> 0 THEN
  16.     SCREEN i&
  17.  
  18. PRINT "Movie:        "; GetKey(omdb, "Title")
  19. PRINT "Release Date: "; GetKey(omdb, "Released")
  20. PRINT GetKey(omdb, "Plot")
  21.  
  22.  
  23.  
  24.  
  25.     FUNCTION URLDownloadToFileA (BYVAL pCaller AS LONG, szURL AS STRING, szFileName AS STRING, BYVAL dwReserved AS LONG, BYVAL lpfnCB AS LONG)
  26.  
  27. FUNCTION OnlineImage& (URL AS STRING, File AS STRING)
  28.     DIM apireq AS STRING
  29.     DIM a%
  30.     DIM i&
  31.     a% = URLDownloadToFileA(0, URL + CHR$(0), File + CHR$(0), 0, 0)
  32.     DIM U AS INTEGER
  33.     U = FREEFILE
  34.     OPEN File FOR BINARY AS #U
  35.     IF LOF(U) <> 0 THEN
  36.         CLOSE #U
  37.         i& = _LOADIMAGE(File, 32)
  38.         KILL File
  39.     ELSE
  40.         i& = 0
  41.         CLOSE #U
  42.         KILL File
  43.     END IF
  44.     OnlineImage = i&
  45.  
  46. FUNCTION API_request$ (URL AS STRING, File AS STRING)
  47.     DIM apireq AS STRING
  48.     DIM a%
  49.     a% = URLDownloadToFileA(0, URL + CHR$(0), File + CHR$(0), 0, 0)
  50.     DIM U AS INTEGER
  51.     U = FREEFILE
  52.     OPEN File FOR BINARY AS #U
  53.     IF LOF(U) <> 0 THEN
  54.         apireq = SPACE$(LOF(U))
  55.         GET #U, , apireq
  56.         CLOSE #U
  57.         KILL File
  58.     ELSE
  59.         CLOSE #U
  60.         KILL File
  61.         API_request = ""
  62.         EXIT FUNCTION
  63.     END IF
  64.     API_request = apireq
  65.  
  66. 'FUNCTION CURL$ (URL AS STRING, File AS STRING)
  67. '    DIM curlrequest AS STRING
  68. '    SHELL _HIDE "curl " + CHR$(34) + URL + CHR$(34) + " -o " + CHR$(34) + File + CHR$(34)
  69. '    DIM U AS INTEGER
  70. '    U = FREEFILE
  71. '    OPEN File FOR BINARY AS #U
  72. '    IF LOF(U) <> 0 THEN
  73. '        curlrequest = SPACE$(LOF(U))
  74. '        GET #U, , curlrequest
  75. '        CLOSE #U
  76. '        KILL File
  77. '    ELSE
  78. '        CLOSE #U
  79. '        KILL File
  80. '        CURL = ""
  81. '        EXIT FUNCTION
  82. '    END IF
  83. '    CURL = curlrequest
  84. 'END FUNCTION
  85.  
  86. FUNCTION GetKey$ (JSON AS STRING, keyname AS STRING)
  87.     DIM jkey AS STRING
  88.     jkey = JSON
  89.     IF INSTR(jkey, CHR$(34) + keyname + CHR$(34)) THEN
  90.         jkey = MID$(jkey, INSTR(jkey, CHR$(34) + keyname + CHR$(34)) + LEN(keyname))
  91.         jkey = MID$(jkey, INSTR(jkey, ":") + 1)
  92.         jkey = String.Replace(jkey, "\" + CHR$(34), "'")
  93.         IF MID$(jkey, 1, 1) = CHR$(34) THEN
  94.             jkey = MID$(jkey, 2)
  95.         END IF
  96.         jkey = MID$(jkey, 1, INSTR(jkey, CHR$(34)) - 1)
  97.         IF RIGHT$(jkey, 1) = "," THEN
  98.             jkey = MID$(jkey, 1, LEN(jkey) - 1)
  99.         END IF
  100.     ELSE
  101.         GetKey = ""
  102.     END IF
  103.     GetKey = jkey
  104.  
  105. SUB GetAllKey (JSON AS STRING, keyname AS STRING, ParseKey() AS STRING)
  106.     DIM unchangejson AS STRING
  107.     DIM jkey AS STRING
  108.     DIM x
  109.     unchangejson = JSON
  110.     DO
  111.         IF INSTR(unchangejson, CHR$(34) + keyname + CHR$(34)) THEN
  112.             x = x + 1
  113.             REDIM _PRESERVE ParseKey(x) AS STRING
  114.             unchangejson = MID$(unchangejson, INSTR(unchangejson, CHR$(34) + keyname + CHR$(34)) + LEN(keyname))
  115.             jkey = unchangejson
  116.             jkey = MID$(jkey, INSTR(jkey, ":") + 1)
  117.             jkey = String.Replace(jkey, "\" + CHR$(34), "'")
  118.             IF MID$(jkey, 1, 1) = CHR$(34) THEN
  119.                 jkey = MID$(jkey, 2)
  120.             END IF
  121.             jkey = MID$(jkey, 1, INSTR(jkey, CHR$(34)) - 1)
  122.             IF RIGHT$(jkey, 1) = "," THEN
  123.                 jkey = MID$(jkey, 1, LEN(jkey) - 1)
  124.             END IF
  125.             ParseKey(x) = jkey
  126.         END IF
  127.     LOOP UNTIL INSTR(unchangejson, CHR$(34) + keyname + CHR$(34)) = 0
  128.  
  129. FUNCTION String.Replace$ (a AS STRING, b AS STRING, c AS STRING)
  130.     DIM j
  131.     DIM r
  132.     DIM r$
  133.     j = INSTR(a, b)
  134.     IF j > 0 THEN
  135.         r$ = LEFT$(a, j - 1) + c + String.Replace(RIGHT$(a, LEN(a) - j + 1 - LEN(b)), b, c)
  136.     ELSE
  137.         r$ = a
  138.     END IF
  139.     String.Replace = r$
  140.  
  141. FUNCTION FormatAsHTTP$ (paramater AS STRING)
  142.     DIM Request AS STRING
  143.     Request = paramater
  144.     DIM start%
  145.     DIM position%
  146.     start% = 1
  147.     DO
  148.         position% = INSTR(start%, Request, " ")
  149.         IF position% THEN
  150.             MID$(Request, position%, 1) = "+"
  151.             start% = position% + 1
  152.         END IF
  153.     LOOP UNTIL position% = 0
  154.     Request = String.Replace(Request, "'", "%27")
  155.     FormatAsHTTP = Request