# Google Drive 3gp to mp4 converter
## Section
```elixir
defmodule GoogleDrive3gpToMp4 do
@moduledoc """
- Lazily traverses google drive path to find .3gp files
- Processes each file immediately when discovered (no sorting, no concurrency)
- converts it to .mp4 using ffmpeg, preserves timestamps
- removes the .3gp file on success.
NOTE: Uses 'touch -r' (Unix-like) to copy timestamps. On Windows, you may
need a Unix-like environment (Git Bash, Cygwin, WSL) or adapt the logic.
**When reuploaded to Google Drive, files loose their timestamps.**
"""
@drive_path Path.expand("<TODO: your Google Drive path here>")
def run do
IO.puts("Scanning lazily for .3gp files in: #{@drive_path}")
stream_3gp_files(@drive_path)
|> Stream.each(&process_file/1)
|> Stream.run()
end
defp stream_3gp_files(base_path) do
# If the base path doesn't exist, return an empty stream
if not File.exists?(base_path) do
Stream.map([], & &1)
else
Stream.resource(
fn ->
[base_path]
end,
fn
[] ->
{:halt, []}
[current | rest] ->
cond do
File.dir?(current) ->
case File.ls(current) do
{:ok, entries} ->
subpaths = Enum.map(entries, &Path.join(current, &1))
{[], rest ++ subpaths}
{:error, _} ->
{[], rest}
end
Path.extname(current) == ".3gp" ->
{[current], rest}
true ->
{[], rest}
end
end,
fn _ -> :ok end
)
end
end
defp process_file(file_path) do
mp4_path = Path.rootname(file_path) <> ".mp4"
IO.puts("""
Found .3gp file:
- #{file_path}
Converting to:
- #{mp4_path}
""")
case convert_to_mp4(file_path, mp4_path) do
:ok ->
# Copy original timestamps to the new .mp4
case preserve_timestamps(file_path, mp4_path) do
:ok ->
File.rm(file_path)
IO.puts(
"✔ Conversion & timestamp preservation successful. Removed original: #{file_path}\n"
)
{:error, reason} ->
IO.puts("⚠ Could not preserve timestamps for #{mp4_path}: #{reason}")
# Decide if you still remove the original if preserving fails
File.rm(file_path)
end
{:error, reason} ->
IO.puts("✘ Conversion failed for #{file_path}:\n#{reason}\n")
end
end
defp convert_to_mp4(input_file, output_file) do
args = [
# Overwrite output if it exists
"-y",
"-i",
input_file,
"-c:v",
"libx264",
"-preset",
"slow",
"-crf",
"23",
"-c:a",
"aac",
"-b:a",
"128k",
output_file
]
{output, exit_code} = System.cmd("/opt/homebrew/bin/ffmpeg", args, stderr_to_stdout: true)
if exit_code == 0 do
:ok
else
{:error, output}
end
end
defp preserve_timestamps(original, new_file) do
case System.cmd("touch", ["-r", original, new_file]) do
{_, 0} ->
:ok
{error_msg, code} ->
{:error, "touch exited with code #{code}: #{error_msg}"}
end
end
end
GoogleDrive3gpToMp4.run()
```