Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Twitter archive browser
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Twitter Archive Browser</title>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<style>
* {
font-family: sans-serif;
}
.tweet {
border: 1px solid grey;
border-radius: 4px;
display: block;
padding: 8px;
margin: 4px;
max-width: 600px;
}
.date {
font-size: 0.8em;
color: grey;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/javascript">
window.YTD = {
tweet: {},
account: {},
}
</script>
<script type="text/javascript" src="./tweet.js"></script>
<script type="text/javascript" src="./account.js"></script>
<script type="text/babel">
const accountName = window.YTD.account.part0[0].account.username;
const createdStr = window.YTD.account.part0[0].account.createdAt;
const createdDate = new Date(/^[0-9]+$/.test(createdStr) ? parseInt(createdStr) : createdStr);
function Media(props) {
const data = props.data;
if (data.type == 'photo') {
return <img src={data.media_url_https} />;
}
else {
return <p>Unknown media attachment.</p>;
}
}
function Tweet(props) {
const data = props.data.tweet ? props.data.tweet : props.data;
const url = "https://twitter.com/" + accountName + "/status/" + data.id_str;
var media = [];
if (data.extended_entities) {
media = data.extended_entities.media.map((media, index) => {
return <Media data={media} key={index} />;
});
}
return (
<div className="tweet">
<p>{data.full_text}</p>
<div>{media}</div>
<a target="_blank" href={url} className="date">{data.created_at}</a>
</div>
);
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
year: createdDate.getFullYear(),
month: 'Jan',
searchTerm: "",
};
}
render() {
const years = [];
const now = new Date();
for (var i = createdDate.getFullYear(); i <= now.getFullYear(); i++) {
const year = i;
const onClick = () => {
this.setState((prevState) => {
return {
...prevState,
year,
};
});
};
years.push(<button key={i} onClick={onClick}>{i}</button>);
}
const months = [
'All',
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
].map((name) => {
const onClick = () => {
this.setState((prevState) => {
return {
...prevState,
month: name,
}
})
}
return <button key={name} onClick={onClick}>{name}</button>;
});
const month = this.state.month;
const year = this.state.year;
const searchTerm = this.state.searchTerm;
const tweets = window.YTD.tweet.part0;
const filteredTweets = tweets.filter((tweet) => {
if (tweet.tweet) {
tweet = tweet.tweet
}
if (searchTerm != "") {
const haystack = tweet.full_text.toLowerCase();
const needle = searchTerm.toLowerCase();
return haystack.indexOf(needle) != -1;
}
else {
return tweet.created_at.endsWith(year.toString()) &&
(month == 'All' || tweet.created_at.indexOf(month) != -1)
}
});
const maxTweets = 1000;
var elements = [];
for (var i = 0; i < Math.min(maxTweets, filteredTweets.length); i++) {
const data = filteredTweets[i];
elements.push(<Tweet key={data.id_str} data={data} />);
}
if (maxTweets < filteredTweets.length) {
elements.push(<div key="toomany">
<p>Too many results, only showing {maxTweets}</p>
</div>)
}
if (filteredTweets.length == 0) {
elements.push(<div key="none">
<p>No tweets found!</p>
</div>)
}
var title;
if (searchTerm != "") {
title = "Search: " + searchTerm;
}
else {
title = month + " " + year;
}
title += " (" + filteredTweets.length + " results)";
const handleChange = (event) => {
const newText = event.target.value;
this.setState((prevState) => {
return {
...prevState,
searchTerm: newText,
}
});
};
return (
<div>
<nav>
{years}
</nav>
<nav>
{months}
</nav>
<input type="text" onChange={handleChange} value={searchTerm} />
<h2>{title}</h2>
<div>
{elements}
</div>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
</script>
</body>
</html>
@superwills

This comment has been minimized.

Copy link

superwills commented Oct 7, 2019

Nice. I think you are loading images from the web, instead of the downloaded media, I didn't do anything about this ;)

In my fork I reduced the size of all images to 100x100 for browsing purposes.

Also added video support

@tiffany352

This comment has been minimized.

Copy link
Owner Author

tiffany352 commented Oct 7, 2019

Nice. I think you are loading images from the web, instead of the downloaded media, I didn't do anything about this ;)

In my fork I reduced the size of all images to 100x100 for browsing purposes.

Also added video support

At the time I wrote this tool (mid 2018), there was seemingly no way of actually mapping the CDN URLs to the images in the dump. The ones in the dump appeared to be a content hash or something. At some point since then, they changed the archive format again. Now, they use a different naming scheme for media, and all of the media are stored inside of zip files nested inside of the archive zip. Using the new naming scheme, it's possible to map CDN URLs to media in the dump thankfully.

I'm currently working on a much more fully featured and updated version of this tool and hopefully it will be ready soon.

@vicentory

This comment has been minimized.

Copy link

vicentory commented Oct 10, 2019

thank you, this tool is very helpful. if you can update this tool so it can view like the official type, it would be very nice.

@tiffany352

This comment has been minimized.

Copy link
Owner Author

tiffany352 commented Oct 10, 2019

@vicentory @superwills

The new version I'm making is here: https://github.com/tiffany352/twitter-archive-browser

It's not a script you drop into your archive, it's instead a desktop application you install, but it has many more features and looks more like the actual Twitter website.

@Downs86

This comment has been minimized.

Copy link

Downs86 commented Nov 21, 2019

would you please email me one of these with the years set to 2014, onward? Downs.86@gmail.com ChrisDownsBooks.com

@EnigmaticZee

This comment has been minimized.

Copy link

EnigmaticZee commented Jan 9, 2020

@vicentory @superwills

The new version I'm making is here: https://github.com/tiffany352/twitter-archive-browser

It's not a script you drop into your archive, it's instead a desktop application you install, but it has many more features and looks more like the actual Twitter website.

Hi, there is no mac version? Any suggestions for a mac user?

@Downs86

This comment has been minimized.

Copy link

Downs86 commented Jan 13, 2020

@Downs86

This comment has been minimized.

Copy link

Downs86 commented Jan 13, 2020

@Downs86

This comment has been minimized.

Copy link

Downs86 commented Jan 14, 2020

Tiffany, I don’t know if you noticed, but this page no longer works with the most recent twitter archives tweet.js . Would you please create a new version able to be a suitable replacement for the index.html file many of us need. Thank you,
Chris

@vossviola

This comment has been minimized.

Copy link

vossviola commented Jan 17, 2020

Same problem here: This helpful solution doesn't work with an archive I dowloaded today. :(

@Downs86

This comment has been minimized.

Copy link

Downs86 commented Jan 19, 2020

Apparently the failure to load tweet data might have something to do with oAuth and the Twitter API but i haven't been into coding that much to know where to start on that.

@mmogithub

This comment has been minimized.

Copy link

mmogithub commented Jan 20, 2020

Same here. Twitter archive created today and Index.html no longer works :(
Page is blank. It seems no tweet.fuction is working.
First error is shown in "TypeError: tweet.created_at is undefined".

01
02

Blame Twitter 'cause for sure they changed something.
I hope you would be able to find a solution. Your file IS extremelly usefull.
Thanks in advance.

@Nonesuch13

This comment has been minimized.

Copy link

Nonesuch13 commented Jan 21, 2020

This was so valuable to me as well. I hope the original contributor sees how much people used and appreciated. Not sure what Twitter had to go and change. Makes no sense they would make it so hard to use their archive files.

@Downs86

This comment has been minimized.

Copy link

Downs86 commented Jan 24, 2020

@tiffany352

This comment has been minimized.

Copy link
Owner Author

tiffany352 commented Jan 25, 2020

@Downs86 @Nonesuch13 @mmogithub @vossviola

Sorry for the delay. I just put up an updated version that has the createdAt issue fixed and also has the issue where it's showing dates from 1969 as well.

The updated script should be compatible with both these new changes Twitter made as well as the archives you have from last year etc.

@tiffany352

This comment has been minimized.

Copy link
Owner Author

tiffany352 commented Jan 25, 2020

@EnigmaticZee I just published a new version of the archive browser app that includes a Mac build. I don't have a way to test it since I don't have a Mac of my own, but you could try it and see if it works. I hope that helps!

@Downs86

This comment has been minimized.

Copy link

Downs86 commented Jan 25, 2020

@Nonesuch13

This comment has been minimized.

Copy link

Nonesuch13 commented Jan 27, 2020

Thank you so much!

@mmogithub

This comment has been minimized.

Copy link

mmogithub commented Jan 28, 2020

Thank you so much.
It works like a charm.
You're the best, amazing work :)

@Downs86

This comment has been minimized.

Copy link

Downs86 commented Jan 28, 2020

@Downs86

This comment has been minimized.

Copy link

Downs86 commented Feb 16, 2020

@Downs86

This comment has been minimized.

Copy link

Downs86 commented Feb 19, 2020

@euan-gwd

This comment has been minimized.

Copy link

euan-gwd commented Feb 20, 2020

@tiffany352 Thanks for doing this, good piece of code, i was going to do it myself then found this, great job. I like your implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.