-
-
Save tiffany352/9ee7e0d4fd7e08ede9d0314df9eab672 to your computer and use it in GitHub Desktop.
<!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> |
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 @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.
@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!
Thank you so much!
Thank you so much.
It works like a charm.
You're the best, amazing work :)
@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.
Hi! Where do I insert this? And if you can give me a free executor it would be helpful, Ive been searching the whole web for one.
In my archive downloaded today there is the usual tweet.js file, but also one called a tweet-part1.js which seems to contain the next bunch of tweets.
Tiffany's index file only gives me the tweets in the tweet.js; when I change line 36 to tweet-part1.js to also get the tweets from that file I get a blank page.
What could be done about that?
I have found a workaround: copy the content of tweet-part1.js at in tweet.js at the end of the file; then the index file works fine for the complete archive. :)
@owns86 hm, this index file also only works with the tweets in the "tweet.js" file, but not with those in the "tweet-part1.js" file?
yes, with that copy&paste workaround it works.
it seems this "scattering" happens when you pass a certain amount of tweets.
twitter has changed the structure of the archive again, and so this helpful file is not working anymore. sigh
@vossviola It’s The Difference Of The Former tweet.js File and The New One Which Contains The PArt WHeir A TWēEt Is SupposEd To Bē EditAble.
So the quickest rout I saw is selecting the first example of the addition/difference, and using a Keyboard Command/Control To Select All Similar BrackEts And THeir Contents, and then simply deleting them so that the formatting of The New tweet.js File be as The Former One, YEt: Still Contain All TWēEt Data.
The Other (More PreferABLE Option) is Adapting The index.html File of The Twitter Archive Browser To CompensATE For The Addition of “Edit TWēEt” Section. (But I couldn’t EVEn Select all within similar BrackEts Using MY BeLovEd Atom Web Developer App (Attacked Devices..).
Thanks!
Thank you for pointing to the difference!
I gave it a short try to delete those sections -- which wasn't very successful. 😎
So I am hoping that somebody will be able and willing to update the index.html file. 🤞
I have found out that Excel can now import json files.
there is only a little change needed to the twitter js files.
see https://www.zdnet.com/paid-content/article/want-to-analyse-your-tweets-how-to-import-twitter-json-data-exports-into-excel/
Hello! I'm very unexperienced in this, but could someone please tell me how do i use this? It would mean a lot to me! (I mostly need it to browse through my DMs.)
I have downloaded the file, put it in the same folder where my Twitter data is (already extracted), but I'm not sure what to do next, as it does nothing. I would really appreciate the help <3
As far as I can see it this script does not work any more because of the new structure of the Twitter archive files. :(
ohh i understand...:( thank you for the reply!<3
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".
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.