Download Any Flash Video in Linux

Noah Petherbridge
kirsle
Posted by Noah Petherbridge on Tuesday, April 05 2011 @ 11:22:07 PM
Reason #65,535 that I love Linux: the flash plugin makes it relatively easy to download any embedded flash video from any site (ex. YouTube and all sites like it).

The old (but still the current) version of the Adobe Flash plugin (10.2) would simply save all Flash videos (.flv files) in /tmp with names like "/tmp/Flash########" where the #'s are random numbers and letters. You could simply buffer a video on YouTube or any other video hosting site, then go to /tmp in your file browser and copy these files somewhere else. They typically get deleted when Flash exits.

The new version of the flash plugin (10.3) tries to be a little more clever. It still creates the files under /tmp, but then it deletes them immediately while still keeping them open. In Linux, if an opened file is deleted, the OS postpones deletion of the file until the process using it lets it go. So the file can't be seen when you look in /tmp, but the flash plugin can still use it until it's done with it.

So how do you get the FLV files from it now?

1) Get the process ID of the flash plugin

$ ps aux | grep flash
kirsle    3324  3.3  1.5 281488 44716 ?        Sl   12:01  10:41 /usr/lib/xulrunner-2/plugin-container /home/kirsle/.mozilla/plugins/libflashplayer.so -grebase /usr/lib/xulrunner-2 -appbase /usr/lib/firefox-4 3164 true plugin
2) Look at the process's open file descriptors in /proc
$ cd /proc/3324/fd
3) List the files to find the FLV file (it will be a symbolic link to a "deleted" /tmp/Flash* file)
$ ls -hal | grep Flash
lr-x------ 1 kirsle kirsle 64 Apr  5 12:01 17 -> /tmp/FlashXX9ew4CF (deleted)
4) Copy it (by ID) somewhere else
$ cp 17 ~/videoname.flv
Easy. One could trivially write a shell script that does this automatically too.

This method should work for the foreseeable future, unless Adobe finds some really clever way to stop this, like encrypting video files somehow, or holding them completely in RAM (not likely because some video files can be pretty large). Processes can't hide anything under /proc. :)

More info about /proc.

UPDATE (May 17 2011): I've written a quick Perl script that copies all active Flash videos to your home directory. It doesn't care about the process name; it just loops through all PIDs in /proc, looking for any that owns a "/tmp/Flash*" name, and copies them to $HOME. It works even when you have multiple videos buffered at the same time.

Download it from: sh.kirsle.net/flashget.

Update - Google Chrome

(Jan 11, 2015)

This method can also work for Google Chrome (the above was for Firefox/other browsers that use the old Flash plugin system). In Chrome, the Pepper API is used for the Flash plugin instead.

1) Get the PID of the Pepper plugin for Flash

$ ps aux | grep ppapi
noah     14152 25.1  1.4 980444 87828 ?        Sl   15:37   0:05 /opt/google/chrome/chrome --type=ppapi --channel=13618.24.1890984534 --ppapi-flash-args --lang=en-US
2) Get into this PID's file descriptors

*NOTE:* This one requires root access, unlike for the Firefox instructions above.

$ sudo -i
# cd /proc/14152/fd
# ls -hal
[...cut...]
24 -> /home/noah/.config/google-chrome/Default/Pepper Data/Shockwave Flash/.com.google.Chrome.p9GBU8 (deleted)
Look for the file descriptor that points to a "deleted" Flash file (in this example, it's file descriptor #24).

3) Copy it somewhere else

# cp 24 ~your_user/24.flv
# chown your_user:your_user ~your_user/24.flv
So, the process is similar for doing it for Firefox, except:
  • Look for the ppapi process instead of the plugin-container or libflashplayer.so
  • Use root privileges to get into that process's file descriptors (it seems Chrome locks down the folder so only root can read it).
  • The "deleted" file isn't under /tmp anymore, but in a user's home folder with a URI that mentions Flash.
