itunes_com/wrappers/
mod.rs

1//! Safe wrappers over the COM API. Available with the `wrappers` Cargo feature
2//!
3//! You usually want to start by creating an instance of the `iTunes` interface by [`iTunes::new`], then use its various methods.
4
5#![allow(non_snake_case)]
6#![allow(non_camel_case_types)]
7
8use std::sync::Arc;
9
10pub mod iter;
11pub mod types;
12use types::*;
13
14// We'd rather use the re-exported versions, so that they are available to our users.
15use crate::sys::*;
16
17use windows::core::BSTR;
18use windows::core::HRESULT;
19use windows::core::Interface;
20use windows::Win32::Media::Multimedia::NS_E_PROPERTY_NOT_FOUND;
21
22use windows::Win32::System::Com::{CoInitializeEx, CoCreateInstance, CLSCTX_ALL, COINIT_MULTITHREADED};
23use windows::Win32::System::Com::{VARIANT_0, VARIANT_0_0, VARIANT_0_0_0};
24
25type DATE = f64; // This type must be a joke. https://learn.microsoft.com/en-us/cpp/atl-mfc-shared/date-type?view=msvc-170
26type LONG = i32;
27
28use widestring::ucstring::U16CString;
29use num_traits::FromPrimitive;
30
31
32
33mod private {
34    //! The only reason for this private module is to have a "private" trait in publicly exported types
35    //!
36    //! See <https://github.com/rust-lang/rust/issues/34537>
37    use super::*;
38
39    pub trait ComObjectWrapper {
40        type WrappedType: Interface;
41
42        fn from_com_object(com_object: Self::WrappedType, iTunes: Arc<iTunes>) -> Self;
43        fn com_object(&self) -> &Self::WrappedType;
44        fn iTunes(&self) -> Arc<iTunes>;
45    }
46}
47use private::ComObjectWrapper;
48
49pub trait ITunesRelatedObject: private::ComObjectWrapper {
50    /// Return the related iTunes instance this object is related to
51    fn iTunes_instance(&self) -> Arc<iTunes> {
52        self.iTunes()
53    }
54}
55
56macro_rules! com_wrapper_struct {
57    ($(#[$attr:meta])* $struct_name:ident) => {
58        ::paste::paste! {
59            com_wrapper_struct!($(#[$attr])* $struct_name as [<IIT $struct_name>]);
60        }
61    };
62    ($(#[$attr:meta])* $struct_name:ident as $com_type:ident) => {
63        $(#[$attr])*
64        pub struct $struct_name {
65            com_object: crate::sys::$com_type,
66            // Using an Arc rather than reference-counting a clone of the COM instance (because that's twice as fast, see the benchmark in the benches/ folder)
67            iTunes: Arc<iTunes>,
68        }
69
70        impl private::ComObjectWrapper for $struct_name {
71            type WrappedType = $com_type;
72
73            fn from_com_object(com_object: crate::sys::$com_type, iTunes: Arc<iTunes>) -> Self {
74                Self {
75                    com_object, iTunes
76                }
77            }
78
79            fn com_object(&self) -> &crate::sys::$com_type {
80                &self.com_object
81            }
82
83            fn iTunes(&self) -> Arc<iTunes> {
84                Arc::clone(&self.iTunes)
85            }
86        }
87
88        impl ITunesRelatedObject for $struct_name {}
89    }
90}
91
92macro_rules! str_to_bstr {
93    ($string_name:ident, $bstr_name:ident) => {
94        let wide = U16CString::from_str_truncate($string_name);
95        let $bstr_name = BSTR::from_wide(wide.as_slice())?;
96    }
97}
98
99macro_rules! no_args {
100    ($(#[$attr:meta])* $vis:vis $func_name:ident) => {
101        no_args!($(#[$attr])* $vis $func_name as <Self as ComObjectWrapper>::WrappedType);
102    };
103    ($(#[$attr:meta])* $vis:vis $func_name:ident as $inherited_type:ty) => {
104        $(#[$attr])*
105        $vis fn $func_name(&self) -> windows::core::Result<()> {
106            let inherited_obj = self.com_object().cast::<$inherited_type>()?;
107            let result: HRESULT = unsafe{ inherited_obj.$func_name() };
108            result.ok()
109        }
110    };
111}
112
113macro_rules! get_bstr {
114    ($(#[$attr:meta])* $vis:vis $func_name:ident) => {
115        get_bstr!($(#[$attr])* $vis $func_name as <Self as ComObjectWrapper>::WrappedType);
116    };
117    ($(#[$attr:meta])* $vis:vis $func_name:ident as $inherited_type:ty) => {
118        $(#[$attr])*
119        $vis fn $func_name(&self) -> windows::core::Result<String> {
120            let mut bstr = BSTR::default();
121            let inherited_obj = self.com_object().cast::<$inherited_type>()?;
122            let result = unsafe{ inherited_obj.$func_name(&mut bstr) };
123            result.ok()?;
124
125            let v: Vec<u16> = bstr.as_wide().to_vec();
126            Ok(U16CString::from_vec_truncate(v).to_string_lossy())
127        }
128    }
129}
130
131macro_rules! internal_set_bstr {
132    ($(#[$attr:meta])* $vis:vis $func_name:ident ( $key:ident ) as $inherited_type:ty) => {
133        $(#[$attr])*
134        $vis fn $func_name(&self, $key: &str) -> windows::core::Result<()> {
135            str_to_bstr!($key, bstr);
136            let inherited_obj = self.com_object().cast::<$inherited_type>()?;
137            let result = unsafe{ inherited_obj.$func_name(bstr) };
138            result.ok()
139        }
140    };
141}
142
143macro_rules! set_bstr {
144    ($(#[$attr:meta])* $vis:vis $key:ident) => {
145        set_bstr!($(#[$attr])* $vis $key as <Self as ComObjectWrapper>::WrappedType);
146    };
147    ($(#[$attr:meta])* $vis:vis $key:ident as $inherited_type:ty) => {
148        ::paste::paste! {
149            internal_set_bstr!($(#[$attr])* $vis [<set_ $key>] ( $key ) as $inherited_type);
150        }
151    };
152    ($(#[$attr:meta])* $vis:vis $key:ident, no_set_prefix) => {
153        internal_set_bstr!($(#[$attr])* $vis $key ($key) as <Self as ComObjectWrapper>::WrappedType);
154    };
155}
156
157macro_rules! get_long {
158    ($(#[$attr:meta])* $vis:vis $func_name:ident) => {
159        get_long!($(#[$attr])* $vis $func_name as <Self as ComObjectWrapper>::WrappedType);
160    };
161    ($(#[$attr:meta])* $vis:vis $func_name:ident as $inherited_type:ty) => {
162        $(#[$attr])*
163        $vis fn $func_name(&self) -> windows::core::Result<LONG> {
164            let mut value: LONG = 0;
165            let inherited_obj = self.com_object().cast::<$inherited_type>()?;
166            let result = unsafe{ inherited_obj.$func_name(&mut value as *mut LONG) };
167            result.ok()?;
168
169            Ok(value)
170        }
171    };
172}
173
174macro_rules! get_rating {
175    ($(#[$attr:meta])* $vis:vis $func_name:ident) => {
176        get_rating!($(#[$attr])* $vis $func_name as <Self as ComObjectWrapper>::WrappedType);
177    };
178    ($(#[$attr:meta])* $vis:vis $func_name:ident as $inherited_type:ty) => {
179        $(#[$attr])*
180        $vis fn $func_name(&self) -> windows::core::Result<Rating> {
181            let mut value: LONG = 0;
182            let inherited_obj = self.com_object().cast::<$inherited_type>()?;
183            let result = unsafe{ inherited_obj.$func_name(&mut value as *mut LONG) };
184            result.ok()?;
185
186            Ok(value.into())
187        }
188    };
189}
190
191
192macro_rules! internal_set_long {
193    ($(#[$attr:meta])* $vis:vis $func_name:ident ( $key:ident ) as $inherited_type:ty) => {
194        $(#[$attr])*
195        $vis fn $func_name(&self, $key: LONG) -> windows::core::Result<()> {
196            let inherited_obj = self.com_object().cast::<$inherited_type>()?;
197            let result = unsafe{ inherited_obj.$func_name($key) };
198            result.ok()
199        }
200    };
201}
202
203macro_rules! set_long {
204    ($(#[$attr:meta])* $vis:vis $key:ident) => {
205        ::paste::paste! {
206            set_long!($(#[$attr])* $vis $key as <Self as ComObjectWrapper>::WrappedType);
207        }
208    };
209    ($(#[$attr:meta])* $vis:vis $key:ident as $inherited_type:ty) => {
210        ::paste::paste! {
211            internal_set_long!($(#[$attr])* $vis [<set_ $key>] ($key) as $inherited_type);
212        }
213    };
214    ($(#[$attr:meta])* $vis:vis $key:ident, no_set_prefix) => {
215        ::paste::paste! {
216            internal_set_long!($(#[$attr])* $vis $key ($key) as <Self as ComObjectWrapper>::WrappedType);
217        }
218    };
219}
220
221macro_rules! set_rating {
222    ($(#[$attr:meta])* $vis:vis $key:ident) => {
223        ::paste::paste! {
224            set_rating!($(#[$attr])* $vis $key as <Self as ComObjectWrapper>::WrappedType);
225        }
226    };
227    ($(#[$attr:meta])* $vis:vis $key:ident as $inherited_type:ty) => {
228        ::paste::paste! {
229            $(#[$attr])*
230            $vis fn [<set_ $key>](&self, $key: Rating) -> windows::core::Result<()> {
231                let long_rating = $key.into();
232                let inherited_obj = self.com_object().cast::<$inherited_type>()?;
233                let result = unsafe{ inherited_obj.[<set_ $key>](long_rating) };
234                result.ok()
235            }
236        }
237    };
238}
239
240macro_rules! set_playlist {
241    ($(#[$attr:meta])* $vis:vis $func_name:ident ( $arg:ident )) => {
242        ::paste::paste! {
243            $(#[$attr])*
244            $vis fn [<set_ $func_name>](&self, $arg: &Playlist) -> windows::core::Result<()> {
245                let vplaylist = $arg.as_variant();
246                let result = unsafe{ self.com_object.[<set_ $func_name>](vplaylist.as_raw() as *const VARIANT) };
247                result.ok()
248            }
249        }
250    };
251}
252
253macro_rules! get_f64 {
254    ($(#[$attr:meta])* $vis:vis $func_name:ident, $float_name:ty) => {
255        get_f64!($(#[$attr])* $vis $func_name, $float_name as <Self as ComObjectWrapper>::WrappedType);
256    };
257    ($(#[$attr:meta])* $vis:vis $func_name:ident, $float_name:ty as $inherited_type:ty) => {
258        $(#[$attr])*
259        $vis fn $func_name(&self) -> windows::core::Result<$float_name> {
260            let mut value: f64 = 0.0;
261            let inherited_obj = self.com_object().cast::<$inherited_type>()?;
262            let result = unsafe{ inherited_obj.$func_name(&mut value) };
263            result.ok()?;
264
265            Ok(value)
266        }
267    };
268}
269
270macro_rules! set_f64 {
271    ($(#[$attr:meta])* $vis:vis $key:ident, $float_name:ty) => {
272        set_f64!($(#[$attr])* $vis $key, $float_name as <Self as ComObjectWrapper>::WrappedType);
273    };
274    ($(#[$attr:meta])* $vis:vis $key:ident, $float_name:ty as $inherited_type:ty) => {
275        ::paste::paste! {
276            $(#[$attr])*
277            $vis fn [<set _$key>](&self, $key: $float_name) -> windows::core::Result<()> {
278                let inherited_obj = self.com_object().cast::<$inherited_type>()?;
279                let result = unsafe{ inherited_obj.[<set _$key>]($key) };
280                result.ok()
281            }
282        }
283    }
284}
285
286macro_rules! get_double {
287    ($(#[$attr:meta])* $vis:vis $key:ident) => {
288        get_f64!($(#[$attr])* $vis $key, f64);
289    };
290    ($(#[$attr:meta])* $vis:vis $key:ident as $inherited_type:ty) => {
291        get_f64!($(#[$attr])* $vis $key, f64 as $inherited_type);
292    }
293}
294
295macro_rules! set_double {
296    ($(#[$attr:meta])*  $vis:vis $key:ident) => {
297        set_f64!($(#[$attr])* $vis $key, f64);
298    }
299}
300
301macro_rules! get_date {
302    ($(#[$attr:meta])* $vis:vis $key:ident) => {
303        get_f64!($(#[$attr])* $vis $key, DATE);
304    };
305    ($(#[$attr:meta])* $vis:vis $key:ident as $inherited_type:ty) => {
306        get_f64!($(#[$attr])* $vis $key, DATE as $inherited_type);
307    };
308}
309
310macro_rules! set_date {
311    ($(#[$attr:meta])* $vis:vis $key:ident) => {
312        set_f64!($(#[$attr])* $vis $key, DATE);
313    };
314    ($(#[$attr:meta])* $vis:vis $key:ident as $inherited_type:ty) => {
315        set_f64!($(#[$attr])* $vis $key, DATE as $inherited_type);
316    };
317}
318
319macro_rules! get_bool {
320    ($(#[$attr:meta])* $vis:vis $func_name:ident) => {
321        get_bool!($(#[$attr])* $vis $func_name as <Self as ComObjectWrapper>::WrappedType);
322    };
323    ($(#[$attr:meta])* $vis:vis $func_name:ident as $inherited_type:ty) => {
324        ::paste::paste! {
325            $(#[$attr])*
326            $vis fn [<is _$func_name>](&self) -> windows::core::Result<bool> {
327                let mut value = crate::sys::FALSE;
328                let inherited_obj = self.com_object().cast::<$inherited_type>()?;
329                let result = unsafe{ inherited_obj.$func_name(&mut value) };
330                result.ok()?;
331
332                Ok(value.as_bool())
333            }
334        }
335    };
336}
337
338
339
340macro_rules! internal_set_bool {
341    ($(#[$attr:meta])* $vis:vis $func_name:ident ( $key:ident ) as $inherited_type:ty) => {
342        $(#[$attr])*
343        $vis fn $func_name(&self, $key: bool) -> windows::core::Result<()> {
344            let variant_bool = match $key {
345                true => crate::sys::TRUE,
346                false => crate::sys::FALSE,
347            };
348            let inherited_obj = self.com_object().cast::<$inherited_type>()?;
349            let result = unsafe{ inherited_obj.$func_name(variant_bool) };
350            result.ok()
351        }
352    };
353}
354
355macro_rules! set_bool {
356    ($(#[$attr:meta])* $vis:vis $key:ident) => {
357        set_bool!($(#[$attr])* $vis $key as <Self as ComObjectWrapper>::WrappedType);
358    };
359    ($(#[$attr:meta])* $vis:vis $key:ident as $inherited_type:ty) => {
360        ::paste::paste! {
361            internal_set_bool!($(#[$attr])* $vis [<set_ $key>] ( $key ) as $inherited_type);
362        }
363    };
364    ($(#[$attr:meta])* $vis:vis $key:ident, no_set_prefix) => {
365        internal_set_bool!($(#[$attr])* $vis $key ($key) as <Self as ComObjectWrapper>::WrappedType);
366    }
367}
368
369
370macro_rules! get_enum {
371    ($(#[$attr:meta])* $vis:vis $fn_name:ident -> $enum_type:ty) => {
372        get_enum!($(#[$attr])* $vis $fn_name -> $enum_type as <Self as ComObjectWrapper>::WrappedType);
373    };
374    ($(#[$attr:meta])* $vis:vis $fn_name:ident -> $enum_type:ty as $inherited_type:ty) => {
375        $(#[$attr])*
376        $vis fn $fn_name(&self) -> windows::core::Result<$enum_type> {
377            let mut value: $enum_type = FromPrimitive::from_i32(0).unwrap();
378            let inherited_obj = self.com_object().cast::<$inherited_type>()?;
379            let result = unsafe{ inherited_obj.$fn_name(&mut value as *mut _) };
380            result.ok()?;
381            Ok(value)
382        }
383    };
384}
385
386macro_rules! set_enum {
387    ($(#[$attr:meta])* $vis:vis $fn_name:ident, $enum_type:ty) => {
388        set_enum!($(#[$attr])* $vis $fn_name, $enum_type as <Self as ComObjectWrapper>::WrappedType);
389    };
390    ($(#[$attr:meta])* $vis:vis $fn_name:ident, $enum_type:ty as $inherited_type:ty) => {
391        ::paste::paste! {
392            $(#[$attr])*
393            $vis fn [<set _$fn_name>](&self, value: $enum_type) -> windows::core::Result<()> {
394                let inherited_obj = self.com_object().cast::<$inherited_type>()?;
395                let result = unsafe{ inherited_obj.[<set _$fn_name>](value) };
396                result.ok()
397            }
398        }
399    };
400}
401
402macro_rules! create_wrapped_object {
403    ($obj_type:ty, $out_obj:ident, $iTunes:ident) => {
404        match $out_obj {
405            None => Err(windows::core::Error::new(
406                NS_E_PROPERTY_NOT_FOUND, // this is the closest matching HRESULT I could find...
407                windows::h!("Item not found").clone(),
408            )),
409            Some(com_object) => Ok(
410                <$obj_type>::from_com_object(com_object, $iTunes)
411            )
412        }
413    };
414}
415
416macro_rules! get_object {
417    ($(#[$attr:meta])* $vis:vis $fn_name:ident -> $obj_type:ty) => {
418        get_object!($(#[$attr])* $vis $fn_name -> $obj_type as <Self as ComObjectWrapper>::WrappedType);
419    };
420    ($(#[$attr:meta])* $vis:vis $fn_name:ident -> $obj_type:ty as $inherited_type:ty) => {
421        $(#[$attr])*
422        $vis fn $fn_name(&self) -> windows::core::Result<$obj_type> {
423            let mut out_obj = None;
424            let inherited_obj = self.com_object().cast::<$inherited_type>()?;
425            let result = unsafe{ inherited_obj.$fn_name(&mut out_obj as *mut _) };
426            result.ok()?;
427
428            let iTunes_arc = self.iTunes();
429            create_wrapped_object!($obj_type, out_obj, iTunes_arc)
430        }
431    };
432}
433
434macro_rules! get_object_from_str {
435    ($(#[$attr:meta])* $vis:vis $fn_name:ident ( $arg_name:ident ) -> $obj_type:ty) => {
436        get_object_from_str!($(#[$attr])* $vis $fn_name($arg_name) -> $obj_type as <Self as ComObjectWrapper>::WrappedType);
437    };
438    ($(#[$attr:meta])* $vis:vis $fn_name:ident ( $arg_name:ident ) -> $obj_type:ty as $inherited_type:ty) => {
439        $(#[$attr])*
440        $vis fn $fn_name(&self, $arg_name: &str) -> windows::core::Result<$obj_type> {
441            str_to_bstr!($arg_name, bstr);
442            let inherited_obj = self.com_object().cast::<$inherited_type>()?;
443
444            let mut out_obj = None;
445            let result = unsafe{ inherited_obj.$fn_name(bstr, &mut out_obj as *mut _) };
446            result.ok()?;
447
448            let iTunes_arc = self.iTunes();
449            create_wrapped_object!($obj_type, out_obj, iTunes_arc)
450        }
451    };
452}
453
454macro_rules! get_object_from_variant {
455    ($(#[$attr:meta])* $vis:vis $fn_name:ident ( $arg_name:ident ) -> $obj_type:ty) => {
456        get_object_from_variant!($(#[$attr])* $vis $fn_name($arg_name) -> $obj_type as <Self as ComObjectWrapper>::WrappedType);
457    };
458    ($(#[$attr:meta])* $vis:vis $fn_name:ident ( $arg_name:ident ) -> $obj_type:ty as $inherited_type:ty) => {
459        $(#[$attr])*
460        $vis fn $fn_name<T>(&self, $arg_name:&Variant<T>) -> windows::core::Result<$obj_type> {
461            let inherited_obj = self.com_object().cast::<$inherited_type>()?;
462
463            let mut out_obj = None;
464            let result = unsafe{ inherited_obj.$fn_name($arg_name.as_raw() as *const VARIANT, &mut out_obj as *mut _) };
465            result.ok()?;
466
467            let iTunes_arc = self.iTunes();
468            create_wrapped_object!($obj_type, out_obj, iTunes_arc)
469        }
470    };
471}
472
473macro_rules! get_object_from_long {
474    ($(#[$attr:meta])* $vis:vis $fn_name:ident ( $arg_name:ident ) -> $obj_type:ty) => {
475        get_object_from_long!($(#[$attr])* $vis $fn_name($arg_name) -> $obj_type as <Self as ComObjectWrapper>::WrappedType);
476    };
477    ($(#[$attr:meta])* $vis:vis $fn_name:ident ( $arg_name:ident ) -> $obj_type:ty as $inherited_type:ty) => {
478        $(#[$attr])*
479        $vis fn $fn_name(&self, $arg_name:LONG) -> windows::core::Result<$obj_type> {
480            let inherited_obj = self.com_object().cast::<$inherited_type>()?;
481
482            let mut out_obj = None;
483            let result = unsafe{ inherited_obj.$fn_name($arg_name, &mut out_obj as *mut _) };
484            result.ok()?;
485
486            let iTunes_arc = self.iTunes();
487            create_wrapped_object!($obj_type, out_obj, iTunes_arc)
488        }
489    };
490}
491
492macro_rules! set_object {
493    ($(#[$attr:meta])* $vis:vis $fn_name:ident, $obj_type:ty) => {
494        ::paste::paste! {
495            $(#[$attr])*
496            $vis fn [<set _$fn_name>](&self, data: $obj_type) -> windows::core::Result<()> {
497                let object_to_set = data.com_object();
498                let result = unsafe{ self.com_object.[<set _$fn_name>](object_to_set as *const _) };
499                result.ok()
500            }
501        }
502    }
503}
504
505macro_rules! item_by_name {
506    ($(#[$attr:meta])* $vis:vis $obj_type:ty) => {
507        $(#[$attr])*
508        $vis fn ItemByName(&self, name: &str) -> windows::core::Result<$obj_type> {
509            str_to_bstr!(name, bstr);
510
511            let mut out_obj = None;
512            let result = unsafe{ self.com_object.ItemByName(bstr, &mut out_obj as *mut _) };
513            result.ok()?;
514
515            let iTunes_arc = self.iTunes();
516            create_wrapped_object!($obj_type, out_obj, iTunes_arc)
517        }
518    }
519}
520
521macro_rules! item_by_persistent_id {
522    ($(#[$attr:meta])* $vis:vis $obj_type:ty) => {
523        $(#[$attr])*
524        $vis fn ItemByPersistentID(&self, id: PersistentId) -> windows::core::Result<$obj_type> {
525            let b = id.to_le_bytes();
526            let id_low = i32::from_le_bytes(b[..4].try_into().unwrap());
527            let id_high = i32::from_le_bytes(b[4..].try_into().unwrap());
528
529            let mut out_obj = None;
530            let result = unsafe{ self.com_object.ItemByPersistentID(id_high, id_low, &mut out_obj as *mut _) };
531            result.ok()?;
532
533            let iTunes_arc = self.iTunes();
534            create_wrapped_object!($obj_type, out_obj, iTunes_arc)
535        }
536    }
537}
538
539
540pub trait Iterable {
541    type Item;
542
543    // Provided by the COM API
544    fn Count(&self) -> windows::core::Result<LONG>;
545    // Provided by the COM API
546    fn item(&self, index: LONG) -> windows::core::Result<<Self as Iterable>::Item>;
547}
548
549macro_rules! iterator {
550    ($obj_type:ty, $item_type:ident) => {
551        impl $obj_type {
552            pub fn iter(&self) -> windows::core::Result<iter::Iterator<$obj_type, $item_type>> {
553                iter::Iterator::new(&self)
554            }
555        }
556
557        impl Iterable for $obj_type {
558            type Item = $item_type;
559
560            get_long!(Count);
561
562            /// Returns an $item_type object corresponding to the given index (1-based).
563            fn item(&self, index: LONG) -> windows::core::Result<<Self as Iterable>::Item> {
564                let mut out_obj = None;
565                let result = unsafe{ self.com_object.Item(index, &mut out_obj as *mut _) };
566                result.ok()?;
567
568                let iTunes_arc = self.iTunes();
569                create_wrapped_object!($item_type, out_obj, iTunes_arc)
570            }
571
572            // /// Returns an IEnumVARIANT object which can enumerate the collection.
573            // ///
574            // /// Note: I have not figured out how to use it (calling `.Skip(1)` on the returned `IEnumVARIANT` causes a `STATUS_ACCESS_VIOLATION`).<br/>
575            // /// Feel free to open an issue or a pull request to fix this.
576            // pub fn _NewEnum(&self, iEnumerator: *mut Option<IEnumVARIANT>) -> windows::core::Result<()> {
577            //     todo!()
578            // }
579        }
580    }
581}
582
583
584
585/// The four IDs that uniquely identify an object
586#[derive(Debug, Eq, PartialEq)]
587pub struct ObjectIDs {
588    pub sourceID: LONG,
589    pub playlistID: LONG,
590    pub trackID: LONG,
591    pub databaseID: LONG,
592}
593
594/// Many COM objects inherit from this class, which provides some extra methods
595pub trait IITObjectWrapper: private::ComObjectWrapper {
596    /// Returns the four IDs that uniquely identify this object.
597    ///
598    /// These ID are "runtime" IDs, only valid for this current session. See [here for more info](https://web.archive.org/web/20201030012249/http://www.joshkunz.com/iTunesControl/interfaceIITObject.html)<br/>
599    /// Use [`iTunes::GetITObjectByID`] for the reverse operation.
600    fn GetITObjectIDs(&self) -> windows::core::Result<ObjectIDs> {
601        let mut sourceID: LONG = 0;
602        let mut playlistID: LONG = 0;
603        let mut trackID: LONG = 0;
604        let mut databaseID: LONG = 0;
605        let iitobject = self.com_object().cast::<IITObject>().unwrap();
606        let result = unsafe{ iitobject.GetITObjectIDs(
607            &mut sourceID as *mut LONG,
608            &mut playlistID as *mut LONG,
609            &mut trackID as *mut LONG,
610            &mut databaseID as *mut LONG,
611        ) };
612        result.ok()?;
613
614        Ok(ObjectIDs{
615            sourceID,
616            playlistID,
617            trackID,
618            databaseID,
619        })
620    }
621
622    // Not sure whether the content of the Variant will be released when dropped.
623    // To be sure, let's assign it a lifetime anyway
624    /// Get a COM `VARIANT` pointing to this object
625    fn as_variant(&self) -> Variant<Self::WrappedType> {
626        let idispatch = self.com_object().cast::<windows::Win32::System::Com::IDispatch>().unwrap(); // unwrapping here is fine, every IITObject inherits from IDispatch
627
628        // See https://microsoft.public.vc.atl.narkive.com/nSoZZbkL/passing-pointers-using-a-variant
629        Variant::new(VARIANT{
630            Anonymous: VARIANT_0 {
631                Anonymous: std::mem::ManuallyDrop::new(VARIANT_0_0 {
632                    vt: windows::Win32::System::Com::VT_DISPATCH,
633                    Anonymous: VARIANT_0_0_0 {
634                        pdispVal: std::mem::ManuallyDrop::new(Some(idispatch)),
635                    },
636                    ..Default::default()
637                })
638            }
639        })
640    }
641
642    /// Convenience function around [`iTunes::GetITObjectPersistentID`]
643    fn persistent_id(&self) -> windows::core::Result<PersistentId> {
644        self.iTunes().GetITObjectPersistentID(&self.as_variant())
645    }
646
647    get_bstr!(
648        /// The name of the object.
649        Name as IITObject);
650
651    set_bstr!(
652        /// The name of the object.
653        Name as IITObject);
654
655    get_long!(
656        /// The index of the object in internal application order (1-based).
657        Index as IITObject);
658
659    get_long!(
660        /// The source ID of the object.
661        sourceID as IITObject);
662
663    get_long!(
664        /// The playlist ID of the object.
665        playlistID as IITObject);
666
667    get_long!(
668        /// The track ID of the object.
669        trackID as IITObject);
670
671    get_long!(
672        /// The track database ID of the object.
673        TrackDatabaseID as IITObject);
674}
675
676/// Enum of all structs that directly inherit from [`IITObject`]
677pub enum PossibleIITObject {
678    Source(Source),
679    Playlist(Playlist),
680    Track(Track),
681}
682
683impl PossibleIITObject {
684    fn from_com_object(com_object: IITObject, iTunes: Arc<iTunes>) -> windows::core::Result<PossibleIITObject> {
685        if let Ok(source) = com_object.cast::<IITSource>() {
686            Ok(PossibleIITObject::Source(Source::from_com_object(source, iTunes)))
687        } else if let Ok(playlist) = com_object.cast::<IITPlaylist>() {
688            Ok(PossibleIITObject::Playlist(Playlist::from_com_object(playlist, iTunes)))
689        } else if let Ok(track) = com_object.cast::<IITTrack>() {
690            Ok(PossibleIITObject::Track(Track::from_com_object(track, iTunes)))
691        } else {
692            Err(windows::core::Error::new(
693                NS_E_PROPERTY_NOT_FOUND, // this is the closest matching HRESULT I could find...
694                windows::h!("Item not found").clone(),
695            ))
696        }
697    }
698
699    pub fn as_source(&self) -> Option<&Source> {
700        match self {
701            PossibleIITObject::Source(s) => Some(s),
702            _ => None
703        }
704    }
705
706    pub fn as_playlist(&self) -> Option<&Playlist> {
707        match self {
708            PossibleIITObject::Playlist(p) => Some(p),
709            _ => None
710        }
711    }
712
713    pub fn as_track(&self) -> Option<&Track> {
714        match self {
715            PossibleIITObject::Track(t) => Some(t),
716            _ => None
717        }
718    }
719}
720
721
722com_wrapper_struct!(
723    /// Safe wrapper over a [`IITSource`](crate::sys::IITSource)
724    Source);
725
726impl IITObjectWrapper for Source {}
727
728impl Source {
729    get_enum!(
730        /// The source kind.
731        pub Kind -> ITSourceKind);
732
733    get_double!(
734        /// The total size of the source, if it has a fixed size.
735        pub Capacity);
736
737    get_double!(
738        /// The free space on the source, if it has a fixed size.
739        pub FreeSpace);
740
741    get_object!(
742        /// Returns a collection of playlists.
743        pub Playlists -> PlaylistCollection);
744}
745
746com_wrapper_struct!(
747    /// Safe wrapper over a [`IITPlaylistCollection`](crate::sys::IITPlaylistCollection)
748    PlaylistCollection);
749
750impl PlaylistCollection {
751    item_by_name!(
752        /// Returns an IITPlaylist object with the specified name.
753        pub Playlist);
754
755    item_by_persistent_id!(
756        /// Returns an IITPlaylist object with the specified persistent ID.
757        pub Playlist);
758}
759
760iterator!(PlaylistCollection, Playlist);
761
762
763/// Several COM objects inherit from this class, which provides some extra methods
764pub trait IITPlaylistWrapper: private::ComObjectWrapper {
765    /// Cast this playlist to a [`UserPlaylist`] in case this is valid to do so
766    fn as_user_playlist(&self) -> Option<UserPlaylist> {
767        let com_user_pl = self.com_object().cast::<IITUserPlaylist>().ok()?;
768        let iTunes = self.iTunes();
769        Some(UserPlaylist::from_com_object(com_user_pl, iTunes))
770    }
771
772    no_args!(
773        /// Delete this playlist.
774        Delete as IITPlaylist);
775
776    no_args!(
777        /// Start playing the first track in this playlist.
778        PlayFirstTrack as IITPlaylist);
779
780    /// Print this playlist.
781    fn Print(&self, showPrintDialog: bool, printKind: ITPlaylistPrintKind, theme: String) -> windows::core::Result<()> {
782        let show = if showPrintDialog { TRUE } else { FALSE };
783        str_to_bstr!(theme, theme);
784
785        let inherited_obj = self.com_object().cast::<IITPlaylist>()?;
786        let result = unsafe{ inherited_obj.Print(show, printKind, theme) };
787        result.ok()
788    }
789
790    /// Search tracks in this playlist for the specified string.
791    fn Search(&self, searchText: String, searchFields: ITPlaylistSearchField) -> windows::core::Result<TrackCollection> {
792        str_to_bstr!(searchText, searchText);
793
794        let mut out_obj = None;
795        let inherited_obj = self.com_object().cast::<IITPlaylist>()?;
796        let result = unsafe{ inherited_obj.Search(searchText, searchFields, &mut out_obj as *mut _) };
797        result.ok()?;
798
799        let iTunes_arc = self.iTunes();
800        create_wrapped_object!(TrackCollection, out_obj, iTunes_arc)
801    }
802
803    get_enum!(
804        /// The playlist kind.
805        Kind -> ITPlaylistKind as IITPlaylist);
806
807    get_object!(
808        /// The source that contains this playlist.
809        Source -> Source as IITPlaylist);
810
811    get_long!(
812        /// The total length of all songs in the playlist (in seconds).
813        Duration as IITPlaylist);
814
815    get_bool!(
816        /// True if songs in the playlist are played in random order.
817        Shuffle as IITPlaylist);
818
819    set_bool!(
820        /// True if songs in the playlist are played in random order.
821        Shuffle as IITPlaylist);
822
823    get_double!(
824        /// The total size of all songs in the playlist (in bytes).
825        Size as IITPlaylist);
826
827    get_enum!(
828        /// The playback repeat mode.
829        SongRepeat -> ITPlaylistRepeatMode as IITPlaylist);
830
831    set_enum!(
832        /// The playback repeat mode.
833        SongRepeat, ITPlaylistRepeatMode as IITPlaylist);
834
835    get_bstr!(
836        /// The total length of all songs in the playlist (in MM:SS format).
837        Time as IITPlaylist);
838
839    get_bool!(
840        /// True if the playlist is visible in the Source list.
841        Visible as IITPlaylist);
842
843    get_object!(
844        /// Returns a collection of tracks in this playlist.
845        Tracks -> TrackCollection as IITPlaylist);
846}
847
848com_wrapper_struct!(
849    /// Safe wrapper over a [`IITPlaylist`](crate::sys::IITPlaylist)
850    Playlist);
851
852impl IITObjectWrapper for Playlist {}
853
854impl IITPlaylistWrapper for Playlist {}
855
856
857com_wrapper_struct!(
858    /// Safe wrapper over a [`IITTrackCollection`](crate::sys::IITTrackCollection)
859    TrackCollection);
860
861impl TrackCollection {
862    get_object_from_long!(
863        /// Returns an IITTrack object corresponding to the given index, where the index is defined by the play order of the playlist containing the track collection (1-based).
864        pub ItemByPlayOrder(Index) -> Track);
865
866    item_by_name!(
867        /// Returns an IITTrack object with the specified name.
868        pub Track);
869
870    item_by_persistent_id!(
871        /// Returns an IITTrack object with the specified persistent ID.
872        pub Track);
873}
874
875iterator!(TrackCollection, Track);
876
877/// Several COM objects inherit from this class, which provides some extra methods
878pub trait IITTrackWrapper: private::ComObjectWrapper {
879    no_args!(
880        /// Delete this track.
881        Delete as IITTrack);
882
883    no_args!(
884        /// Start playing this track.
885        Play as IITTrack);
886
887    get_object_from_str!(
888        /// Add artwork from an image file to this track.
889        AddArtworkFromFile(filePath) -> Artwork as IITTrack);
890
891    get_enum!(
892        /// The track kind.
893        Kind -> ITTrackKind as IITTrack);
894
895    get_object!(
896        /// The playlist that contains this track.
897        Playlist -> Playlist as IITTrack);
898
899    get_bstr!(
900        /// The album containing the track.
901        Album as IITTrack);
902
903    set_bstr!(
904        /// The album containing the track.
905        Album as IITTrack);
906
907    get_bstr!(
908        /// The artist/source of the track.
909        Artist as IITTrack);
910
911    set_bstr!(
912        /// The artist/source of the track.
913        Artist as IITTrack);
914
915    get_long!(
916        /// The bit rate of the track (in kbps).
917        BitRate as IITTrack);
918
919    get_long!(
920        /// The tempo of the track (in beats per minute).
921        BPM as IITTrack);
922
923    set_long!(
924        /// The tempo of the track (in beats per minute).
925        BPM as IITTrack);
926
927    get_bstr!(
928        /// Freeform notes about the track.
929        Comment as IITTrack);
930
931    set_bstr!(
932        /// Freeform notes about the track.
933        Comment as IITTrack);
934
935    get_bool!(
936        /// True if this track is from a compilation album.
937        Compilation as IITTrack);
938
939    set_bool!(
940        /// True if this track is from a compilation album.
941        Compilation as IITTrack);
942
943    get_bstr!(
944        /// The composer of the track.
945        Composer as IITTrack);
946
947    set_bstr!(
948        /// The composer of the track.
949        Composer as IITTrack);
950
951    get_date!(
952        /// The date the track was added to the playlist.
953        DateAdded as IITTrack);
954
955    get_long!(
956        /// The total number of discs in the source album.
957        DiscCount as IITTrack);
958
959    set_long!(
960        /// The total number of discs in the source album.
961        DiscCount as IITTrack);
962
963    get_long!(
964        /// The index of the disc containing the track on the source album.
965        DiscNumber as IITTrack);
966
967    set_long!(
968        /// The index of the disc containing the track on the source album.
969        DiscNumber as IITTrack);
970
971    get_long!(
972        /// The length of the track (in seconds).
973        Duration as IITTrack);
974
975    get_bool!(
976        /// True if the track is checked for playback.
977        Enabled as IITTrack);
978
979    set_bool!(
980        /// True if the track is checked for playback.
981        Enabled as IITTrack);
982
983    get_bstr!(
984        /// The name of the EQ preset of the track.
985        EQ as IITTrack);
986
987    set_bstr!(
988        /// The name of the EQ preset of the track.
989        EQ as IITTrack);
990
991    set_long!(
992        /// The stop time of the track (in seconds).
993        Finish as IITTrack);
994
995    get_long!(
996        /// The stop time of the track (in seconds).
997        Finish as IITTrack);
998
999    get_bstr!(
1000        /// The music/audio genre (category) of the track.
1001        Genre as IITTrack);
1002
1003    set_bstr!(
1004        /// The music/audio genre (category) of the track.
1005        Genre as IITTrack);
1006
1007    get_bstr!(
1008        /// The grouping (piece) of the track.  Generally used to denote movements within classical work.
1009        Grouping as IITTrack);
1010
1011    set_bstr!(
1012        /// The grouping (piece) of the track.  Generally used to denote movements within classical work.
1013        Grouping as IITTrack);
1014
1015    get_bstr!(
1016        /// A text description of the track.
1017        KindAsString as IITTrack);
1018
1019    get_date!(
1020        /// The modification date of the content of the track.
1021        ModificationDate as IITTrack);
1022
1023    get_long!(
1024        /// The number of times the track has been played.
1025        PlayedCount as IITTrack);
1026
1027    set_long!(
1028        /// The number of times the track has been played.
1029        PlayedCount as IITTrack);
1030
1031    get_date!(
1032        /// The date and time the track was last played.  A value of zero means no played date.
1033        PlayedDate as IITTrack);
1034
1035    set_date!(
1036        /// The date and time the track was last played.  A value of zero means no played date.
1037        PlayedDate as IITTrack);
1038
1039    get_long!(
1040        /// The play order index of the track in the owner playlist (1-based).
1041        PlayOrderIndex as IITTrack);
1042
1043    get_rating!(
1044        /// The rating of the track.
1045        Rating as IITTrack);
1046
1047    set_rating!(
1048        /// The rating of the track.
1049        Rating as IITTrack);
1050
1051    get_long!(
1052        /// The sample rate of the track (in Hz).
1053        SampleRate as IITTrack);
1054
1055    get_long!(
1056        /// The size of the track (in bytes).
1057        Size as IITTrack);
1058
1059    get_long!(
1060        /// The start time of the track (in seconds).
1061        Start as IITTrack);
1062
1063    set_long!(
1064        /// The start time of the track (in seconds).
1065        Start as IITTrack);
1066
1067    get_bstr!(
1068        /// The length of the track (in MM:SS format).
1069        Time as IITTrack);
1070
1071    get_long!(
1072        /// The total number of tracks on the source album.
1073        TrackCount as IITTrack);
1074
1075    set_long!(
1076        /// The total number of tracks on the source album.
1077        TrackCount as IITTrack);
1078
1079    get_long!(
1080        /// The index of the track on the source album.
1081        TrackNumber as IITTrack);
1082
1083    set_long!(
1084        /// The index of the track on the source album.
1085        TrackNumber as IITTrack);
1086
1087    get_long!(
1088        /// The relative volume adjustment of the track (-100% to 100%).
1089        VolumeAdjustment as IITTrack);
1090
1091    set_long!(
1092        /// The relative volume adjustment of the track (-100% to 100%).
1093        VolumeAdjustment as IITTrack);
1094
1095    get_long!(
1096        /// The year the track was recorded/released.
1097        Year as IITTrack);
1098
1099    set_long!(
1100        /// The year the track was recorded/released.
1101        Year as IITTrack);
1102
1103    get_object!(
1104        /// Returns a collection of artwork.
1105        Artwork -> ArtworkCollection as IITTrack);
1106}
1107
1108com_wrapper_struct!(
1109    /// Safe wrapper over a [`IITTrack`](crate::sys::IITTrack)
1110    Track);
1111
1112impl IITObjectWrapper for Track {}
1113
1114impl IITTrackWrapper for Track {}
1115
1116impl Track {
1117    /// In case the concrete COM object for this track actually is a derived `FileOrCDTrack`, this is a way to retrieve it
1118    pub fn as_file_or_cd_track(&self) -> Option<FileOrCDTrack> {
1119        let foct = self.com_object.cast::<IITFileOrCDTrack>().ok()?;
1120        let iTunes_arc = self.iTunes();
1121        Some(FileOrCDTrack::from_com_object(foct, iTunes_arc))
1122    }
1123}
1124
1125com_wrapper_struct!(
1126    /// Safe wrapper over a [`IITArtwork`](crate::sys::IITArtwork)
1127    Artwork);
1128
1129impl Artwork {
1130    no_args!(
1131        /// Delete this piece of artwork from the track.
1132        pub Delete);
1133
1134    set_bstr!(
1135        /// Replace existing artwork data with new artwork from an image file.
1136        pub SetArtworkFromFile, no_set_prefix);
1137
1138    set_bstr!(
1139        /// Save artwork data to an image file.
1140        pub SaveArtworkToFile, no_set_prefix);
1141
1142    get_enum!(
1143        /// The format of the artwork.
1144        pub Format -> ITArtworkFormat);
1145
1146    get_bool!(
1147        /// True if the artwork was downloaded by iTunes.
1148        pub IsDownloadedArtwork);
1149
1150    get_bstr!(
1151        /// The description for the artwork.
1152        pub Description);
1153
1154    set_bstr!(
1155        /// The description for the artwork.
1156        pub Description);
1157}
1158
1159com_wrapper_struct!(
1160    /// Safe wrapper over a [`IITArtworkCollection`](crate::sys::IITArtworkCollection)
1161    ArtworkCollection);
1162
1163impl ArtworkCollection {}
1164
1165iterator!(ArtworkCollection, Artwork);
1166
1167com_wrapper_struct!(
1168    /// Safe wrapper over a [`IITSourceCollection`](crate::sys::IITSourceCollection)
1169    SourceCollection);
1170
1171impl SourceCollection {
1172    item_by_name!(
1173        /// Returns an IITSource object with the specified name.
1174        pub Source);
1175
1176    item_by_persistent_id!(
1177        /// Returns an IITSource object with the specified persistent ID.
1178        pub Source);
1179}
1180
1181iterator!(SourceCollection, Source);
1182
1183com_wrapper_struct!(
1184    /// Safe wrapper over a [`IITEncoder`](crate::sys::IITEncoder)
1185    Encoder);
1186
1187impl Encoder {
1188    get_bstr!(
1189        /// The name of the the encoder.
1190        pub Name);
1191
1192    get_bstr!(
1193        /// The data format created by the encoder.
1194        pub Format);
1195}
1196
1197com_wrapper_struct!(
1198    /// Safe wrapper over a [`IITEncoderCollection`](crate::sys::IITEncoderCollection)
1199    EncoderCollection);
1200
1201impl EncoderCollection {
1202    item_by_name!(
1203        /// Returns an IITEncoder object with the specified name.
1204        pub Encoder);
1205}
1206
1207iterator!(EncoderCollection, Encoder);
1208
1209com_wrapper_struct!(
1210    /// Safe wrapper over a [`IITEQPreset`](crate::sys::IITEQPreset)
1211    EQPreset);
1212
1213impl EQPreset {
1214    get_bstr!(
1215        /// The name of the the EQ preset.
1216        pub Name);
1217
1218    get_bool!(
1219        /// True if this EQ preset can be modified.
1220        pub Modifiable);
1221
1222    get_double!(
1223        /// The equalizer preamp level (-12.0 db to +12.0 db).
1224        pub Preamp);
1225
1226    set_double!(
1227        /// The equalizer preamp level (-12.0 db to +12.0 db).
1228        pub Preamp);
1229
1230    get_double!(
1231        /// The equalizer 32Hz band level (-12.0 db to +12.0 db).
1232        pub Band1);
1233
1234    set_double!(
1235        /// The equalizer 32Hz band level (-12.0 db to +12.0 db).
1236        pub Band1);
1237
1238    get_double!(
1239        /// The equalizer 64Hz band level (-12.0 db to +12.0 db).
1240        pub Band2);
1241
1242    set_double!(
1243        /// The equalizer 64Hz band level (-12.0 db to +12.0 db).
1244        pub Band2);
1245
1246    get_double!(
1247        /// The equalizer 125Hz band level (-12.0 db to +12.0 db).
1248        pub Band3);
1249
1250    set_double!(
1251        /// The equalizer 125Hz band level (-12.0 db to +12.0 db).
1252        pub Band3);
1253
1254    get_double!(
1255        /// The equalizer 250Hz band level (-12.0 db to +12.0 db).
1256        pub Band4);
1257
1258    set_double!(
1259        /// The equalizer 250Hz band level (-12.0 db to +12.0 db).
1260        pub Band4);
1261
1262    get_double!(
1263        /// The equalizer 500Hz band level (-12.0 db to +12.0 db).
1264        pub Band5);
1265
1266    set_double!(
1267        /// The equalizer 500Hz band level (-12.0 db to +12.0 db).
1268        pub Band5);
1269
1270    get_double!(
1271        /// The equalizer 1KHz band level (-12.0 db to +12.0 db).
1272        pub Band6);
1273
1274    set_double!(
1275        /// The equalizer 1KHz band level (-12.0 db to +12.0 db).
1276        pub Band6);
1277
1278    get_double!(
1279        /// The equalizer 2KHz band level (-12.0 db to +12.0 db).
1280        pub Band7);
1281
1282    set_double!(
1283        /// The equalizer 2KHz band level (-12.0 db to +12.0 db).
1284        pub Band7);
1285
1286    get_double!(
1287        /// The equalizer 4KHz band level (-12.0 db to +12.0 db).
1288        pub Band8);
1289
1290    set_double!(
1291        /// The equalizer 4KHz band level (-12.0 db to +12.0 db).
1292        pub Band8);
1293
1294    get_double!(
1295        /// The equalizer 8KHz band level (-12.0 db to +12.0 db).
1296        pub Band9);
1297
1298    set_double!(
1299        /// The equalizer 8KHz band level (-12.0 db to +12.0 db).
1300        pub Band9);
1301
1302    get_double!(
1303        /// The equalizer 16KHz band level (-12.0 db to +12.0 db).
1304        pub Band10);
1305
1306    set_double!(
1307        /// The equalizer 16KHz band level (-12.0 db to +12.0 db).
1308        pub Band10);
1309
1310    internal_set_bool!(
1311        /// Delete this EQ preset.
1312        pub Delete(updateAllTracks) as <Self as ComObjectWrapper>::WrappedType);
1313
1314    /// Rename this EQ preset.
1315    pub fn Rename(&self, newName: String, updateAllTracks: bool) -> windows::core::Result<()> {
1316        str_to_bstr!(newName, bstr);
1317        let var_bool = if updateAllTracks { TRUE } else { FALSE };
1318        let result = unsafe { self.com_object.Rename(bstr, var_bool) };
1319        result.ok()
1320    }
1321}
1322
1323com_wrapper_struct!(
1324    /// Safe wrapper over a [`IITEQPresetCollection`](crate::sys::IITEQPresetCollection)
1325    EQPresetCollection);
1326
1327impl EQPresetCollection {
1328    item_by_name!(
1329        /// Returns an IITEQPreset object with the specified name.
1330        pub EQPreset);
1331}
1332
1333iterator!(EQPresetCollection, EQPreset);
1334
1335com_wrapper_struct!(
1336    /// Safe wrapper over a [`IITOperationStatus`](crate::sys::IITOperationStatus)
1337    OperationStatus);
1338
1339impl OperationStatus {
1340    get_bool!(
1341        /// True if the operation is still in progress.
1342        pub InProgress);
1343
1344    get_object!(
1345        /// Returns a collection containing the tracks that were generated by the operation.
1346        pub Tracks -> TrackCollection);
1347}
1348
1349/// The three items of a `ConversionStatus`
1350#[derive(Debug)]
1351pub struct ConversionStatus {
1352    pub trackName: String,
1353    pub progressValue: LONG,
1354    pub maxProgressValue: LONG,
1355}
1356
1357com_wrapper_struct!(
1358    /// Safe wrapper over a [`IITConvertOperationStatus`](crate::sys::IITConvertOperationStatus)
1359    ConvertOperationStatus);
1360
1361impl ConvertOperationStatus {
1362    /// Returns the current conversion status.
1363    pub fn GetConversionStatus(&self) -> windows::core::Result<ConversionStatus> {
1364        let mut bstr = BSTR::default();
1365        let mut progressValue = 0;
1366        let mut maxProgressValue = 0;
1367        let result = unsafe{ self.com_object.GetConversionStatus(&mut bstr, &mut progressValue as *mut LONG, &mut maxProgressValue as *mut LONG) };
1368        result.ok()?;
1369
1370        let v: Vec<u16> = bstr.as_wide().to_vec();
1371        let trackName = U16CString::from_vec_truncate(v).to_string_lossy();
1372
1373        Ok(ConversionStatus{ trackName, progressValue, maxProgressValue })
1374    }
1375
1376    no_args!(
1377        /// Stops the current conversion operation.
1378        pub StopConversion);
1379
1380    get_bstr!(
1381        /// Returns the name of the track currently being converted.
1382        pub trackName);
1383
1384    get_long!(
1385        /// Returns the current progress value for the track being converted.
1386        pub progressValue);
1387
1388    get_long!(
1389        /// Returns the maximum progress value for the track being converted.
1390        pub maxProgressValue);
1391}
1392
1393com_wrapper_struct!(
1394    /// Safe wrapper over a [`IITLibraryPlaylist`](crate::sys::IITLibraryPlaylist)
1395    LibraryPlaylist);
1396
1397impl IITObjectWrapper for LibraryPlaylist {}
1398
1399impl IITPlaylistWrapper for LibraryPlaylist {}
1400
1401impl LibraryPlaylist {
1402    get_object_from_str!(
1403        /// Add the specified file path to the library.
1404        pub AddFile(filePath) -> OperationStatus);
1405
1406    get_object_from_variant!(
1407        /// Add the specified array of file paths to the library. filePaths can be of type VT_ARRAY|VT_VARIANT, where each entry is a VT_BSTR, or VT_ARRAY|VT_BSTR.  You can also pass a JScript Array object.
1408        pub AddFiles(filePaths) -> OperationStatus);
1409
1410    get_object_from_str!(
1411        /// Add the specified streaming audio URL to the library.
1412        pub AddURL(URL) -> URLTrack);
1413
1414    get_object_from_variant!(
1415        /// Add the specified track to the library.  iTrackToAdd is a VARIANT of type VT_DISPATCH that points to an IITTrack.
1416        pub AddTrack(iTrackToAdd) -> Track);
1417}
1418
1419com_wrapper_struct!(
1420    /// Safe wrapper over a [`IITURLTrack`](crate::sys::IITURLTrack)
1421    URLTrack);
1422
1423impl IITObjectWrapper for URLTrack {}
1424
1425impl IITTrackWrapper for URLTrack {}
1426
1427impl URLTrack {
1428    get_bstr!(
1429        /// The URL of the stream represented by this track.
1430        pub URL);
1431
1432    set_bstr!(
1433        /// The URL of the stream represented by this track.
1434        pub URL);
1435
1436    get_bool!(
1437        /// True if this is a podcast track.
1438        pub Podcast);
1439
1440    no_args!(
1441        /// Update the podcast feed for this track.
1442        pub UpdatePodcastFeed);
1443
1444    no_args!(
1445        /// Start downloading the podcast episode that corresponds to this track.
1446        pub DownloadPodcastEpisode);
1447
1448    get_bstr!(
1449        /// Category for the track.
1450        pub Category);
1451
1452    set_bstr!(
1453        /// Category for the track.
1454        pub Category);
1455
1456    get_bstr!(
1457        /// Description for the track.
1458        pub Description);
1459
1460    set_bstr!(
1461        /// Description for the track.
1462        pub Description);
1463
1464    get_bstr!(
1465        /// Long description for the track.
1466        pub LongDescription);
1467
1468    set_bstr!(
1469        /// Long description for the track.
1470        pub LongDescription);
1471
1472    no_args!(
1473        /// Reveal the track in the main browser window.
1474        pub Reveal);
1475
1476    get_rating!(
1477        /// The user or computed rating of the album that this track belongs to.
1478        pub AlbumRating);
1479
1480    set_rating!(
1481        /// The user or computed rating of the album that this track belongs to.
1482        pub AlbumRating);
1483
1484    get_enum!(
1485        /// The album rating kind.
1486        pub AlbumRatingKind -> ITRatingKind);
1487
1488    get_enum!(
1489        /// The track rating kind.
1490        pub ratingKind -> ITRatingKind);
1491
1492    get_object!(
1493        /// Returns a collection of playlists that contain the song that this track represents.
1494        pub Playlists -> PlaylistCollection);
1495}
1496
1497com_wrapper_struct!(
1498    /// Safe wrapper over a [`IITUserPlaylist`](crate::sys::IITUserPlaylist)
1499    UserPlaylist);
1500
1501impl IITObjectWrapper for UserPlaylist {}
1502
1503impl IITPlaylistWrapper for UserPlaylist {}
1504
1505impl UserPlaylist {
1506    get_object_from_str!(
1507        /// Add the specified file path to the user playlist.
1508        pub AddFile(filePath) -> OperationStatus);
1509
1510    get_object_from_variant!(
1511        /// Add the specified array of file paths to the user playlist. filePaths can be of type VT_ARRAY|VT_VARIANT, where each entry is a VT_BSTR, or VT_ARRAY|VT_BSTR.  You can also pass a JScript Array object.
1512        pub AddFiles(filePaths) -> OperationStatus);
1513
1514    get_object_from_str!(
1515        /// Add the specified streaming audio URL to the user playlist.
1516        pub AddURL(URL) -> URLTrack);
1517
1518    get_object_from_variant!(
1519        /// Add the specified track to the user playlist.  iTrackToAdd is a VARIANT of type VT_DISPATCH that points to an IITTrack.
1520        pub AddTrack(iTrackToAdd) -> Track);
1521
1522    get_bool!(
1523        /// True if the user playlist is being shared.
1524        pub Shared);
1525
1526    set_bool!(
1527        /// True if the user playlist is being shared.
1528        pub Shared);
1529
1530    get_bool!(
1531        /// True if this is a smart playlist.
1532        pub Smart);
1533
1534    get_enum!(
1535        /// The playlist special kind.
1536        pub SpecialKind -> ITUserPlaylistSpecialKind);
1537
1538    get_object!(
1539        /// The parent of this playlist.
1540        pub Parent -> UserPlaylist);
1541
1542    get_object_from_str!(
1543        /// Creates a new playlist in a folder playlist.
1544        pub CreatePlaylist(playlistName) -> Playlist);
1545
1546    get_object_from_str!(
1547        /// Creates a new folder in a folder playlist.
1548        pub CreateFolder(folderName) -> Playlist);
1549
1550    set_playlist!(
1551        /// The parent of this playlist.
1552        pub Parent(iParentPlayList));
1553
1554    no_args!(
1555        /// Reveal the user playlist in the main browser window.
1556        pub Reveal);
1557}
1558
1559com_wrapper_struct!(
1560    /// Safe wrapper over a [`IITVisual`](crate::sys::IITVisual)
1561    Visual);
1562
1563impl Visual {
1564    get_bstr!(
1565        /// The name of the the visual plug-in.
1566        pub Name);
1567}
1568
1569com_wrapper_struct!(
1570    /// Safe wrapper over a [`IITVisualCollection`](crate::sys::IITVisualCollection)
1571    VisualCollection);
1572
1573impl VisualCollection {
1574    item_by_name!(
1575        /// Returns an IITVisual object with the specified name.
1576        pub Visual);
1577}
1578
1579iterator!(VisualCollection, Visual);
1580
1581com_wrapper_struct!(
1582    /// Safe wrapper over a [`IITWindow`](crate::sys::IITWindow)
1583    Window);
1584
1585impl Window {
1586    get_bstr!(
1587        /// The title of the window.
1588        pub Name);
1589
1590    get_enum!(
1591        /// The window kind.
1592        pub Kind -> ITWindowKind);
1593
1594    get_bool!(
1595        /// True if the window is visible. Note that the main browser window cannot be hidden.
1596        pub Visible);
1597
1598    set_bool!(
1599        /// True if the window is visible. Note that the main browser window cannot be hidden.
1600        pub Visible);
1601
1602    get_bool!(
1603        /// True if the window is resizable.
1604        pub Resizable);
1605
1606    get_bool!(
1607        /// True if the window is minimized.
1608        pub Minimized);
1609
1610    set_bool!(
1611        /// True if the window is minimized.
1612        pub Minimized);
1613
1614    get_bool!(
1615        /// True if the window is maximizable.
1616        pub Maximizable);
1617
1618    get_bool!(
1619        /// True if the window is maximized.
1620        pub Maximized);
1621
1622    set_bool!(
1623        /// True if the window is maximized.
1624        pub Maximized);
1625
1626    get_bool!(
1627        /// True if the window is zoomable.
1628        pub Zoomable);
1629
1630    get_bool!(
1631        /// True if the window is zoomed.
1632        pub Zoomed);
1633
1634    set_bool!(
1635        /// True if the window is zoomed.
1636        pub Zoomed);
1637
1638    get_long!(
1639        /// The screen coordinate of the top edge of the window.
1640        pub Top);
1641
1642    set_long!(
1643        /// The screen coordinate of the top edge of the window.
1644        pub Top);
1645
1646    get_long!(
1647        /// The screen coordinate of the left edge of the window.
1648        pub Left);
1649
1650    set_long!(
1651        /// The screen coordinate of the left edge of the window.
1652        pub Left);
1653
1654    get_long!(
1655        /// The screen coordinate of the bottom edge of the window.
1656        pub Bottom);
1657
1658    set_long!(
1659        /// The screen coordinate of the bottom edge of the window.
1660        pub Bottom);
1661
1662    get_long!(
1663        /// The screen coordinate of the right edge of the window.
1664        pub Right);
1665
1666    set_long!(
1667        /// The screen coordinate of the right edge of the window.
1668        pub Right);
1669
1670    get_long!(
1671        /// The width of the window.
1672        pub Width);
1673
1674    set_long!(
1675        /// The width of the window.
1676        pub Width);
1677
1678    get_long!(
1679        /// The height of the window.
1680        pub Height);
1681
1682    set_long!(
1683        /// The height of the window.
1684        pub Height);
1685}
1686
1687com_wrapper_struct!(
1688    /// Safe wrapper over a [`IITBrowserWindow`](crate::sys::IITBrowserWindow)
1689    BrowserWindow);
1690
1691impl BrowserWindow {
1692    get_bool!(
1693        /// True if window is in MiniPlayer mode.
1694        pub MiniPlayer);
1695
1696    set_bool!(
1697        /// True if window is in MiniPlayer mode.
1698        pub MiniPlayer);
1699
1700    get_object!(
1701        /// Returns a collection containing the currently selected track or tracks.
1702        pub SelectedTracks -> TrackCollection);
1703
1704    get_object!(
1705        /// The currently selected playlist in the Source list.
1706        pub SelectedPlaylist -> Playlist);
1707
1708    set_playlist!(
1709        /// The currently selected playlist in the Source list.
1710        pub SelectedPlaylist(iPlaylist));
1711}
1712
1713com_wrapper_struct!(
1714    /// Safe wrapper over a [`IITWindowCollection`](crate::sys::IITWindowCollection)
1715    WindowCollection);
1716
1717impl WindowCollection {
1718    item_by_name!(
1719        /// Returns an IITWindow object with the specified name.
1720        pub Window);
1721}
1722
1723iterator!(WindowCollection, Window);
1724
1725/// The three items of a `PlayerButtonState`
1726#[derive(Debug, Eq, PartialEq)]
1727pub struct PlayerButtonState {
1728    pub previousEnabled: bool,
1729    pub playPauseStopState: ITPlayButtonState,
1730    pub nextEnabled: bool,
1731}
1732
1733/// Safe wrapper over a [`IiTunes`](crate::sys::IiTunes)
1734pub struct iTunes {
1735    com_object: crate::sys::IiTunes,
1736}
1737
1738impl private::ComObjectWrapper for iTunes {
1739    type WrappedType = crate::sys::IiTunes;
1740
1741    fn from_com_object(_com_object: crate::sys::IiTunes, _iTunes: Arc<iTunes>) -> Self {
1742        // Nothing is supposed to build an iTunes instance, apart from iTunes::new()
1743        panic!("This function is not supposed to be called");
1744    }
1745
1746    fn com_object(&self) -> &crate::sys::IiTunes {
1747        &self.com_object
1748    }
1749
1750    fn iTunes(&self) -> Arc<iTunes> {
1751        Arc::new(Self{
1752            com_object: self.com_object.clone()
1753        })
1754    }
1755}
1756
1757impl iTunes {
1758    /// Create a new COM object to communicate with iTunes
1759    ///
1760    /// # Remarks
1761    ///
1762    /// This should not be called from UI threads.
1763    pub fn new() -> windows::core::Result<Self> {
1764        unsafe {
1765            // Note: from the docs (https://learn.microsoft.com/en-us/windows/win32/api/objbase/ne-objbase-coinit#remarks)
1766            // The multi-threaded apartment is intended for use by non-GUI threads.
1767            // Threads in multi-threaded apartments should not perform UI actions.
1768            // This is because UI threads require a message pump, and COM does not pump messages for threads in a multi-threaded apartment.
1769            CoInitializeEx(None, COINIT_MULTITHREADED)?;
1770        }
1771
1772        Ok(Self {
1773            com_object: unsafe { CoCreateInstance(&crate::sys::ITUNES_APP_COM_GUID, None, CLSCTX_ALL)? },
1774        })
1775    }
1776
1777    no_args!(
1778        /// Reposition to the beginning of the current track or go to the previous track if already at start of current track.
1779        pub BackTrack);
1780
1781    no_args!(
1782        /// Skip forward in a playing track.
1783        pub FastForward);
1784
1785    no_args!(
1786        /// Advance to the next track in the current playlist.
1787        pub NextTrack);
1788
1789    no_args!(
1790        /// Pause playback.
1791        pub Pause);
1792
1793    no_args!(
1794        /// Play the currently targeted track.
1795        pub Play);
1796
1797    set_bstr!(
1798        /// Play the specified file path, adding it to the library if not already present.
1799        pub PlayFile, no_set_prefix);
1800
1801    no_args!(
1802        /// Toggle the playing/paused state of the current track.
1803        pub PlayPause);
1804
1805    no_args!(
1806        /// Return to the previous track in the current playlist.
1807        pub PreviousTrack);
1808
1809    no_args!(
1810        /// Disable fast forward/rewind and resume playback, if playing.
1811        pub Resume);
1812
1813    no_args!(
1814        /// Skip backwards in a playing track.
1815        pub Rewind);
1816
1817    no_args!(
1818        /// Stop playback.
1819        pub Stop);
1820
1821    get_object_from_str!(
1822        /// Start converting the specified file path.
1823        pub ConvertFile(filePath) -> OperationStatus);
1824
1825    get_object_from_variant!(
1826        /// Start converting the specified array of file paths. filePaths can be of type VT_ARRAY|VT_VARIANT, where each entry is a VT_BSTR, or VT_ARRAY|VT_BSTR.  You can also pass a JScript Array object.
1827        pub ConvertFiles(filePaths) -> OperationStatus);
1828
1829    get_object_from_variant!(
1830        /// Start converting the specified track.  iTrackToConvert is a VARIANT of type VT_DISPATCH that points to an IITTrack.
1831        pub ConvertTrack(iTrackToConvert) -> OperationStatus);
1832
1833    get_object_from_variant!(
1834        /// Start converting the specified tracks.  iTracksToConvert is a VARIANT of type VT_DISPATCH that points to an IITTrackCollection.
1835        pub ConvertTracks(iTracksToConvert) -> OperationStatus);
1836
1837    /// Returns true if this version of the iTunes type library is compatible with the specified version.
1838    pub fn CheckVersion(&self, majorVersion: LONG, minorVersion: LONG) -> windows::core::Result<bool> {
1839        let mut bool_result = FALSE;
1840        let result = unsafe{ self.com_object.CheckVersion(majorVersion, minorVersion, &mut bool_result) };
1841        result.ok()?;
1842        Ok(bool_result.as_bool())
1843    }
1844
1845    /// Returns an IITObject corresponding to the specified IDs.
1846    pub fn GetITObjectByID(&self, ids: ObjectIDs) -> windows::core::Result<PossibleIITObject> {
1847        let mut out_obj: Option<IITObject> = None;
1848        let result = unsafe{ self.com_object.GetITObjectByID(
1849            ids.sourceID,
1850            ids.playlistID,
1851            ids.trackID,
1852            ids.databaseID,
1853            &mut out_obj as *mut _
1854        ) };
1855        result.ok()?;
1856
1857        match out_obj {
1858            None => Err(windows::core::Error::new(
1859                NS_E_PROPERTY_NOT_FOUND, // this is the closest matching HRESULT I could find...
1860                windows::h!("Item not found").clone(),
1861            )),
1862            Some(obj) => {
1863                let iTunes_arc = self.iTunes();
1864                PossibleIITObject::from_com_object(obj, iTunes_arc)
1865            },
1866        }
1867    }
1868
1869    get_object_from_str!(
1870        /// Creates a new playlist in the main library.
1871        pub CreatePlaylist(playlistName) -> Playlist);
1872
1873    set_bstr!(
1874        /// Open the specified iTunes Store or streaming audio URL.
1875        pub OpenURL, no_set_prefix);
1876
1877    no_args!(
1878        /// Go to the iTunes Store home page.
1879        pub GotoMusicStoreHomePage);
1880
1881    no_args!(
1882        /// Update the contents of the iPod.
1883        pub UpdateIPod);
1884
1885    // /// [id(0x60020015)]
1886    // /// (no other documentation provided)
1887    // pub fn Authorize(&self, numElems: LONG, data: *const VARIANT, names: *const BSTR) -> windows::core::Result<()> {
1888    //     todo!()
1889    // }
1890
1891    no_args!(
1892        /// Exits the iTunes application.
1893        pub Quit);
1894
1895    get_object!(
1896        /// Returns a collection of music sources (music library, CD, device, etc.).
1897        pub Sources -> SourceCollection);
1898
1899    get_object!(
1900        /// Returns a collection of encoders.
1901        pub Encoders -> EncoderCollection);
1902
1903    get_object!(
1904        /// Returns a collection of EQ presets.
1905        pub EQPresets -> EQPresetCollection);
1906
1907    get_object!(
1908        /// Returns a collection of visual plug-ins.
1909        pub Visuals -> VisualCollection);
1910
1911    get_object!(
1912        /// Returns a collection of windows.
1913        pub Windows -> WindowCollection);
1914
1915    get_long!(
1916        /// Returns the sound output volume (0 = minimum, 100 = maximum).
1917        pub SoundVolume);
1918
1919    set_long!(
1920        /// Returns the sound output volume (0 = minimum, 100 = maximum).
1921        pub SoundVolume);
1922
1923    get_bool!(
1924        /// True if sound output is muted.
1925        pub Mute);
1926
1927    set_bool!(
1928        /// True if sound output is muted.
1929        pub Mute);
1930
1931    get_enum!(
1932        /// Returns the current player state.
1933        pub PlayerState -> ITPlayerState);
1934
1935    get_long!(
1936        /// Returns the player's position within the currently playing track in seconds.
1937        pub PlayerPosition);
1938
1939    set_long!(
1940        /// Returns the player's position within the currently playing track in seconds.
1941        pub PlayerPosition);
1942
1943    get_object!(
1944        /// Returns the currently selected encoder (AAC, MP3, AIFF, WAV, etc.).
1945        pub CurrentEncoder -> Encoder);
1946
1947    set_object!(
1948        /// Returns the currently selected encoder (AAC, MP3, AIFF, WAV, etc.).
1949        pub CurrentEncoder, Encoder);
1950
1951    get_bool!(
1952        /// True if visuals are currently being displayed.
1953        pub VisualsEnabled);
1954
1955    set_bool!(
1956        /// True if visuals are currently being displayed.
1957        pub VisualsEnabled);
1958
1959    get_bool!(
1960        /// True if the visuals are displayed using the entire screen.
1961        pub FullScreenVisuals);
1962
1963    set_bool!(
1964        /// True if the visuals are displayed using the entire screen.
1965        pub FullScreenVisuals);
1966
1967    get_enum!(
1968        /// Returns the size of the displayed visual.
1969        pub VisualSize -> ITVisualSize);
1970
1971    set_enum!(
1972        /// Returns the size of the displayed visual.
1973        pub VisualSize, ITVisualSize);
1974
1975    get_object!(
1976        /// Returns the currently selected visual plug-in.
1977        pub CurrentVisual -> Visual);
1978
1979    set_object!(
1980        /// Returns the currently selected visual plug-in.
1981        pub CurrentVisual, Visual);
1982
1983    get_bool!(
1984        /// True if the equalizer is enabled.
1985        pub EQEnabled);
1986
1987    set_bool!(
1988        /// True if the equalizer is enabled.
1989        pub EQEnabled);
1990
1991    get_object!(
1992        /// Returns the currently selected EQ preset.
1993        pub CurrentEQPreset -> EQPreset);
1994
1995    set_object!(
1996        /// Returns the currently selected EQ preset.
1997        pub CurrentEQPreset, EQPreset);
1998
1999    get_bstr!(
2000        /// The name of the current song in the playing stream (provided by streaming server).
2001        pub CurrentStreamTitle);
2002
2003    get_bstr!(
2004        /// The URL of the playing stream or streaming web site (provided by streaming server).
2005        pub set_CurrentStreamURL);
2006
2007    get_object!(
2008        /// Returns the main iTunes browser window.
2009        pub BrowserWindow -> BrowserWindow);
2010
2011    get_object!(
2012        /// Returns the EQ window.
2013        pub EQWindow -> Window);
2014
2015    get_object!(
2016        /// Returns the source that represents the main library.
2017        pub LibrarySource -> Source);
2018
2019    get_object!(
2020        /// Returns the main library playlist in the main library source.
2021        pub LibraryPlaylist -> LibraryPlaylist);
2022
2023    get_object!(
2024        /// Returns the currently targeted track.
2025        pub CurrentTrack -> Track);
2026
2027    get_object!(
2028        /// Returns the playlist containing the currently targeted track.
2029        pub CurrentPlaylist -> Playlist);
2030
2031    get_object!(
2032        /// Returns a collection containing the currently selected track or tracks.
2033        pub SelectedTracks -> TrackCollection);
2034
2035    get_bstr!(
2036        /// Returns the version of the iTunes application.
2037        pub Version);
2038
2039    set_long!(
2040        /// [id(0x6002003b)]
2041        /// (no other documentation provided)
2042        pub SetOptions, no_set_prefix);
2043
2044    get_object_from_str!(
2045        /// Start converting the specified file path.
2046        pub ConvertFile2(filePath) -> ConvertOperationStatus);
2047
2048    get_object_from_variant!(
2049        /// Start converting the specified array of file paths. filePaths can be of type VT_ARRAY|VT_VARIANT, where each entry is a VT_BSTR, or VT_ARRAY|VT_BSTR.  You can also pass a JScript Array object.
2050        pub ConvertFiles2(filePaths) -> ConvertOperationStatus);
2051
2052    get_object_from_variant!(
2053        /// Start converting the specified track.  iTrackToConvert is a VARIANT of type VT_DISPATCH that points to an IITTrack.
2054        pub ConvertTrack2(iTrackToConvert) -> ConvertOperationStatus);
2055
2056    get_object_from_variant!(
2057        /// Start converting the specified tracks.  iTracksToConvert is a VARIANT of type VT_DISPATCH that points to an IITTrackCollection.
2058        pub ConvertTracks2(iTracksToConvert) -> ConvertOperationStatus);
2059
2060    get_bool!(
2061        /// True if iTunes will process APPCOMMAND Windows messages.
2062        pub AppCommandMessageProcessingEnabled);
2063
2064    set_bool!(
2065        /// True if iTunes will process APPCOMMAND Windows messages.
2066        pub AppCommandMessageProcessingEnabled);
2067
2068    get_bool!(
2069        /// True if iTunes will force itself to be the foreground application when it displays a dialog.
2070        pub ForceToForegroundOnDialog);
2071
2072    set_bool!(
2073        /// True if iTunes will force itself to be the foreground application when it displays a dialog.
2074        pub ForceToForegroundOnDialog);
2075
2076    get_object_from_str!(
2077        /// Create a new EQ preset.
2078        pub CreateEQPreset(eqPresetName) -> EQPreset);
2079
2080    /// Creates a new playlist in an existing source.
2081    pub fn CreatePlaylistInSource(&self, playlistName: &str, source: &Source) -> windows::core::Result<Playlist> {
2082        str_to_bstr!(playlistName, bstr);
2083        let vsource = source.as_variant();
2084        let mut out_playlist = None;
2085        let result = unsafe{ self.com_object.CreatePlaylistInSource(bstr, vsource.as_raw() as *const VARIANT, &mut out_playlist as *mut _) };
2086        result.ok()?;
2087
2088        let iTunes_arc = self.iTunes();
2089        create_wrapped_object!(Playlist, out_playlist, iTunes_arc)
2090    }
2091
2092    /// Retrieves the current state of the player buttons.
2093    pub fn GetPlayerButtonsState(&self) -> windows::core::Result<PlayerButtonState> {
2094        let mut previousEnabled = FALSE;
2095        let mut playPauseStopState = ITPlayButtonState::ITPlayButtonStatePlayDisabled;
2096        let mut nextEnabled = FALSE;
2097        let result = unsafe{ self.com_object.GetPlayerButtonsState(&mut previousEnabled, &mut playPauseStopState, &mut nextEnabled) };
2098        result.ok()?;
2099        Ok(PlayerButtonState{
2100            previousEnabled: previousEnabled.as_bool(),
2101            playPauseStopState,
2102            nextEnabled: nextEnabled.as_bool(),
2103        })
2104    }
2105
2106    /// Simulate click on a player control button.
2107    pub fn PlayerButtonClicked(&self, playerButton: ITPlayerButton, playerButtonModifierKeys: LONG) -> windows::core::Result<()> {
2108        let result = unsafe{ self.com_object.PlayerButtonClicked(playerButton, playerButtonModifierKeys) };
2109        result.ok()
2110    }
2111
2112    /// True if the Shuffle property is writable for the specified playlist.
2113    pub fn CanSetShuffle(&self, iPlaylist: &Playlist) -> windows::core::Result<bool> {
2114        let vplaylist = iPlaylist.as_variant();
2115        let mut out_bool = FALSE;
2116        let result = unsafe{ self.com_object.CanSetShuffle(vplaylist.as_raw() as *const VARIANT, &mut out_bool) };
2117        result.ok()?;
2118        Ok(out_bool.as_bool())
2119    }
2120
2121    /// True if the SongRepeat property is writable for the specified playlist.
2122    pub fn CanSetSongRepeat(&self, iPlaylist: &Playlist) -> windows::core::Result<bool> {
2123        let vplaylist = iPlaylist.as_variant();
2124        let mut out_bool = FALSE;
2125        let result = unsafe{ self.com_object.CanSetSongRepeat(vplaylist.as_raw() as *const VARIANT, &mut out_bool) };
2126        result.ok()?;
2127        Ok(out_bool.as_bool())
2128    }
2129
2130    get_object!(
2131        /// Returns an IITConvertOperationStatus object if there is currently a conversion in progress.
2132        pub ConvertOperationStatus -> ConvertOperationStatus);
2133
2134    set_bstr!(
2135        /// Subscribe to the specified podcast feed URL.
2136        pub SubscribeToPodcast, no_set_prefix);
2137
2138    no_args!(
2139        /// Update all podcast feeds.
2140        pub UpdatePodcastFeeds);
2141
2142    get_object_from_str!(
2143        /// Creates a new folder in the main library.
2144        pub CreateFolder(folderName) -> Playlist);
2145
2146    /// Creates a new folder in an existing source.
2147    pub fn CreateFolderInSource(&self, folderName: &str, iSource: &Source) -> windows::core::Result<Playlist> {
2148        str_to_bstr!(folderName, bstr);
2149        let vsource = iSource.as_variant();
2150        let mut out_playlist = None;
2151        let result = unsafe{ self.com_object.CreateFolderInSource(bstr, vsource.as_raw() as *const VARIANT, &mut out_playlist as *mut _) };
2152        result.ok()?;
2153
2154        let iTunes_arc = self.iTunes();
2155        create_wrapped_object!(Playlist, out_playlist, iTunes_arc)
2156    }
2157
2158    get_bool!(
2159        /// True if the sound volume control is enabled.
2160        pub SoundVolumeControlEnabled);
2161
2162    get_bstr!(
2163        /// The full path to the current iTunes library XML file.
2164        pub LibraryXMLPath);
2165
2166    /// Returns the persistent ID of the specified IITObject.
2167    ///
2168    /// See also the convience function [`IITObjectWrapper::persistent_id`]
2169    pub fn GetITObjectPersistentID<T>(&self, iObject: &Variant<T>) -> windows::core::Result<PersistentId> {
2170        let mut highID: LONG = 0;
2171        let mut lowID: LONG = 0;
2172        let result = unsafe{ self.com_object.GetITObjectPersistentIDs(iObject.as_raw() as *const VARIANT, &mut highID, &mut lowID) };
2173        result.ok()?;
2174
2175        let bytes = [lowID.to_le_bytes(), highID.to_le_bytes()].concat();
2176        Ok(PersistentId::from_le_bytes(bytes.try_into().unwrap()))  // cannot panic, the slice has the correct size
2177    }
2178
2179    get_long!(
2180        /// Returns the player's position within the currently playing track in milliseconds.
2181        pub PlayerPositionMS);
2182
2183    set_long!(
2184        /// Returns the player's position within the currently playing track in milliseconds.
2185        pub PlayerPositionMS);
2186}
2187
2188// We've initialized the COM library with COINIT_MULTITHREADED, so it is possible to call functions on the IITunes object from multiple threads.
2189// However, the documentation (https://learn.microsoft.com/en-us/windows/win32/api/objbase/ne-objbase-coinit#remarks):
2190// says that "This means, however, that the code for objects must enforce its own concurrency model, typically through the use of synchronization primitives, such as critical sections, semaphores, or mutexes"
2191// Maybe we could assume Apple developers have done this.
2192// Maybe not. We actually have no clue whether the underlying COM object is thread-safe.
2193// So, let's not implement `Sync` blindly.
2194// unsafe impl Sync for iTunes {}
2195
2196com_wrapper_struct!(
2197    /// Safe wrapper over a [`IITAudioCDPlaylist`](crate::sys::IITAudioCDPlaylist)
2198    AudioCDPlaylist);
2199
2200impl IITObjectWrapper for AudioCDPlaylist {}
2201
2202impl IITPlaylistWrapper for AudioCDPlaylist {}
2203
2204impl AudioCDPlaylist {
2205    get_bstr!(
2206        /// The artist of the CD.
2207        pub Artist);
2208
2209    get_bool!(
2210        /// True if this CD is a compilation album.
2211        pub Compilation);
2212
2213    get_bstr!(
2214        /// The composer of the CD.
2215        pub Composer);
2216
2217    get_long!(
2218        /// The total number of discs in this CD's album.
2219        pub DiscCount);
2220
2221    get_long!(
2222        /// The index of the CD disc in the source album.
2223        pub DiscNumber);
2224
2225    get_bstr!(
2226        /// The genre of the CD.
2227        pub Genre);
2228
2229    get_long!(
2230        /// The year the album was recorded/released.
2231        pub Year);
2232
2233    no_args!(
2234        /// Reveal the CD playlist in the main browser window.
2235        pub Reveal);
2236}
2237
2238com_wrapper_struct!(
2239    /// Safe wrapper over a [`IITIPodSource`](crate::sys::IITIPodSource)
2240    IPodSource);
2241
2242impl IITObjectWrapper for IPodSource {}
2243
2244impl IPodSource {
2245    no_args!(
2246        /// Update the contents of the iPod.
2247        pub UpdateIPod);
2248
2249    no_args!(
2250        /// Eject the iPod.
2251        pub EjectIPod);
2252
2253    get_bstr!(
2254        /// The iPod software version.
2255        pub SoftwareVersion);
2256}
2257
2258com_wrapper_struct!(
2259    /// Safe wrapper over a [`IITFileOrCDTrack`](crate::sys::IITFileOrCDTrack)
2260    FileOrCDTrack);
2261
2262impl IITObjectWrapper for FileOrCDTrack {}
2263
2264impl IITTrackWrapper for FileOrCDTrack {}
2265
2266impl FileOrCDTrack {
2267    get_bstr!(
2268        /// The full path to the file represented by this track.
2269        pub Location);
2270
2271    no_args!(
2272        /// Update this track's information with the information stored in its file.
2273        pub UpdateInfoFromFile);
2274
2275    get_bool!(
2276        /// True if this is a podcast track.
2277        pub Podcast);
2278
2279    no_args!(
2280        /// Update the podcast feed for this track.
2281        pub UpdatePodcastFeed);
2282
2283    get_bool!(
2284        /// True if playback position is remembered.
2285        pub RememberBookmark);
2286
2287    set_bool!(
2288        /// True if playback position is remembered.
2289        pub RememberBookmark);
2290
2291    get_bool!(
2292        /// True if track is skipped when shuffling.
2293        pub ExcludeFromShuffle);
2294
2295    set_bool!(
2296        /// True if track is skipped when shuffling.
2297        pub ExcludeFromShuffle);
2298
2299    get_bstr!(
2300        /// Lyrics for the track.
2301        pub Lyrics);
2302
2303    set_bstr!(
2304        /// Lyrics for the track.
2305        pub Lyrics);
2306
2307    get_bstr!(
2308        /// Category for the track.
2309        pub Category);
2310
2311    set_bstr!(
2312        /// Category for the track.
2313        pub Category);
2314
2315    get_bstr!(
2316        /// Description for the track.
2317        pub Description);
2318
2319    set_bstr!(
2320        /// Description for the track.
2321        pub Description);
2322
2323    get_bstr!(
2324        /// Long description for the track.
2325        pub LongDescription);
2326
2327    set_bstr!(
2328        /// Long description for the track.
2329        pub LongDescription);
2330
2331    get_long!(
2332        /// The bookmark time of the track (in seconds).
2333        pub BookmarkTime);
2334
2335    set_long!(
2336        /// The bookmark time of the track (in seconds).
2337        pub BookmarkTime);
2338
2339    get_enum!(
2340        /// The video track kind.
2341        pub VideoKind -> ITVideoKind);
2342
2343    set_enum!(
2344        /// The video track kind.
2345        pub VideoKind, ITVideoKind);
2346
2347    get_long!(
2348        /// The number of times the track has been skipped.
2349        pub SkippedCount);
2350
2351    set_long!(
2352        /// The number of times the track has been skipped.
2353        pub SkippedCount);
2354
2355    get_date!(
2356        /// The date and time the track was last skipped.  A value of zero means no skipped date.
2357        pub SkippedDate);
2358
2359    set_date!(
2360        /// The date and time the track was last skipped.  A value of zero means no skipped date.
2361        pub SkippedDate);
2362
2363    get_bool!(
2364        /// True if track is part of a gapless album.
2365        pub PartOfGaplessAlbum);
2366
2367    set_bool!(
2368        /// True if track is part of a gapless album.
2369        pub PartOfGaplessAlbum);
2370
2371    get_bstr!(
2372        /// The album artist of the track.
2373        pub AlbumArtist);
2374
2375    set_bstr!(
2376        /// The album artist of the track.
2377        pub AlbumArtist);
2378
2379    get_bstr!(
2380        /// The show name of the track.
2381        pub Show);
2382
2383    set_bstr!(
2384        /// The show name of the track.
2385        pub Show);
2386
2387    get_long!(
2388        /// The season number of the track.
2389        pub SeasonNumber);
2390
2391    set_long!(
2392        /// The season number of the track.
2393        pub SeasonNumber);
2394
2395    get_bstr!(
2396        /// The episode ID of the track.
2397        pub EpisodeID);
2398
2399    set_bstr!(
2400        /// The episode ID of the track.
2401        pub EpisodeID);
2402
2403    get_long!(
2404        /// The episode number of the track.
2405        pub EpisodeNumber);
2406
2407    set_long!(
2408        /// The episode number of the track.
2409        pub EpisodeNumber);
2410
2411    /// The size of the track (in bytes)
2412    pub fn Size(&self) -> windows::core::Result<i64> {
2413        let mut highSize = 0;
2414        let mut lowSize = 0;
2415        let result = unsafe{ self.com_object.Size64High(&mut highSize) };
2416        result.ok()?;
2417        let result = unsafe{ self.com_object.Size64Low(&mut lowSize) };
2418        result.ok()?;
2419
2420        let bytes = [lowSize.to_le_bytes(), highSize.to_le_bytes()].concat();
2421        Ok(i64::from_le_bytes(bytes.try_into().unwrap())) // cannot panic, the slice has the correct size
2422    }
2423
2424    get_bool!(
2425        /// True if track has not been played.
2426        pub Unplayed);
2427
2428    set_bool!(
2429        /// True if track has not been played.
2430        pub Unplayed);
2431
2432    get_bstr!(
2433        /// The album used for sorting.
2434        pub SortAlbum);
2435
2436    set_bstr!(
2437        /// The album used for sorting.
2438        pub SortAlbum);
2439
2440    get_bstr!(
2441        /// The album artist used for sorting.
2442        pub SortAlbumArtist);
2443
2444    set_bstr!(
2445        /// The album artist used for sorting.
2446        pub SortAlbumArtist);
2447
2448    get_bstr!(
2449        /// The artist used for sorting.
2450        pub SortArtist);
2451
2452    set_bstr!(
2453        /// The artist used for sorting.
2454        pub SortArtist);
2455
2456    get_bstr!(
2457        /// The composer used for sorting.
2458        pub SortComposer);
2459
2460    set_bstr!(
2461        /// The composer used for sorting.
2462        pub SortComposer);
2463
2464    get_bstr!(
2465        /// The track name used for sorting.
2466        pub SortName);
2467
2468    set_bstr!(
2469        /// The track name used for sorting.
2470        pub SortName);
2471
2472    get_bstr!(
2473        /// The show name used for sorting.
2474        pub SortShow);
2475
2476    set_bstr!(
2477        /// The show name used for sorting.
2478        pub SortShow);
2479
2480    no_args!(
2481        /// Reveal the track in the main browser window.
2482        pub Reveal);
2483
2484    get_rating!(
2485        /// The user or computed rating of the album that this track belongs to.
2486        pub AlbumRating);
2487
2488    set_rating!(
2489        /// The user or computed rating of the album that this track belongs to.
2490        pub AlbumRating);
2491
2492    get_enum!(
2493        /// The album rating kind.
2494        pub AlbumRatingKind -> ITRatingKind);
2495
2496    get_enum!(
2497        /// The track rating kind.
2498        pub ratingKind -> ITRatingKind);
2499
2500    get_object!(
2501        /// Returns a collection of playlists that contain the song that this track represents.
2502        pub Playlists -> PlaylistCollection);
2503
2504    set_bstr!(
2505        /// The full path to the file represented by this track.
2506        pub Location);
2507
2508    get_date!(
2509        /// The release date of the track.  A value of zero means no release date.
2510        pub ReleaseDate);
2511}
2512
2513com_wrapper_struct!(
2514    /// Safe wrapper over a [`IITPlaylistWindow`](crate::sys::IITPlaylistWindow)
2515    PlaylistWindow);
2516
2517impl PlaylistWindow {
2518    get_object!(
2519        /// Returns a collection containing the currently selected track or tracks.
2520        pub SelectedTracks -> TrackCollection);
2521
2522    get_object!(
2523        /// The playlist displayed in the window.
2524        pub Playlist -> Playlist);
2525}