GuillemCastro / spotify-dl

A command-line utility to download songs and playlists directly from Spotify's servers
MIT License
90 stars 13 forks source link

Skip if file already exists #21

Open flornet opened 3 weeks ago

flornet commented 3 weeks ago

Hi,

Could anybody help me to tweak to code in order to allow to skip already existing local files when I download a playlist?

Cheers ;-)

flornet commented 3 weeks ago

Claude.ai did it for me. If it's interesting for someone, here is the updated download_track function :

#[tracing::instrument(name = "download_track", skip(self))]
    async fn download_track(&self, track: Track, options: &DownloadOptions) -> Result<()> {
        let metadata = track.metadata(&self.session).await?;
        tracing::info!("Downloading track: {:?}", metadata);

        let file_name = self.get_file_name(&metadata);
        let path = options
            .destination
            .join(file_name.clone())
            .with_extension(options.format.extension());

        // Verify if the file already exists
        if path.exists() {
            tracing::info!("The file already exists: {:?}", path);
            return Ok(());
        }

        let path_str = path
            .to_str()
            .ok_or(anyhow::anyhow!("Could not set the output path"))?
            .to_string();

        let (sink, mut sink_channel) = ChannelSink::new(metadata);

        let file_size = sink.get_approximate_size();

        let (mut player, _) = Player::new(
            self.player_config.clone(),
            self.session.clone(),
            self.volume_getter(),
            move || Box::new(sink),
        );

        let pb = self.progress_bar.add(ProgressBar::new(file_size as u64));
        pb.enable_steady_tick(Duration::from_millis(100));
        pb.set_style(ProgressStyle::with_template("{spinner:.green} {msg} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})")?
            .with_key("eta", |state: &ProgressState, w: &mut dyn Write| write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap())
            .progress_chars("#>-"));
        pb.set_message(file_name.clone());

        player.load(track.id, true, 0);

        let mut samples = Vec::<i32>::new();

        tokio::spawn(async move {
            player.await_end_of_track().await;
            player.stop();
        });

        while let Some(event) = sink_channel.recv().await {
            match event {
                SinkEvent::Write { bytes, total, mut content } => {
                    tracing::trace!("Written {} bytes out of {}", bytes, total);
                    pb.set_position(bytes as u64);
                    samples.append(&mut content);
                }
                SinkEvent::Finished => {
                    tracing::info!("Finished downloading track: {:?}", file_name);
                    break;
                }
            }
        }

        tracing::info!("Encoding track: {:?}", file_name);
        pb.set_message(format!("Encoding {}", &file_name));
        let samples = Samples::new(samples, 44100, 2, 16);
        let encoder = crate::encoder::get_encoder(options.format);
        let stream = encoder.encode(samples).await?;

        pb.set_message(format!("Writing {}", &file_name));
        tracing::info!("Writing track: {:?} to file: {}", file_name, &path_str);
        stream.write_to_file(&path_str).await?;

        pb.finish_with_message(format!("Downloaded {}", &file_name));
        Ok(())
    }