annotate foosdk/sdk/foobar2000/foo_sample/IO.cpp @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
1
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
1 #include "stdafx.h"
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
2 #include <helpers/file_list_helper.h>
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
3 #include <helpers/filetimetools.h>
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
4 #include <memory>
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
5
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
6 void RunIOTest() {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
7 try {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
8 auto request = http_client::get()->create_request("GET");
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
9 request->run("https://www.foobar2000.org", fb2k::noAbort);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
10 } catch (std::exception const & e) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
11 popup_message::g_show( PFC_string_formatter() << "Network test failure:\n" << e, "Information");
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
12 return;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
13 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
14 popup_message::g_show(PFC_string_formatter() << "Network test OK", "Information");
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
15 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
16
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
17 namespace { // anon namespace local classes for good measure
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
18 class tpc_copyFiles : public threaded_process_callback {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
19 public:
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
20 tpc_copyFiles ( metadb_handle_list_cref items, const char * pathTo ) : m_pathTo(pathTo), m_outFS(filesystem::get(pathTo)) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
21 m_lstFiles.init_from_list( items );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
22 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
23
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
24 void on_init(ctx_t p_wnd) override {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
25 // Main thread, called before run() gets started
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
26 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
27 void run(threaded_process_status & p_status, abort_callback & p_abort) override {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
28 // Worker thread
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
29 for( size_t fileWalk = 0; fileWalk < m_lstFiles.get_size(); ++ fileWalk ) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
30
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
31 // always do this in every timeconsuming loop
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
32 // will throw exception_aborted if the user pushed 'cancel' on us
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
33 p_abort.check();
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
34
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
35 const char * inPath = m_lstFiles[fileWalk];
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
36 p_status.set_progress(fileWalk, m_lstFiles.get_size());
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
37 p_status.set_item_path( inPath );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
38
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
39 try {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
40 workWithFile(inPath, p_abort);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
41 } catch(exception_aborted) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
42 // User abort, just bail
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
43 throw;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
44 } catch(std::exception const & e) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
45 m_errorLog << "Could not copy: " << file_path_display(inPath) << ", reason: " << e << "\n";
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
46 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
47
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
48 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
49 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
50
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
51 void workWithFile( const char * inPath, abort_callback & abort ) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
52 FB2K_console_formatter() << "File: " << file_path_display(inPath);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
53
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
54 // Filesystem API for inPath
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
55 const filesystem::ptr inFS = filesystem::get( inPath );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
56 pfc::string8 inFN;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
57 // Extract filename+extension according to this filesystem's rules
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
58 // If it's HTTP or so, there might be ?key=value that needs stripping
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
59 inFS->extract_filename_ext(inPath, inFN);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
60
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
61 pfc::string8 outPath = m_pathTo;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
62 // Suffix with outFS path separator. On Windows local filesystem this is always a backslash.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
63 outPath.end_with( m_outFS->pathSeparator() );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
64 outPath += inFN;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
65
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
66 const double openTimeout = 1.0; // wait and keep retrying for up to 1 second in case of a sharing violation error
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
67
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
68 // Not strictly needed, but we do it anyway
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
69 // Acquire a read lock on the file, so anyone trying to acquire a write lock will just wait till we have finished
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
70 auto inLock = file_lock_manager::get()->acquire_read( inPath, abort );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
71
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
72 auto inFile = inFS->openRead( inPath, abort, openTimeout );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
73
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
74 // Let's toy around with inFile
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
75 {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
76 auto stats = inFile->get_stats(abort);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
77
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
78 if ( stats.m_size != filesize_invalid ) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
79 FB2K_console_formatter() << "Size: " << pfc::format_file_size_short(stats.m_size);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
80 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
81 if ( stats.m_timestamp != filetimestamp_invalid ) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
82 FB2K_console_formatter() << "Last modified: " << format_filetimestamp(stats.m_timestamp);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
83 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
84 pfc::string8 contentType;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
85 if ( inFile->get_content_type( contentType ) ) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
86 FB2K_console_formatter() << "Content type: " << contentType;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
87 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
88
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
89 uint8_t buffer[256];
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
90 size_t got = inFile->read(buffer, sizeof(buffer), abort);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
91
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
92 if ( got > 0 ) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
93 FB2K_console_formatter() << "Header bytes: " << pfc::format_hexdump( buffer, got );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
94 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
95
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
96 if ( inFile->is_remote() ) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
97 FB2K_console_formatter() << "File is remote";
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
98 } else {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
99 FB2K_console_formatter() << "File is local";
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
100 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
101
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
102 // For seekable files, reopen() seeks to the beginning.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
103 // For nonseekable stream, reopen() restarts reading the stream.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
104 inFile->reopen(abort);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
105 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
106
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
107 // This is a glorified strcmp() for file paths.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
108 if ( metadb::path_compare( inPath, outPath ) == 0 ) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
109 // Same path, go no further. Specifically don't attempt acquiring a writelock because that will never complete, unless user aborted.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
110 FB2K_console_formatter() << "Input and output paths are the same - not copying!";
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
111 return;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
112 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
113
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
114 // Required to write to files being currently played.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
115 // See file_lock_manager documentation for details.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
116 auto outLock = file_lock_manager::get()->acquire_write( outPath, abort );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
117
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
118 // WARNING : if a file exists at outPath prior to this, it will be reset to zero bytes (win32 CREATE_ALWAYS semantics)
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
119 auto outFile = m_outFS->openWriteNew(outPath, abort, openTimeout);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
120
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
121
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
122
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
123 try {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
124 // Refer to g_transfer_file implementation details in the SDK for lowlevel reading & writing details
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
125 file::g_transfer_file(inFile, outFile, abort);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
126 } catch(...) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
127
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
128 if ( inFile->is_remote() ) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
129 // Remote file was being downloaded? Suppress deletion of incomplete output
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
130 throw;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
131 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
132 // Failed for some reason
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
133 // Release our destination file hadnle
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
134 outFile.release();
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
135
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
136 // .. and delete the incomplete file
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
137 try {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
138 auto & noAbort = fb2k::noAbort; // we might be being aborted, don't let that prevent deletion
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
139 m_outFS->remove( outPath, noAbort );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
140 } catch(...) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
141 // disregard errors - just report original copy error
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
142 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
143
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
144 throw; // rethrow the original copy error
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
145 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
146
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
147 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
148
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
149 void on_done(ctx_t p_wnd, bool p_was_aborted) override {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
150 // All done, main thread again
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
151
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
152 if (! p_was_aborted && m_errorLog.length() > 0 ) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
153 popup_message::g_show(m_errorLog, "Information");
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
154 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
155
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
156 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
157
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
158
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
159
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
160 // This is a helper class that generates a sorted list of unique file paths in this metadb_handle_list.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
161 // The metadb_handle_list might contain duplicate tracks or multiple subsongs in the same file. m_lstFiles will list each file only once.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
162 file_list_helper::file_list_from_metadb_handle_list m_lstFiles;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
163
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
164 // Destination path
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
165 const pfc::string8 m_pathTo;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
166 // Destination filesystem API. Obtained via filesystem::get() with destination path.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
167 const filesystem::ptr m_outFS;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
168
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
169 // Error log
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
170 pfc::string_formatter m_errorLog;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
171 };
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
172 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
173
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
174 void RunCopyFilesHere(metadb_handle_list_cref data, const char * copyTo, fb2k::hwnd_t wndParent) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
175
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
176 // Create worker object, a threaded_process_callback implementation.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
177 auto worker = fb2k::service_new<tpc_copyFiles>(data, copyTo);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
178 const uint32_t flags = threaded_process::flag_show_abort | threaded_process::flag_show_progress | threaded_process::flag_show_item;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
179 // Start the process asynchronously.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
180 threaded_process::get()->run_modeless( worker, flags, wndParent, "Sample Component: Copying Files" );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
181
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
182 // Our worker is now running.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
183 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
184 void RunCopyFiles(metadb_handle_list_cref data) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
185
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
186 #ifdef _WIN32
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
187 // Detect modal dialog wars.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
188 // If another modal dialog is active, bump it instead of allowing our modal dialog (uBrowseForFolder) to run.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
189 // Suppress this if the relevant code is intended to be launched by a modal dialog.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
190 if (!ModalDialogPrologue()) return;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
191
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
192 const HWND wndParent = core_api::get_main_window();
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
193 pfc::string8 copyTo;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
194 // shared.dll method
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
195 if (!uBrowseForFolder( wndParent, "Choose destination folder", copyTo )) return;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
196
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
197 // shared.dll methods are win32 API wrappers and return plain paths with no protocol prepended
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
198 // Prefix with file:// before passing to fb2k filesystem methods.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
199 // Actually the standard fb2k filesystem implementation recognizes paths even without the prefix, but we enforce it here as a good practice.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
200 pfc::string8 copyTo2 = PFC_string_formatter() << "file://" << copyTo;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
201
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
202 RunCopyFilesHere(data, copyTo2, wndParent);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
203 #else
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
204 auto tracksCopy = std::make_shared<metadb_handle_list>( data );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
205 auto setup = fb2k::fileDialog::get()->setupOpenFolder();
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
206 setup->setTitle("Choose destination folder");
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
207 setup->runSimple( [tracksCopy] (fb2k::stringRef path) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
208 RunCopyFilesHere(*tracksCopy, path->c_str(), core_api::get_main_window());
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
209 } );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
210
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
211 #endif
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
212 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
213
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
214
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
215 namespace {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
216 class processLLtags : public threaded_process_callback {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
217 public:
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
218 processLLtags( metadb_handle_list_cref data ) : m_items(data) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
219 m_hints = metadb_io_v2::get()->create_hint_list();
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
220 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
221 void on_init(ctx_t p_wnd) override {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
222 // Main thread, called before run() gets started
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
223 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
224 void run(threaded_process_status & p_status, abort_callback & p_abort) override {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
225 // Worker thread
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
226
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
227 // Note:
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
228 // We should look for references to the same file (such as multiple subsongs) in the track list and update each file only once.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
229 // But for the sake of simplicity we don't do this in a sample component.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
230 for( size_t itemWalk = 0; itemWalk < m_items.get_size(); ++ itemWalk ) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
231
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
232 // always do this in every timeconsuming loop
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
233 // will throw exception_aborted if the user pushed 'cancel' on us
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
234 p_abort.check();
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
235
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
236 auto item = m_items[ itemWalk ];
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
237 p_status.set_progress( itemWalk, m_items.get_size() );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
238 p_status.set_item_path( item->get_path() );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
239
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
240 try {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
241 workWithTrack(item, p_abort);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
242 } catch(exception_aborted) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
243 // User abort, just bail
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
244 throw;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
245 } catch(std::exception const & e) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
246 m_errorLog << "Could not update: " << item << ", reason: " << e << "\n";
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
247 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
248 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
249 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
250 void on_done(ctx_t p_wnd, bool p_was_aborted) override {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
251 // All done, main thread again
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
252 if ( m_hints.is_valid() ) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
253 // This is the proper time to finalize the hint list
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
254 // All playlists showing these files and such will now be refreshed
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
255 m_hints->on_done();
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
256 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
257
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
258 if (!p_was_aborted && m_errorLog.length() > 0) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
259 popup_message::g_show(m_errorLog, "Information");
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
260 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
261 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
262
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
263 private:
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
264 void workWithTrack( metadb_handle_ptr item, abort_callback & abort ) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
265
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
266 FB2K_console_formatter() << "foo_sample will update tags on: " << item;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
267
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
268 const auto subsong = item->get_subsong_index();
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
269 const auto path = item->get_path();
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
270
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
271 const double openTimeout = 1.0; // wait and keep retrying for up to 1 second in case of a sharing violation error
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
272
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
273 // Required to write to files being currently played.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
274 // See file_lock_manager documentation for details.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
275 auto lock = file_lock_manager::get()->acquire_write( path, abort );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
276
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
277 input_info_writer::ptr writer;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
278 input_entry::g_open_for_info_write_timeout(writer, nullptr, path, abort, openTimeout );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
279
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
280 { // let's toy around with info that the writer can hand to us
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
281 auto stats = writer->get_file_stats( abort );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
282 if (stats.m_timestamp != filetimestamp_invalid) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
283 FB2K_console_formatter() << "Last-modified before tag update: " << format_filetimestamp_utc(stats.m_timestamp);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
284 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
285 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
286
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
287 file_info_impl info;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
288 writer->get_info( subsong, info, abort );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
289
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
290 info.meta_set("comment", "foo_sample lowlevel tags write demo was here");
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
291
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
292 // Touchy subject
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
293 // Should we let the user abort an incomplete tag write?
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
294 // Let's better not
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
295 auto & noAbort = fb2k::noAbort;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
296
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
297 // This can be called many times for files with multiple subsongs
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
298 writer->set_info( subsong, info, noAbort );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
299
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
300 // This is called once - when we're done set_info()'ing
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
301 writer->commit( noAbort );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
302
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
303 { // let's toy around with info that the writer can hand to us
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
304 auto stats = writer->get_file_stats(abort);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
305 if (stats.m_timestamp != filetimestamp_invalid) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
306 FB2K_console_formatter() << "Last-modified after tag update: " << format_filetimestamp_utc(stats.m_timestamp);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
307 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
308 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
309
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
310 // Now send new info to metadb
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
311 // If we don't do this, old info may still be shown in playlists etc.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
312 if ( true ) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
313 // Method #1: feed altered info directly to the hintlist.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
314 // Makes sense here as we updated just one subsong.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
315 auto stats = writer->get_file_stats(abort);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
316 m_hints->add_hint(item, info, stats, true);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
317 } else {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
318 // Method #2: let metadb talk to our writer object (more commonly used).
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
319 // The writer is a subclass of input_info_reader and can therefore be legitimately fed to add_hint_reader()
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
320 // This will read info from all subsongs in the file and update metadb if appropriate.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
321 m_hints->add_hint_reader(path, writer, abort);
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
322 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
323 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
324 metadb_hint_list::ptr m_hints;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
325 const metadb_handle_list m_items;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
326 pfc::string_formatter m_errorLog;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
327 };
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
328 }
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
329 void RunAlterTagsLL(metadb_handle_list_cref data) {
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
330
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
331 const auto wndParent = core_api::get_main_window();
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
332
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
333 // Our worker object, a threaded_process_callback subclass.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
334 auto worker = fb2k::service_new< processLLtags > ( data );
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
335
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
336 const uint32_t flags = threaded_process::flag_show_abort | threaded_process::flag_show_progress | threaded_process::flag_show_item;
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
337
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
338 // Start the worker asynchronously.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
339 threaded_process::get()->run_modeless(worker, flags, wndParent, "Sample Component: Updating Tags");
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
340
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
341 // The worker is now running.
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
342
20d02a178406 *: check in everything else
Paper <paper@tflc.us>
parents:
diff changeset
343 }