Kirsle.net logo Kirsle.net

Download Any Flash Video in Linux

April 6, 2011 by Noah
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).
Tags:

Comments

There are 18 comments on this page. Add yours.

Avatar image
chris posted on April 6, 2011 @ 18:06 UTC

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

Avatar image
Timm Gleason posted on May 13, 2011 @ 15:08 UTC

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

Avatar image
Update posted on May 10, 2012 @ 15:17 UTC

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 ....

Avatar image
Not working as of May 2012 posted on June 9, 2012 @ 03:13 UTC

Not working as of May 2012

Avatar image
ABAGAG posted on July 1, 2012 @ 18:36 UTC

works fine... less on youtube page..

Avatar image
zack posted on November 18, 2012 @ 23:52 UTC

works on any site, no issues

-using crunchbang linux

Avatar image
Ram posted on December 23, 2012 @ 03:33 UTC

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

Avatar image
ralph posted on January 5, 2013 @ 16:38 UTC

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 :)

Avatar image
MrBiTs posted on January 20, 2013 @ 14:14 UTC

My man !

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

Keep walking... keep haking.

Avatar image
Tico posted on February 3, 2013 @ 20:03 UTC

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

Avatar image
ralph posted on February 7, 2013 @ 12:10 UTC

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.

Avatar image
ralph posted on February 7, 2013 @ 12:16 UTC

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 :)

Avatar image
marc posted on February 17, 2013 @ 21:51 UTC

Thanks a lot for this, very useful.

Avatar image
sab posted on September 27, 2013 @ 17:16 UTC

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...

Avatar image
marneezy posted on December 8, 2013 @ 06:03 UTC

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

Avatar image
AK Srivastava posted on December 10, 2013 @ 06:56 UTC

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, line 1946.

now what to do

Avatar image
daft117 posted on July 11, 2014 @ 10:59 UTC

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

Avatar image
Enrique posted on June 23, 2016 @ 08:57 UTC

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

Used for your Gravatar and optional thread subscription. Privacy policy.
You may format your message using GitHub Flavored Markdown syntax.