I have updated my Perl script, flashget, so that it works with both Firefox and Chrome style Flash plugins. You need to run it as root if you want to pluck videos from Chrome, however. Usage has been updated so that you can specify an output directory (otherwise, running it as root would output the Chrome videos into /root instead of your main user's home folder).

Categories:

[ Blog ]

Comments

There are 18 comments on this page.

guest
guest
Posted on Wednesday, April 06 2011 @ 11:06:03 AM by chris.

Is there a way to download Youtube Videos from the shell without GUI Maybe one could modify the Java Applet from www.keepvid.com ...

guest
guest
Posted on Friday, May 13 2011 @ 08:08:50 AM by Timm Gleason.

Here's a small shell script that will do this.

#!/bin/bash
#
#
#          FILE:  save_flash.sh
# 
#         USAGE:  ./save_flash.sh 
# 
#   DESCRIPTION:  use to save Flash Videos
#  Will not work properly if displaying flash on more than
#  tab at a time currently
# 
#       OPTIONS:  ---
#  REQUIREMENTS:  ---
#          BUGS:  ---
#         NOTES:  ---
#        AUTHOR:  Timm Gleason(timmgleason_at_gmail_dot_com), 
#       COMPANY:  
#       VERSION:  1.0
#       CREATED:  05/13/2011 07:16:37 AM PDT
#      REVISION:  ---
#

PID=$(pidof plugin-container)

echo $PID

cd /proc/$PID/fd
FILE_INODEls -hal | grep /tmp/Flash | awk '{print $9}'

x1
while [ $x -gt 0 ] ; do
  if [ -e "$HOME/saved_flash_$x.flv" ] ; then
    ((x++))
  else
    cp $FILE_INODE $HOME/saved_flash_$x.flv
    x0
  fi
done

Edit by Kirsle: Fixed code formatting

guest
guest
Posted on Thursday, May 10 2012 @ 08:17:31 AM by Update.

The 4 steps above work if the video is from Vimeo, but not the last 2 if it's from YouTube (cannot locate and copy them).

The cat and the mouse ....

guest
guest
Posted on Friday, June 08 2012 @ 08:13:54 PM by Not working as of May 2012.

Not working as of May 2012

guest
guest
Posted on Sunday, July 01 2012 @ 11:36:46 AM by ABAGAG.

works fine... less on youtube page..

guest
guest
Posted on Sunday, November 18 2012 @ 03:52:25 PM by zack.

works on any site, no issues

-using crunchbang linux

guest
guest
Posted on Saturday, December 22 2012 @ 07:33:05 PM by Ram.

Hello Kirsle,

Merry Xmas!!

I did follow the steps in here to try and download the flash video, but couldn't.

Here is what I did when the video was playing and also after the video completed playing.

My operating system is

[root@localhost ~]# uname -a
Linux localhost.localdomain 3.6.2-4.fc17.x86_64 #1 SMP Wed Oct 17 02:43:21 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

The steps I followed below


[root@localhost proc]# ps -ef | grep -i flash
root     13729 13565  0 16:17 pts/1    00:00:00 grep --color=auto -i flash
[root@localhost proc]# ps -ef | grep -i flash
deepu    13730  1648 43 16:17 ?        00:00:03 /usr/lib64/xulrunner/plugin-container /usr/lib64/flash-plugin/libflashplayer.so -greomni /usr/lib64/xulrunner/omni.ja -appomni /usr/lib64/firefox/omni.ja 1648 true plugin
root     13766 13565  0 16:17 pts/1    00:00:00 grep --color=auto -i flash

[root@localhost kde-deepu]# cd /proc/13730/fd
[root@localhost fd]# ls -lrt
total 0
lrwx------. 1 deepu deepu 64 Dec 23 16:17 9 -> socket:[317254]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 8 -> anon_inode:[eventpoll]
l-wx------. 1 deepu deepu 64 Dec 23 16:17 7 -> pipe:[313137]
lr-x------. 1 deepu deepu 64 Dec 23 16:17 6 -> pipe:[313137]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 5 -> anon_inode:[eventfd]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 4 -> socket:[39675]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 3 -> socket:[313136]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 23 -> socket:[317343]
l-wx------. 1 deepu deepu 64 Dec 23 16:17 22 -> pipe:[319792]
lr-x------. 1 deepu deepu 64 Dec 23 16:17 21 -> pipe:[319792]
l-wx------. 1 deepu deepu 64 Dec 23 16:17 20 -> pipe:[319791]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 2 -> /home/deepu/.xsession-errors
lr-x------. 1 deepu deepu 64 Dec 23 16:17 19 -> anon_inode:inotify
lrwx------. 1 deepu deepu 64 Dec 23 16:17 18 -> socket:[319610]
lr-x------. 1 deepu deepu 64 Dec 23 16:17 17 -> pipe:[319791]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 16 -> socket:[319715]
lr-x------. 1 deepu deepu 64 Dec 23 16:17 15 -> /home/deepu/.mozilla/firefox/fg5l80h5.default/key3.db
lr-x------. 1 deepu deepu 64 Dec 23 16:17 14 -> /home/deepu/.mozilla/firefox/fg5l80h5.default/cert8.db
lrwx------. 1 deepu deepu 64 Dec 23 16:17 13 -> socket:[313139]
l-wx------. 1 deepu deepu 64 Dec 23 16:17 12 -> pipe:[317256]
lr-x------. 1 deepu deepu 64 Dec 23 16:17 11 -> pipe:[317256]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 10 -> socket:[317255]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 1 -> /home/deepu/.xsession-errors
lr-x------. 1 deepu deepu 64 Dec 23 16:17 0 -> pipe:[23895]
[root@localhost fd]# ls -hal | grep -i flash
[root@localhost fd]# ls -lha
total 0
dr-x------. 2 deepu deepu  0 Dec 23 16:17 .
dr-xr-xr-x. 8 deepu deepu  0 Dec 23 16:17 ..
lr-x------. 1 deepu deepu 64 Dec 23 16:17 0 -> pipe:[23895]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 1 -> /home/deepu/.xsession-errors
lrwx------. 1 deepu deepu 64 Dec 23 16:17 10 -> socket:[317255]
lr-x------. 1 deepu deepu 64 Dec 23 16:17 11 -> pipe:[317256]
l-wx------. 1 deepu deepu 64 Dec 23 16:17 12 -> pipe:[317256]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 13 -> socket:[313139]
lr-x------. 1 deepu deepu 64 Dec 23 16:17 14 -> /home/deepu/.mozilla/firefox/fg5l80h5.default/cert8.db
lr-x------. 1 deepu deepu 64 Dec 23 16:17 15 -> /home/deepu/.mozilla/firefox/fg5l80h5.default/key3.db
lrwx------. 1 deepu deepu 64 Dec 23 16:17 16 -> socket:[319715]
lr-x------. 1 deepu deepu 64 Dec 23 16:17 17 -> pipe:[319791]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 18 -> socket:[319610]
lr-x------. 1 deepu deepu 64 Dec 23 16:17 19 -> anon_inode:inotify
lrwx------. 1 deepu deepu 64 Dec 23 16:17 2 -> /home/deepu/.xsession-errors
l-wx------. 1 deepu deepu 64 Dec 23 16:17 20 -> pipe:[319791]
lr-x------. 1 deepu deepu 64 Dec 23 16:17 21 -> pipe:[319792]
l-wx------. 1 deepu deepu 64 Dec 23 16:17 22 -> pipe:[319792]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 23 -> socket:[317343]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 3 -> socket:[313136]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 4 -> socket:[39675]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 5 -> anon_inode:[eventfd]
lr-x------. 1 deepu deepu 64 Dec 23 16:17 6 -> pipe:[313137]
l-wx------. 1 deepu deepu 64 Dec 23 16:17 7 -> pipe:[313137]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 8 -> anon_inode:[eventpoll]
lrwx------. 1 deepu deepu 64 Dec 23 16:17 9 -> socket:[317254]

The script dint find any either :(

Please could you help me here...

Kind Regards
Ram

Edit by Kirsle: fixed formatting

guest
guest
Posted on Saturday, January 05 2013 @ 08:38:00 AM by ralph.

this will get everything AFAIK.

#!/bin/bash

args=("$@")

args=`echo $args | sed 's/[/]$//'`

pids=`eval pgrep -f flashplayer`
for pid in $pids; do
  lsoutput=$(lsof -p $pid | grep '/tmp/Flash[^ ]*')

  IFS=$'\n'
  for line in $lsoutput; do
    lsout1=`echo $line | awk '{print "/proc/" $2 "/fd/" $4}' | sed 's/[rwu]$//'`
    lsout2=`echo $line | awk '{print $9}' | awk -F '/' '{print $3}'`

    if [ -n "$args" ];then
      if [ -d $args ]; then
        echo "Copying $lsout2 to $args/"
        eval "cp $lsout1 $args/$lsout2.flv"
      else
        echo "The directory \"$args\" doesn't exist"
        break
      fi
    else
      echo "Copying $lsout2"
      eval "cp $lsout1 $lsout2.flv"
    fi
  done
done

Edited by Kirsle: made your code prettier :)

guest
guest
Posted on Sunday, January 20 2013 @ 06:14:18 AM by MrBiTs.

My man !

That was a genius hacking. Thanks a lot and my best wishes to you.

Keep walking... keep haking.

guest
guest
Posted on Sunday, February 03 2013 @ 12:03:40 PM by Tico.

Hi! Do you know any method to play/download a video from Youtube without Flash? I'm wondering if you even get to get the video URL without some flash/cookies working, alas..
BR! Tico

guest
guest
Posted on Thursday, February 07 2013 @ 04:10:12 AM by ralph.

Tico:

I think your asking if a standalone player will play youtube videos?
Flash video Downloader firefox plugin wukk get the base url of the video, I think. the .flv file it's self.
Hope thats close.

guest
guest
Posted on Thursday, February 07 2013 @ 04:16:39 AM by ralph.

MrBiTs: I wish I was that badass of a shell script coder. I have no idea the original author. Kudos to him/her.

This is a python script that accomplishes the same thing, for you ppl who script/like Python.

Later!


#!/usr/bin/perl
#Filename: FlashVideoCapture.pl
use strict;

##################################################
# Setup the variables
##################################################
my $PROGNAME = $0; $PROGNAME =~ s|.*/||;

my $LSOF = 'lsof';

my $FIND = 'flash';       # Find flash files
my $POST = 'flv'; # Postfix to save to

# Where we save files
#   %f is $FIND
#   %d is the next available number
#   %p is .$POST
my $DEST = "found%f.%d%p";

##################################################
# Usage
##################################################
sub fatal {
        foreach my $msg (@_) { print STDERR "[$PROGNAME] ERROR:  $msg\n"; }
        exit(-1);
}

sub usage {
        foreach my $msg (@_) { print STDERR "ERROR:  $msg\n"; }
        print STDERR <<USAGE;

Usage:\t$PROGNAME [-d]
  Copies deleted flash files currently open in your browser's cache
  -d             Set debug mode
  -find <str>    What to search for  [default $FIND]
  -post <str>    Postfix for saving files [default $POST]
  -dest <str>    Or just specify full destination [default $DEST]
                 (see the script for meanings of %f, %d, %p)

USAGE
        exit -1;
}

sub parseArgs {
        usage("You need to be on a system that uses /proc") unless -d '/proc';

        my $opt = {
                find => $FIND,
                post => $POST,
                dest => $DEST,
        };
        while (my $arg=shift(@ARGV)) {
                if ($arg =~ /^-h$/) { usage(); }
                if ($arg =~ /^-d$/) { $MAIN::DEBUG=1; next; }
                if ($arg =~ /^-find$/) { $opt->{find} = shift(@ARGV); next; }
                if ($arg =~ /^-post$/) { $opt->{post} = shift(@ARGV); next; }
                if ($arg =~ /^-dest$/) { $opt->{dest} = shift(@ARGV); next; }
                if ($arg =~ /^-/) { usage("Unknown option: $arg"); }
                usage("Too many files specified [$arg and $opt->{file}]") if $opt->{file};
        }

        usage("You need to specify a destination with -dest")
                unless $opt->{dest};

        usage("You need to specify something to search for with -find")
                unless $opt->{find};

        $opt;
}

sub debug {
        return unless $MAIN::DEBUG;
        foreach my $msg (@_) { print STDERR "[$PROGNAME] $msg\n"; }
}

##################################################
# Main code
##################################################
sub findFiles {
        my ($opt) = @_;
        my @found;
        # 'lsof /'  (The '/' just does files, no sockets, and is faster)
        open(LSOF,"$LSOF /|") || usage("Can't run [$LSOF]");
        while (<LSOF>) {
                next unless /delete/i;
                next unless /\Q$opt->{find}\E/i;
                next if /\.adobe/;     # Ignore adobe 'flash' db files
                chomp;
                # procname  pid  user   fd
                usage("Found it, can't parse it [$_]")
                        unless /^\S+\s+(\d+)\s+\S+\s+(\d+)/;
                push(@found, [$1,$2]);
        }
        usage("Couldn't find any deleted cached $opt->{find} files")
                unless @found;
        @found;
}

sub procPath {
        my ($pid,$fd) = @_;
        my $path = "/proc/$pid";
        usage("Couldn't find $path") unless -d $path;
        $path .= '/fd';
        usage("Couldn't find $path") unless -d $path;
        $path .= "/$fd";
        usage("Couldn't read $path") unless -e $path;
        $path;
}

sub destPath {
        my ($opt) = @_;
        my $p = $opt->{dest};
        $p =~ s/%f/\Q$opt->{find}\E/g;
        $p =~ s/%p/.\Q$opt->{post}\E/g;
        my $num = 0;
        my $path;
        do {
                $path = $p;  $num++;
                $path =~ s/%d/$num/g;
        } until ! -f $path;
        $path;
}

sub main {
        my $opt = parseArgs();

        my @found = findFiles($opt);
        foreach my $found ( @found ) {
                my $src = procPath(@$found);
                my $dest = destPath($opt);
                print "$src -> $dest\n";
                system("/bin/cp",$src,$dest);
        }
}
main();

Edited by Kirsle: made your code prettier :)

guest
guest
Posted on Sunday, February 17 2013 @ 01:51:59 PM by marc.

Thanks a lot for this, very useful.

guest
guest
Posted on Friday, September 27 2013 @ 10:16:03 AM by sab.

It seems this is no more working with flash 11... I can't see any /tmp/Flash* file even when I have found the corresponding processus (plugin-container in my lsof output is cut out to plugin-co). However, the video stops playing when I try accessing a file in /proc/pid/fd/ I think Adobe has restricted flash even more so we cannot more download videos in this way...

guest
guest
Posted on Saturday, December 07 2013 @ 10:03:02 PM by marneezy.

@ralph
...that is a perl script, not python. hope you didnt write that.

guest
guest
Posted on Monday, December 09 2013 @ 10:56:16 PM by AK Srivastava.

This is the error that i m getting..
Can't use string ("flash") as a HASH ref while "strict refs" in use at ./FlashVideoCapture.pl line 85, <LSOF> line 1946.

now what to do

Avatar
guest
Posted on Friday, July 11 2014 @ 03:59:30 AM by daft117.

sudo apt-get install -y youtube-dl
youtube-dl "https://www.youtube.com/watch?v=II3bftjNQTw"

guest
guest
Posted on Thursday, June 23 2016 @ 01:57:46 AM by Enrique.

Hi Kirsle, very nice tutorial - Download Any Flash Video in Linux.
I keep coming to it everytime I need to keep any flash video.
Thanks.

Add a Comment

Your name:
Your Email:
Message:
Comments can be formatted with Markdown, and you can use
emoticons in your comment.

If you can see this, don't touch the following fields.